Merge commit 'origin/stable'
authorJohn Darrington <john@darrington.wattle.id.au>
Tue, 7 Apr 2009 11:30:23 +0000 (19:30 +0800)
committerJohn Darrington <john@darrington.wattle.id.au>
Tue, 7 Apr 2009 11:30:23 +0000 (19:30 +0800)
Conflicts:

src/language/stats/crosstabs.q
src/language/stats/examine.q
src/language/stats/frequencies.q
src/language/stats/oneway.q
tests/command/examine-extremes.sh
tests/command/examine.sh

451 files changed:
.gitignore
AUTHORS
INSTALL
Makefile.am
NEWS
Smake
configure.ac
doc/automake.mk
doc/bugs.texi
doc/combining.texi [new file with mode: 0644]
doc/command-index.texi
doc/concept-index.texi
doc/data-io.texi
doc/data-selection.texi
doc/dev/i18n.texi [new file with mode: 0644]
doc/dev/system-file-format.texi
doc/expressions.texi
doc/files.texi
doc/flow-control.texi
doc/function-index.texi
doc/installing.texi
doc/introduction.texi
doc/invoking.texi
doc/language.texi
doc/not-implemented.texi
doc/pspp-dev.texinfo
doc/pspp.texinfo
doc/regression.texi
doc/statistics.texi
doc/transformation.texi
doc/utilities.texi
doc/variables.texi
examples/grid.sps [new file with mode: 0644]
glade/automake.mk
glade/dictview.c [new file with mode: 0644]
glade/psppire.xml
lib/automake.mk
lib/gtk-contrib/COPYING.LESSER [new file with mode: 0644]
lib/gtk-contrib/OChangeLog [new file with mode: 0644]
lib/gtk-contrib/README [new file with mode: 0644]
lib/gtk-contrib/automake.mk [new file with mode: 0644]
lib/gtk-contrib/gtk-builder-convert [new file with mode: 0755]
lib/gtk-contrib/gtkextra-sheet.h [new file with mode: 0644]
lib/gtk-contrib/gtkxpaned.c [new file with mode: 0644]
lib/gtk-contrib/gtkxpaned.h [new file with mode: 0644]
lib/gtk-contrib/psppire-sheet.c [new file with mode: 0644]
lib/gtk-contrib/psppire-sheet.h [new file with mode: 0644]
lib/gtksheet/COPYING.LESSER [deleted file]
lib/gtksheet/OChangeLog [deleted file]
lib/gtksheet/README [deleted file]
lib/gtksheet/automake.mk [deleted file]
lib/gtksheet/gsheet-column-iface.c [deleted file]
lib/gtksheet/gsheet-column-iface.h [deleted file]
lib/gtksheet/gsheet-hetero-column.c [deleted file]
lib/gtksheet/gsheet-hetero-column.h [deleted file]
lib/gtksheet/gsheet-row-iface.c [deleted file]
lib/gtksheet/gsheet-row-iface.h [deleted file]
lib/gtksheet/gsheet-uniform-column.c [deleted file]
lib/gtksheet/gsheet-uniform-column.h [deleted file]
lib/gtksheet/gsheet-uniform-row.c [deleted file]
lib/gtksheet/gsheet-uniform-row.h [deleted file]
lib/gtksheet/gsheetmodel.c [deleted file]
lib/gtksheet/gsheetmodel.h [deleted file]
lib/gtksheet/gtkextra-marshal.c [deleted file]
lib/gtksheet/gtkextra-marshal.h [deleted file]
lib/gtksheet/gtkextra-sheet.h [deleted file]
lib/gtksheet/gtkextra.c [deleted file]
lib/gtksheet/gtkextrafeatures.h [deleted file]
lib/gtksheet/gtkitementry.c [deleted file]
lib/gtksheet/gtkitementry.h [deleted file]
lib/gtksheet/gtksheet.c [deleted file]
lib/gtksheet/gtksheet.h [deleted file]
lib/linreg/automake.mk
perl-module/COPYING [new file with mode: 0644]
perl-module/Changes [new file with mode: 0644]
perl-module/Examples.pod [new file with mode: 0644]
perl-module/MANIFEST [new file with mode: 0644]
perl-module/Makefile.PL [new file with mode: 0644]
perl-module/PSPP.bs [new file with mode: 0644]
perl-module/PSPP.xs [new file with mode: 0644]
perl-module/README [new file with mode: 0644]
perl-module/automake.mk [new file with mode: 0644]
perl-module/lib/PSPP.pm [new file with mode: 0644]
perl-module/ppport.h [new file with mode: 0644]
perl-module/t/Pspp.t [new file with mode: 0644]
perl-module/typemap [new file with mode: 0644]
po/en_GB.po
src/automake.mk
src/data/attributes.c [new file with mode: 0644]
src/data/attributes.h [new file with mode: 0644]
src/data/automake.mk
src/data/case-map.c
src/data/case-map.h
src/data/case-matcher.c [new file with mode: 0644]
src/data/case-matcher.h [new file with mode: 0644]
src/data/case-ordering.c [deleted file]
src/data/case-ordering.h [deleted file]
src/data/case-tmpfile.c
src/data/case-tmpfile.h
src/data/case.c
src/data/case.h
src/data/casegrouper.c
src/data/casegrouper.h
src/data/caseinit.c
src/data/casereader-filter.c
src/data/casereader-provider.h
src/data/casereader-translator.c
src/data/casereader.c
src/data/casereader.h
src/data/casewindow.c
src/data/casewindow.h
src/data/casewriter-provider.h
src/data/casewriter-translator.c
src/data/casewriter.c
src/data/casewriter.h
src/data/category.c
src/data/data-in.h
src/data/datasheet.c
src/data/datasheet.h
src/data/dictionary.c
src/data/dictionary.h
src/data/format-guesser.h
src/data/gnumeric-reader.c
src/data/lazy-casereader.c
src/data/por-file-reader.c
src/data/por-file-reader.h
src/data/por-file-writer.c
src/data/por-file-writer.h
src/data/procedure.c
src/data/procedure.h
src/data/psql-reader.c
src/data/scratch-reader.h
src/data/scratch-writer.c
src/data/scratch-writer.h
src/data/settings.c
src/data/settings.h
src/data/sparse-cases.c
src/data/subcase.c [new file with mode: 0644]
src/data/subcase.h [new file with mode: 0644]
src/data/sys-file-reader.c
src/data/sys-file-reader.h
src/data/sys-file-writer.c
src/data/sys-file-writer.h
src/data/transformations.c
src/data/transformations.h
src/data/value.c
src/data/value.h
src/data/vardict.h
src/data/variable.c
src/data/variable.h
src/language/automake.mk
src/language/command.def
src/language/control/do-if.c
src/language/control/loop.c
src/language/data-io/automake.mk
src/language/data-io/combine-files.c [new file with mode: 0644]
src/language/data-io/data-list.c
src/language/data-io/data-parser.c
src/language/data-io/data-parser.h
src/language/data-io/get.c
src/language/data-io/inpt-pgm.c
src/language/data-io/list.q
src/language/data-io/print-space.c
src/language/data-io/print.c
src/language/data-io/save.c [new file with mode: 0644]
src/language/data-io/trim.c [new file with mode: 0644]
src/language/data-io/trim.h [new file with mode: 0644]
src/language/dictionary/apply-dictionary.c
src/language/dictionary/attributes.c [new file with mode: 0644]
src/language/dictionary/automake.mk
src/language/dictionary/sys-file-info.c
src/language/dictionary/value-labels.c
src/language/expressions/evaluate.c
src/language/expressions/helpers.c
src/language/expressions/operations.def
src/language/lexer/lexer.c
src/language/lexer/lexer.h
src/language/lexer/q2c.c
src/language/stats/.gitignore
src/language/stats/aggregate.c
src/language/stats/automake.mk
src/language/stats/autorecode.c
src/language/stats/binomial.c
src/language/stats/binomial.h
src/language/stats/chisquare.c
src/language/stats/chisquare.h
src/language/stats/crosstabs.q
src/language/stats/descriptives.c
src/language/stats/examine.q
src/language/stats/flip.c
src/language/stats/freq.c
src/language/stats/frequencies.q
src/language/stats/glm.q
src/language/stats/npar-summary.c
src/language/stats/npar.h
src/language/stats/npar.q
src/language/stats/oneway.q
src/language/stats/rank.q
src/language/stats/regression.q
src/language/stats/reliability.q [new file with mode: 0644]
src/language/stats/sort-cases.c
src/language/stats/sort-criteria.c
src/language/stats/sort-criteria.h
src/language/stats/t-test.q
src/language/stats/wilcoxon.c [new file with mode: 0644]
src/language/stats/wilcoxon.h [new file with mode: 0644]
src/language/tests/.gitignore [new file with mode: 0644]
src/language/tests/automake.mk
src/language/tests/check-model.q
src/language/tests/datasheet-check.c [new file with mode: 0644]
src/language/tests/datasheet-check.h [new file with mode: 0644]
src/language/tests/datasheet-test.c
src/language/tests/model-checker.c [new file with mode: 0644]
src/language/tests/model-checker.h [new file with mode: 0644]
src/language/utilities/include.c
src/language/xforms/compute.c
src/language/xforms/count.c
src/language/xforms/fail.c
src/language/xforms/recode.c
src/language/xforms/sample.c
src/language/xforms/select-if.c
src/libpspp/automake.mk
src/libpspp/hash-functions.c [new file with mode: 0644]
src/libpspp/hash-functions.h [new file with mode: 0644]
src/libpspp/hash.c
src/libpspp/hash.h
src/libpspp/hmap.c [new file with mode: 0644]
src/libpspp/hmap.h [new file with mode: 0644]
src/libpspp/hmapx.c [new file with mode: 0644]
src/libpspp/hmapx.h [new file with mode: 0644]
src/libpspp/i18n.c
src/libpspp/i18n.h
src/libpspp/ll.h
src/libpspp/message.c
src/libpspp/misc.h
src/libpspp/model-checker.c [deleted file]
src/libpspp/model-checker.h [deleted file]
src/libpspp/sparse-array.c
src/libpspp/str.c
src/libpspp/tower.c
src/libpspp/tower.h
src/math/automake.mk
src/math/box-whisker.c [new file with mode: 0644]
src/math/box-whisker.h [new file with mode: 0644]
src/math/coefficient.c
src/math/covariance-matrix.c
src/math/covariance-matrix.h
src/math/design-matrix.c
src/math/design-matrix.h
src/math/extrema.c [new file with mode: 0644]
src/math/extrema.h [new file with mode: 0644]
src/math/factor-stats.c [deleted file]
src/math/factor-stats.h [deleted file]
src/math/group.c
src/math/group.h
src/math/histogram.c
src/math/histogram.h
src/math/interaction.c
src/math/interaction.h
src/math/levene.c
src/math/linreg.c
src/math/linreg.h
src/math/merge.c
src/math/merge.h
src/math/np.c [new file with mode: 0644]
src/math/np.h [new file with mode: 0644]
src/math/order-stats.c [new file with mode: 0644]
src/math/order-stats.h [new file with mode: 0644]
src/math/percentiles.c
src/math/percentiles.h
src/math/sort.c
src/math/sort.h
src/math/statistic.h [new file with mode: 0644]
src/math/trimmed-mean.c [new file with mode: 0644]
src/math/trimmed-mean.h [new file with mode: 0644]
src/math/ts/automake.mk
src/math/ts/innovations.c
src/math/tukey-hinges.c [new file with mode: 0644]
src/math/tukey-hinges.h [new file with mode: 0644]
src/math/wilcoxon-sig.c [new file with mode: 0644]
src/math/wilcoxon-sig.h [new file with mode: 0644]
src/output/automake.mk
src/output/charts/automake.mk
src/output/charts/box-whisker.c
src/output/charts/box-whisker.h
src/output/charts/dummy-chart.c
src/output/charts/plot-hist.c
src/output/charts/plot-hist.h
src/output/dummy-chart.c
src/ui/automake.mk
src/ui/command-line.c [new file with mode: 0644]
src/ui/command-line.h [new file with mode: 0644]
src/ui/gui/.gitignore
src/ui/gui/about.c
src/ui/gui/automake.mk
src/ui/gui/checkbox-treeview.c
src/ui/gui/comments-dialog.c
src/ui/gui/compute-dialog.c
src/ui/gui/compute-dialog.h
src/ui/gui/crosstabs-dialog.c
src/ui/gui/crosstabs-dialog.h
src/ui/gui/crosstabs.glade
src/ui/gui/data-editor.c [deleted file]
src/ui/gui/data-editor.glade
src/ui/gui/data-editor.h [deleted file]
src/ui/gui/descriptives-dialog.c
src/ui/gui/descriptives-dialog.glade
src/ui/gui/descriptives-dialog.h
src/ui/gui/dict-display.c
src/ui/gui/dict-display.h
src/ui/gui/examine-dialog.c
src/ui/gui/examine-dialog.h
src/ui/gui/examine.glade
src/ui/gui/find-dialog.c
src/ui/gui/frequencies-dialog.c
src/ui/gui/frequencies-dialog.h
src/ui/gui/frequencies.glade
src/ui/gui/glade-register.c [deleted file]
src/ui/gui/goto-case-dialog.c
src/ui/gui/goto-case-dialog.h
src/ui/gui/helper.c
src/ui/gui/helper.h
src/ui/gui/main.c
src/ui/gui/marshaller-list [new file with mode: 0644]
src/ui/gui/message-dialog.c
src/ui/gui/message-dialog.glade
src/ui/gui/message-dialog.h
src/ui/gui/missing-val-dialog.c
src/ui/gui/missing-val-dialog.h
src/ui/gui/oneway-anova-dialog.c
src/ui/gui/oneway.glade
src/ui/gui/output-viewer.c [deleted file]
src/ui/gui/output-viewer.glade
src/ui/gui/output-viewer.h [deleted file]
src/ui/gui/pspp.desktop [new file with mode: 0644]
src/ui/gui/psppire-buttonbox.c
src/ui/gui/psppire-buttonbox.h
src/ui/gui/psppire-case-file.c [deleted file]
src/ui/gui/psppire-case-file.h [deleted file]
src/ui/gui/psppire-conf.c [new file with mode: 0644]
src/ui/gui/psppire-conf.h [new file with mode: 0644]
src/ui/gui/psppire-data-editor.c
src/ui/gui/psppire-data-editor.h
src/ui/gui/psppire-data-store.c
src/ui/gui/psppire-data-store.h
src/ui/gui/psppire-data-window.c [new file with mode: 0644]
src/ui/gui/psppire-data-window.h [new file with mode: 0644]
src/ui/gui/psppire-dialog.c
src/ui/gui/psppire-dialog.h
src/ui/gui/psppire-dict.c
src/ui/gui/psppire-dict.h
src/ui/gui/psppire-dictview.c [new file with mode: 0644]
src/ui/gui/psppire-dictview.h [new file with mode: 0644]
src/ui/gui/psppire-output-window.c [new file with mode: 0644]
src/ui/gui/psppire-output-window.h [new file with mode: 0644]
src/ui/gui/psppire-selector.c
src/ui/gui/psppire-selector.h
src/ui/gui/psppire-syntax-window.c [new file with mode: 0644]
src/ui/gui/psppire-syntax-window.h [new file with mode: 0644]
src/ui/gui/psppire-var-sheet.c
src/ui/gui/psppire-var-sheet.h
src/ui/gui/psppire-var-store.c
src/ui/gui/psppire-var-store.h
src/ui/gui/psppire-window-register.c [new file with mode: 0644]
src/ui/gui/psppire-window-register.h [new file with mode: 0644]
src/ui/gui/psppire-window.c [new file with mode: 0644]
src/ui/gui/psppire-window.h [new file with mode: 0644]
src/ui/gui/psppire.c
src/ui/gui/psppire.glade
src/ui/gui/psppire.h
src/ui/gui/rank-dialog.c
src/ui/gui/rank-dialog.h
src/ui/gui/rank.glade
src/ui/gui/recode-dialog.c
src/ui/gui/recode.glade
src/ui/gui/regression-dialog.c
src/ui/gui/regression-dialog.h
src/ui/gui/regression.glade
src/ui/gui/reliability-dialog.c [new file with mode: 0644]
src/ui/gui/reliability-dialog.h [new file with mode: 0644]
src/ui/gui/reliability.glade [new file with mode: 0644]
src/ui/gui/select-cases-dialog.c
src/ui/gui/select-cases-dialog.h
src/ui/gui/sheet/automake.mk [new file with mode: 0644]
src/ui/gui/sheet/psppire-axis.c [new file with mode: 0644]
src/ui/gui/sheet/psppire-axis.h [new file with mode: 0644]
src/ui/gui/sheet/psppire-sheetmodel.c [new file with mode: 0644]
src/ui/gui/sheet/psppire-sheetmodel.h [new file with mode: 0644]
src/ui/gui/sort-cases-dialog.c
src/ui/gui/sort-cases-dialog.h
src/ui/gui/split-file-dialog.c
src/ui/gui/syntax-editor-source.c
src/ui/gui/syntax-editor-source.h
src/ui/gui/syntax-editor.c [deleted file]
src/ui/gui/syntax-editor.glade
src/ui/gui/syntax-editor.h [deleted file]
src/ui/gui/t-test-independent-samples-dialog.c
src/ui/gui/t-test-one-sample.c
src/ui/gui/t-test-options.c
src/ui/gui/t-test-options.h
src/ui/gui/t-test-paired-samples.c
src/ui/gui/t-test.glade
src/ui/gui/text-data-import-dialog.c
src/ui/gui/text-data-import.glade
src/ui/gui/transpose-dialog.c
src/ui/gui/val-labs-dialog.c
src/ui/gui/val-labs-dialog.h
src/ui/gui/var-display.c
src/ui/gui/var-sheet-dialogs.glade [new file with mode: 0644]
src/ui/gui/var-type-dialog.c
src/ui/gui/var-type-dialog.h
src/ui/gui/variable-info-dialog.c
src/ui/gui/variable-info-dialog.glade [new file with mode: 0644]
src/ui/gui/variable-info-dialog.h
src/ui/gui/weight-cases-dialog.c
src/ui/gui/widgets.c [new file with mode: 0644]
src/ui/gui/widgets.h [new file with mode: 0644]
src/ui/gui/window-manager.c [deleted file]
src/ui/gui/window-manager.h [deleted file]
src/ui/source-init-opts.c [new file with mode: 0644]
src/ui/source-init-opts.h [new file with mode: 0644]
src/ui/syntax-gen.h
src/ui/terminal/automake.mk
src/ui/terminal/command-line.c [deleted file]
src/ui/terminal/command-line.h [deleted file]
src/ui/terminal/main.c
src/ui/terminal/msg-ui.c
src/ui/terminal/terminal-opts.c [new file with mode: 0644]
src/ui/terminal/terminal-opts.h [new file with mode: 0644]
tests/automake.mk
tests/bugs/examine-crash.sh [new file with mode: 0755]
tests/bugs/examine-crash2.sh [new file with mode: 0755]
tests/bugs/examine-missing2.sh
tests/command/add-files.sh [new file with mode: 0755]
tests/command/aggregate.sh
tests/command/attributes.sh [new file with mode: 0755]
tests/command/examine-extremes.sh
tests/command/examine.sh
tests/command/insert.sh
tests/command/line-ends.sh [new file with mode: 0755]
tests/command/match-files.sh
tests/command/npar-binomial.sh
tests/command/npar-wilcoxon.sh [new file with mode: 0755]
tests/command/reliability.sh [new file with mode: 0755]
tests/command/update.sh [new file with mode: 0755]
tests/dissect-sysfile.c
tests/expressions/valuelabel.sh
tests/libpspp/hmap-test.c [new file with mode: 0644]
tests/libpspp/hmapx-test.c [new file with mode: 0644]
tests/libpspp/tower-test.c
tests/xforms/recode.sh

index c29aff49c289ab840d09237d12b2b4002885adf8..b50c6a377a2ab182f2ebaddd06e8abfc12ac5ca3 100644 (file)
@@ -32,3 +32,11 @@ reloc-ldflags
 stamp-h1
 texinfo.tex
 gitlog-to-changelog
+*~
+*.o
+*.lo
+*.a
+*.dirstamp
+*.deps
+*.la
+*.libs
diff --git a/AUTHORS b/AUTHORS
index 12b25ab7179ea4b5c75b3682cf48fb35bfc45900..af7e9075f8da674b0013c55ec974b998ff9db0e8 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,6 +14,7 @@ to other modules.
 including lib/gslextras and the linear regression features. Jason 
 is also an important contributor to GSL, which is used by PSPP. 
 
+
 We also thank past contributors:
 
 * John Williams wrote an initial draft of the T-TEST procedure.
@@ -21,3 +22,8 @@ We also thank past contributors:
 * Michael Kiefte contributed bug fixes and other enhancements.
 
 * Patrick Kobly contributed bug fixes and other enhancements.
+
+* Rob van Son wrote the original version of the routine for
+  calculation of the significance of the Wilcoxon matched pairs signed
+  rank statistic used by the NPAR TEST command.
+
diff --git a/INSTALL b/INSTALL
index ea43342eef7223bce3b2fcb76d5ba817166bf2fd..d3a469cf6ee124c7c583abc233e10abfa3081035 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -50,9 +50,6 @@ use the GUI, you must run `configure' with --without-gui.
 
     * GTK+ (http://www.gtk.org/), version 2.12.0 or later.
 
-    * libglade (http://www.jamesh.id.au/software/libglade/), version
-      2.6 or later.
-
 Installing the following packages will allow your PSPP binary to read
 Gnumeric files.
 
@@ -112,6 +109,10 @@ release.
      available.  The most common of these are listed under "Optional
      Features", below.
 
+     It is best to build and install PSPP in directories whose names do
+     not contain unusual characters such as spaces or single-quotes, due
+     to limitations of the tools involved in the build process.
+
      If you installed some of the libraries that PSPP uses in a
      non-standard location (on many systems, anywhere other than
      /usr), you may need to provide some special flags to `configure'
@@ -148,8 +149,9 @@ release.
 
   4. Type `make install' to install the programs and any data files
      and documentation.  Ordinarily you will need root permissions to
-     do this; if you cannot get root permissions, see "Installation
-     Names", below.
+     do this.  The "su" and "sudo" commands are common ways to obtain
+     root permissions.  If you cannot get root permissions, see
+     "Installation Names", below.
 
   5. You can remove the program binaries and object files from the
      source code directory by typing `make clean'.  To also remove the
@@ -232,7 +234,12 @@ Optional Features
    of libraries are detected.   Use of this option is not recommended.
    If you use it, some features may be missing and the build may fail
    with obscure error messages.
-   
+
+`--enable-relocatable'
+   This option is useful for building a package which can be installed
+   into an arbitrary directory and freely copied to any other directory.
+   If you use this option, you will probably want to install the pspp
+   with a command similar to "make install DESTDIR=<distination>".
 
 Defining Variables
 ==================
index ab32f9967861de2e0b5bc6c7836f185f9dc7f0ce..f606cbf1123b5d8c8d8f468fd6fadb90dc1c2512 100644 (file)
@@ -32,13 +32,15 @@ EXTRA_DIST = OChangeLog ONEWS config.rpath pspp-mode.el
 CLEANFILES = 
 ACLOCAL_AMFLAGS = -I m4 -I gl/m4
 noinst_LIBRARIES=
+noinst_LTLIBRARIES=
 noinst_PROGRAMS=
 check_PROGRAMS=
 bin_PROGRAMS=
 DIST_HOOKS =
+INSTALL_DATA_HOOKS = 
+UNINSTALL_DATA_HOOKS =
 PHONY =
 
-DIST_HOOKS += generate-changelog
 generate-changelog:
        if test -d $(top_srcdir)/.git; then                     \
          $(top_srcdir)/gitlog-to-changelog --since=2008-07-27  \
@@ -47,6 +49,9 @@ generate-changelog:
          mv $(distdir)/cl-t $(distdir)/ChangeLog;              \
        fi
 
+DIST_HOOKS += generate-changelog
+
+
 include $(top_srcdir)/lib/automake.mk
 include $(top_srcdir)/doc/automake.mk
 include $(top_srcdir)/config/automake.mk
@@ -59,6 +64,19 @@ if WITH_GUI_TOOLS
 include $(top_srcdir)/glade/automake.mk
 endif
 
-PHONY += $(DIST_HOOKS)
-dist-hook: $(DIST_HOOKS)
+if WITH_PERL_MODULE
+include $(top_srcdir)/perl-module/automake.mk
+endif
+
+PHONY += $(DIST_HOOKS) $(INSTALL_DATA_HOOKS) $(UNINSTALL_DATA_HOOKS)
+
 .PHONY: $(PHONY)
+
+dist-hook: $(DIST_HOOKS)
+
+install-data-hook: $(INSTALL_DATA_HOOKS)
+
+uninstall-hook: $(UNINSTALL_DATA_HOOKS)
+
+
+
diff --git a/NEWS b/NEWS
index bff9a7872848263511ca2ca50aad01a735680368..a83f83102ef3c29c5d04f1e9651bbcbf9fca9a66 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,27 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2008-10-09 21:32:07 blp>
-Copyright (C) 1996-9, 2000, 2008 Free Software Foundation, Inc.
+Time-stamp: <2009-02-05 20:31:51 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.1 to 0.7.2:
+
+ * Updated Perl module interface.
+
+Changes from 0.7.0 to 0.7.1:
+
+ *  Added a perl module to facilitate reading and writing of pspp system 
+    files from perl programs.
+
+Changes from 0.6.1 to 0.7.0:
+
+  * Custom variable and data file attributes are now supported.
+    Commands VARIABLE ATTRIBUTE and DATAFILE ATTRIBUTE have been added
+    for setting and clear attributes.  Support for attributes has also
+    been added to commands that read and write system files, such as
+    SAVE and GET, as well as to the DISPLAY command.
+
 Changes from 0.6.0 to 0.6.1:
 
   * Statistical bug fixes:
diff --git a/Smake b/Smake
index 5a76b3c1635f657c1440b7827b303b70ecf2fc5f..4c8d83bc3bdae38f08560b305bb7a754a1220f42 100644 (file)
--- a/Smake
+++ b/Smake
@@ -5,6 +5,7 @@ GNULIB = ../gnulib
 GNULIB_TOOL = $(GNULIB)/gnulib-tool
 
 GNULIB_MODULES = \
+       argp \
        assert \
        byteswap \
        c-ctype \
@@ -112,6 +113,7 @@ gettextize:
 po/POTFILES.in:
        for f in `find src \( -name \*.[qc] -o -name \*.glade \) ! -name .\* -print` ; do \
                if test $$f = src/libpspp/version.c; then continue; fi;   \
+               if test $$f = src/ui/gui/psppire-marshal.c; then continue; fi; \
                if test -e `dirname $$f`/`basename $$f .c`.q ; then continue; fi; \
                echo $$f ; \
        done | sort | uniq > $@.tmp
index 3284ec818d32a5ebbfadfd5d6b5e8d39f5d04e93..7e7a4c6077c23585c036847fc7535dd728a807db 100644 (file)
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script.
 
 dnl Initialize.
 AC_PREREQ(2.60)
-AC_INIT([pspp],[0.6.1],[bug-gnu-pspp@gnu.org])
+AC_INIT([pspp],[0.7.2],[bug-gnu-pspp@gnu.org])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE
 
@@ -15,6 +15,7 @@ AC_LIBTOOL_WIN32_DLL
 AC_LIBTOOL_DLOPEN
 AC_PROG_LIBTOOL
 PKG_PROG_PKG_CONFIG
+m4_pattern_forbid([PKG_CHECK_MODULES])
 
 AC_ARG_ENABLE(
   anachronistic-dependencies, 
@@ -49,15 +50,10 @@ AC_ARG_WITH(
   [AS_HELP_STRING([--without-gui], [don't build the PSPPIRE gui])])
 
 required_gtk_version=2.12
-if test x"$enable_anachronistic_dependencies" = x"yes"  ; then
-  required_gtk_version=2.8.20
-fi
 
 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)])])
-  PKG_CHECK_MODULES(GLADE, libglade-2.0 >= 2.6.0,,
-    [PSPP_REQUIRED_PREREQ([libglade 2.0 v2.6.0 or later (or use --without-gui)])])
 fi
 AM_CONDITIONAL(WITHGUI, test x"$with_gui" != x"no")
 
@@ -125,12 +121,14 @@ AM_CONDITIONAL(GNM_SUPPORT, test x"$gnm_support" = x"yes")
 
 AC_ARG_WITH(
   gui_tools,
-  [AS_HELP_STRING([--with-gui-tools], [build the gui developer tools])])
+  [AS_HELP_STRING([--with-gui-tools], [build the gui developer tools.  For DEVELOPERS only! There is no reason why users will need this flag.])])
 if test x"$with_gui_tools" = x"yes" ; then 
        PKG_CHECK_MODULES(GLADE_UI, gladeui-1.0)
 fi
 AM_CONDITIONAL(WITH_GUI_TOOLS, test x"$with_gui_tools" = x"yes")
 
+AM_CONDITIONAL(WITH_PERL_MODULE, test x"$cross_compiling" != x"yes")
+
 AC_SEARCH_LIBS([cblas_dsdot], [gslcblas],,[PSPP_REQUIRED_PREREQ([libgslcblas])])
 AC_SEARCH_LIBS([gsl_cdf_binomial_P], [gsl],,[PSPP_REQUIRED_PREREQ([libgsl (version 1.8 or later)])])
 PSPP_GSL_NEEDS_FGNU89_INLINE
index 3445379a1ca756e00e62751623ab4759cdf12dc0..67c2d83c8fe91d7e4a21ecd6b510fb73c414763c 100644 (file)
@@ -11,6 +11,7 @@ doc_pspp_TEXINFOS = doc/version.texi \
        doc/data-selection.texi \
        doc/expressions.texi \
        doc/files.texi \
+       doc/combining.texi \
        doc/flow-control.texi \
        doc/function-index.texi \
        doc/installing.texi \
@@ -32,6 +33,7 @@ doc_pspp_dev_TEXINFOS = doc/version-dev.texi \
        doc/dev/concepts.texi \
        doc/dev/syntax.texi \
        doc/dev/data.texi \
+       doc/dev/i18n.texi \
        doc/dev/output.texi \
        doc/dev/system-file-format.texi \
        doc/dev/portable-file-format.texi \
@@ -44,22 +46,14 @@ doc/ni.texi: $(top_srcdir)/src/language/command.def doc/get-commands.pl
        @$(MKDIR_P)  doc
        @PERL@ $(top_srcdir)/doc/get-commands.pl $(top_srcdir)/src/language/command.def > $@
 
-# It seems that recent versions of yelp, upon which the gui relies to display the reference
-# manual, are broken.  It only works on compressed info files.  So we must compress them.
-if WITHGUI
-YELP_CHECK = yelp-check
-else
-YELP_CHECK =
-endif
-install-data-hook:: $(YELP_CHECK)
-       for ifile in $(DESTDIR)$(infodir)/pspp.info-[0-9] \
-               $(DESTDIR)$(infodir)/pspp.info  ; do \
-         gzip -f $$ifile ; \
-       done
+doc/pspp.xml: doc/pspp.texinfo $(doc_pspp_TEXINFOS)
+       @$(MKDIR_P)  doc
+       $(MAKEINFO) --docbook -I $(top_srcdir) $< -o $@
+       $(SED) -i -e 's/Time-&-Date/Time-\&amp;-Date/g' $@
+
+docbookdir = $(docdir)
+docbook_DATA = doc/pspp.xml
 
-uninstall-hook::
-       rm -f $(DESTDIR)$(infodir)/pspp.info-[0-9].gz
-       rm -f $(DESTDIR)$(infodir)/pspp.info.gz
 
 EXTRA_DIST += doc/OChangeLog
-CLEANFILES += pspp-dev.dvi
+CLEANFILES += pspp-dev.dvi $(docbook_DATA)
index 86208d92102bf74398e43b174b628f468e23913b..e597a82c3c01e342a7e875bebde82e13d6fb5876 100644 (file)
@@ -8,15 +8,8 @@ visit PSPP's project webpage at
 @uref{https://savannah.gnu.org/projects/pspp}.  You can also submit
 your own bug report there: click on ``Bugs,'' then on ``Submit a
 Bug,'' and fill out the form.  Alternatively, PSPP bug reports may be
-sent by email to
-@ifinfo
-<bug-gnu-pspp@@gnu.org>.
-@end ifinfo
-@iftex
-@code{<bug-gnu-pspp@@gnu.org>}.
-@end iftex
+sent by email to @email{bug-gnu-pspp@@gnu.org}.
 
 For known bugs in individual language features, see the documentation
 for that feature.
 
-@setfilename ignored
diff --git a/doc/combining.texi b/doc/combining.texi
new file mode 100644 (file)
index 0000000..a77c3b0
--- /dev/null
@@ -0,0 +1,336 @@
+@node Combining Data Files
+@chapter Combining Data Files
+
+This chapter describes commands that allow data from system files,
+portable file, scratch files, and the active file to be combined to
+form a new active file.  These commands can combine data files in the
+following ways:
+
+@itemize
+@item
+@cmd{ADD FILES} interleaves or appends the cases from each input file.
+It is used with input files that have variables in common, but
+distinct sets of cases.
+
+@item
+@cmd{MATCH FILES} adds the data together in cases that match across
+multiple input files.  It is used with input files that have cases in
+common, but different information about each case.
+
+@item
+@cmd{UPDATE} updates a master data file from data in a set of
+transaction files.  Each case in a transaction data file modifies a
+matching case in the primary data file, or it adds a new case if no
+matching case can be found.
+@end itemize
+
+These commands share the majority of their syntax, which is described
+in the following section, followed by one section for each command
+that describes its specific syntax and semantics.
+
+@menu
+* Combining Files Common Syntax::
+* ADD FILES::                   Interleave cases from multiple files.
+* MATCH FILES::                 Merge cases from multiple files.
+* UPDATE::                      Update cases using transactional data.
+@end menu
+
+@node Combining Files Common Syntax
+@section Common Syntax
+
+@display
+Per input file:
+        /FILE=@{*,'file-name'@}
+        [/RENAME=(src_names=target_names)@dots{}]
+        [/IN=var_name]
+        [/SORT]
+
+Once per command:
+        /BY var_list[(@{D|A@})] [var_list[(@{D|A@}]]@dots{}
+        [/DROP=var_list]
+        [/KEEP=var_list]
+        [/FIRST=var_name]
+        [/LAST=var_name]
+        [/MAP]
+@end display
+
+This section describes the syntactical features in common among the
+@cmd{ADD FILES}, @cmd{MATCH FILES}, and @cmd{UPDATE} commands.  The
+following sections describe details specific to each command.
+
+Each of these commands reads two or more input files and combines
+them.  The command's output becomes the new active file.  The input
+files are not changed on disk.
+
+The syntax of each command begins with a specification of the files to
+be read as input.  For each input file, specify FILE with a system,
+portable, or scratch file's name as a string or a file handle
+(@pxref{File Handles}), or specify an asterisk (@samp{*}) to use the
+active file as input.  Use of portable or scratch files on FILE is a
+PSPP extension.
+
+At least two FILE subcommands must be specified.  If the active file
+is used as an input source, then @cmd{TEMPORARY} must not be in
+effect.
+
+Each FILE subcommand may be followed by any number of RENAME
+subcommands that specify a parenthesized group or groups of variable
+names as they appear in the input file, followed by those variables'
+new names, separated by an equals sign (@samp{=}),
+e.g. @samp{/RENAME=(OLD1=NEW1)(OLD2=NEW2)}.  To rename a single
+variable, the parentheses may be omitted: @samp{/RENAME=OLD=NEW}.
+Within a parenthesized group, variables are renamed simultaneously, so
+that @samp{/RENAME=(A B=B A)} exchanges the names of variables A and
+B.  Otherwise, renaming occurs in left-to-right order.
+
+Each FILE subcommand may optionally be followed by a single IN
+subcommand, which creates a numeric variable with the specified name
+and format F1.0.  The IN variable takes value 1 in an output case if
+the given input file contributed to that output case, and 0 otherwise.
+The DROP, KEEP, and RENAME subcommands have no effect on IN variables.
+
+If BY is used (see below), the SORT keyword must be specified after a
+FILE if that input file is not already sorted on the BY variables.
+When SORT is specified, PSPP sorts the input file's data on the BY
+variables before it applies it to the command.  When SORT is used, BY
+is required.  SORT is a PSPP extension.
+
+PSPP merges the dictionaries of all of the input files to form the new
+active file dictionary, like so:
+
+@itemize @bullet
+@item
+The new active file's variables are the union of all the input files'
+variables, matched based on their name.  When a single input file
+contains a variable with a given name, the output file will contain
+exactly that variable.  When more than one input file contains a
+variable with a given name, those variables must all have the same
+type (numeric or string) and, for string variables, the same width.
+Variables are matched after renaming with the RENAME subcommand.
+Thus, RENAME can be used to resolve conflicts.
+
+@item
+The variable label for each output variable is taken from the first
+specified input file that has a variable label for that variable, and
+similarly for value labels and missing values.
+
+@item
+The new active file's file label (@pxref{FILE LABEL}) is that of the
+first specified FILE that has a file label.
+
+@item
+The new active file's documents (@pxref{DOCUMENT}) are the
+concatenation of all the input files' documents, in the order in which
+the FILE subcommands are specified.
+
+@item
+If all of the input files are weighted on the same variable, then the
+new active file is weighted on that variable.  Otherwise, the new
+active file is not weighted.
+@end itemize
+
+The remaining subcommands apply to the output file as a whole, rather
+than to individual input files.  They must be specified at the end of
+the command specification, following all of the FILE and related
+subcommands.  The most important of these subcommands is BY, which
+specifies a set of one or more variables that may be used to find
+corresponding cases in each of the input files.  The variables
+specified on BY must be present in all of the input files.
+Furthermore, if any of the input files are not sorted on the BY
+variables, then SORT must be specified for those input files.
+
+The variables listed on BY may include (A) or (D) annotations to
+specify ascending or descending sort order.  @xref{SORT CASES}, for
+more details on this notation.  Adding (A) or (D) to the BY subcommand
+specification is a PSPP extension.
+
+The DROP subcommand can be used to specify a list of variables to
+exclude from the output.  By contrast, the KEEP subcommand can be used
+to specify variables to include in the output; all variables not
+listed are dropped.  DROP and KEEP are executed in left-to-right order
+and may be repeated any number of times.  DROP and KEEP do not affect
+variables created by the IN, FIRST, and LAST subcommands, which are
+always included in the new active file, but they can be used to drop
+BY variables.
+
+The FIRST and LAST subcommands are optional.  They may only be
+specified on @cmd{MATCH FILES} and @cmd{ADD FILES}, and only when BY
+is used.  FIRST and LIST each adds a numeric variable to the new
+active file, with the name given as the subcommand's argument and F1.0
+print and write formats.  The value of the FIRST variable is 1 in the
+first output case with a given set of values for the BY variables, and
+0 in other cases.  Similarly, the LAST variable is 1 in the last case
+with a given of BY values, and 0 in other cases.
+
+When any of these commands creates an output case, variables that are
+only in files that are not present for the current case are set to the
+system-missing value for numeric variables or spaces for string
+variables.
+
+@node ADD FILES
+@section ADD FILES
+@vindex ADD FILES
+
+@display
+ADD FILES
+
+Per input file:
+        /FILE=@{*,'file-name'@}
+        [/RENAME=(src_names=target_names)@dots{}]
+        [/IN=var_name]
+        [/SORT]
+
+Once per command:
+        [/BY var_list[(@{D|A@})] [var_list[(@{D|A@})]@dots{}]]
+        [/DROP=var_list]
+        [/KEEP=var_list]
+        [/FIRST=var_name]
+        [/LAST=var_name]
+        [/MAP]
+@end display
+
+@cmd{ADD FILES} adds cases from multiple input files.  The output,
+which replaces the active file, consists all of the cases in all of
+the input files.
+
+ADD FILES shares the bulk of its syntax with other PSPP commands for
+combining multiple data files.  @xref{Combining Files Common Syntax},
+above, for an explanation of this common syntax.
+
+When BY is not used, the output of ADD FILES consists of all the cases
+from the first input file specified, followed by all the cases from
+the second file specified, and so on.  When BY is used, the output is
+additionally sorted on the BY variables.
+
+When ADD FILES creates an output case, variables that are not part of
+the input file from which the case was drawn are set to the
+system-missing value for numeric variables or spaces for string
+variables.
+
+@node MATCH FILES
+@section MATCH FILES
+@vindex MATCH FILES
+
+@display
+MATCH FILES
+
+Per input file:
+        /@{FILE,TABLE@}=@{*,'file-name'@}
+        [/RENAME=(src_names=target_names)@dots{}]
+        [/IN=var_name]
+        [/SORT]
+
+Once per command:
+        /BY var_list[(@{D|A@}] [var_list[(@{D|A@})]@dots{}]
+        [/DROP=var_list]
+        [/KEEP=var_list]
+        [/FIRST=var_name]
+        [/LAST=var_name]
+        [/MAP]
+@end display
+
+@cmd{MATCH FILES} merges sets of corresponding cases in multiple
+input files into single cases in the output, combining their data.
+
+MATCH FILES shares the bulk of its syntax with other PSPP commands for
+combining multiple data files.  @xref{Combining Files Common Syntax},
+above, for an explanation of this common syntax.
+
+How MATCH FILES matches up cases from the input files depends on
+whether BY is specified:
+
+@itemize @bullet
+@item
+If BY is not used, MATCH FILES combines the first case from each input
+file to produce the first output case, then the second case from each
+input file for the second output case, and so on.  If some input files
+have fewer cases than others, then the shorter files do not contribute
+to cases output after their input has been exhausted.
+
+@item
+If BY is used, MATCH FILES combines cases from each input file that
+have identical values for the BY variables.
+
+When BY is used, TABLE subcommands may be used to introduce @dfn{table
+lookup file}.  TABLE has same syntax as FILE, and the RENAME, IN, and
+SORT subcommands may follow a TABLE in the same way as a FILE.
+Regardless of the number of TABLEs, at least one FILE must specified.
+Table lookup files are treated in the same way as other input files
+for most purposes and, in particular, table lookup files must be
+sorted on the BY variables or the SORT subcommand must be specified
+for that TABLE.
+
+Cases in table lookup files are not consumed after they have been used
+once.  This means that data in table lookup files can correspond to
+any number of cases in FILE input files.  Table lookup files are
+analogous to lookup tables in traditional relational database systems.
+
+If a table lookup file contains more than one case with a given set of
+BY variables, only the first case is used.
+@end itemize
+
+When MATCH FILES creates an output case, variables that are only in
+files that are not present for the current case are set to the
+system-missing value for numeric variables or spaces for string
+variables.
+
+@node UPDATE
+@section UPDATE
+@vindex UPDATE
+
+@display
+UPDATE
+
+Per input file:
+        /FILE=@{*,'file-name'@}
+        [/RENAME=(src_names=target_names)@dots{}]
+        [/IN=var_name]
+        [/SORT]
+
+Once per command:
+        /BY var_list[(@{D|A@})] [var_list[(@{D|A@})]]@dots{}
+        [/DROP=var_list]
+        [/KEEP=var_list]
+        [/MAP]
+@end display
+
+@cmd{UPDATE} updates a @dfn{master file} by applying modifications
+from one or more @dfn{transaction files}.  
+
+UPDATE shares the bulk of its syntax with other PSPP commands for
+combining multiple data files.  @xref{Combining Files Common Syntax},
+above, for an explanation of this common syntax.
+
+At least two FILE subcommands must be specified.  The first FILE
+subcommand names the master file, and the rest name transaction files.
+Every input file must either be sorted on the variables named on the
+BY subcommand, or the SORT subcommand must be used just after the FILE
+subcommand for that input file.
+
+UPDATE uses the variables specified on the BY subcommand, which is
+required, to attempt to match each case in a transaction file with a
+case in the master file:
+
+@itemize @bullet
+@item
+When a match is found, then the values of the variables present in the
+transaction file replace those variable's values in the new active
+file.  If there are matching cases in more than more transaction file,
+PSPP applies the replacements from the first transaction file, then
+from the second transaction file, and so on.  Similarly, if a single
+transaction file has cases with duplicate BY values, then those are
+applied in order to the master file.
+
+When a variable in a transaction file has a missing value or a string
+variable's value is all blanks, that value is never used to update the
+master file.
+
+@item
+If a case in the master file has no matching case in any transaction
+file, then it is copied unchanged to the output.
+
+@item
+If a case in a transaction file has no matching case in the master
+file, then it causes a new case to be added to the output, initialized
+from the values in the transaction file.
+@end itemize
index c53b1db6e86af0c1ef9f593b1d37480105a77ef7..d26f9074704f2ac3024871f07403aae4e80e069f 100644 (file)
@@ -1,4 +1,3 @@
 @node Command Index
 @chapter Command Index
 @printindex vr
-@setfilename ignored
index 742488e4d7ab543389664df9a8ff72da3097c537..29d8a624d2cbae8b329e57aabd438eb081b45473 100644 (file)
@@ -1,4 +1,3 @@
 @node Concept Index
 @chapter Concept Index
 @printindex cp
-@setfilename ignored
index b6a3a6d2a4a5754f7962ca8ceb21bbd45cbc7a0f..0b9583b0200779d9b4c8eda507158f3fd2499adb 100644 (file)
@@ -25,6 +25,7 @@ actually be read until a procedure is executed.
 @menu
 * BEGIN DATA::                  Embed data within a syntax file.
 * CLOSE FILE HANDLE::           Close a file handle.
+* DATAFILE ATTRIBUTE::          Set custom attributes on data files.
 * DATA LIST::                   Fundamental data reading command.
 * END CASE::                    Output the current case.
 * END FILE::                    Terminate the current input program.
@@ -89,6 +90,52 @@ DATA} and @cmd{END DATA}, cannot be closed.  Attempts to close it with
 
 @cmd{CLOSE FILE HANDLE} is a PSPP extension.
 
+@node DATAFILE ATTRIBUTE
+@section DATAFILE ATTRIBUTE
+@vindex DATAFILE ATTRIBUTE
+
+@display
+DATAFILE ATTRIBUTE
+         ATTRIBUTE=name('value') [name('value')]@dots{}
+         ATTRIBUTE=name@b{[}index@b{]}('value') [name@b{[}index@b{]}('value')]@dots{}
+         DELETE=name [name]@dots{}
+         DELETE=name@b{[}index@b{]} [name@b{[}index@b{]}]@dots{}
+@end display
+
+@cmd{DATAFILE ATTRIBUTE} adds, modifies, or removes user-defined
+attributes associated with the active file.  Custom data file
+attributes are not interpreted by PSPP, but they are saved as part of
+system files and may be used by other software that reads them.
+
+Use the ATTRIBUTE subcommand to add or modify a custom data file
+attribute.  Specify the name of the attribute as an identifier
+(@pxref{Tokens}), followed by the desired value, in parentheses, as a
+quoted string.  Attribute names that begin with @code{$} are reserved
+for PSPP's internal use, and attribute names that begin with @code{@@}
+or @code{$@@} are not displayed by most PSPP commands that display
+other attributes.  Other attribute names are not treated specially.
+
+Attributes may also be organized into arrays.  To assign to an array
+element, add an integer array index enclosed in square brackets
+(@code{[} and @code{]}) between the attribute name and value.  Array
+indexes start at 1, not 0.  An attribute array that has a single
+element (number 1) is not distinguished from a non-array attribute.
+
+Use the DELETE subcommand to delete an attribute.  Specify an
+attribute name by itself to delete an entire attribute, including all
+array elements for attribute arrays.  Specify an attribute name
+followed by an array index in square brackets to delete a single
+element of an attribute array.  In the latter case, all the array
+elements numbered higher than the deleted element are shifted down,
+filling the vacated position.
+
+To associate custom attributes with particular variables, instead of
+with the entire active file, use @cmd{VARIABLE ATTRIBUTE} (@pxref{VARIABLE ATTRIBUTE}) instead.
+
+@cmd{DATAFILE ATTRIBUTE} takes effect immediately.  It is not affected
+by conditional and looping structures such as @cmd{DO IF} or
+@cmd{LOOP}.
+
 @node DATA LIST
 @section DATA LIST
 @vindex DATA LIST
@@ -1095,4 +1142,3 @@ specified output format, whereas @cmd{WRITE} outputs the
 system-missing value as a field filled with spaces.  Binary formats
 are an exception.
 @end itemize
-@setfilename ignored
index e7a1285397fb5302b51c4d17b6e1eff95cae3397..04269f32413733af6d626c3320b3142755264912 100644 (file)
@@ -264,4 +264,3 @@ the next procedure (@pxref{TEMPORARY}).
 
 @cmd{WEIGHT} does not cause cases in the active file to be replicated in
 memory.
-@setfilename ignored
diff --git a/doc/dev/i18n.texi b/doc/dev/i18n.texi
new file mode 100644 (file)
index 0000000..836ff81
--- /dev/null
@@ -0,0 +1,134 @@
+@node Internationalisation
+@chapter Internationalisation
+
+Internationalisation in pspp is complicated.
+The most annoying aspect is that of character-encoding.
+Currently, pspp does not fully deal with the issues.
+This chapter attempts to describe the problems and current ways 
+in which they are addressed.
+
+
+@section The working locales
+Pspp has three ``working'' locales:
+
+@itemize
+@item The local of the user interface.
+@item The local of the output.
+@item The local of the data.
+@end itemize
+
+Each of these locales may, at different times take 
+separate (or identical) values.
+So for example, a French statistician can use pspp to prepare a report 
+in the English language, using 
+a datafile which has been created by a Japanese researcher hence 
+uses a Japanese character set.
+
+It's rarely, if ever, necessary to interrogate the system to find out
+the values of the 3 locales.
+However it's important to be aware of the source (destination) locale
+when reading (writing) string data.
+When transfering data between a source and a destination, the appropriate
+recoding must be performed.
+
+
+@subsection The user interface locale
+This is the locale which is visible to the person using pspp.
+Error messages and confidence indications  are written in this locale.
+For example ``Cannot open file'' will be written in the user interface locale.
+
+This locale is set from the environment of the user who starts pspp@{ire@} or
+from the system locale if not set.
+
+@subsection The output locale
+This locale is the one that should be visible to the person reading a 
+report generated by pspp.  Non-data related strings (Eg: ``Page number'',
+``Standard Deviation'' etc.) will appear in this locale.
+
+@subsection The data locale
+This locale is the one associated with the data being analysed with pspp.
+The only important aspect of this locale is the character encoding.
+@footnote {It might also be desirable for the LC_COLLATE category to be used for the purposes of sorting data.}
+Any string data stored in a @union{value} will be encoded in the character set
+of the data locale.
+
+The data locale defaults to the locale of the user who starts pspp@{ire@}.
+Spss has a @cmd{SET LOCALE} command (not currently supported in pspp) which 
+can be used to specify the character encoding of the data locale.
+
+
+@section System files
+@file{*.sav} files contain a field which is supposed to identify the encoding
+of the data they contain (@pxref{Machine Integer Info Record}).  
+This field is currently unused by Pspp.
+Probably, would be appropriate to set the data locale from this field when
+reading a new data file, and set it back to the default value
+upon a @cmd{NEW FILE} command.
+However, many
+files produced by early versions of spss set this to ``2'' (ASCII) regardless
+of the encoding of the data.
+
+
+@section GUI
+The psppire graphic user interface is written using the Gtk+ api, for which 
+all strings must be encoded in UTF8.
+All strings passed to the Gtk+/Glib library functions must be UTF-8 encoded 
+otherwise errors will occur.
+Thus, for the purposes of the programming psppire, the user interface locale 
+should be assumed to be UTF8, even if setlocale and/or nl_langinfo
+indicates otherwise.
+
+
+@section Existing locale handling functions
+The major aspect of locale handling which the programmer has to consider is
+that of character encoding.
+
+The following function is used to recode strings:
+
+@deftypefun char * recode_string (enum conv_id @var{how}, const char *@var{text}, int @var{len});
+Converts the string @var{text} to a new encoding according to @var{how}.
+@var{How} can (currently) take the values @code{CONV_PSPP_TO_UTF8}, @code{CONV_SYSTEM_TO_PSPP} or @code{CONV_UTF8_TO_PSPP} @footnote{The label ``_PSPP'' ought to be changed to ``_DATA''}.
+If @var{len} is not -1, then it must be the number of bytes in @var{text}.
+It is the caller's responsibility to free the returned string when no 
+longer required.
+@end deftypefun
+
+
+For example, in order to display a string variable's value in a label widget in the psppire gui one would use code similar to
+@example
+
+struct variable *var = /* assigned from somewhere */
+struct case c = /* from somewhere else */
+
+const union value *val = case_data (&c, var);
+
+char *utf8string = recode_string (CONV_PSPP_TO_UTF8, val->s,
+      var_get_width (var));
+
+GtkWidget *entry = gtk_entry_new();
+gtk_entry_set_text (entry, utf8string);
+gtk_widget_show (entry);
+
+free (utf8string);
+
+@end example
+
+
+
+@section Quirks
+For historical reasons, not all locale handling follows posix conventions.
+This makes it difficult (impossible?) to elegantly handle the issues.
+For example, it would make sense for the gui's datasheet to display
+numbers formatted according to the LC_NUMERIC category of the data locale.
+Instead however there is the @func{data_out} function 
+(@pxref{Obtaining Properties of Format Types}) which uses the
+@func{settings_get_decimal_char} function instead of the decimal separator 
+of the locale.  Similarly, formatting of monetary values is displayed 
+in a pspp/spss specific fashion instead of using the LC_MONETARY category.
+
+
+
+@c  LocalWords:  pspp itemize Eg LC Spss cmd sav pxref spss GUI psppire Gtk api
+@c  LocalWords:  UTF gtk setlocale nl langinfo deftypefun enum conv var const
+@c  LocalWords:  int len gui struct val utf GtkWidget posix gui's datasheet
+@c  LocalWords:  func
index 70fa385c7525efea051a5bfb90fbf046bdb7e14b..3e764c8ce714099f1becf877c927681911b23a54 100644 (file)
@@ -96,6 +96,7 @@ Each type of record is described separately below.
 * Variable Display Parameter Record::
 * Long Variable Names Record::
 * Very Long String Record::
+* Data File and Variable Attributes Records::
 * Miscellaneous Informational Records::
 * Dictionary Termination Record::
 * Data Record::
@@ -791,6 +792,85 @@ After the last tuple, there may be a single byte 00, or @{00, 09@}.
 The total length is @code{count} bytes.
 @end table
 
+@node Data File and Variable Attributes Records
+@section Data File and Variable Attributes Records
+
+The data file and variable attributes records represent custom
+attributes for the system file or for individual variables in the
+system file, as defined on the DATAFILE ATTRIBUTE (@pxref{DATAFILE
+ATTRIBUTE,,,pspp, PSPP Users Guide}) and VARIABLE ATTRIBUTE commands
+(@pxref{VARIABLE ATTRIBUTE,,,pspp, PSPP Users Guide}), respectively.
+
+@example
+/* @r{Header.} */
+int32               rec_type;
+int32               subtype;
+int32               size;
+int32               count;
+
+/* @r{Exactly @code{count} bytes of data.} */
+char                attributes[];
+@end example
+
+@table @code
+@item int32 rec_type;
+Record type.  Always set to 7.
+
+@item int32 subtype;
+Record subtype.  Always set to 17 for a data file attribute record or
+to 18 for a variable attributes record.
+
+@item int32 size;
+The size of each element in the @code{attributes} member. Always set to 1.
+
+@item int32 count;
+The total number of bytes in @code{attributes}.
+
+@item char attributes[];
+The attributes, in a text-based format.
+
+In record type 17, this field contains a single attribute set.  An
+attribute set is a sequence of one or more attributes concatenated
+together.  Each attribute consists of a name, which has the same
+syntax as a variable name, followed by, inside parentheses, a sequence
+of one or more values.  Each value consists of a string enclosed in
+single quotes (@code{'}) followed by a line feed (byte 0x0a).  A value
+may contain single quote characters, which are not themselves escaped
+or quoted or required to be present in pairs.  There is no apparent
+way to embed a line feed in a value.  There is no distinction between
+an attribute with a single value and an attribute array with one
+element.
+
+In record type 18, this field contains a sequence of one or more
+variable attribute sets.  If more than one variable attribute set is
+present, each one after the first is delimited from the previous by
+@code{/}.  Each variable attribute set consists of a variable name,
+followed by @code{:}, followed by an attribute set with the same
+syntax as on record type 17.
+
+The total length is @code{count} bytes.
+@end table
+
+@subheading Example
+
+A system file produced with the following VARIABLE ATTRIBUTE commands
+in effect:
+
+@example
+VARIABLE ATTRIBUTE VARIABLES=dummy ATTRIBUTE=fred[1]('23') fred[2]('34').
+VARIABLE ATTRIBUTE VARIABLES=dummy ATTRIBUTE=bert('123').
+@end example
+
+@noindent
+will contain a variable attribute record with the following contents:
+
+@example
+00000000  07 00 00 00 12 00 00 00  01 00 00 00 22 00 00 00  |............"...|
+00000010  64 75 6d 6d 79 3a 66 72  65 64 28 27 32 33 27 0a  |dummy:fred('23'.|
+00000020  27 33 34 27 0a 29 62 65  72 74 28 27 31 32 33 27  |'34'.)bert('123'|
+00000030  0a 29                                             |.)              |
+@end example
+
 @node Miscellaneous Informational Records
 @section Miscellaneous Informational Records
 
index 1021708ab418c565755f0c528b11c1cb4f49966c..32ace6c2dca2763ce716388c097d5e996f84650c 100644 (file)
@@ -1516,4 +1516,3 @@ subtraction.
 @item
 @code{AND  NOT  OR}
 @end enumerate
-@setfilename ignored
index 30a023aeb90d3a22793169e4a4cce20edaaed77a..1323749057cf68a301ebc964edbf4b941fd796e5 100644 (file)
@@ -1,5 +1,5 @@
-@node System and Portable Files
-@chapter System Files and Portable Files
+@node System and Portable File IO
+@chapter System and Portable File I/O
 
 The commands in this chapter read, write, and examine system files and
 portable files.
@@ -10,7 +10,6 @@ portable files.
 * GET::                         Read from a system file.
 * GET DATA::                    Read from foreign files.
 * IMPORT::                      Read from a portable file.
-* MATCH FILES::                 Merge system files.
 * SAVE::                        Write to a system file.
 * SYSFILE INFO::                Display system file dictionary.
 * XEXPORT::                     Write to a portable file, as a transformation.
@@ -39,25 +38,46 @@ Only variables with names that exist in both the active file and the
 system file are considered.  Variables with the same name but different
 types (numeric, string) will cause an error message.  Otherwise, the
 system file variables' attributes will replace those in their matching
-active file variables, as described below.
+active file variables:
 
+@itemize @bullet
+@item
 If a system file variable has a variable label, then it will replace the
 active file variable's variable label.  If the system file variable does
 not have a variable label, then the active file variable's variable
-label, if any, will be retained.
+label, if any, will be retained.  
+
+@item
+If the system file variable has custom attributes (@pxref{VARIABLE
+ATTRIBUTE}), then those attributes replace the active file variable's
+custom attributes.  If the system file variable does not have custom
+attributes, then the active file variable's custom attributes, if any,
+will be retained.
 
+@item
 If the active file variable is numeric or short string, then value
 labels and missing values, if any, will be copied to the active file
 variable.  If the system file variable does not have value labels or
 missing values, then those in the active file variable, if any, will not
 be disturbed.
+@end itemize
 
-Finally, weighting of the active file is updated (@pxref{WEIGHT}).  If
-the active file has a weighting variable, and the system file does not,
-or if the weighting variable in the system file does not exist in the
-active file, then the active file weighting variable, if any, is
-retained.  Otherwise, the weighting variable in the system file becomes
-the active file weighting variable.
+In addition to properties of variables, some properties of the active
+file dictionary as a whole are updated:
+
+@itemize @bullet
+@item
+If the system file has custom attributes (@pxref{DATAFILE ATTRIBUTE}),
+then those attributes replace the active file variable's custom
+attributes.
+
+@item
+If the active file has a weighting variable (@pxref{WEIGHT}), and the
+system file does not, or if the weighting variable in the system file
+does not exist in the active file, then the active file weighting
+variable, if any, is retained.  Otherwise, the weighting variable in
+the system file becomes the active file weighting variable.
+@end itemize
 
 @cmd{APPLY DICTIONARY} takes effect immediately.  It does not read the
 active
@@ -630,99 +650,6 @@ data is read later, when a procedure is executed.
 Use of @cmd{IMPORT} to read a system file or scratch file is a PSPP
 extension.
 
-@node MATCH FILES
-@section MATCH FILES
-@vindex MATCH FILES
-
-@display
-MATCH FILES
-        /@{FILE,TABLE@}=@{*,'file-name'@}
-        /RENAME=(src_names=target_names)@dots{}
-        /IN=var_name
-
-        /BY=var_list
-        /DROP=var_list
-        /KEEP=var_list
-        /FIRST=var_name
-        /LAST=var_name
-        /MAP
-@end display
-
-@cmd{MATCH FILES} merges one or more system, portable, or scratch files,
-optionally
-including the active file.  Cases with the same values for BY
-variables are combined into a single case.  Cases with different
-values are output in order.  Thus, multiple sorted files are
-combined into a single sorted file based on the value of the BY
-variables.  The results of the merge become the new active file.
-
-Specify FILE with a system, portable, or scratch file as a file name
-string or file handle
-(@pxref{File Handles}), or with an asterisk (@samp{*}) to
-indicate the current active file.  The files specified on FILE are
-merged together based on the BY variables, or combined case-by-case if
-BY is not specified.
-
-Specify TABLE with a file to use it as a @dfn{table
-lookup file}.  Cases in table lookup files are not used up after
-they've been used once.  This means that data in table lookup files can
-correspond to any number of cases in FILE files.  Table lookup files
-correspond to lookup tables in traditional relational database systems.
-If a table lookup file contains more than one case with a given set of
-BY variables, only the first case is used.
-
-Any number of FILE and TABLE subcommands may be specified.
-Ordinarily, at least two FILE subcommands, or one FILE and at least
-one TABLE, should be specified.  Each instance of FILE or TABLE can be
-followed by any sequence of RENAME subcommands.  These have the same
-form and meaning as the corresponding subcommands of @cmd{GET}
-(@pxref{GET}), but apply only to variables in the given file.
-
-Each FILE or TABLE may optionally be followed by an IN subcommand,
-which creates a numeric variable with the specified name and format
-F1.0.  The IN variable takes value 1 in a case if the given file
-contributed a row to the merged file, 0 otherwise.  The DROP, KEEP,
-and RENAME subcommands do not affect IN variables.
-
-When more than one FILE or TABLE contains a variable with a given
-name, those variables must all have the same type (numeric or string)
-and, for string variables, the same width.  This rules applies to
-variable names after renaming with RENAME; thus, RENAME can be used to
-resolve conflicts.
-
-FILE and TABLE must be specified at the beginning of the command, with
-any RENAME or IN specifications immediately after the corresponding
-FILE or TABLE.  These subcommands are followed by BY, DROP, KEEP,
-FIRST, LAST, and MAP.
-
-The BY subcommand specifies a list of variables that are used to match
-cases from each of the files.  When TABLE or IN is used, BY is
-required; otherwise, it is optional.  When BY is specified, all the
-files named on FILE and TABLE subcommands must be sorted in ascending
-order of the BY variables.  Variables belonging to files that are not
-present for the current case are set to the system-missing value for
-numeric variables or spaces for string variables.
-
-The DROP and KEEP subcommands allow variables to be dropped from or
-reordered within the new active file.  These subcommands have the same
-form and meaning as the corresponding subcommands of @cmd{GET}
-(@pxref{GET}).  They apply to the new active file as a whole, not to
-individual input files.  The variable names specified on DROP and KEEP
-are those after any renaming with RENAME.
-
-The optional FIRST and LAST subcommands name variables that @cmd{MATCH
-FILES} adds to the active file.  The new variables are numeric with
-print and write format F1.0.  The value of the FIRST variable is 1 in
-the first case with a given set of values for the BY variables, and 0
-in other cases.  Similarly, the LAST variable is 1 in the last case
-with a given of BY values, and 0 in other cases.
-
-@cmd{MATCH FILES} may not be specified following @cmd{TEMPORARY}
-(@pxref{TEMPORARY}) if the active file is used as an input source.
-
-Use of portable or scratch files on @cmd{MATCH FILES} is a PSPP
-extension.
-
 @node SAVE
 @section SAVE
 @vindex SAVE
@@ -873,4 +800,3 @@ the data is read by a procedure or procedure-like command.
 @end itemize
 
 @xref{SAVE}, for more information.
-@setfilename ignored
index f501c65ae7f0cc62431d145924770cdce9be6ec6..868143b35a5a0ba8b73c5f628834f9b7a39cc87a 100644 (file)
@@ -170,4 +170,3 @@ variable as the loop index.
 When @cmd{LOOP} or @cmd{END LOOP} is specified following @cmd{TEMPORARY}
 (@pxref{TEMPORARY}), the @cmd{LAG} function may not be used
 (@pxref{LAG}).
-@setfilename ignored
index 2a63e136f5657721ca8dbfb392f105a257df6f2a..a41e60de1a3df93226c6c6c2037f126f9f4693e4 100644 (file)
@@ -1,4 +1,3 @@
 @node Function Index
 @chapter Function Index
 @printindex fn
-@setfilename ignored
index fe5caa38a253a724c38fde21635ca9af8f26fb24..5a9c2791c8184386244aa877285212b10ad65360 100644 (file)
@@ -56,4 +56,3 @@ but only if that directory already exists.
 (optional) Type @samp{make clean} to delete the PSPP binaries
 from the source tree.
 @end enumerate
-@setfilename ignored
index e87731b56304ab685a2541d2255908e4767b5696..735a54c5a20720859c62c4103f3b4ff1afb5b253 100644 (file)
@@ -31,4 +31,3 @@ The author hopes to fully support all features in the products
 that PSPP replaces, eventually.  The author welcomes questions,
 comments, donations, and code submissions.  @xref{Bugs,,Submitting Bug
 Reports}, for instructions on contacting the author.
-@setfilename ignored
index b5f157e6656e4c96af00bda27437ef1a95e70b5a..6b45392b5b810332a94148ed1281fce1df933681 100644 (file)
@@ -7,12 +7,12 @@
 @cindex options, command-line
 @example
 pspp [ -B @var{dir} | --config-dir=@var{dir} ] [ -o @var{device} | --device=@var{device} ]
-       [ -d @var{var}[=@var{value}] | --define=@var{var}[=@var{value}] ] [-u @var{var} | --undef=@var{var} ]
-       [ -f @var{file} | --out-file=@var{file} ] [ -p | --pipe ] [ -I- | --no-include ]
+       [ -a @{compatible|enhanced@} | --algorithm=@{compatible|enhanced@}]
+       [ -x @{compatible|enhanced@} | --syntax=@{compatible|enhanced@}]
+       [ -I- | --no-include ]
        [ -I @var{dir} | --include=@var{dir} ] [ -i | --interactive ] 
-       [ -n | --edit | --dry-run | --just-print | --recon ] 
        [ -r | --no-statrc ] [ -h | --help ] [ -l | --list ] 
-       [ -c @var{command} | --command @var{command} ] [ -s | --safer ]
+       [ -s | --safer ]
        [ --testing-mode ] [ -V | --version ] [ -v | --verbose ] 
        [ @var{key}=@var{value} ] @var{file}@enddots{}
 @end example
@@ -101,19 +101,6 @@ Input and output options affect how PSPP reads input and writes
 output.  These are the input and output options:
 
 @table @code
-@item -f @var{file}
-@itemx --out-file=@var{file}
-
-This overrides the output file name for devices designated as listing
-devices.  If a file named @var{file} already exists, it is overwritten.
-
-@item -p
-@itemx --pipe
-
-Allows PSPP to be used as a filter by causing the syntax file to be
-read from stdin and output to be written to stdout.  Conflicts with the
-@code{-f @var{file}} and @code{--file=@var{file}} options.
-
 @item -I-
 @itemx --no-include
 
@@ -127,12 +114,6 @@ configuring}.
 Appends directory @var{dir} to the path that is searched for include
 files in PSPP syntax files.
 
-@item -c @var{command}
-@itemx --command=@var{command}
-
-Execute literal command @var{command}.  The command is executed before
-startup syntax files, if any.
-
 @item --testing-mode
 
 Invoke heuristics to assist with testing PSPP.  For use by @code{make
@@ -158,16 +139,6 @@ mode, rather than the default batch mode.  @xref{Tokenizing lines}, for
 information on the differences between batch mode and interactive mode
 command interpretation.
 
-@item -n
-@itemx --edit
-@itemx --dry-run
-@itemx --just-print
-@itemx --recon
-
-Only the syntax of any syntax file specified or of commands entered at
-the command line is checked.  Transformations are not performed and
-procedures are not executed.  Not yet implemented.
-
 @item -r
 @itemx --no-statrc
 
@@ -253,4 +224,3 @@ Individual directories included in file searches.
 Each verbosity level also includes messages from lower verbosity levels.
 
 @end table
-@setfilename ignored
index 1345433613d3f24e7c7b26cdac457f7132761834..6226741463a4086a1d7021e3c159f4043918c0ab 100644 (file)
@@ -497,6 +497,11 @@ they are displayed.  Example: a width of 8, with 2 decimal places.
 @item Write format
 Similar to print format, but used by the @cmd{WRITE} command
 (@pxref{WRITE}).
+
+@cindex custom attributes
+@item Custom attributes
+User-defined associations between names and values.  @xref{VARIABLE
+ATTRIBUTE}.
 @end table
 
 @node System Variables
@@ -1167,6 +1172,7 @@ trailing white space.
 The maximum width for time and date formats is 40 columns.  Minimum
 input and output width for each of the time and date formats is shown
 below:
+
 @float
 @multitable {DATETIME} {Min. Input Width} {Min. Output Width} {4-digit year}
 @headitem Format @tab Min. Input Width @tab Min. Output Width @tab Option 
@@ -1488,4 +1494,3 @@ The first nonterminal defined in a set of productions is called the
 @dfn{start symbol}.  The start symbol defines the entire syntax for
 that command.
 @end itemize
-@setfilename ignored
index 0dd77d499803e456e82093145d825dfe804e0ad0..42e02b7c8f22ee0d4206fc6d7527a64a8e52c32d 100644 (file)
@@ -9,4 +9,3 @@ implemented.
 
 @include doc/ni.texi
 
-@setfilename ignored
index c2507412b7a6c314d08f71451156ac0ffb385822..4b92654da31cbe63f151441875b0d7c06b1e11de 100644 (file)
@@ -79,6 +79,7 @@ modify this GNU manual.''
 * Parsing Command Syntax::      How to parse command syntax.
 * Processing Data::             Data input, output, and processing.
 * Presenting Output::           Producing machine- and human-readable output.
+* Internationalisation::        Dealing with locale issues.
 
 * Function Index::              Index of PSPP functions.
 * Concept Index::               Index of concepts.
@@ -95,6 +96,7 @@ modify this GNU manual.''
 @include dev/syntax.texi
 @include dev/data.texi
 @include dev/output.texi
+@include dev/i18n.texi
 
 @include function-index.texi
 @include concept-index.texi
index 6171df6d54d08d784fef3c63d1f6acec419efdd6..975ee660b815ccbc66fe23d51ee2a3ae1bd22c17 100644 (file)
@@ -70,7 +70,8 @@ modify this GNU manual.''
 * Expressions::                 Numeric and string expression syntax.
 
 * Data Input and Output::       Reading data from user files.
-* System and Portable Files::   Dealing with system & portable files.
+* System and Portable File IO:: Reading and writing system & portable files.
+* Combining Data Files::        Combining data from multiple files.
 * Variable Attributes::         Adjusting and examining variables.
 * Data Manipulation::           Simple operations on data.
 * Data Selection::              Select certain cases for analysis.
@@ -98,6 +99,7 @@ modify this GNU manual.''
 @include expressions.texi
 @include data-io.texi
 @include files.texi
+@include combining.texi
 @include variables.texi
 @include transformation.texi
 @include data-selection.texi
index 90caf866d5f786a52c1be4ef99d01bb31b665539..b47416b70191c1a1af0129ca073a36fa7a390056 100644 (file)
@@ -110,4 +110,3 @@ list.
 regression /variables=v0 v1 v2 /statistics defaults /dependent=v2 
            /save pred resid /method=enter.
 @end example
-@setfilename ignored
index 8fa93b1566a38fe304836d8d473fcc2dde7189b3..985560604b02b52b02440dcd39a36bd1b773c118 100644 (file)
@@ -14,6 +14,7 @@ far.
 * ONEWAY::                      One way analysis of variance.
 * RANK::                        Compute rank scores.
 * REGRESSION::                  Linear regression.
+* RELIABILITY::                 Reliability analysis.
 @end menu
 
 @node DESCRIPTIVES
@@ -232,7 +233,7 @@ EXAMINE
         /PLOT=@{BOXPLOT, NPPLOT, HISTOGRAM, ALL, NONE@}
         /CINTERVAL n
         /COMPARE=@{GROUPS,VARIABLES@}
-        /ID=@{case_number, var_name@}
+        /ID=var_name
         /@{TOTAL,NOTOTAL@}
         /PERCENTILE=[value_list]=@{HAVERAGE, WAVERAGE, ROUND, AEMPIRICAL, EMPIRICAL @}
         /MISSING=@{LISTWISE, PAIRWISE@} [@{EXCLUDE, INCLUDE@}] 
@@ -271,6 +272,12 @@ If /COMPARE=VARIABLES is specified, then one plot per factor is produced, each
 each containing one boxplot per dependent variable.
 If the /COMPARE subcommand is ommitted, then PSPP uses the default value of 
 /COMPARE=GROUPS.
+The ID subcommand also pertains to boxplots.  If given, it must
+specify a variable name.   Outliers and extreme cases plotted in
+boxplots will be labelled with the case from that variable.  Numeric or
+string variables are permissible.  If the ID subcommand is not given,
+then the casenumber will be used for labelling.
 
 The CINTERVAL subcommand specifies the confidence interval to use in
 calculation of the descriptives command.  The default it 95%.
@@ -499,6 +506,8 @@ NPAR TESTS
      [ /STATISTICS=@{DESCRIPTIVES@} ]
 
      [ /MISSING=@{ANALYSIS, LISTWISE@} @{INCLUDE, EXCLUDE@} ]
+
+     [ /METHOD=EXACT [ TIMER [(n)] ] ]
 @end display
 
 NPAR TESTS performs nonparametric tests. 
@@ -508,10 +517,21 @@ One or more tests may be specified by using the corresponding subcommand.
 If the /STATISTICS subcommand is also specified, then summary statistics are 
 produces for each variable that is the subject of any test.
 
+Certain tests may take a long time to execute, if an exact figure is required.
+Therefore, by default asymptotic approximations are used unless the
+subcommand /METHOD=EXACT is specified.  
+Exact tests give more accurate results, but may take an unacceptably long 
+time to perform.  If the TIMER keyword is used, it sets a maximum time,
+after which the test will be abandoned, and a warning message printed.
+The time, in minutes, should be specified in parentheses after the TIMER keyword.
+If the TIMER keyword is given without this figure, then a default value of 5 minutes 
+is used.
+
 
 @menu
 * BINOMIAL::                Binomial Test
 * CHISQUARE::               Chisquare Test
+* WILCOXON::                Wilcoxon Signed Ranks Test
 @end menu
 
 
@@ -591,6 +611,34 @@ sum of the frequencies need not be 1.
 If no /EXPECTED subcommand is given, then then equal frequencies 
 are expected.
 
+@node WILCOXON
+@subsection Wilcoxon
+@comment  node-name,  next,  previous,  up
+@vindex WILCOXON
+@cindex wilcoxon matched pairs signed ranks test
+
+@display
+     [ /WILCOXON varlist [ WITH varlist [ (PAIRED) ]]]
+@end display
+
+The wilcoxon subcommand tests for differences between means of the 
+variables listed.  The test does not make any assumptions about the
+variances of the samples.
+
+If the @code{WITH} keyword is omitted, then tests for all
+combinations of the listed variables are performed.
+If the @code{WITH} keyword is given, and the @code{(PAIRED)} keyword
+is also given, then the number of variables preceding @code{WITH}
+must be the same as the number following it.
+In this case, tests for each respective pair of variables are
+performed.
+If the @code{WITH} keyword is given, but the
+@code{(PAIRED)} keyword is omitted, then tests for each combination
+of variable preceding @code{WITH} against variable following
+@code{WITH} are performed.
+
+If the number of observations is large, and exact tests have been
+requested. then the test may take a very long time to complete.
 
 @node T-TEST
 @comment  node-name,  next,  previous,  up
@@ -766,7 +814,6 @@ If the total sum of the coefficients are not zero, then PSPP will
 display a warning, but will proceed with the analysis.
 The @code{CONTRAST} subcommand may be given up to 10 times in order
 to specify different contrast tests.
-@setfilename ignored
 
 @node RANK
 @comment  node-name,  next,  previous,  up
@@ -831,3 +878,50 @@ user-missing are to be excluded from the rank scores. A setting of
 INCLUDE means they are to be included.  The default is EXCLUDE.
 
 @include regression.texi
+
+
+@node RELIABILITY
+@section RELIABILITY
+
+@vindex RELIABILITY
+@display
+RELIABILITY
+        /VARIABLES=var_list
+        /SCALE (@var{name}) = @{var_list, ALL@}
+        /MODEL=@{ALPHA, SPLIT[(N)]@}
+        /SUMMARY=@{TOTAL,ALL@}
+        /MISSING=@{EXCLUDE,INCLUDE@}
+@end display
+
+@cindex Cronbach's Alpha
+The @cmd{RELIABILTY} command performs reliablity analysis on the data.
+
+The VARIABLES subcommand is required. It determines the set of variables 
+upon which analysis is to be performed.
+
+The SCALE subcommand determines which variables reliability is to be 
+calculated for.  If it is omitted, then analysis for all variables named
+in the VARIABLES subcommand will be used.
+Optionally, the @var{name} parameter may be specified to set a string name 
+for the scale.
+
+The MODEL subcommand determines the type of analysis. If ALPHA is specified, 
+then Cronbach's Alpha is calculated for the scale.  If the model is SPLIT, 
+then the variables  are divided into 2 subsets.  An optional parameter 
+@var{N} may be given, to specify how many variables to be in the first subset.
+If @var{N} is omitted, then it defaults to one half of the variables in the 
+scale, or one half minus one if there are an odd number of variables.
+The default model is ALPHA.
+
+By default, any cases with user missing, or system missing values for 
+any variables given 
+in the VARIABLES subcommand will be omitted from analysis.
+The MISSING subcommand determines whether user missing values are to 
+be included or excluded in the analysis.
+
+The SUMMARY subcommand determines the type of summary analysis to be performed.
+Currently there is only one type: SUMMARY=TOTAL, which displays per-item
+analysis tested against the totals.
+
+
+
index 27bbb2da8d039204be8c27f9b996f867b9ba6511..5eedba64f742fd566ba75a5c65ccf347d7d6f3ee 100644 (file)
@@ -83,7 +83,7 @@ list.
 Each set must have exactly as many source variables as aggregation
 variables.  Each aggregation variable receives the results of applying
 the specified aggregation function to the corresponding source
-variable.  The MEAN, SD, and SUM aggregation functions may only be
+variable.  The MEAN, MEDIAN, SD, and SUM aggregation functions may only be
 applied to numeric variables.  All the rest may be applied to numeric
 and short and long string variables.
 
@@ -128,6 +128,9 @@ dictionary information from the source variable.
 Arithmetic mean.  Limited to numeric values.  The default format is
 F8.2.
 
+@item MEDIAN(var_name)
+The median value.  Limited to numeric values.  The default format is F8.2.
+
 @item MIN(var_name)
 Minimum value.  The aggregation variable receives the complete
 dictionary information from the source variable.
@@ -550,4 +553,3 @@ If workspace is exhausted, it falls back to a merge sort algorithm that
 involves creates numerous temporary files.
 
 @cmd{SORT CASES} may not be specified following TEMPORARY.  
-@setfilename ignored
index 35ea20e2131d42cb4e660db4d29973a2a7408cea..95cfa25dd89d472c615461f51f4eabb1aa0299dc 100644 (file)
@@ -414,10 +414,13 @@ default.  Any real value may be assigned.
 
 @item DECIMAL
 @anchor{SET DECIMAL}
-The default DOT setting causes the decimal point character to be
-@samp{.} and the grouping character to be @samp{,}.  A setting of COMMA
+This value may be set to DOT or COMMA.
+Setting it to DOT causes the decimal point character to be
+@samp{.} and the grouping character to be @samp{,}.
+Setting it to COMMA
 causes the decimal point character to be @samp{,} and the grouping
 character to be @samp{.}.
+The default value is determined from the system locale.
 
 @item FORMAT
 Allows the default numeric input/output format to be specified.  The
@@ -784,4 +787,3 @@ on the output device.
 Specify a title as a string in quotes.  The alternate syntax that did
 not require quotes is now obsolete.  If it is used then the title is
 converted to all uppercase.
-@setfilename ignored
index a66e423292054adf60892eb059449e8db7dff2a1..08c4bdeac0cd72a34be7f06dfcbba4f9e499e224 100644 (file)
@@ -7,8 +7,7 @@ several utility functions for examining and adjusting them.
 @menu
 * ADD VALUE LABELS::            Add value labels to variables.
 * DELETE VARIABLES::            Delete variables.
-* DISPLAY::                     Display variable names & descriptions.
-* DISPLAY VECTORS::             Display a list of vectors.
+* DISPLAY::                     Display information about the active file.
 * FORMATS::                     Set print and write formats.
 * LEAVE::                       Don't clear variables between cases.
 * MISSING VALUES::              Set missing values for variables.
@@ -18,6 +17,7 @@ several utility functions for examining and adjusting them.
 * RENAME VARIABLES::            Rename variables.
 * VALUE LABELS::                Set value labels for variables.
 * STRING::                      Create new string variables.
+* VARIABLE ATTRIBUTE::          Set custom attributes on variables.
 * VARIABLE LABELS::             Set variable labels for variables.
 * VARIABLE ALIGNMENT::          Set the alignment for display.
 * VARIABLE WIDTH::              Set the display width.
@@ -61,15 +61,26 @@ effect, it causes the temporary transformations to become permanent.
 @vindex DISPLAY
 
 @display
-DISPLAY @{NAMES,INDEX,LABELS,VARIABLES,DICTIONARY,SCRATCH@}
-        [SORTED] [var_list]
+DISPLAY [SORTED] NAMES [[/VARIABLES=]var_list].
+DISPLAY [SORTED] INDEX [[/VARIABLES=]var_list].
+DISPLAY [SORTED] LABELS [[/VARIABLES=]var_list].
+DISPLAY [SORTED] VARIABLES [[/VARIABLES=]var_list].
+DISPLAY [SORTED] DICTIONARY [[/VARIABLES=]var_list].
+DISPLAY [SORTED] SCRATCH [[/VARIABLES=]var_list].
+DISPLAY [SORTED] ATTRIBUTES [[/VARIABLES=]var_list].
+DISPLAY [SORTED] @@ATTRIBUTES [[/VARIABLES=]var_list].
+DISPLAY [SORTED] VECTORS.
 @end display
 
-@cmd{DISPLAY} displays requested information on variables.  Variables can
-optionally be sorted alphabetically.  The entire dictionary or just
-specified variables can be described.
+@cmd{DISPLAY} displays information about the active file.  A variety
+of different forms of information can be requested.
 
-One of the following keywords can be present:
+The following keywords primarily cause information about variables to
+be displayed.  With these keywords, by default information is
+displayed about all variable in the active file, in the order that
+variables occur in the active file dictionary.  The SORTED keyword
+causes output to be sorted alphabetically by variable name.  The
+VARIABLES subcommand limits output to the specified variables.
 
 @table @asis
 @item NAMES
@@ -91,23 +102,24 @@ Variable names, positions, print and write formats, missing values,
 variable labels, and value labels are displayed.
 
 @item SCRATCH
-Varible names are displayed, for scratch variables only (@pxref{Scratch
+Variable names are displayed, for scratch variables only (@pxref{Scratch
 Variables}).
-@end table
 
-If SORTED is specified, then the variables are displayed in ascending
-order based on their names; otherwise, they are displayed in the order
-that they occur in the active file dictionary.
+@item ATTRIBUTES
+Datafile and variable attributes are displayed, except that attributes
+whose names begin with @code{@@} or @code{$@@} are omitted.
 
-@node DISPLAY VECTORS
-@section DISPLAY VECTORS
-@vindex DISPLAY VECTORS
+@itemx @@ATTRIBUTES
+All datafile and variable attributes are displayed.
+@end table
 
-@display
-DISPLAY VECTORS.
-@end display
+With the @code{VECTOR} keyword, @cmd{DISPLAY} lists all the currently
+declared vectors.  If the SORTED keyword is given, the vectors are
+listed in alphabetical order; otherwise, they are listed in textual
+order of definition within the PSPP syntax file.
 
-@cmd{DISPLAY VECTORS} lists all the currently declared vectors.
+For related commands, see @ref{DISPLAY DOCUMENTS} and @ref{DISPLAY
+FILE LABEL}.
 
 @node FORMATS
 @section FORMATS
@@ -357,6 +369,60 @@ implicitly derived from the specified output formats.
 Created variables are initialized to spaces.
 
 
+@node VARIABLE ATTRIBUTE
+@section VARIABLE ATTRIBUTE
+@vindex VARIABLE ATTRIBUTE
+
+@display
+VARIABLE ATTRIBUTE
+         VARIABLES=var_list
+         ATTRIBUTE=name('value') [name('value')]@dots{}
+         ATTRIBUTE=name@b{[}index@b{]}('value') [name@b{[}index@b{]}('value')]@dots{}
+         DELETE=name [name]@dots{}
+         DELETE=name@b{[}index@b{]} [name@b{[}index@b{]}]@dots{}
+@end display
+
+@cmd{VARIABLE ATTRIBUTE} adds, modifies, or removes user-defined
+attributes associated with variables in the active file.  Custom
+variable attributes are not interpreted by PSPP, but they are saved as
+part of system files and may be used by other software that reads
+them.
+
+The required VARIABLES subcommand must come first.  Specify the
+variables to which the following ATTRIBUTE or DELETE subcommand
+should apply.
+
+Use the ATTRIBUTE subcommand to add or modify custom variable
+attributes.  Specify the name of the attribute as an identifier
+(@pxref{Tokens}), followed by the desired value, in parentheses, as a
+quoted string.  The specified attributes are then added or modified in
+the variables specified on VARIABLES.  Attribute names that begin with
+@code{$} are reserved for PSPP's internal use, and attribute names
+that begin with @code{@@} or @code{$@@} are not displayed by most PSPP
+commands that display other attributes.  Other attribute names are not
+treated specially.
+
+Attributes may also be organized into arrays.  To assign to an array
+element, add an integer array index enclosed in square brackets
+(@code{[} and @code{]}) between the attribute name and value.  Array
+indexes start at 1, not 0.  An attribute array that has a single
+element (number 1) is not distinguished from a non-array attribute.
+
+Use the DELETE subcommand to delete an attribute from the variable
+specified on VARIABLES.  Specify an attribute name by itself to delete
+an entire attribute, including all array elements for attribute
+arrays.  Specify an attribute name followed by an array index in
+square brackets to delete a single element of an attribute array.  In
+the latter case, all the array elements numbered higher than the
+deleted element are shifted down, filling the vacated position.
+
+To associate custom attributes with the entire active file, instead of
+with particular variables, use @cmd{DATAFILE ATTRIBUTE} (@pxref{DATAFILE ATTRIBUTE}) instead.
+
+@cmd{VARIABLE ATTRIBUTE} takes effect immediately.  It is not affected
+by conditional and looping structures such as @cmd{DO IF} or
+@cmd{LOOP}.
+
 @node VARIABLE LABELS
 @section VARIABLE LABELS
 @vindex VARIABLE LABELS
@@ -489,4 +555,3 @@ variables
 to the specified format specification.  Its syntax is identical to
 that of FORMATS (@pxref{FORMATS}), but @cmd{WRITE FORMATS} sets only
 write formats, not print formats.
-@setfilename ignored
diff --git a/examples/grid.sps b/examples/grid.sps
new file mode 100644 (file)
index 0000000..ea7f9d7
--- /dev/null
@@ -0,0 +1,20 @@
+COMMENT  -*-pspp-*- .
+
+COMMENT This program is useful for testing the behaviour of the Data and Variable Sheets.
+
+input program.
+vector var(500 F8.3).
+  loop #c = 1 to 1000.
+    loop #v = 1 to 500.
+      compute var(#v) = #v + #c / 1000.
+    end loop.
+    end case.
+  end loop.
+  end file.
+end input program.
+
+variable label  var1 'First variable' var2 'Second variable' var3 'Third variable'.
+
+save outfile='grid.sav'.
+
+execute.
index 7827516e3b9e3517aa33e44614eb16b958f856d8..8e51e1f720244b9e96c4eca5233f5aadf29f11a1 100644 (file)
@@ -14,12 +14,15 @@ libglade_psppire_la_SOURCES = \
        glade/bbox.c \
        glade/selector.c \
        glade/acr.c \
+       glade/dictview.c \
+       src/ui/gui/psppire-conf.c \
        src/ui/gui/psppire-acr.c \
        src/ui/gui/psppire-buttonbox.c \
        src/ui/gui/psppire-hbuttonbox.c \
        src/ui/gui/psppire-vbuttonbox.c \
        src/ui/gui/psppire-dialog.c \
        src/ui/gui/psppire-keypad.c \
+       src/ui/gui/psppire-dictview.c \
        src/ui/gui/psppire-selector.c
 
 dist_catalog_DATA = \
diff --git a/glade/dictview.c b/glade/dictview.c
new file mode 100644 (file)
index 0000000..5d23b06
--- /dev/null
@@ -0,0 +1,82 @@
+#include <config.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "psppire-dictview.h"
+
+#include <gladeui/glade.h>
+
+
+GType
+psppire_dict_get_type ()
+{
+  return 0;
+}
+
+
+
+void
+glade_psppire_dictview_post_create (GladeWidgetAdaptor *adaptor,
+                                   GObject            *object,
+                                   GladeCreateReason   reason)
+{
+  GladeWidget *widget ;
+
+  PsppireDictView *dictview = PSPPIRE_DICT_VIEW (object);
+
+  g_return_if_fail (PSPPIRE_IS_DICT_VIEW (dictview));
+
+  widget = glade_widget_get_from_gobject (GTK_WIDGET (dictview));
+  if (!widget)
+    return;
+
+  if (reason == GLADE_CREATE_USER)
+    {
+      /* HIG complient border-width defaults on dictviews */
+      glade_widget_property_set (widget, "border-width", 5);
+    }
+}
+
+
+GtkWidget *
+glade_psppire_dictview_get_internal_child (GladeWidgetAdaptor  *adaptor,
+                                        PsppireDictView       *dictview,
+                                        const gchar         *name)
+{
+#if DEBUGGING
+  g_print ("%s\n", __FUNCTION__);
+#endif
+  return GTK_WIDGET (dictview);
+}
+
+
+
+void
+glade_psppire_dictview_set_property (GladeWidgetAdaptor *adaptor,
+                                    GObject            *object,
+                                    const gchar        *id,
+                                    const GValue       *value)
+{
+#if DEBUGGING
+  g_print ("%s(%p) Type=\"%s\" Id=\"%s\"\n", __FUNCTION__, object,
+          G_OBJECT_TYPE_NAME( object ),
+          id);
+#endif
+
+  GWA_GET_CLASS (GTK_TYPE_WINDOW)->set_property (adaptor, object,
+                                                id, value);
+}
+
+
+GList *
+glade_psppire_dictview_get_children (GladeWidgetAdaptor  *adaptor,
+                                    PsppireDictView  *dv)
+{
+  GList *list = NULL;
+
+  g_return_val_if_fail (PSPPIRE_IS_DICT_VIEW (dv), NULL);
+
+  list = glade_util_container_get_all_children (GTK_CONTAINER (dv));
+
+  return list;
+}
index e2dc4b7661da6061a75c0f3f1a59517c77539085..50a0366186ea4da876516362fec16840196e5e63 100644 (file)
 
     </glade-widget-class>
 
+
+
+    <glade-widget-class name="PsppireDictView" generic-name="psppire-dictview" title="Dictionary Treeview">
+
+      <post-create-function>glade_psppire_dictview_post_create</post-create-function>
+      <get-children-function>glade_psppire_dictview_get_children</get-children-function>
+      <get-internal-child-function>glade_psppire_dictview_get_internal_child</get-internal-child-function>
+
+
+    <properties>
+      <property id="child"                disabled="True" />
+      <property id="homogeneous"          disabled="True" />
+      <property id="visible" ignore="True" default="True" />
+    </properties>
+
+    </glade-widget-class>
+
+
   </glade-widget-classes>
  
 
   <glade-widget-class-ref name="PsppireDialog"/>
   <glade-widget-class-ref name="PsppireHButtonBox"/>
   <glade-widget-class-ref name="PsppireVButtonBox"/>
-  <glade-widget-class-ref name="PsppireKeypad"/>
+  <glade-widget-class-ref name="PsppireDictView"/>
   <glade-widget-class-ref name="PsppireSelector"/>
+  <glade-widget-class-ref name="PsppireKeypad"/>
   <glade-widget-class-ref name="PsppireAcr"/>
  </glade-widget-group>
 
index 6410988b8ba7304989ad347f387a0f0b2efdb219..6b20c67478909fe6232730b58d7b4056de720490 100644 (file)
@@ -3,7 +3,7 @@
 include $(top_srcdir)/lib/linreg/automake.mk
 
 if WITHGUI
-include $(top_srcdir)/lib/gtksheet/automake.mk
+include $(top_srcdir)/lib/gtk-contrib/automake.mk
 endif
 
 EXTRA_DIST += lib/OChangeLog
diff --git a/lib/gtk-contrib/COPYING.LESSER b/lib/gtk-contrib/COPYING.LESSER
new file mode 100644 (file)
index 0000000..8add30a
--- /dev/null
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/lib/gtk-contrib/OChangeLog b/lib/gtk-contrib/OChangeLog
new file mode 100644 (file)
index 0000000..b5ba1c8
--- /dev/null
@@ -0,0 +1,149 @@
+2008-05-08  Ben Pfaff  <blp@gnu.org>
+
+       Patch #6506.  Reviewed by John Darrington.
+
+       * gtksheet.c (gtk_sheet_unrealize): Don't call gtk_widget_unparent
+       on sheet->button if it's null.
+
+2008-05-06  Ben Pfaff  <blp@gnu.org>
+
+       * gtksheet.c (gtk_sheet_dispose): Set the sheet's entry_container
+       and button members to NULL after unref'ing them, so that a later
+       call to gtk_sheet_for_all will not try to dereference a dangling
+       pointer.
+
+2008-03-06 John Darrington <john@darrington.wattle.id.au>
+
+       * gsheet-row-iface.c gsheet-row-iface.h: Delete unused, unneccesary
+       gpointer variable from the interface.
+
+       * gtksheet.c: Update to match new gsheet-row-iface
+
+2008-02-27 John Darrington <john@darrington.wattle.id.au>
+       * gtksheet.c gtksheet.h: Corrected some leaks and other problems
+       related to de-allocating the sheet.
+
+2008-02-27 John Darrington <john@darrington.wattle.id.au>
+       * gtksheet.c: (gtk_sheet_expose) Don't queue a redraw on the entry
+       widget.  Fixes bug #21073
+
+2008-02-20 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c gtksheet.h: Removed some unused signals.
+       Made the models properties of the widget.
+
+2008-02-08 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c: Removed the sheet_locked feature, which we never
+       used, and interfered with the editability of the entry widget.
+
+       * gtksheet.c: Add one to the row to which we scroll. Seems like
+       the best way to cope with granularity problems.
+       
+21 Septempber 2007 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c (range_update_callback): Scroll to cell 0,0 if the
+       current position is outside the model's range.
+
+24 July 2007 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c gtksheet.h: Removed the `clip' feature, which IMO 
+       is a croc, and we're unlikely to use.  In its place, added a primary 
+       selection which supports text and html targets.
+
+16 July 2007 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c gtksheet.h: Removed some legacy functions called from 
+       gtk_sheet_finalize which caused unnecessary delays when shutting down.
+
+12 July 2007 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c gtksheet.h: Removed view member and replaced with 
+        function call.  Removed hadjustment_changed and vadjustment_changed 
+        functions which did nothing.  Added some whitespace arount != 
+        operators.
+
+09 July 2007 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c gtksheet.h (gtk_sheet_get_active_cell): Allowed row,
+       column  to be NULL.
+
+07 July 2007 John Darrington <john@darrington.wattle.id.au>
+        
+       * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c
+       gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle"
+       feature on row/column titles, which shows tooltip-like popups.  
+
+03 July 2007 John Darrington <john@darrington.wattle.id.au>
+
+       * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature 
+       that was causing us grief.
+
+28 June 2007 John Darrington <john@darrington.wattle.id.au>
+
+        * gtksheet.c: Removed some features that we dont use, to get better 
+       speed.
+
+Sat Feb 17 17:36:56 2007  Ben Pfaff  <blp@gnu.org>
+
+       * gsheet-column-iface.c gsheet-hetero-column.c gsheet-row-iface.c
+       gsheet-uniform-column.c gsheet-uniform-row.c gsheetmodel.c
+       gtkextra-marshal.c gtkextra.c gtkiconlist.c gtkitementry.c
+       gtksheet.c: Add "#include <config.h>".
+
+Mon Jun 19 18:03:21 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+       * gsheet-column-iface.c gsheet-column-iface.h
+       gsheet-hetero-column.c gsheet-row-iface.c gsheet-row-iface.h
+       gsheet-uniform-column.c gsheet-uniform-row.c gtksheet.c
+       gtksheet.h:  Fixed some warnings.  Corrected errors updating
+               row/column titles
+       
+Di Mai 30 19:51:19 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gtksheet.c gtksheet.h: constness. Removed dependence on glib2.10
+
+Sat May 27 16:29:36 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gtksheet.c: Removed call to gtk_entry_set_text, which caused warnings 
+       and was unnecessary.
+
+Thu May 25 17:58:51 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gsheet-column-iface.c gsheet-column-iface.h gsheet-hetero-column.c
+    gsheet-row-iface.c gsheet-row-iface.h gsheet-uniform-row.c
+    gtksheet-extra.h gtksheet.c:  Plugged memory leaks.  Rationalised the way
+    that GtkSheetButtons are created.
+
+Sat May 20 21:02:03 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gsheetmodel.c gsheetmodel.h: Added columns-inserted and columns-deleted 
+    signals.  Added g_sheet_get_{row,column}_count functions.
+
+    * gtksheet.c gtksheet.h: Allowed -1 to be passed to
+    gtk_sheet_set_active_cell to indicate no active cell.
+
+Mon May 15 16:10:49 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gtksheet.c: Removed code which rendered the title buttons a second 
+    time.  Cut and Paste error ?
+
+Sat May 13 07:58:32 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+        * gsheetmodel.c gsheetmodel.h gtksheet.c gtksheeet.h: Added
+       free_strings flag to tell the sheet whether to free the string
+       data passed from the model.
+
+Thu May 11 22:20:04 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gtksheet.c, gtksheet.h: Fixed broken deallocation of sheet->pixmap.
+
+Thu May  4 17:55:48 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * gtksheet.c: Added callback on inserted rows.
+
+Sat Jan 28 08:48:08 2006 UTC John Darrington <john@darrington.wattle.id.au>
+
+    * Separated the data out of the GtkSheet.  The gtksheet should now be
+    regarded as a way of looking into the data.  The data is represented by a
+    GSheetModel and the rows and columns by  GSheetRow and GSheetColumn.
diff --git a/lib/gtk-contrib/README b/lib/gtk-contrib/README
new file mode 100644 (file)
index 0000000..e6e23d9
--- /dev/null
@@ -0,0 +1,10 @@
+This is not part of the GNU PSPP program, but is used with GNU PSPP.
+
+This directory contains a version of the GtkXPaned widget.  It includes
+minor modifications. GtkXPaned is licensed under the GNU Lesser
+General Public License.  See COPYING.LESSER. 
+
+
+This directory also contains the PsppireSheet widget which is a very
+heavily modified version of GtkSheet widget.  This modified version if
+licensed under the GNU General Public License version 3 or later.
diff --git a/lib/gtk-contrib/automake.mk b/lib/gtk-contrib/automake.mk
new file mode 100644 (file)
index 0000000..f3cd96e
--- /dev/null
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in  -*- makefile -*-
+
+noinst_LIBRARIES += lib/gtk-contrib/libgtksheet.a
+
+lib_gtk_contrib_libgtksheet_a_CFLAGS = $(GTK_CFLAGS) -Wall -DGDK_MULTIHEAD_SAFE=1
+
+lib_gtk_contrib_libgtksheet_a_SOURCES = \
+       lib/gtk-contrib/gtkextra-sheet.h \
+       lib/gtk-contrib/psppire-sheet.c \
+       lib/gtk-contrib/psppire-sheet.h \
+       lib/gtk-contrib/gtkxpaned.c \
+       lib/gtk-contrib/gtkxpaned.h
+
+EXTRA_DIST += lib/gtk-contrib/OChangeLog \
+       lib/gtk-contrib/README \
+       lib/gtk-contrib/gtk-builder-convert
+
diff --git a/lib/gtk-contrib/gtk-builder-convert b/lib/gtk-contrib/gtk-builder-convert
new file mode 100755 (executable)
index 0000000..bfcb03c
--- /dev/null
@@ -0,0 +1,750 @@
+#!/usr/bin/env python
+#
+#    This file was downloaded from 
+#    http://svn.gnome.org/svn/gtk+/tags/GTK_2_14_7/gtk/gtk-builder-convert
+#    on 11 March 2009
+#
+# Copyright (C) 2006-2008 Async Open Source
+#                         Henrique Romano <henrique@async.com.br>
+#                         Johan Dahlin <jdahlin@async.com.br>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is 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 Lesser General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# TODO:
+#  Toolbars
+
+"""Usage: gtk-builder-convert [OPTION] [INPUT] [OUTPUT]
+Converts Glade files into XML files which can be loaded with GtkBuilder.
+The [INPUT] file is
+
+  -w, --skip-windows     Convert everything but GtkWindow subclasses.
+  -r, --root             Convert only widget named root and its children
+  -h, --help             display this help and exit
+
+When OUTPUT is -, write to standard input.
+
+Examples:
+  gtk-builder-convert preference.glade preferences.ui
+
+Report bugs to http://bugzilla.gnome.org/."""
+
+import getopt
+import os
+import sys
+
+from xml.dom import minidom, Node
+
+WINDOWS = ['GtkWindow',
+           'GtkDialog',
+           'GtkFileChooserDialog',
+           'GtkMessageDialog']
+
+# The subprocess is only available in Python 2.4+
+try:
+    import subprocess
+    subprocess # pyflakes
+except ImportError:
+    subprocess = None
+
+def get_child_nodes(node):
+    assert node.tagName == 'object'
+    nodes = []
+    for child in node.childNodes:
+        if child.nodeType != Node.ELEMENT_NODE:
+            continue
+        if child.tagName != 'child':
+            continue
+        nodes.append(child)
+    return nodes
+
+def get_properties(node):
+    assert node.tagName == 'object'
+    properties = {}
+    for child in node.childNodes:
+        if child.nodeType != Node.ELEMENT_NODE:
+            continue
+        if child.tagName != 'property':
+            continue
+        value = child.childNodes[0].data
+        properties[child.getAttribute('name')] = value
+    return properties
+
+def get_property(node, property_name):
+    assert node.tagName == 'object'
+    properties = get_properties(node)
+    return properties.get(property_name)
+
+def get_property_node(node, property_name):
+    assert node.tagName == 'object'
+    properties = {}
+    for child in node.childNodes:
+        if child.nodeType != Node.ELEMENT_NODE:
+            continue
+        if child.tagName != 'property':
+            continue
+        if child.getAttribute('name') == property_name:
+            return child
+
+def get_signal_nodes(node):
+    assert node.tagName == 'object'
+    signals = []
+    for child in node.childNodes:
+        if child.nodeType != Node.ELEMENT_NODE:
+            continue
+        if child.tagName == 'signal':
+            signals.append(child)
+    return signals
+
+def get_property_nodes(node):
+    assert node.tagName == 'object'
+    properties = []
+    for child in node.childNodes:
+        if child.nodeType != Node.ELEMENT_NODE:
+            continue
+        # FIXME: handle comments
+        if child.tagName == 'property':
+            properties.append(child)
+    return properties
+
+def get_accelerator_nodes(node):
+    assert node.tagName == 'object'
+    accelerators = []
+    for child in node.childNodes:
+        if child.nodeType != Node.ELEMENT_NODE:
+            continue
+        if child.tagName == 'accelerator':
+            accelerators.append(child)
+    return accelerators
+
+def get_object_node(child_node):
+    assert child_node.tagName == 'child', child_node
+    nodes = []
+    for node in child_node.childNodes:
+        if node.nodeType != Node.ELEMENT_NODE:
+            continue
+        if node.tagName == 'object':
+            nodes.append(node)
+    assert len(nodes) == 1, nodes
+    return nodes[0]
+
+def copy_properties(node, props, prop_dict):
+    assert node.tagName == 'object'
+    for prop_name in props:
+        child = get_property_node(node, prop_name)
+        if child is not None:
+            prop_dict[prop_name] = child
+
+    return node
+
+class GtkBuilderConverter(object):
+
+    def __init__(self, skip_windows, root):
+        self.skip_windows = skip_windows
+        self.root = root
+        self.root_objects = []
+        self.objects = {}
+
+    #
+    # Public API
+    #
+
+    def parse_file(self, file):
+        self._dom = minidom.parse(file)
+        self._parse()
+
+    def parse_buffer(self, buffer):
+        self._dom = minidom.parseString(buffer)
+        self._parse()
+
+    def to_xml(self):
+        xml = self._dom.toprettyxml("", "")
+        return xml.encode('utf-8')
+
+    #
+    # Private
+    #
+
+    def _get_object(self, name):
+        return self.objects.get(name)
+
+    def _get_objects_by_attr(self, attribute, value):
+        return [w for w in self._dom.getElementsByTagName("object")
+                      if w.getAttribute(attribute) == value]
+
+    def _create_object(self, obj_class, obj_id, template=None, properties=None):
+        """
+        Creates a new <object> tag.
+        Optionally a name template can be provided which will be used
+        to avoid naming collisions.
+        The properties dictionary can either contain string values or Node
+        values. If a node is provided the name of the node will be overridden
+        by the dictionary key.
+
+        @param obj_class: class of the object (class tag)
+        @param obj_id: identifier of the object (id tag)
+        @param template: name template to use, for example 'button'
+        @param properties: dictionary of properties
+        @type properties: string or Node.
+        @returns: Newly created node of the object
+        """
+        if template is not None:
+            count = 1
+            while True:
+                obj_id = template + str(count)
+                widget = self._get_object(obj_id)
+                if widget is None:
+                    break
+
+                count += 1
+
+        obj = self._dom.createElement('object')
+        obj.setAttribute('class', obj_class)
+        obj.setAttribute('id', obj_id)
+        if properties:
+            for name, value in properties.items():
+                if isinstance(value, Node):
+                    # Reuse the node, so translatable and context still will be
+                    # set when converting nodes. See also #509153
+                    prop = value
+                else:
+                    prop = self._dom.createElement('property')
+                    prop.appendChild(self._dom.createTextNode(value))
+
+                prop.setAttribute('name', str(name))
+                obj.appendChild(prop)
+        self.objects[obj_id] = obj
+        return obj
+
+    def _create_root_object(self, obj_class, template, properties=None):
+        obj = self._create_object(obj_class, None, template, properties)
+        self.root_objects.append(obj)
+        return obj
+
+    def _parse(self):
+        glade_iface = self._dom.getElementsByTagName("glade-interface")
+        assert glade_iface, ("Badly formed XML, there is "
+                             "no <glade-interface> tag.")
+        # Rename glade-interface to interface
+        glade_iface[0].tagName = 'interface'
+        self._interface = glade_iface[0]
+
+        # Remove glade-interface doc type
+        for node in self._dom.childNodes:
+            if node.nodeType == Node.DOCUMENT_TYPE_NODE:
+                if node.name == 'glade-interface':
+                    self._dom.removeChild(node)
+
+        # Strip unsupported tags
+        for tag in ['requires', 'requires-version']:
+            for child in self._dom.getElementsByTagName(tag):
+                child.parentNode.removeChild(child)
+
+        if self.root:
+            self._strip_root(self.root)
+
+        # Rename widget to object
+        objects = self._dom.getElementsByTagName("widget")
+        for node in objects:
+            node.tagName = "object"
+
+        for node in objects:
+            self._convert(node.getAttribute("class"), node)
+            self.objects[node.getAttribute('id')] = node
+
+        # Convert Gazpachos UI tag
+        for node in self._dom.getElementsByTagName("ui"):
+            self._convert_ui(node)
+
+        # Convert accessibility tag
+        for node in self._dom.getElementsByTagName("accessibility"):
+            self._convert_accessibility(node)
+
+        # Output the newly created root objects and sort them
+        # by attribute id
+        for obj in sorted(self.root_objects,
+                          key=lambda n: n.getAttribute('id'),
+                          reverse=True):
+            self._interface.childNodes.insert(0, obj)
+
+    def _convert(self, klass, node):
+        if klass == 'GtkNotebook':
+            self._packing_prop_to_child_attr(node, "type", "tab")
+        elif klass in ['GtkExpander', 'GtkFrame']:
+            self._packing_prop_to_child_attr(
+                node, "type", "label_item", "label")
+        elif klass == "GtkMenuBar":
+            self._convert_menu(node)
+        elif klass == "GtkMenu":
+            # Only convert toplevel popups
+            if node.parentNode == self._interface:
+                self._convert_menu(node, popup=True)
+        elif klass in WINDOWS and self.skip_windows:
+            self._remove_window(node)
+        self._default_widget_converter(node)
+
+    def _default_widget_converter(self, node):
+        klass = node.getAttribute("class")
+        for prop in get_property_nodes(node):
+            prop_name = prop.getAttribute("name")
+            if prop_name == "sizegroup":
+                self._convert_sizegroup(node, prop)
+            elif prop_name == "tooltip" and klass != "GtkAction":
+                prop.setAttribute("name", "tooltip-text")
+            elif prop_name in ["response_id", 'response-id']:
+                # It does not make sense to convert responses when
+                # we're not going to output dialogs
+                if self.skip_windows:
+                    continue
+                object_id = node.getAttribute('id')
+                response = prop.childNodes[0].data
+                self._convert_dialog_response(node, object_id, response)
+                prop.parentNode.removeChild(prop)
+            elif prop_name == "adjustment":
+                self._convert_adjustment(prop)
+            elif prop_name == "items" and klass in ['GtkComboBox',
+                                                    'GtkComboBoxEntry']:
+                self._convert_combobox_items(node, prop)
+            elif prop_name == "text" and klass == 'GtkTextView':
+                self._convert_textview_text(prop)
+
+    def _remove_window(self, node):
+        object_node = get_object_node(get_child_nodes(node)[0])
+        parent = node.parentNode
+        parent.removeChild(node)
+        parent.appendChild(object_node)
+
+    def _convert_menu(self, node, popup=False):
+        if node.hasAttribute('constructor'):
+            return
+
+        uimgr = self._create_root_object('GtkUIManager',
+                                         template='uimanager')
+
+        if popup:
+            name = 'popup'
+        else:
+            name = 'menubar'
+
+        menu = self._dom.createElement(name)
+        menu.setAttribute('name', node.getAttribute('id'))
+        node.setAttribute('constructor', uimgr.getAttribute('id'))
+
+        for child in get_child_nodes(node):
+            obj_node = get_object_node(child)
+            item = self._convert_menuitem(uimgr, obj_node)
+            menu.appendChild(item)
+            child.removeChild(obj_node)
+            child.parentNode.removeChild(child)
+
+        ui = self._dom.createElement('ui')
+        uimgr.appendChild(ui)
+
+        ui.appendChild(menu)
+
+    def _convert_menuitem(self, uimgr, obj_node):
+        children = get_child_nodes(obj_node)
+        name = 'menuitem'
+        if children:
+            child_node = children[0]
+            menu_node = get_object_node(child_node)
+            # Can be GtkImage, which will take care of later.
+            if menu_node.getAttribute('class') == 'GtkMenu':
+                name = 'menu'
+
+        object_class = obj_node.getAttribute('class')
+        if object_class in ['GtkMenuItem',
+                            'GtkImageMenuItem',
+                            'GtkCheckMenuItem',
+                            'GtkRadioMenuItem']:
+            menu = self._dom.createElement(name)
+        elif object_class == 'GtkSeparatorMenuItem':
+            return self._dom.createElement('separator')
+        else:
+            raise NotImplementedError(object_class)
+
+        menu.setAttribute('action', obj_node.getAttribute('id'))
+        self._add_action_from_menuitem(uimgr, obj_node)
+        if children:
+            for child in get_child_nodes(menu_node):
+                obj_node = get_object_node(child)
+                item = self._convert_menuitem(uimgr, obj_node)
+                menu.appendChild(item)
+                child.removeChild(obj_node)
+                child.parentNode.removeChild(child)
+        return menu
+
+    def _menuitem_to_action(self, node, properties):
+        copy_properties(node, ['label', 'tooltip'], properties)
+
+    def _togglemenuitem_to_action(self, node, properties):
+        self._menuitem_to_action(node, properties)
+        copy_properties(node, ['active'], properties)
+
+    def _radiomenuitem_to_action(self, node, properties):
+        self._togglemenuitem_to_action(node, properties)
+        copy_properties(node, ['group'], properties)
+
+    def _add_action_from_menuitem(self, uimgr, node):
+        properties = {}
+        object_class = node.getAttribute('class')
+        object_id = node.getAttribute('id')
+        if object_class == 'GtkMenuItem':
+            name = 'GtkAction'
+            self._menuitem_to_action(node, properties)
+        elif object_class == 'GtkCheckMenuItem':
+            name = 'GtkToggleAction'
+            self._togglemenuitem_to_action(node, properties)
+        elif object_class == 'GtkRadioMenuItem':
+            name = 'GtkRadioAction'
+            self._radiomenuitem_to_action(node, properties)
+        elif object_class == 'GtkImageMenuItem':
+            name = 'GtkAction'
+            children = get_child_nodes(node)
+            if (children and
+                children[0].getAttribute('internal-child') == 'image'):
+                image = get_object_node(children[0])
+                child = get_property_node(image, 'stock')
+                if child is not None:
+                    properties['stock_id'] = child
+            self._menuitem_to_action(node, properties)
+        elif object_class == 'GtkSeparatorMenuItem':
+            return
+        else:
+            raise NotImplementedError(object_class)
+
+        if get_property(node, 'use_stock') == 'True':
+            if 'label' in properties:
+                properties['stock_id'] = properties['label']
+                del properties['label']
+
+        properties['name'] = object_id
+        action = self._create_object(name,
+                                     object_id,
+                                     properties=properties)
+        for signal in get_signal_nodes(node):
+            signal_name = signal.getAttribute('name')
+            if signal_name in ['activate', 'toggled']:
+                action.appendChild(signal)
+            else:
+                print 'Unhandled signal %s::%s' % (node.getAttribute('class'),
+                                                   signal_name)
+
+        if not uimgr.childNodes:
+            child = self._dom.createElement('child')
+            uimgr.appendChild(child)
+
+            group = self._create_object('GtkActionGroup', None,
+                                        template='actiongroup')
+            child.appendChild(group)
+        else:
+            group = uimgr.childNodes[0].childNodes[0]
+
+        child = self._dom.createElement('child')
+        group.appendChild(child)
+        child.appendChild(action)
+
+        for accelerator in get_accelerator_nodes(node):
+            signal_name = accelerator.getAttribute('signal')
+            if signal_name != 'activate':
+                print 'Unhandled accelerator signal for %s::%s' % (
+                    node.getAttribute('class'), signal_name)
+                continue
+            accelerator.removeAttribute('signal')
+            child.appendChild(accelerator)
+
+    def _convert_sizegroup(self, node, prop):
+        # This is Gazpacho only
+        node.removeChild(prop)
+        obj = self._get_object(prop.childNodes[0].data)
+        if obj is None:
+            widgets = self._get_objects_by_attr("class", "GtkSizeGroup")
+            if widgets:
+                obj = widgets[-1]
+            else:
+                obj = self._create_root_object('GtkSizeGroup',
+                                               template='sizegroup')
+
+        widgets = obj.getElementsByTagName("widgets")
+        if widgets:
+            assert len(widgets) == 1
+            widgets = widgets[0]
+        else:
+            widgets = self._dom.createElement("widgets")
+            obj.appendChild(widgets)
+
+        member = self._dom.createElement("widget")
+        member.setAttribute("name", node.getAttribute("id"))
+        widgets.appendChild(member)
+
+    def _convert_dialog_response(self, node, object_name, response):
+        # 1) Get parent dialog node
+        while True:
+            # If we can't find the parent dialog, give up
+            if node == self._dom:
+                return
+
+            if (node.tagName == 'object' and
+                node.getAttribute('class') == 'GtkDialog'):
+                dialog = node
+                break
+            node = node.parentNode
+            assert node
+
+        # 2) Get dialogs action-widgets tag, create if not found
+        for child in dialog.childNodes:
+            if child.nodeType != Node.ELEMENT_NODE:
+                continue
+            if child.tagName == 'action-widgets':
+                actions = child
+                break
+        else:
+            actions = self._dom.createElement("action-widgets")
+            dialog.appendChild(actions)
+
+        # 3) Add action-widget tag for the response
+        action = self._dom.createElement("action-widget")
+        action.setAttribute("response", response)
+        action.appendChild(self._dom.createTextNode(object_name))
+        actions.appendChild(action)
+
+    def _convert_adjustment(self, prop):
+        properties = {}
+        if prop.childNodes:
+            data = prop.childNodes[0].data
+            value, lower, upper, step, page, page_size = data.split(' ')
+            properties.update(value=value,
+                              lower=lower,
+                              upper=upper,
+                              step_increment=step,
+                              page_increment=page,
+                              page_size=page_size)
+        else:
+            prop.appendChild(self._dom.createTextNode(""))
+
+        adj = self._create_root_object("GtkAdjustment",
+                                       template='adjustment',
+                                       properties=properties)
+        prop.childNodes[0].data = adj.getAttribute('id')
+
+    def _convert_combobox_items(self, node, prop):
+        parent = prop.parentNode
+        if not prop.childNodes:
+            parent.removeChild(prop)
+            return
+        value = prop.childNodes[0].data
+        model = self._create_root_object("GtkListStore",
+                                         template="model")
+
+        columns = self._dom.createElement('columns')
+        model.appendChild(columns)
+
+        column = self._dom.createElement('column')
+        column.setAttribute('type', 'gchararray')
+        columns.appendChild(column)
+
+        data = self._dom.createElement('data')
+        model.appendChild(data)
+
+        for item in value.split('\n'):
+            row = self._dom.createElement('row')
+            data.appendChild(row)
+
+            col = self._dom.createElement('col')
+            col.setAttribute('id', '0')
+            col.appendChild(self._dom.createTextNode(item))
+            row.appendChild(col)
+
+        model_prop = self._dom.createElement('property')
+        model_prop.setAttribute('name', 'model')
+        model_prop.appendChild(
+            self._dom.createTextNode(model.getAttribute('id')))
+        parent.appendChild(model_prop)
+
+        parent.removeChild(prop)
+
+        child = self._dom.createElement('child')
+        node.appendChild(child)
+        cell_renderer = self._create_object('GtkCellRendererText', None,
+                                            template='renderer')
+        child.appendChild(cell_renderer)
+
+        attributes = self._dom.createElement('attributes')
+        child.appendChild(attributes)
+
+        attribute = self._dom.createElement('attribute')
+        attributes.appendChild(attribute)
+        attribute.setAttribute('name', 'text')
+        attribute.appendChild(self._dom.createTextNode('0'))
+
+    def _convert_textview_text(self, prop):
+        if not prop.childNodes:
+            prop.parentNode.removeChild(prop)
+            return
+
+        data = prop.childNodes[0].data
+        if prop.hasAttribute('translatable'):
+            prop.removeAttribute('translatable')
+        tbuffer = self._create_root_object("GtkTextBuffer",
+                                           template='textbuffer',
+                                           properties=dict(text=data))
+        prop.childNodes[0].data = tbuffer.getAttribute('id')
+        prop.setAttribute('name', 'buffer')
+
+    def _packing_prop_to_child_attr(self, node, prop_name, prop_val,
+                                   attr_val=None):
+        for child in get_child_nodes(node):
+            packing_props = [p for p in child.childNodes if p.nodeName == "packing"]
+            if not packing_props:
+                continue
+            assert len(packing_props) == 1
+            packing_prop = packing_props[0]
+            properties = packing_prop.getElementsByTagName("property")
+            for prop in properties:
+                if (prop.getAttribute("name") != prop_name or
+                    prop.childNodes[0].data != prop_val):
+                    continue
+                packing_prop.removeChild(prop)
+                child.setAttribute(prop_name, attr_val or prop_val)
+            if len(properties) == 1:
+                child.removeChild(packing_prop)
+
+    def _convert_ui(self, node):
+        cdata = node.childNodes[0]
+        data = cdata.toxml().strip()
+        if not data.startswith("<![CDATA[") or not data.endswith("]]>"):
+            return
+        data = data[9:-3]
+        child = minidom.parseString(data).childNodes[0]
+        nodes = child.childNodes[:]
+        for child_node in nodes:
+            node.appendChild(child_node)
+        node.removeChild(cdata)
+        if not node.hasAttribute("id"):
+            return
+
+        # Updating references made by widgets
+        parent_id = node.parentNode.getAttribute("id")
+        for widget in self._get_objects_by_attr("constructor",
+                                                node.getAttribute("id")):
+            widget.getAttributeNode("constructor").value = parent_id
+        node.removeAttribute("id")
+
+    def _convert_accessibility(self, node):
+        objectNode = node.parentNode
+        parent_id = objectNode.getAttribute("id")
+
+        properties = {}
+        for node in node.childNodes:
+            if node.nodeName == 'atkproperty':
+                node.tagName = 'property'
+                properties[node.getAttribute('name')] = node
+                node.parentNode.removeChild(node)
+            elif node.nodeName == 'atkrelation':
+                node.tagName = 'relation'
+                relation_type = node.getAttribute('type')
+                relation_type = relation_type.replace('_', '-')
+                node.setAttribute('type', relation_type)
+            elif node.nodeName == 'atkaction':
+                node.tagName = 'action'
+
+        if properties:
+            child = self._dom.createElement('child')
+            child.setAttribute("internal-child", "accessible")
+
+            atkobject = self._create_object(
+                "AtkObject", None,
+                template='a11y-%s' % (parent_id,),
+                properties=properties)
+            child.appendChild(atkobject)
+            objectNode.appendChild(child)
+
+    def _strip_root(self, root_name):
+        for widget in self._dom.getElementsByTagName("widget"):
+            if widget.getAttribute('id') == root_name:
+                break
+        else:
+            raise SystemExit("Could not find an object called `%s'" % (
+                root_name))
+
+        for child in self._interface.childNodes[:]:
+            if child.nodeType != Node.ELEMENT_NODE:
+                continue
+            child.parentNode.removeChild(child)
+
+        self._interface.appendChild(widget)
+
+
+def _indent(output):
+    if not subprocess:
+        return output
+
+    for directory in os.environ['PATH'].split(os.pathsep):
+        filename = os.path.join(directory, 'xmllint')
+        if os.path.exists(filename):
+            break
+    else:
+        return output
+
+    s = subprocess.Popen([filename, '--format', '-'],
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE)
+    s.stdin.write(output)
+    s.stdin.close()
+    return s.stdout.read()
+
+def usage():
+    print __doc__
+
+def main(args):
+    try:
+        opts, args = getopt.getopt(args[1:], "hwr:",
+                                   ["help", "skip-windows", "root="])
+    except getopt.GetoptError:
+        usage()
+        return 2
+
+    if len(args) != 2:
+        usage()
+        return 2
+
+    input_filename, output_filename = args
+
+    skip_windows = False
+    split = False
+    root = None
+    for o, a in opts:
+        if o in ("-h", "--help"):
+            usage()
+            sys.exit()
+        elif o in ("-r", "--root"):
+            root = a
+        elif o in ("-w", "--skip-windows"):
+            skip_windows = True
+
+    conv = GtkBuilderConverter(skip_windows=skip_windows,
+                               root=root)
+    conv.parse_file(input_filename)
+
+    xml = _indent(conv.to_xml())
+    if output_filename == "-":
+        print xml
+    else:
+        open(output_filename, 'w').write(xml)
+        print "Wrote", output_filename
+
+    return 0
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
diff --git a/lib/gtk-contrib/gtkextra-sheet.h b/lib/gtk-contrib/gtkextra-sheet.h
new file mode 100644 (file)
index 0000000..6cad27f
--- /dev/null
@@ -0,0 +1,62 @@
+/* This version of GtkSheet has been heavily modified, for the specific
+ *  requirements of PSPPIRE.
+ *
+ * GtkSheet widget for Gtk+.
+ * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
+ *
+ * Based on GtkClist widget by Jay Painter, but major changes.
+ * Memory allocation routines inspired on SC (Spreadsheet Calculator)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef PSPPIRE_EXTRA_SHEET_H__
+#define PSPPIRE_EXTRA_SHEET_H__
+
+
+struct _PsppireSheet ;
+
+typedef struct _PsppireSheet PsppireSheet;
+
+
+struct _PsppireSheetButton
+{
+  GtkStateType state;
+  gchar *label;
+
+  gboolean label_visible;
+
+  GtkJustification justification;
+  gboolean overstruck;
+};
+
+struct _PsppireSheetCell
+{
+  gint row;
+  gint col;
+};
+
+typedef struct _PsppireSheetButton PsppireSheetButton;
+typedef struct _PsppireSheetCell PsppireSheetCell;
+
+PsppireSheetButton * psppire_sheet_button_new (void);
+
+void psppire_sheet_button_free (PsppireSheetButton *button);
+
+
+#endif /* PSPPIRE_EXTRA_SHEET_H__ */
+
+
diff --git a/lib/gtk-contrib/gtkxpaned.c b/lib/gtk-contrib/gtkxpaned.c
new file mode 100644 (file)
index 0000000..e350295
--- /dev/null
@@ -0,0 +1,3261 @@
+/*******************************************************************************
+**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 
+**      10        20        30        40        50        60        70        80
+**
+**  library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+
+**  Copyright (C) 2005-2006 Mirco "MacSlow" Müller <macslow@bangang.de>
+**
+**  This library is free software; you can redistribute it and/or
+**  modify it under the terms of the GNU Lesser General Public
+**  License as published by the Free Software Foundation; either
+**  version 2.1 of the License, or (at your option) any later version.
+**
+**  This library 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
+**  Lesser General Public License for more details.
+**
+**  You should have received a copy of the GNU Lesser General Public
+**  License along with this library; if not, write to the Free Software
+**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**
+**  GtkXPaned is based on GtkPaned which was done by...
+**
+**  "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald"
+**
+**  and later modified by...
+**
+**  "the GTK+ Team and others 1997-2000"
+**
+*******************************************************************************/
+
+#include <config.h>
+#include "gtkxpaned.h"
+#include <ui/gui/psppire-marshal.h>
+#include <gtk/gtkbindings.h>
+#include <gtk/gtksignal.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkmain.h>
+
+enum WidgetProperties
+{
+       PROP_0,
+       PROP_X_POSITION,
+       PROP_Y_POSITION,
+       PROP_POSITION_SET,
+       PROP_MIN_X_POSITION,
+       PROP_MIN_Y_POSITION,
+       PROP_MAX_X_POSITION,
+       PROP_MAX_Y_POSITION
+};
+
+enum ChildProperties
+{
+       CHILD_PROP_0,
+       CHILD_PROP_RESIZE,
+       CHILD_PROP_SHRINK
+};
+
+enum WidgetSignals
+{
+       CYCLE_CHILD_FOCUS,
+       TOGGLE_HANDLE_FOCUS,
+       MOVE_HANDLE,
+       CYCLE_HANDLE_FOCUS,
+       ACCEPT_POSITION,
+       CANCEL_POSITION,
+       LAST_SIGNAL
+};
+
+static void gtk_xpaned_class_init (GtkXPanedClass* klass);
+
+static void gtk_xpaned_init (GtkXPaned* xpaned);
+
+static void gtk_xpaned_size_request (GtkWidget* widget,
+                                                                        GtkRequisition* requisition);
+
+static void gtk_xpaned_size_allocate (GtkWidget* widget,
+                                                                         GtkAllocation* allocation);
+
+static void gtk_xpaned_set_property (GObject* object,
+                                                                        guint prop_id,
+                                                                        const GValue* value,
+                                                                        GParamSpec* pspec);
+
+static void gtk_xpaned_get_property (GObject* object,
+                                                                        guint prop_id,
+                                                                        GValue* value,
+                                                                        GParamSpec* pspec);
+
+static void gtk_xpaned_set_child_property (GtkContainer* container,
+                                                                                  GtkWidget* child,
+                                                                                  guint property_id,
+                                                                                  const GValue* value,
+                                                                                  GParamSpec* pspec);
+
+static void gtk_xpaned_get_child_property (GtkContainer* container,
+                                                                                  GtkWidget* child,
+                                                                                  guint property_id,
+                                                                                  GValue* value,
+                                                                                  GParamSpec* pspec);
+
+static void gtk_xpaned_finalize (GObject* object);
+
+static void gtk_xpaned_realize (GtkWidget* widget);
+
+static void gtk_xpaned_unrealize (GtkWidget* widget);
+
+static void gtk_xpaned_map (GtkWidget* widget);
+
+static void gtk_xpaned_unmap (GtkWidget* widget);
+
+static gboolean gtk_xpaned_expose (GtkWidget* widget, GdkEventExpose* event);
+
+static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event);
+
+static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event);
+
+static gboolean gtk_xpaned_button_press (GtkWidget* widget,
+                                                                                GdkEventButton* event);
+
+static gboolean gtk_xpaned_button_release (GtkWidget* widget,
+                                                                                  GdkEventButton* event);
+
+static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event);
+
+static gboolean gtk_xpaned_focus (GtkWidget* widget,
+                                                                 GtkDirectionType direction);
+
+static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget);
+
+static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget);
+
+static void gtk_xpaned_forall (GtkContainer* container,
+                                                          gboolean include_internals,
+                                                          GtkCallback callback,
+                                                          gpointer callback_data);
+
+static void gtk_xpaned_set_focus_child (GtkContainer* container,
+                                                                           GtkWidget* child);
+
+static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget);
+
+static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned,
+                                                                                GtkXPaned* first_xpaned);
+
+static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned,
+                                                                                                         GtkWidget* widget);
+
+static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned,
+                                                                                                          GtkWidget* widget);
+
+static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned,
+                                                                                                                GtkWidget* widget);
+
+static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned,
+                                                                                                                 GtkWidget* widget);
+
+static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned,
+                                                                                         gboolean reverse);
+
+static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned,
+                                                                                          gboolean reverse);
+
+static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned,
+                                                                           GtkScrollType scroll);
+
+static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned);
+
+static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned);
+
+static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned);
+
+static GType gtk_xpaned_child_type (GtkContainer* container);
+
+static GtkContainerClass* parent_class = NULL;
+
+struct _GtkXPanedPrivate
+{
+       GtkWidget *saved_focus;
+       GtkXPaned *first_xpaned;
+};
+
+GType gtk_xpaned_get_type (void)
+{
+       static GType xpaned_type = 0;
+  
+       if (!xpaned_type)
+       {
+               static const GTypeInfo xpaned_info =
+               {
+                       sizeof (GtkXPanedClass),
+                       NULL,           /* base_init */
+                       NULL,           /* base_finalize */
+                       (GClassInitFunc) gtk_xpaned_class_init,
+                       NULL,           /* class_finalize */
+                       NULL,           /* class_data */
+                       sizeof (GtkXPaned),
+                       0,              /* n_preallocs */
+                       (GInstanceInitFunc) gtk_xpaned_init
+               };
+
+               xpaned_type = g_type_register_static (GTK_TYPE_CONTAINER,
+                                                                                         "GtkXPaned",
+                                                                                         &xpaned_info,
+                                                                                         0);
+       }
+
+       return xpaned_type;
+}
+
+GtkWidget* gtk_xpaned_new (void)
+{
+       GtkXPaned* xpaned;
+
+       xpaned = g_object_new (GTK_TYPE_XPANED, NULL);
+
+       return GTK_WIDGET (xpaned);
+}
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void add_tab_bindings (GtkBindingSet* binding_set,
+                                                         GdkModifierType modifiers)
+{
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_Tab,
+                                                                 modifiers,
+                                                                 "toggle_handle_focus",
+                                                                 0);
+
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_KP_Tab,
+                                                                 modifiers,
+                                                                 "toggle_handle_focus",
+                                                                 0);
+}
+
+static void add_move_binding (GtkBindingSet* binding_set,
+                                                         guint keyval,
+                                                         GdkModifierType mask,
+                                                         GtkScrollType scroll)
+{
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 keyval,
+                                                                 mask,
+                                                                 "move_handle",
+                                                                 1,
+                                                                 GTK_TYPE_SCROLL_TYPE,
+                                                                 scroll);
+}
+
+static void gtk_xpaned_class_init (GtkXPanedClass* class)
+{
+       GObjectClass* object_class;
+       GtkWidgetClass* widget_class;
+       GtkContainerClass* container_class;
+       GtkXPanedClass* xpaned_class;
+       GtkBindingSet* binding_set;
+
+       object_class = (GObjectClass *) class;
+       widget_class = (GtkWidgetClass *) class;
+       container_class = (GtkContainerClass *) class;
+       xpaned_class = (GtkXPanedClass *) class;
+
+       parent_class = g_type_class_peek_parent (class);
+
+       object_class->set_property = gtk_xpaned_set_property;
+       object_class->get_property = gtk_xpaned_get_property;
+       object_class->finalize = gtk_xpaned_finalize;
+
+       widget_class->realize = gtk_xpaned_realize;
+       widget_class->unrealize = gtk_xpaned_unrealize;
+       widget_class->map = gtk_xpaned_map;
+       widget_class->unmap = gtk_xpaned_unmap;
+       widget_class->expose_event = gtk_xpaned_expose;
+       widget_class->focus = gtk_xpaned_focus;
+       widget_class->enter_notify_event = gtk_xpaned_enter;
+       widget_class->leave_notify_event = gtk_xpaned_leave;
+       widget_class->button_press_event = gtk_xpaned_button_press;
+       widget_class->button_release_event = gtk_xpaned_button_release;
+       widget_class->motion_notify_event = gtk_xpaned_motion;
+       widget_class->size_request = gtk_xpaned_size_request;
+       widget_class->size_allocate = gtk_xpaned_size_allocate;
+
+       container_class->add = gtk_xpaned_add;
+       container_class->remove = gtk_xpaned_remove;
+       container_class->forall = gtk_xpaned_forall;
+       container_class->child_type = gtk_xpaned_child_type;
+       container_class->set_focus_child = gtk_xpaned_set_focus_child;
+       container_class->set_child_property = gtk_xpaned_set_child_property;
+       container_class->get_child_property = gtk_xpaned_get_child_property;
+
+       xpaned_class->cycle_child_focus = gtk_xpaned_cycle_child_focus;
+       xpaned_class->toggle_handle_focus = gtk_xpaned_toggle_handle_focus;
+       xpaned_class->move_handle = gtk_xpaned_move_handle;
+       xpaned_class->cycle_handle_focus = gtk_xpaned_cycle_handle_focus;
+       xpaned_class->accept_position = gtk_xpaned_accept_position;
+       xpaned_class->cancel_position = gtk_xpaned_cancel_position;
+
+       g_object_class_install_property (object_class,
+                                                                        PROP_X_POSITION,
+                                                                        g_param_spec_int ("x-position",
+                                                                                                          ("x-Position"),
+                                                                                                          ("x-Position of paned separator in pixels (0 means all the way to the left)"),
+                                                                                                          0,
+                                                                                                          G_MAXINT,
+                                                                                                          0,
+                                                                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+       g_object_class_install_property (object_class,
+                                                                        PROP_Y_POSITION,
+                                                                        g_param_spec_int ("y-position",
+                                                                                                          "y-Position",
+                                                                                                          "y-Position of paned separator in pixels (0 means all the way to the top)",
+                                                                                                          0,
+                                                                                                          G_MAXINT,
+                                                                                                          0,
+                                                                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+       g_object_class_install_property (object_class,
+                                                                        PROP_POSITION_SET,
+                                                                        g_param_spec_boolean ("position-set",
+                                                                                                                  "Position Set",
+                                                                                                                  "TRUE if the Position property should be used",
+                                                                                                                  FALSE,
+                                                                                                                  G_PARAM_READABLE | G_PARAM_WRITABLE));
+                                  
+       gtk_widget_class_install_style_property (widget_class,
+                                                                                        g_param_spec_int ("handle-size",
+                                                                                                                          "Handle Size",
+                                                                                                                          "Width of handle",
+                                                                                                                          0,
+                                                                                                                          G_MAXINT,
+                                                                                                                          3,
+                                                                                                                          G_PARAM_READABLE));
+       /**
+       * GtkXPaned:min-x-position:
+       *
+       * The smallest possible value for the x-position property. This property is derived from the
+       * size and shrinkability of the widget's children.
+       *
+       * Since: 2.4
+       */
+       g_object_class_install_property (object_class,
+                                                                        PROP_MIN_X_POSITION,
+                                                                        g_param_spec_int ("min-x-position",
+                                                                                                          "Minimal x-Position",
+                                                                                                          "Smallest possible value for the \"x-position\" property",
+                                                                                                          0,
+                                                                                                          G_MAXINT,
+                                                                                                          0,
+                                                                                                          G_PARAM_READABLE));
+
+       /**
+       * GtkXPaned:min-y-position:
+       *
+       * The smallest possible value for the y-position property. This property is derived from the
+       * size and shrinkability of the widget's children.
+       *
+       * Since: 2.4
+       */
+       g_object_class_install_property (object_class,
+                                                                        PROP_MIN_Y_POSITION,
+                                                                        g_param_spec_int ("min-y-position",
+                                                                                                          "Minimal y-Position",
+                                                                                                          "Smallest possible value for the \"y-position\" property",
+                                                                                                          0,
+                                                                                                          G_MAXINT,
+                                                                                                          0,
+                                                                                                          G_PARAM_READABLE));
+
+       /**
+       * GtkPaned:max-x-position:
+       *
+       * The largest possible value for the x-position property. This property is derived from the
+       * size and shrinkability of the widget's children.
+       *
+       * Since: 2.4
+       */
+       g_object_class_install_property (object_class,
+                                                                        PROP_MAX_X_POSITION,
+                                                                        g_param_spec_int ("max-x-position",
+                                                                                                          "Maximal x-Position",
+                                                                                                          "Largest possible value for the \"x-position\" property",
+                                                                                                          0,
+                                                                                                          G_MAXINT,
+                                                                                                          G_MAXINT,
+                                                                                                          G_PARAM_READABLE));
+
+       /**
+       * GtkPaned:max-y-position:
+       *
+       * The largest possible value for the y-position property. This property is derived from the
+       * size and shrinkability of the widget's children.
+       *
+       * Since: 2.4
+       */
+       g_object_class_install_property (object_class,
+                                                                        PROP_MAX_Y_POSITION,
+                                                                        g_param_spec_int ("max-y-position",
+                                                                                                          "Maximal y-Position",
+                                                                                                          "Largest possible value for the \"y-position\" property",
+                                                                                                          0,
+                                                                                                          G_MAXINT,
+                                                                                                          G_MAXINT,
+                                                                                                          G_PARAM_READABLE));
+
+       /**
+       * GtkPaned:resize:
+       *
+       * The "resize" child property determines whether the child expands and 
+       * shrinks along with the paned widget.
+       * 
+       * Since: 2.4 
+       */
+       gtk_container_class_install_child_property (container_class,
+                                                                                               CHILD_PROP_RESIZE,
+                                                                                               g_param_spec_boolean ("resize",
+                                                                                                                                         "Resize",
+                                                                                                                                         "If TRUE, the child expands and shrinks along with the paned widget",
+                                                                                                                                         TRUE,
+                                                                                                                                         G_PARAM_READWRITE));
+
+       /**
+       * GtkPaned:shrink:
+       *
+       * The "shrink" child property determines whether the child can be made 
+       * smaller than its requisition.
+       * 
+       * Since: 2.4 
+       */
+       gtk_container_class_install_child_property (container_class,
+                                                                                               CHILD_PROP_SHRINK,
+                                                                                               g_param_spec_boolean ("shrink", 
+                                                                                                                                         "Shrink",
+                                                                                                                                         "If TRUE, the child can be made smaller than its requisition",
+                                                                                                                                         TRUE,
+                                                                                                                                         G_PARAM_READWRITE));
+
+       signals [CYCLE_CHILD_FOCUS] = g_signal_new ("cycle-child-focus",
+                                                                                               G_TYPE_FROM_CLASS (object_class),
+                                                                                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                                                                               G_STRUCT_OFFSET (GtkXPanedClass, cycle_child_focus),
+                                                                                               NULL, NULL,
+                                                                                               psppire_marshal_BOOLEAN__BOOLEAN,
+                                                                                               G_TYPE_BOOLEAN, 1,
+                                                                                               G_TYPE_BOOLEAN);
+
+       signals [TOGGLE_HANDLE_FOCUS] = g_signal_new ("toggle-handle-focus",
+                                                                                                 G_TYPE_FROM_CLASS (object_class),
+                                                                                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                                                                                 G_STRUCT_OFFSET (GtkXPanedClass, toggle_handle_focus),
+                                                                                                 NULL, NULL,
+                                                                                                 psppire_marshal_BOOLEAN__VOID,
+                                                                                                 G_TYPE_BOOLEAN, 0);
+
+       signals[MOVE_HANDLE] = g_signal_new ("move-handle",
+                                                                                G_TYPE_FROM_CLASS (object_class),
+                                                                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                                                                G_STRUCT_OFFSET (GtkXPanedClass, move_handle),
+                                                                                NULL, NULL,
+                                                                                psppire_marshal_BOOLEAN__ENUM,
+                                                                                G_TYPE_BOOLEAN, 1,
+                                                                                GTK_TYPE_SCROLL_TYPE);
+
+       signals [CYCLE_HANDLE_FOCUS] = g_signal_new ("cycle-handle-focus",
+                                                                                                G_TYPE_FROM_CLASS (object_class),
+                                                                                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                                                                                G_STRUCT_OFFSET (GtkXPanedClass, cycle_handle_focus),
+                                                                                                NULL, NULL,
+                                                                                                psppire_marshal_BOOLEAN__BOOLEAN,
+                                                                                                G_TYPE_BOOLEAN, 1,
+                                                                                                G_TYPE_BOOLEAN);
+
+       signals [ACCEPT_POSITION] = g_signal_new ("accept-position",
+                                                                                         G_TYPE_FROM_CLASS (object_class),
+                                                                                         G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                                                                         G_STRUCT_OFFSET (GtkXPanedClass, accept_position),
+                                                                                         NULL, NULL,
+                                                                                         psppire_marshal_BOOLEAN__VOID,
+                                                                                         G_TYPE_BOOLEAN, 0);
+
+       signals [CANCEL_POSITION] = g_signal_new ("cancel-position",
+                                                                                         G_TYPE_FROM_CLASS (object_class),
+                                                                                         G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                                                                         G_STRUCT_OFFSET (GtkXPanedClass, cancel_position),
+                                                                                         NULL, NULL,
+                                                                                         psppire_marshal_BOOLEAN__VOID,
+                                                                                         G_TYPE_BOOLEAN, 0);
+
+       binding_set = gtk_binding_set_by_class (class);
+
+       /* F6 and friends */
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_F6, 0,
+                                                                 "cycle-child-focus", 1, 
+                                                                 G_TYPE_BOOLEAN, FALSE);
+
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_F6, GDK_SHIFT_MASK,
+                                                                 "cycle-child-focus", 1,
+                                                                 G_TYPE_BOOLEAN, TRUE);
+
+       /* F8 and friends */
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_F8, 0,
+                                                                 "cycle-handle-focus", 1,
+                                                                 G_TYPE_BOOLEAN, FALSE);
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_F8, GDK_SHIFT_MASK,
+                                                                 "cycle-handle-focus", 1,
+                                                                 G_TYPE_BOOLEAN, TRUE);
+       add_tab_bindings (binding_set, 0);
+       add_tab_bindings (binding_set, GDK_CONTROL_MASK);
+       add_tab_bindings (binding_set, GDK_SHIFT_MASK);
+       add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
+
+       /* accept and cancel positions */
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_Escape, 0,
+                                                                 "cancel-position", 0);
+
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_Return, 0,
+                                                                 "accept-position", 0);
+
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_KP_Enter, 0,
+                                                                 "accept-position", 0);
+
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_space, 0,
+                                                                 "accept-position", 0);
+                                                                 
+       gtk_binding_entry_add_signal (binding_set,
+                                                                 GDK_KP_Space, 0,
+                                                                 "accept-position", 0);
+
+       /* move handle */
+       add_move_binding (binding_set, GDK_Left, 0, GTK_SCROLL_STEP_LEFT);
+       add_move_binding (binding_set, GDK_KP_Left, 0, GTK_SCROLL_STEP_LEFT);
+       add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
+       add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
+
+       add_move_binding (binding_set, GDK_Right, 0, GTK_SCROLL_STEP_RIGHT);
+       add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
+       add_move_binding (binding_set, GDK_KP_Right, 0, GTK_SCROLL_STEP_RIGHT);
+       add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
+
+       add_move_binding (binding_set, GDK_Up, 0, GTK_SCROLL_STEP_UP);
+       add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
+       add_move_binding (binding_set, GDK_KP_Up, 0, GTK_SCROLL_STEP_UP);
+       add_move_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
+       add_move_binding (binding_set, GDK_Page_Up, 0, GTK_SCROLL_PAGE_UP);
+       add_move_binding (binding_set, GDK_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP);
+
+       add_move_binding (binding_set, GDK_Down, 0, GTK_SCROLL_STEP_DOWN);
+       add_move_binding (binding_set, GDK_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
+       add_move_binding (binding_set, GDK_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
+       add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
+       add_move_binding (binding_set, GDK_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
+       add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
+
+       add_move_binding (binding_set, GDK_Home, 0, GTK_SCROLL_START);
+       add_move_binding (binding_set, GDK_KP_Home, 0, GTK_SCROLL_START);
+       add_move_binding (binding_set, GDK_End, 0, GTK_SCROLL_END);
+       add_move_binding (binding_set, GDK_KP_End, 0, GTK_SCROLL_END);
+}
+
+static GType gtk_xpaned_child_type (GtkContainer* container)
+{
+       if (!GTK_XPANED (container)->top_left_child || 
+               !GTK_XPANED (container)->top_right_child ||
+               !GTK_XPANED (container)->bottom_left_child || 
+               !GTK_XPANED (container)->bottom_right_child)
+               return GTK_TYPE_WIDGET;
+         else
+               return G_TYPE_NONE;
+}
+
+static void gtk_xpaned_init (GtkXPaned* xpaned)
+{
+       GTK_WIDGET_SET_FLAGS (xpaned, GTK_NO_WINDOW | GTK_CAN_FOCUS);
+  
+       xpaned->top_left_child = NULL;
+       xpaned->top_right_child = NULL;
+       xpaned->bottom_left_child = NULL;
+       xpaned->bottom_right_child = NULL;
+       xpaned->handle_east = NULL;
+       xpaned->handle_west = NULL;
+       xpaned->handle_north = NULL;
+       xpaned->handle_south = NULL;
+       xpaned->handle_middle = NULL;
+       xpaned->xor_gc = NULL;
+       xpaned->cursor_type_east = GDK_SB_V_DOUBLE_ARROW;
+       xpaned->cursor_type_west = GDK_SB_V_DOUBLE_ARROW;
+       xpaned->cursor_type_north = GDK_SB_H_DOUBLE_ARROW;
+       xpaned->cursor_type_south = GDK_SB_H_DOUBLE_ARROW;
+       xpaned->cursor_type_middle = GDK_FLEUR;
+
+       xpaned->handle_pos_east.width = 5;
+       xpaned->handle_pos_east.height = 5;
+       xpaned->handle_pos_west.width = 5;
+       xpaned->handle_pos_west.height = 5;
+       xpaned->handle_pos_north.width = 5;
+       xpaned->handle_pos_north.height = 5;
+       xpaned->handle_pos_south.width = 5;
+       xpaned->handle_pos_south.height = 5;
+       xpaned->handle_pos_middle.width = 5;
+       xpaned->handle_pos_middle.height = 5;
+
+       xpaned->position_set = FALSE;
+       xpaned->last_allocation.width = -1;
+       xpaned->last_allocation.height = -1;
+       xpaned->in_drag_vert = FALSE;
+       xpaned->in_drag_horiz = FALSE;
+       xpaned->in_drag_vert_and_horiz = FALSE;
+
+       xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE;
+       xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE;
+       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE;
+       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE;
+
+       xpaned->priv = g_new0 (GtkXPanedPrivate, 1);
+       xpaned->last_top_left_child_focus = NULL;
+       xpaned->last_top_right_child_focus = NULL;
+       xpaned->last_bottom_left_child_focus = NULL;
+       xpaned->last_bottom_right_child_focus = NULL;
+       xpaned->in_recursion = FALSE;
+       xpaned->handle_prelit = FALSE;
+       xpaned->original_position.x = -1;
+       xpaned->original_position.y = -1;
+       xpaned->unmaximized_position.x = -1;
+       xpaned->unmaximized_position.y = -1;
+
+       xpaned->handle_pos_east.x = -1;
+       xpaned->handle_pos_east.y = -1;
+       xpaned->handle_pos_west.x = -1;
+       xpaned->handle_pos_west.y = -1;
+       xpaned->handle_pos_north.x = -1;
+       xpaned->handle_pos_north.y = -1;
+       xpaned->handle_pos_south.x = -1;
+       xpaned->handle_pos_south.y = -1;
+       xpaned->handle_pos_middle.x = -1;
+       xpaned->handle_pos_middle.y = -1;
+
+       xpaned->drag_pos.x = -1;
+       xpaned->drag_pos.y = -1;
+}
+
+static void gtk_xpaned_size_request (GtkWidget* widget,
+                                                                        GtkRequisition* requisition)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+       GtkRequisition child_requisition;
+
+       requisition->width = 0;
+       requisition->height = 0;
+
+       if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child))
+       {
+               gtk_widget_size_request (xpaned->top_left_child, &child_requisition);
+
+               requisition->width = child_requisition.width;
+               requisition->height = child_requisition.height;
+       }
+
+       if (xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child))
+       {
+               gtk_widget_size_request (xpaned->top_right_child, &child_requisition);
+
+               requisition->width += child_requisition.width;
+               requisition->height = MAX (requisition->height, child_requisition.height);
+       }
+
+       if (xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child))
+       {
+               gtk_widget_size_request (xpaned->bottom_left_child, &child_requisition);
+
+               requisition->width = MAX (requisition->width, child_requisition.width);
+               requisition->height += child_requisition.height;
+       }
+
+       if (xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child))
+       {
+               gtk_widget_size_request (xpaned->bottom_right_child, &child_requisition);
+
+               requisition->width = MAX (requisition->width, child_requisition.width);
+               requisition->height = MAX (requisition->height, child_requisition.height);
+       }
+
+       /* add 2 times the set border-width to the GtkXPaneds requisition */
+       requisition->width += GTK_CONTAINER (xpaned)->border_width * 2;
+       requisition->height += GTK_CONTAINER (xpaned)->border_width * 2;
+
+       /* also add the handle "thickness" to GtkXPaneds width- and height-requisitions */
+       if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) &&
+               xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) &&
+               xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) &&
+               xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child))
+       {
+               gint handle_size;
+
+               gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+               requisition->width += handle_size;
+               requisition->height += handle_size;
+       }
+}
+
+void
+gtk_xpaned_compute_position (GtkXPaned* xpaned,
+                            const GtkAllocation* allocation,
+                            GtkRequisition* top_left_child_req,
+                            GtkRequisition* top_right_child_req,
+                            GtkRequisition* bottom_left_child_req,
+                            GtkRequisition* bottom_right_child_req);
+
+
+static void gtk_xpaned_size_allocate (GtkWidget* widget,
+                                                                         GtkAllocation* allocation)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+       gint border_width = GTK_CONTAINER (xpaned)->border_width;
+       GtkAllocation top_left_child_allocation;
+       GtkAllocation top_right_child_allocation;
+       GtkAllocation bottom_left_child_allocation;
+       GtkAllocation bottom_right_child_allocation;
+       GtkRequisition top_left_child_requisition;
+       GtkRequisition top_right_child_requisition;
+       GtkRequisition bottom_left_child_requisition;
+       GtkRequisition bottom_right_child_requisition;
+       gint handle_size;
+
+       /* determine size of handle(s) */
+       gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+
+       widget->allocation = *allocation;
+
+       if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) &&
+               xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) &&
+               xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) &&
+               xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child))
+    {
+               /* what sizes do the children want to be at least at */
+               gtk_widget_get_child_requisition (xpaned->top_left_child,
+                                                                                 &top_left_child_requisition);
+               gtk_widget_get_child_requisition (xpaned->top_right_child,
+                                                                                 &top_right_child_requisition);
+               gtk_widget_get_child_requisition (xpaned->bottom_left_child,
+                                                                                 &bottom_left_child_requisition);
+               gtk_widget_get_child_requisition (xpaned->bottom_right_child,
+                                                                                 &bottom_right_child_requisition);
+
+               /* determine the total requisition-sum of all requisitions of borders,
+                * handles, children etc. */
+               gtk_xpaned_compute_position (xpaned,
+                                                                        allocation,
+                                                                        &top_left_child_requisition,
+                                                                        &top_right_child_requisition,
+                                                                        &bottom_left_child_requisition,
+                                                                        &bottom_right_child_requisition);
+
+               /* calculate the current positions and sizes of the handles */
+               xpaned->handle_pos_east.x = widget->allocation.x + border_width + xpaned->top_left_child_size.width + handle_size;
+               xpaned->handle_pos_east.y = widget->allocation.y + border_width + xpaned->top_left_child_size.height;
+               xpaned->handle_pos_east.width = widget->allocation.width - xpaned->top_left_child_size.width - 2 * border_width - handle_size;
+               xpaned->handle_pos_east.height = handle_size;
+
+               xpaned->handle_pos_west.x = widget->allocation.x + border_width;
+               xpaned->handle_pos_west.y = xpaned->handle_pos_east.y;
+               xpaned->handle_pos_west.width = widget->allocation.width - xpaned->handle_pos_east.width - 2 * border_width - handle_size;
+               xpaned->handle_pos_west.height = handle_size;
+
+               xpaned->handle_pos_north.x = xpaned->handle_pos_east.x - handle_size;
+               xpaned->handle_pos_north.y = widget->allocation.y + border_width;
+               xpaned->handle_pos_north.width = handle_size;
+               xpaned->handle_pos_north.height = xpaned->handle_pos_east.y - widget->allocation.y - border_width;
+
+               xpaned->handle_pos_south.x = xpaned->handle_pos_north.x;
+               xpaned->handle_pos_south.y = xpaned->handle_pos_east.y + handle_size;
+               xpaned->handle_pos_south.width = handle_size;
+               xpaned->handle_pos_south.height = widget->allocation.height - xpaned->handle_pos_north.height - 2 * border_width - handle_size;
+
+
+#define CENTRUM 20
+               xpaned->handle_pos_middle.x = xpaned->handle_pos_north.x ;
+               xpaned->handle_pos_middle.y = xpaned->handle_pos_east.y ;
+               xpaned->handle_pos_middle.width = handle_size + CENTRUM ;
+               xpaned->handle_pos_middle.height = handle_size + CENTRUM;
+
+               /* set allocation for top-left child */
+               top_left_child_allocation.x = widget->allocation.x + border_width;
+               top_left_child_allocation.y = widget->allocation.y + border_width;
+               top_left_child_allocation.width = xpaned->handle_pos_west.width;
+               top_left_child_allocation.height = xpaned->handle_pos_north.height;
+
+               /* set allocation for top-right child */
+               top_right_child_allocation.x = widget->allocation.x + border_width + handle_size + top_left_child_allocation.width;
+               top_right_child_allocation.y = widget->allocation.y + border_width;
+               top_right_child_allocation.width = xpaned->handle_pos_east.width;
+               top_right_child_allocation.height = xpaned->handle_pos_north.height;
+
+               /* set allocation for bottom-left child */
+               bottom_left_child_allocation.x = xpaned->handle_pos_west.x;
+               bottom_left_child_allocation.y = xpaned->handle_pos_south.y;
+               bottom_left_child_allocation.width = xpaned->handle_pos_west.width;
+               bottom_left_child_allocation.height = xpaned->handle_pos_south.height;
+
+               /* set allocation for bottom-right child */
+               bottom_right_child_allocation.x = top_right_child_allocation.x;
+               bottom_right_child_allocation.y = bottom_left_child_allocation.y;
+               bottom_right_child_allocation.width = xpaned->handle_pos_east.width;
+               bottom_right_child_allocation.height = xpaned->handle_pos_south.height;
+
+               if (GTK_WIDGET_REALIZED (widget))
+               {
+                       if (GTK_WIDGET_MAPPED (widget))
+                       {
+                               gdk_window_show (xpaned->handle_east);
+                               gdk_window_show (xpaned->handle_west);
+                               gdk_window_show (xpaned->handle_north);
+                               gdk_window_show (xpaned->handle_south);
+                               gdk_window_show (xpaned->handle_middle);
+                       }
+
+                       gdk_window_move_resize (xpaned->handle_east,
+                                                                       xpaned->handle_pos_east.x,
+                                                                       xpaned->handle_pos_east.y,
+                                                                       xpaned->handle_pos_east.width,
+                                                                       xpaned->handle_pos_east.height);
+
+                       gdk_window_move_resize (xpaned->handle_west,
+                                                                       xpaned->handle_pos_west.x,
+                                                                       xpaned->handle_pos_west.y,
+                                                                       xpaned->handle_pos_west.width,
+                                                                       xpaned->handle_pos_west.height);
+
+                       gdk_window_move_resize (xpaned->handle_north,
+                                                                       xpaned->handle_pos_north.x,
+                                                                       xpaned->handle_pos_north.y,
+                                                                       xpaned->handle_pos_north.width,
+                                                                       xpaned->handle_pos_north.height);
+
+                       gdk_window_move_resize (xpaned->handle_south,
+                                                                       xpaned->handle_pos_south.x,
+                                                                       xpaned->handle_pos_south.y,
+                                                                       xpaned->handle_pos_south.width,
+                                                                       xpaned->handle_pos_south.height);
+
+                       gdk_window_move_resize (xpaned->handle_middle,
+                                               xpaned->handle_pos_middle.x,
+                                               xpaned->handle_pos_middle.y,
+                                               xpaned->handle_pos_middle.width,
+                                               xpaned->handle_pos_middle.height);
+               }
+
+               /* Now allocate the childen, making sure, when resizing not to
+               * overlap the windows
+               */
+               if (GTK_WIDGET_MAPPED (widget))
+               {
+                       gtk_widget_size_allocate (xpaned->top_right_child, &top_right_child_allocation);
+                       gtk_widget_size_allocate (xpaned->top_left_child, &top_left_child_allocation);
+                       gtk_widget_size_allocate (xpaned->bottom_left_child, &bottom_left_child_allocation);
+                       gtk_widget_size_allocate (xpaned->bottom_right_child, &bottom_right_child_allocation);
+               }
+       }
+}
+
+static void gtk_xpaned_set_property (GObject* object,
+                                                                        guint prop_id,
+                                                                        const GValue* value,
+                                                                        GParamSpec* pspec)
+{
+       GtkXPaned* xpaned = GTK_XPANED (object);
+  
+       switch (prop_id)
+       {
+               case PROP_X_POSITION:
+                       gtk_xpaned_set_position_x (xpaned, g_value_get_int (value));
+               break;
+
+               case PROP_Y_POSITION:
+                       gtk_xpaned_set_position_y (xpaned, g_value_get_int (value));
+               break;
+
+               case PROP_POSITION_SET:
+                       xpaned->position_set = g_value_get_boolean (value);
+                       gtk_widget_queue_resize (GTK_WIDGET (xpaned));
+               break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void gtk_xpaned_get_property (GObject* object,
+                                                                        guint prop_id,
+                                                                        GValue* value,
+                                                                        GParamSpec* pspec)
+{
+       GtkXPaned* xpaned = GTK_XPANED (object);
+  
+       switch (prop_id)
+       {
+               case PROP_X_POSITION:
+                       g_value_set_int (value, xpaned->top_left_child_size.width);
+               break;
+
+               case PROP_Y_POSITION:
+                       g_value_set_int (value, xpaned->top_left_child_size.height);
+               break;
+
+               case PROP_POSITION_SET:
+                       g_value_set_boolean (value, xpaned->position_set);
+               break;
+
+               case PROP_MIN_X_POSITION:
+                       g_value_set_int (value, xpaned->min_position.x);
+               break;
+
+               case PROP_MIN_Y_POSITION:
+                       g_value_set_int (value, xpaned->min_position.y);
+               break;
+
+               case PROP_MAX_X_POSITION:
+                       g_value_set_int (value, xpaned->max_position.x);
+               break;
+
+               case PROP_MAX_Y_POSITION:
+                       g_value_set_int (value, xpaned->max_position.y);
+               break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void gtk_xpaned_set_child_property (GtkContainer* container,
+                                                                                  GtkWidget* child,
+                                                                                  guint property_id,
+                                                                                  const GValue* value,
+                                                                                  GParamSpec* pspec)
+{
+       GtkXPaned* xpaned = GTK_XPANED (container);
+       gboolean old_value = FALSE;
+       gboolean new_value = FALSE;
+
+       g_assert (child == xpaned->top_left_child ||
+                         child == xpaned->top_right_child ||
+                         child == xpaned->bottom_left_child ||
+                         child == xpaned->bottom_right_child);
+
+       new_value = g_value_get_boolean (value);
+
+       switch (property_id)
+       {
+               case CHILD_PROP_RESIZE:
+                       if (child == xpaned->top_left_child)
+                       {
+                               old_value = xpaned->top_left_child_resize;
+                               xpaned->top_left_child_resize = new_value;
+                       }
+                       else if (child == xpaned->top_right_child)
+                       {
+                               old_value = xpaned->top_right_child_resize;
+                               xpaned->top_right_child_resize = new_value;
+                       }
+                       else if (child == xpaned->bottom_left_child)
+                       {
+                               old_value = xpaned->bottom_left_child_resize;
+                               xpaned->bottom_left_child_resize = new_value;
+                       }
+                       else if (child == xpaned->bottom_right_child)
+                       {
+                               old_value = xpaned->bottom_right_child_resize;
+                               xpaned->bottom_right_child_resize = new_value;
+                       }
+               break;
+
+               case CHILD_PROP_SHRINK :
+                       if (child == xpaned->top_left_child)
+                       {
+                               old_value = xpaned->top_left_child_shrink;
+                               xpaned->top_left_child_shrink = new_value;
+                       }
+                       else if (child == xpaned->top_right_child)
+                       {
+                               old_value = xpaned->top_right_child_shrink;
+                               xpaned->top_right_child_shrink = new_value;
+                       }
+                       else if (child == xpaned->bottom_left_child)
+                       {
+                               old_value = xpaned->bottom_left_child_shrink;
+                               xpaned->bottom_left_child_shrink = new_value;
+                       }
+                       else if (child == xpaned->bottom_right_child)
+                       {
+                               old_value = xpaned->bottom_right_child_shrink;
+                               xpaned->bottom_right_child_shrink = new_value;
+                       }
+               break;
+
+               default:
+                       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container,
+                                                                                                                 property_id,
+                                                                                                                 pspec);
+                       old_value = -1; /* quiet gcc */
+               break;
+       }
+
+       if (old_value != new_value)
+               gtk_widget_queue_resize (GTK_WIDGET (container));
+}
+
+static void gtk_xpaned_get_child_property (GtkContainer* container,
+                                                                                  GtkWidget* child,
+                                                                                  guint property_id,
+                                                                                  GValue* value,
+                                                                                  GParamSpec* pspec)
+{
+       GtkXPaned* xpaned = GTK_XPANED (container);
+
+       g_assert (child == xpaned->top_left_child ||
+                         child == xpaned->top_right_child ||
+                         child == xpaned->bottom_left_child ||
+                         child == xpaned->bottom_right_child);
+
+       switch (property_id)
+       {
+               case CHILD_PROP_RESIZE :
+                       if (child == xpaned->top_left_child)
+                               g_value_set_boolean (value, xpaned->top_left_child_resize);
+                       else if (child == xpaned->top_right_child)
+                               g_value_set_boolean (value, xpaned->top_right_child_resize);
+                       else if (child == xpaned->bottom_left_child)
+                               g_value_set_boolean (value, xpaned->bottom_left_child_resize);
+                       else if (child == xpaned->bottom_right_child)
+                               g_value_set_boolean (value, xpaned->bottom_right_child_resize);                 
+               break;
+
+               case CHILD_PROP_SHRINK :
+                       if (child == xpaned->top_left_child)
+                               g_value_set_boolean (value, xpaned->top_left_child_shrink);
+                       else if (child == xpaned->top_right_child)
+                               g_value_set_boolean (value, xpaned->top_right_child_shrink);
+                       else if (child == xpaned->bottom_left_child)
+                               g_value_set_boolean (value, xpaned->bottom_left_child_shrink);
+                       else if (child == xpaned->bottom_right_child)
+                               g_value_set_boolean (value, xpaned->bottom_right_child_shrink);
+               break;
+
+               default:
+                       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container,
+                                                                                                                 property_id,
+                                                                                                                 pspec);
+               break;
+       }
+}
+
+static void gtk_xpaned_finalize (GObject* object)
+{
+       GtkXPaned* xpaned = GTK_XPANED (object);
+  
+       gtk_xpaned_set_saved_focus (xpaned, NULL);
+       gtk_xpaned_set_first_xpaned (xpaned, NULL);
+
+       g_free (xpaned->priv);
+
+       G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void gtk_xpaned_realize (GtkWidget* widget)
+{
+       GtkXPaned* xpaned;
+       GdkWindowAttr attributes_east;
+       GdkWindowAttr attributes_west;
+       GdkWindowAttr attributes_north;
+       GdkWindowAttr attributes_south;
+       GdkWindowAttr attributes_middle;
+       gint attributes_mask_east;
+       gint attributes_mask_west;
+       gint attributes_mask_north;
+       gint attributes_mask_south;
+       gint attributes_mask_middle;
+
+       GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+       xpaned = GTK_XPANED (widget);
+
+       widget->window = gtk_widget_get_parent_window (widget);
+       g_object_ref (widget->window);
+  
+       attributes_east.window_type = GDK_WINDOW_CHILD;
+       attributes_west.window_type = GDK_WINDOW_CHILD;
+       attributes_north.window_type = GDK_WINDOW_CHILD;
+       attributes_south.window_type = GDK_WINDOW_CHILD;
+       attributes_middle.window_type = GDK_WINDOW_CHILD;
+
+       attributes_east.wclass = GDK_INPUT_ONLY;
+       attributes_west.wclass = GDK_INPUT_ONLY;
+       attributes_north.wclass = GDK_INPUT_ONLY;
+       attributes_south.wclass = GDK_INPUT_ONLY;
+       attributes_middle.wclass = GDK_INPUT_ONLY;
+
+       attributes_east.x = xpaned->handle_pos_east.x;
+       attributes_east.y = xpaned->handle_pos_east.y;
+       attributes_east.width = xpaned->handle_pos_east.width;
+       attributes_east.height = xpaned->handle_pos_east.height;
+
+       attributes_west.x = xpaned->handle_pos_west.x;
+       attributes_west.y = xpaned->handle_pos_west.y;
+       attributes_west.width = xpaned->handle_pos_west.width;
+       attributes_west.height = xpaned->handle_pos_west.height;
+
+       attributes_north.x = xpaned->handle_pos_north.x;
+       attributes_north.y = xpaned->handle_pos_north.y;
+       attributes_north.width = xpaned->handle_pos_north.width;
+       attributes_north.height = xpaned->handle_pos_north.height;
+
+       attributes_south.x = xpaned->handle_pos_south.x;
+       attributes_south.y = xpaned->handle_pos_south.y;
+       attributes_south.width = xpaned->handle_pos_south.width;
+       attributes_south.height = xpaned->handle_pos_south.height;
+
+       attributes_middle.x = xpaned->handle_pos_middle.x;
+       attributes_middle.y = xpaned->handle_pos_middle.y;
+       attributes_middle.width = xpaned->handle_pos_middle.width;
+       attributes_middle.height = xpaned->handle_pos_middle.height;
+
+       attributes_east.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                                                                       xpaned->cursor_type_east);
+       attributes_west.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                                                                       xpaned->cursor_type_west);
+       attributes_north.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                                                                       xpaned->cursor_type_north);
+       attributes_south.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                                                                       xpaned->cursor_type_south);
+       attributes_middle.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+                                                                                                       xpaned->cursor_type_middle);
+
+       attributes_east.event_mask = gtk_widget_get_events (widget);
+       attributes_west.event_mask = gtk_widget_get_events (widget);
+       attributes_north.event_mask = gtk_widget_get_events (widget);
+       attributes_south.event_mask = gtk_widget_get_events (widget);
+       attributes_middle.event_mask = gtk_widget_get_events (widget);
+
+       attributes_east.event_mask |= (GDK_BUTTON_PRESS_MASK |
+                                                         GDK_BUTTON_RELEASE_MASK |
+                                                         GDK_ENTER_NOTIFY_MASK |
+                                                         GDK_LEAVE_NOTIFY_MASK |
+                                                         GDK_POINTER_MOTION_MASK |
+                                                         GDK_POINTER_MOTION_HINT_MASK);
+       attributes_west.event_mask |= (GDK_BUTTON_PRESS_MASK |
+                                                         GDK_BUTTON_RELEASE_MASK |
+                                                         GDK_ENTER_NOTIFY_MASK |
+                                                         GDK_LEAVE_NOTIFY_MASK |
+                                                         GDK_POINTER_MOTION_MASK |
+                                                         GDK_POINTER_MOTION_HINT_MASK);
+       attributes_north.event_mask |= (GDK_BUTTON_PRESS_MASK |
+                                                         GDK_BUTTON_RELEASE_MASK |
+                                                         GDK_ENTER_NOTIFY_MASK |
+                                                         GDK_LEAVE_NOTIFY_MASK |
+                                                         GDK_POINTER_MOTION_MASK |
+                                                         GDK_POINTER_MOTION_HINT_MASK);
+       attributes_south.event_mask |= (GDK_BUTTON_PRESS_MASK |
+                                                         GDK_BUTTON_RELEASE_MASK |
+                                                         GDK_ENTER_NOTIFY_MASK |
+                                                         GDK_LEAVE_NOTIFY_MASK |
+                                                         GDK_POINTER_MOTION_MASK |
+                                                         GDK_POINTER_MOTION_HINT_MASK);
+       attributes_middle.event_mask |= (GDK_BUTTON_PRESS_MASK |
+                                                         GDK_BUTTON_RELEASE_MASK |
+                                                         GDK_ENTER_NOTIFY_MASK |
+                                                         GDK_LEAVE_NOTIFY_MASK |
+                                                         GDK_POINTER_MOTION_MASK |
+                                                         GDK_POINTER_MOTION_HINT_MASK);
+
+       attributes_mask_east = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
+       attributes_mask_west = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
+       attributes_mask_north = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
+       attributes_mask_south = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
+       attributes_mask_middle = GDK_WA_X | GDK_WA_Y | GDK_WA_CURSOR;
+
+       xpaned->handle_east = gdk_window_new (widget->window,
+                                                                                 &attributes_east,
+                                                                                 attributes_mask_east);
+       xpaned->handle_west = gdk_window_new (widget->window,
+                                                                                 &attributes_west,
+                                                                                 attributes_mask_west);
+       xpaned->handle_north = gdk_window_new (widget->window,
+                                                                                 &attributes_north,
+                                                                                 attributes_mask_north);
+       xpaned->handle_south = gdk_window_new (widget->window,
+                                                                                 &attributes_south,
+                                                                                 attributes_mask_south);
+       xpaned->handle_middle = gdk_window_new (widget->window,
+                                                                                 &attributes_middle,
+                                                                                 attributes_mask_middle);
+
+       gdk_window_set_user_data (xpaned->handle_east, xpaned);
+       gdk_window_set_user_data (xpaned->handle_west, xpaned);
+       gdk_window_set_user_data (xpaned->handle_north, xpaned);
+       gdk_window_set_user_data (xpaned->handle_south, xpaned);
+       gdk_window_set_user_data (xpaned->handle_middle, xpaned);
+
+       gdk_cursor_unref (attributes_east.cursor);
+       gdk_cursor_unref (attributes_west.cursor);
+       gdk_cursor_unref (attributes_north.cursor);
+       gdk_cursor_unref (attributes_south.cursor);
+       gdk_cursor_unref (attributes_middle.cursor);
+
+       widget->style = gtk_style_attach (widget->style, widget->window);
+
+       if (xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) &&
+               xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) &&
+               xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) &&
+               xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child))
+       {
+               gdk_window_show (xpaned->handle_east);
+               gdk_window_show (xpaned->handle_west);
+               gdk_window_show (xpaned->handle_north);
+               gdk_window_show (xpaned->handle_south);
+               gdk_window_show (xpaned->handle_middle);
+       }
+}
+
+static void gtk_xpaned_unrealize (GtkWidget *widget)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       if (xpaned->xor_gc)
+       {
+               g_object_unref (xpaned->xor_gc);
+               xpaned->xor_gc = NULL;
+       }
+
+       if (xpaned->handle_east)
+       {
+               gdk_window_set_user_data (xpaned->handle_east, NULL);
+               gdk_window_destroy (xpaned->handle_east);
+               xpaned->handle_east = NULL;
+       }
+
+       if (xpaned->handle_west)
+       {
+               gdk_window_set_user_data (xpaned->handle_west, NULL);
+               gdk_window_destroy (xpaned->handle_west);
+               xpaned->handle_west = NULL;
+       }
+
+       if (xpaned->handle_north)
+       {
+               gdk_window_set_user_data (xpaned->handle_north, NULL);
+               gdk_window_destroy (xpaned->handle_north);
+               xpaned->handle_north = NULL;
+       }
+
+       if (xpaned->handle_south)
+       {
+               gdk_window_set_user_data (xpaned->handle_south, NULL);
+               gdk_window_destroy (xpaned->handle_south);
+               xpaned->handle_south = NULL;
+       }
+
+       if (xpaned->handle_middle)
+       {
+               gdk_window_set_user_data (xpaned->handle_middle, NULL);
+               gdk_window_destroy (xpaned->handle_middle);
+               xpaned->handle_middle = NULL;
+       }
+
+       gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL);
+       gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL);
+       gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL);
+       gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL);
+       gtk_xpaned_set_saved_focus (xpaned, NULL);
+       gtk_xpaned_set_first_xpaned (xpaned, NULL);
+  
+       if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+               (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void gtk_xpaned_map (GtkWidget* widget)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       gdk_window_show (xpaned->handle_east);
+       gdk_window_show (xpaned->handle_west);
+       gdk_window_show (xpaned->handle_north);
+       gdk_window_show (xpaned->handle_south);
+       gdk_window_show (xpaned->handle_middle);
+
+       GTK_WIDGET_CLASS (parent_class)->map (widget);
+}
+
+static void gtk_xpaned_unmap (GtkWidget* widget)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       gdk_window_hide (xpaned->handle_east);
+       gdk_window_hide (xpaned->handle_west);
+       gdk_window_hide (xpaned->handle_north);
+       gdk_window_hide (xpaned->handle_south);
+       gdk_window_hide (xpaned->handle_middle);
+
+       GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static gboolean gtk_xpaned_expose (GtkWidget* widget,
+                                                                  GdkEventExpose* event)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+       gint handle_size;
+       GdkRectangle horizontalClipArea;
+       GdkRectangle verticalClipArea;
+
+       /* determine size of handle(s) */
+       gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+
+       /* I want the handle-"thickness" to be at least 3 */
+       g_assert (handle_size >= 3);
+
+       if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
+               xpaned->top_left_child && GTK_WIDGET_VISIBLE (xpaned->top_left_child) &&
+               xpaned->top_right_child && GTK_WIDGET_VISIBLE (xpaned->top_right_child) &&
+               xpaned->bottom_left_child && GTK_WIDGET_VISIBLE (xpaned->bottom_left_child) &&
+               xpaned->bottom_right_child && GTK_WIDGET_VISIBLE (xpaned->bottom_right_child))
+    {
+               GtkStateType state;
+
+               if (gtk_widget_is_focus (widget))
+                       state = GTK_STATE_SELECTED;
+               else if (xpaned->handle_prelit)
+                       state = GTK_STATE_PRELIGHT;
+               else
+                       state = GTK_WIDGET_STATE (widget);
+
+               horizontalClipArea.x = xpaned->handle_pos_west.x;
+               horizontalClipArea.y = xpaned->handle_pos_west.y;
+               horizontalClipArea.width = xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width;
+               horizontalClipArea.height = handle_size;
+
+               verticalClipArea.x = xpaned->handle_pos_north.x;
+               verticalClipArea.y = xpaned->handle_pos_north.y;
+               verticalClipArea.width = handle_size;
+               verticalClipArea.height = xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height;
+
+               gtk_paint_handle (widget->style,
+                                                 widget->window,
+                                                 state,
+                                                 GTK_SHADOW_NONE,
+                                                 &horizontalClipArea,
+                                                 widget,
+                                                 "paned",
+                                                 xpaned->handle_pos_east.x - handle_size - 256 / 2,
+                                                 xpaned->handle_pos_west.y + 1,
+                                                 256 + handle_size,
+                                                 handle_size - 2,
+                                                 /*xpaned->handle_pos_west.x,
+                                                 xpaned->handle_pos_west.y + 1,
+                                                 xpaned->handle_pos_west.width + handle_size + xpaned->handle_pos_east.width,
+                                                 handle_size - 2,*/
+                                                 GTK_ORIENTATION_HORIZONTAL);
+               gtk_paint_handle (widget->style,
+                                                 widget->window,
+                                                 state,
+                                                 GTK_SHADOW_NONE,
+                                                 &verticalClipArea,
+                                                 widget,
+                                                 "paned",
+                                                 xpaned->handle_pos_north.x + 1,
+                                                 xpaned->handle_pos_south.y - handle_size - 256 / 2,
+                                                 handle_size - 2,
+                                                 256 + handle_size,
+                                                 /*xpaned->handle_pos_north.x + 1,
+                                                 xpaned->handle_pos_north.y,
+                                                 handle_size - 2,
+                                                 xpaned->handle_pos_north.height + handle_size + xpaned->handle_pos_south.height,*/
+                                                 GTK_ORIENTATION_VERTICAL);
+       }
+
+       /* Chain up to draw children */
+       GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+  
+       return FALSE;
+}
+
+static gboolean is_rtl (GtkXPaned* xpaned)
+{
+       if (gtk_widget_get_direction (GTK_WIDGET (xpaned)) == GTK_TEXT_DIR_RTL)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void update_drag (GtkXPaned* xpaned)
+{
+       GdkPoint pos;
+       gint handle_size;
+       GtkRequisition size;
+  
+       gtk_widget_get_pointer (GTK_WIDGET (xpaned), &pos.x, &pos.y);
+
+       if (xpaned->in_drag_vert)
+       {
+               pos.y -= xpaned->drag_pos.y;
+
+               if (is_rtl (xpaned))
+           {
+                       gtk_widget_style_get (GTK_WIDGET (xpaned),
+                                                                 "handle-size", &handle_size,
+                                                                 NULL);
+      
+                       size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size;
+               }
+               else
+               {
+                       size.height = pos.y;
+               }
+
+               size.height -= GTK_CONTAINER (xpaned)->border_width;
+
+               size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y);
+
+               if (size.height != xpaned->top_left_child_size.height)
+                       gtk_xpaned_set_position_y (xpaned, size.height);
+       }
+
+       if (xpaned->in_drag_horiz)
+       {
+               pos.x -= xpaned->drag_pos.x;
+       
+               if (is_rtl (xpaned))
+           {
+                       gtk_widget_style_get (GTK_WIDGET (xpaned),
+                                                                 "handle-size", &handle_size,
+                                                                 NULL);
+
+                       size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size;
+               }
+               else
+               {
+                       size.width = pos.x;
+               }
+
+               size.width -= GTK_CONTAINER (xpaned)->border_width;
+
+               size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x);
+
+               if (size.width != xpaned->top_left_child_size.width)
+                       gtk_xpaned_set_position_x (xpaned, size.width);
+       }
+
+       if (xpaned->in_drag_vert_and_horiz)
+       {
+               pos.x -= xpaned->drag_pos.x;
+               pos.y -= xpaned->drag_pos.y;
+
+               if (is_rtl (xpaned))
+           {
+                       gtk_widget_style_get (GTK_WIDGET (xpaned),
+                                                                 "handle-size", &handle_size,
+                                                                 NULL);
+      
+                       size.width = GTK_WIDGET (xpaned)->allocation.width - pos.x - handle_size;
+                       size.height = GTK_WIDGET (xpaned)->allocation.height - pos.y - handle_size;
+               }
+               else
+               {
+                       size.width = pos.x;
+                       size.height = pos.y;
+               }
+
+               size.width -= GTK_CONTAINER (xpaned)->border_width;
+               size.height -= GTK_CONTAINER (xpaned)->border_width;
+
+               size.width = CLAMP (size.width, xpaned->min_position.x, xpaned->max_position.x);
+               size.height = CLAMP (size.height, xpaned->min_position.y, xpaned->max_position.y);
+
+               if (size.width != xpaned->top_left_child_size.width)
+                       gtk_xpaned_set_position_x (xpaned, size.width);
+
+               if (size.height != xpaned->top_left_child_size.height)
+                       gtk_xpaned_set_position_y (xpaned, size.height);
+       }
+}
+
+static gboolean gtk_xpaned_enter (GtkWidget* widget, GdkEventCrossing* event)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       if (xpaned->in_drag_vert ||
+               xpaned->in_drag_horiz ||
+               xpaned->in_drag_vert_and_horiz)
+               update_drag (xpaned);
+       else
+       {
+               xpaned->handle_prelit = TRUE;
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_east.x,
+                                                                       xpaned->handle_pos_east.y,
+                                                                       xpaned->handle_pos_east.width,
+                                                                       xpaned->handle_pos_east.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_west.x,
+                                                                       xpaned->handle_pos_west.y,
+                                                                       xpaned->handle_pos_west.width,
+                                                                       xpaned->handle_pos_west.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_north.x,
+                                                                       xpaned->handle_pos_north.y,
+                                                                       xpaned->handle_pos_north.width,
+                                                                       xpaned->handle_pos_north.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_south.x,
+                                                                       xpaned->handle_pos_south.y,
+                                                                       xpaned->handle_pos_south.width,
+                                                                       xpaned->handle_pos_south.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_middle.x,
+                                                                       xpaned->handle_pos_middle.y,
+                                                                       xpaned->handle_pos_middle.width,
+                                                                       xpaned->handle_pos_middle.height);
+       }
+
+       return TRUE;
+}
+
+static gboolean gtk_xpaned_leave (GtkWidget* widget, GdkEventCrossing* event)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       if (xpaned->in_drag_vert ||
+               xpaned->in_drag_horiz ||
+               xpaned->in_drag_vert_and_horiz)
+               update_drag (xpaned);
+       else
+       {
+               xpaned->handle_prelit = FALSE;
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_east.x,
+                                                                       xpaned->handle_pos_east.y,
+                                                                       xpaned->handle_pos_east.width,
+                                                                       xpaned->handle_pos_east.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_west.x,
+                                                                       xpaned->handle_pos_west.y,
+                                                                       xpaned->handle_pos_west.width,
+                                                                       xpaned->handle_pos_west.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_north.x,
+                                                                       xpaned->handle_pos_north.y,
+                                                                       xpaned->handle_pos_north.width,
+                                                                       xpaned->handle_pos_north.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_south.x,
+                                                                       xpaned->handle_pos_south.y,
+                                                                       xpaned->handle_pos_south.width,
+                                                                       xpaned->handle_pos_south.height);
+
+               gtk_widget_queue_draw_area (widget,
+                                                                       xpaned->handle_pos_middle.x,
+                                                                       xpaned->handle_pos_middle.y,
+                                                                       xpaned->handle_pos_middle.width,
+                                                                       xpaned->handle_pos_middle.height);
+       }
+
+       return TRUE;
+}
+
+static gboolean gtk_xpaned_focus (GtkWidget* widget, GtkDirectionType direction)
+{
+       gboolean retval;
+
+       /* This is a hack, but how can this be done without
+       * excessive cut-and-paste from gtkcontainer.c?
+       */
+
+       GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS);
+       retval = (* GTK_WIDGET_CLASS (parent_class)->focus) (widget, direction);
+       GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+
+  return retval;
+}
+
+static gboolean gtk_xpaned_button_press (GtkWidget* widget,
+                                                                                GdkEventButton* event)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       /* if any child is currently maximized, jump right back */
+       if (xpaned->maximized[GTK_XPANED_TOP_LEFT]    ||
+               xpaned->maximized[GTK_XPANED_TOP_RIGHT]   ||
+               xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
+               xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               return FALSE;
+
+       /* if user is dragging the handles around */
+       if (!xpaned->in_drag_vert_and_horiz &&
+                        event->window != xpaned->handle_east &&
+                        event->window != xpaned->handle_west &&
+                        event->window != xpaned->handle_north &&
+                        event->window != xpaned->handle_south &&
+                        event->window == xpaned->handle_middle &&
+                        event->button == 1)
+       {
+               xpaned->in_drag_vert_and_horiz = TRUE;
+
+               /* We need a server grab here, not gtk_grab_add(), since
+               * we don't want to pass events on to the widget's children */
+               if (gdk_pointer_grab (xpaned->handle_middle,
+                                                         FALSE,
+                                                         GDK_POINTER_MOTION_HINT_MASK
+                                                         | GDK_BUTTON1_MOTION_MASK
+                                                         | GDK_BUTTON_RELEASE_MASK
+                                                         | GDK_ENTER_NOTIFY_MASK
+                                                         | GDK_LEAVE_NOTIFY_MASK,
+                                                         NULL,
+                                                         NULL,
+                                                         event->time) == GDK_GRAB_SUCCESS)
+               {
+               }
+
+               xpaned->drag_pos.x = event->x;
+               xpaned->drag_pos.y = event->y;
+
+               return TRUE;
+       }
+       else if (!xpaned->in_drag_vert &&
+               event->window == xpaned->handle_east &&
+               event->window != xpaned->handle_west &&
+               event->window != xpaned->handle_north &&
+               event->window != xpaned->handle_south &&
+               event->window != xpaned->handle_middle &&
+               event->button == 1)
+       {
+               xpaned->in_drag_vert = TRUE;
+
+               /* We need a server grab here, not gtk_grab_add(), since
+               * we don't want to pass events on to the widget's children */
+               if (gdk_pointer_grab (xpaned->handle_east,
+                                                         FALSE,
+                                                         GDK_POINTER_MOTION_HINT_MASK
+                                                         | GDK_BUTTON1_MOTION_MASK
+                                                         | GDK_BUTTON_RELEASE_MASK
+                                                         | GDK_ENTER_NOTIFY_MASK
+                                                         | GDK_LEAVE_NOTIFY_MASK,
+                                                         NULL,
+                                                         NULL,
+                                                         event->time) == GDK_GRAB_SUCCESS)
+               {
+               }
+
+               xpaned->drag_pos.y = event->y;
+
+               return TRUE;
+       }
+       else if (!xpaned->in_drag_vert &&
+                        event->window != xpaned->handle_east &&
+                        event->window == xpaned->handle_west &&
+                        event->window != xpaned->handle_north &&
+                        event->window != xpaned->handle_south &&
+                        event->window != xpaned->handle_middle &&
+                        event->button == 1)
+       {
+               xpaned->in_drag_vert = TRUE;
+
+               /* We need a server grab here, not gtk_grab_add(), since
+               * we don't want to pass events on to the widget's children */
+               if (gdk_pointer_grab (xpaned->handle_west,
+                                                         FALSE,
+                                                         GDK_POINTER_MOTION_HINT_MASK
+                                                         | GDK_BUTTON1_MOTION_MASK
+                                                         | GDK_BUTTON_RELEASE_MASK
+                                                         | GDK_ENTER_NOTIFY_MASK
+                                                         | GDK_LEAVE_NOTIFY_MASK,
+                                                         NULL,
+                                                         NULL,
+                                                         event->time) == GDK_GRAB_SUCCESS)
+               {
+               }
+
+               xpaned->drag_pos.y = event->y;
+
+               return TRUE;
+       }
+       else if (!xpaned->in_drag_horiz &&
+                        event->window != xpaned->handle_east &&
+                        event->window != xpaned->handle_west &&
+                        event->window == xpaned->handle_north &&
+                        event->window != xpaned->handle_south &&
+                        event->window != xpaned->handle_middle &&
+                        event->button == 1)
+       {
+               xpaned->in_drag_horiz = TRUE;
+
+               /* We need a server grab here, not gtk_grab_add(), since
+               * we don't want to pass events on to the widget's children */
+               if (gdk_pointer_grab (xpaned->handle_north,
+                                                         FALSE,
+                                                         GDK_POINTER_MOTION_HINT_MASK
+                                                         | GDK_BUTTON1_MOTION_MASK
+                                                         | GDK_BUTTON_RELEASE_MASK
+                                                         | GDK_ENTER_NOTIFY_MASK
+                                                         | GDK_LEAVE_NOTIFY_MASK,
+                                                         NULL,
+                                                         NULL,
+                                                         event->time) == GDK_GRAB_SUCCESS)
+               {
+               }
+
+               xpaned->drag_pos.x = event->x;
+
+               return TRUE;
+       }
+       else if (!xpaned->in_drag_horiz &&
+                        event->window != xpaned->handle_east &&
+                        event->window != xpaned->handle_west &&
+                        event->window != xpaned->handle_north &&
+                        event->window == xpaned->handle_south &&
+                        event->window != xpaned->handle_middle &&
+                        event->button == 1)
+       {
+               xpaned->in_drag_horiz = TRUE;
+
+               /* We need a server grab here, not gtk_grab_add(), since
+               * we don't want to pass events on to the widget's children */
+               if (gdk_pointer_grab (xpaned->handle_south,
+                                                         FALSE,
+                                                         GDK_POINTER_MOTION_HINT_MASK
+                                                         | GDK_BUTTON1_MOTION_MASK
+                                                         | GDK_BUTTON_RELEASE_MASK
+                                                         | GDK_ENTER_NOTIFY_MASK
+                                                         | GDK_LEAVE_NOTIFY_MASK,
+                                                         NULL,
+                                                         NULL,
+                                                         event->time) == GDK_GRAB_SUCCESS)
+               {
+               }
+
+               xpaned->drag_pos.x = event->x;
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static gboolean gtk_xpaned_button_release (GtkWidget* widget,
+                                                                                  GdkEventButton* event)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       if (xpaned->in_drag_vert && (event->button == 1))
+       {
+               xpaned->in_drag_vert = FALSE;
+               xpaned->drag_pos.y = -1;
+               xpaned->position_set = TRUE;
+               gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+                                                                       event->time);
+               return TRUE;
+    }
+       else if (xpaned->in_drag_horiz && (event->button == 1))
+       {
+               xpaned->in_drag_horiz = FALSE;
+               xpaned->drag_pos.x = -1;
+               xpaned->position_set = TRUE;
+               gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+                                                                       event->time);
+               return TRUE;
+    }
+       else if (xpaned->in_drag_vert_and_horiz && (event->button == 1))
+       {
+               xpaned->in_drag_vert_and_horiz = FALSE;
+               xpaned->drag_pos.x = -1;
+               xpaned->drag_pos.y = -1;
+               xpaned->position_set = TRUE;
+               gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+                                                                       event->time);
+               return TRUE;
+    }
+
+       return FALSE;
+}
+
+static gboolean gtk_xpaned_motion (GtkWidget* widget, GdkEventMotion* event)
+{
+       GtkXPaned* xpaned = GTK_XPANED (widget);
+
+       if (xpaned->in_drag_vert ||
+               xpaned->in_drag_horiz ||
+               xpaned->in_drag_vert_and_horiz)
+
+       {
+               update_drag (xpaned);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget *widget)
+{
+       gtk_xpaned_pack_top_left (xpaned, widget, FALSE, TRUE);
+}
+
+void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget *widget)
+{
+       gtk_xpaned_pack_top_right (xpaned, widget, FALSE, TRUE);
+}
+
+void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget *widget)
+{
+       gtk_xpaned_pack_bottom_left (xpaned, widget, FALSE, TRUE);
+}
+
+void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget *widget)
+{
+       gtk_xpaned_pack_bottom_right (xpaned, widget, FALSE, TRUE);
+}
+
+void gtk_xpaned_pack_top_left (GtkXPaned* xpaned,
+                                                          GtkWidget* child,
+                                                          gboolean   resize,
+                                                          gboolean   shrink)
+{
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+       g_return_if_fail (GTK_IS_WIDGET (child));
+
+       if (!xpaned->top_left_child)
+       {
+               xpaned->top_left_child = child;
+               xpaned->top_left_child_resize = resize;
+               xpaned->top_left_child_shrink = shrink;
+
+               gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
+       }
+}
+
+void gtk_xpaned_pack_top_right (GtkXPaned* xpaned,
+                                                               GtkWidget* child,
+                                                               gboolean   resize,
+                                                               gboolean   shrink)
+{
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+       g_return_if_fail (GTK_IS_WIDGET (child));
+
+       if (!xpaned->top_right_child)
+       {
+               xpaned->top_right_child = child;
+               xpaned->top_right_child_resize = resize;
+               xpaned->top_right_child_shrink = shrink;
+
+               gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
+       }
+}
+
+void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned,
+                                                                 GtkWidget* child,
+                                                                 gboolean   resize,
+                                                                 gboolean   shrink)
+{
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+       g_return_if_fail (GTK_IS_WIDGET (child));
+
+       if (!xpaned->bottom_left_child)
+       {
+               xpaned->bottom_left_child = child;
+               xpaned->bottom_left_child_resize = resize;
+               xpaned->bottom_left_child_shrink = shrink;
+
+               gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
+       }
+}
+
+void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned,
+                                                                  GtkWidget* child,
+                                                                  gboolean   resize,
+                                                                  gboolean   shrink)
+{
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+       g_return_if_fail (GTK_IS_WIDGET (child));
+
+       if (!xpaned->bottom_right_child)
+       {
+               xpaned->bottom_right_child = child;
+               xpaned->bottom_right_child_resize = resize;
+               xpaned->bottom_right_child_shrink = shrink;
+
+               gtk_widget_set_parent (child, GTK_WIDGET (xpaned));
+       }
+}
+
+static void gtk_xpaned_add (GtkContainer* container, GtkWidget* widget)
+{
+       GtkXPaned* xpaned;
+
+       g_return_if_fail (GTK_IS_XPANED (container));
+
+       xpaned = GTK_XPANED (container);
+
+       if (!xpaned->top_left_child)
+               gtk_xpaned_add_top_left (xpaned, widget);
+       else if (!xpaned->top_right_child)
+               gtk_xpaned_add_top_right (xpaned, widget);
+       else if (!xpaned->bottom_left_child)
+               gtk_xpaned_add_bottom_left (xpaned, widget);
+       else if (!xpaned->bottom_right_child)
+               gtk_xpaned_add_bottom_right (xpaned, widget);
+       else
+               g_warning ("GtkXPaned cannot have more than 4 children\n");
+}
+
+static void gtk_xpaned_remove (GtkContainer* container, GtkWidget* widget)
+{
+       GtkXPaned* xpaned;
+       gboolean was_visible;
+
+       xpaned = GTK_XPANED (container);
+       was_visible = GTK_WIDGET_VISIBLE (widget);
+
+       if (xpaned->top_left_child == widget)
+       {
+               gtk_widget_unparent (widget);
+
+               xpaned->top_left_child = NULL;
+
+               if (was_visible && GTK_WIDGET_VISIBLE (container))
+                       gtk_widget_queue_resize (GTK_WIDGET (container));
+       }
+       else if (xpaned->top_right_child == widget)
+       {
+               gtk_widget_unparent (widget);
+
+               xpaned->top_right_child = NULL;
+
+               if (was_visible && GTK_WIDGET_VISIBLE (container))
+                       gtk_widget_queue_resize (GTK_WIDGET (container));
+       }
+       else if (xpaned->bottom_left_child == widget)
+       {
+               gtk_widget_unparent (widget);
+
+               xpaned->bottom_left_child = NULL;
+
+               if (was_visible && GTK_WIDGET_VISIBLE (container))
+                       gtk_widget_queue_resize (GTK_WIDGET (container));
+       }
+       else if (xpaned->bottom_right_child == widget)
+       {
+               gtk_widget_unparent (widget);
+
+               xpaned->bottom_right_child = NULL;
+
+               if (was_visible && GTK_WIDGET_VISIBLE (container))
+                       gtk_widget_queue_resize (GTK_WIDGET (container));
+       }
+       else
+               g_warning ("GtkXPaned has no more children attached\n");
+
+}
+
+static void gtk_xpaned_forall (GtkContainer* container,
+                                                          gboolean      include_internals,
+                                                          GtkCallback   callback,
+                                                          gpointer      callback_data)
+{
+       GtkXPaned* xpaned;
+
+       g_return_if_fail (callback != NULL);
+
+       xpaned = GTK_XPANED (container);
+
+       if (xpaned->top_left_child)
+       (*callback) (xpaned->top_left_child, callback_data);
+       if (xpaned->top_right_child)
+               (*callback) (xpaned->top_right_child, callback_data);
+       if (xpaned->bottom_left_child)
+       (*callback) (xpaned->bottom_left_child, callback_data);
+       if (xpaned->bottom_right_child)
+               (*callback) (xpaned->bottom_right_child, callback_data);
+}
+
+/**
+ * gtk_xpaned_get_position_x:
+ * @paned: a #GtkXPaned widget
+ * 
+ * Obtains the x-position of the divider.
+ * 
+ * Return value: x-position of the divider
+ **/
+gint gtk_xpaned_get_position_x (GtkXPaned* xpaned)
+{
+       g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0);
+
+       return xpaned->top_left_child_size.width;
+}
+
+/**
+ * gtk_xpaned_get_position_y:
+ * @paned: a #GtkXPaned widget
+ * 
+ * Obtains the y-position of the divider.
+ * 
+ * Return value: y-position of the divider
+ **/
+gint gtk_xpaned_get_position_y (GtkXPaned* xpaned)
+{
+       g_return_val_if_fail (GTK_IS_XPANED (xpaned), 0);
+
+       return xpaned->top_left_child_size.height;
+}
+
+/**
+ * gtk_xpaned_set_position_x:
+ * @paned: a #GtkXPaned widget
+ * @xposition: pixel x-position of divider, a negative values
+ *                        of a component mean that the position is unset.
+ * 
+ * Sets the x-position of the divider between the four panes.
+ **/
+void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition)
+{
+       GObject* object;
+  
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+
+       /* if any child is currently maximized, jump right back */
+       if (xpaned->maximized[GTK_XPANED_TOP_LEFT]    ||
+               xpaned->maximized[GTK_XPANED_TOP_RIGHT]   ||
+               xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
+               xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               return;
+
+       object = G_OBJECT (xpaned);
+
+       if (xposition >= 0)
+       {
+               /* We don't clamp here - the assumption is that
+               * if the total allocation changes at the same time
+               * as the position, the position set is with reference
+               * to the new total size. If only the position changes,
+               * then clamping will occur in gtk_paned_compute_position()
+               */
+
+               xpaned->top_left_child_size.width = xposition;
+               xpaned->position_set = TRUE;
+       }
+       else
+       {
+               xpaned->position_set = FALSE;
+       }
+
+       g_object_freeze_notify (object);
+       g_object_notify (object, "x-position");
+       g_object_notify (object, "position-set");
+       g_object_thaw_notify (object);
+
+       gtk_widget_queue_resize (GTK_WIDGET (xpaned));
+}
+
+/**
+ * gtk_xpaned_set_position_y:
+ * @paned: a #GtkXPaned widget
+ * @yposition: pixel y-position of divider, a negative values
+ *                        of a component mean that the position is unset.
+ * 
+ * Sets the y-position of the divider between the four panes.
+ **/
+void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition)
+{
+       GObject* object;
+  
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+
+       /* if any child is currently maximized, jump right back */
+       if (xpaned->maximized[GTK_XPANED_TOP_LEFT]    ||
+               xpaned->maximized[GTK_XPANED_TOP_RIGHT]   ||
+               xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] ||
+               xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               return;
+
+       object = G_OBJECT (xpaned);
+
+       if (yposition >= 0)
+       {
+               /* We don't clamp here - the assumption is that
+               * if the total allocation changes at the same time
+               * as the position, the position set is with reference
+               * to the new total size. If only the position changes,
+               * then clamping will occur in gtk_paned_compute_position()
+               */
+
+               xpaned->top_left_child_size.height = yposition;
+               xpaned->position_set = TRUE;
+       }
+       else
+       {
+               xpaned->position_set = FALSE;
+       }
+
+       g_object_freeze_notify (object);
+       g_object_notify (object, "y-position");
+       g_object_notify (object, "position-set");
+       g_object_thaw_notify (object);
+
+       gtk_widget_queue_resize (GTK_WIDGET (xpaned));
+}
+
+/* this call is private and only intended for internal use! */
+void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned)
+{
+       xpaned->unmaximized_position.x = gtk_xpaned_get_position_x (xpaned);
+}
+
+/* this call is private and only intended for internal use! */
+void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned)
+{
+       xpaned->unmaximized_position.y = gtk_xpaned_get_position_y (xpaned);
+}
+
+/* this call is private and only intended for internal use! */
+gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned)
+{
+       return xpaned->unmaximized_position.x;
+}
+
+/* this call is private and only intended for internal use! */
+gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned)
+{
+       return xpaned->unmaximized_position.y;
+}
+
+/**
+ * gtk_xpaned_get_top_left_child:
+ * @xpaned: a #GtkXPaned widget
+ * 
+ * Obtains the top-left child of the xpaned widget.
+ * 
+ * Return value: top-left child, or %NULL if it is not set.
+ *
+ * Since: 2.4
+ **/
+GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned)
+{
+       g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
+
+       return xpaned->top_left_child;
+}
+
+/**
+ * gtk_xpaned_get_top_right_child:
+ * @xpaned: a #GtkXPaned widget
+ * 
+ * Obtains the top-right child of the xpaned widget.
+ * 
+ * Return value: top-right child, or %NULL if it is not set.
+ *
+ * Since: 2.4
+ **/
+GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned)
+{
+       g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
+
+       return xpaned->top_right_child;
+}
+
+/**
+ * gtk_xpaned_get_bottom_left_child:
+ * @xpaned: a #GtkXPaned widget
+ * 
+ * Obtains the bottom-left child of the xpaned widget.
+ * 
+ * Return value: bottom-left child, or %NULL if it is not set.
+ *
+ * Since: 2.4
+ **/
+GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned)
+{
+       g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
+
+       return xpaned->bottom_left_child;
+}
+
+/**
+ * gtk_xpaned_get_bottom_right_child:
+ * @xpaned: a #GtkXPaned widget
+ * 
+ * Obtains the bottom-right child of the xpaned widget.
+ * 
+ * Return value: bottom-right child, or %NULL if it is not set.
+ *
+ * Since: 2.4
+ **/
+GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned)
+{
+       g_return_val_if_fail (GTK_IS_XPANED (xpaned), NULL);
+
+       return xpaned->bottom_right_child;
+}
+
+gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize)
+{
+       if (maximize)
+       {
+               /* see if any child is already maximized */
+               if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               {
+                       /* save current position */
+                       gtk_xpaned_save_unmaximized_x (xpaned);
+                       gtk_xpaned_save_unmaximized_y (xpaned);
+
+                       /* set new maximized position */
+                       gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x);
+                       gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y);
+
+                       /* mark maximized flag for top-left child */
+                       xpaned->maximized[GTK_XPANED_TOP_LEFT] = TRUE;
+
+                       return TRUE;
+               }
+               /* already one child maximized, report error */
+               else
+                       return FALSE;
+       }
+       else
+       {
+               /* verify that top-left child is really currently maximized */
+               if (xpaned->maximized[GTK_XPANED_TOP_LEFT])
+               {
+                       /* clear maximized flat for top-left child */
+                       xpaned->maximized[GTK_XPANED_TOP_LEFT] = FALSE;
+
+                       /* restore unmaximized position */
+                       gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned));
+                       gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned));
+
+                       return TRUE;
+               }
+               /* top-left child is currently not maximized, report error */
+               else
+                       return FALSE;
+       }
+}
+
+gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize)
+{
+       if (maximize)
+       {
+               /* see if any child is already maximized */
+               if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               {
+                       /* save current position */
+                       gtk_xpaned_save_unmaximized_x (xpaned);
+                       gtk_xpaned_save_unmaximized_y (xpaned);
+
+                       /* set new maximized position */
+                       gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x);
+                       gtk_xpaned_set_position_y (xpaned, xpaned->max_position.y);
+               
+                       /* mark maximized flag for top-right child */
+                       xpaned->maximized[GTK_XPANED_TOP_RIGHT] = TRUE;
+
+                       return TRUE;
+               }
+               /* already one child maximized, report error */
+               else
+                       return FALSE;
+       }
+       else
+       {
+               /* verify that top-right child is really currently maximized */
+               if (xpaned->maximized[GTK_XPANED_TOP_RIGHT])
+               {
+                       /* clear maximized flat for top-right child */
+                       xpaned->maximized[GTK_XPANED_TOP_RIGHT] = FALSE;
+
+                       /* restore unmaximized position */
+                       gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned));
+                       gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned));
+
+                       return TRUE;
+               }
+               /* top-right child is currently not maximized, report error */
+               else
+                       return FALSE;
+       }
+}
+
+gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize)
+{
+       if (maximize)
+       {
+               /* see if any child is already maximized */
+               if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               {
+                       /* save current position */
+                       gtk_xpaned_save_unmaximized_x (xpaned);
+                       gtk_xpaned_save_unmaximized_y (xpaned);
+
+                       /* set new maximized position */
+                       gtk_xpaned_set_position_x (xpaned, xpaned->max_position.x);
+                       gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y);
+
+                       /* mark maximized flag for bottom-left child */
+                       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = TRUE;
+
+                       return TRUE;
+               }
+               /* already one child maximized, report error */
+               else
+                       return FALSE;
+       }
+       else
+       {
+               /* verify that bottom-left child is really currently maximized */
+               if (xpaned->maximized[GTK_XPANED_BOTTOM_LEFT])
+               {
+                       /* clear maximized flat for bottom-left child */
+                       xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] = FALSE;
+
+                       /* restore unmaximized position */
+                       gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned));
+                       gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned));
+
+                       return TRUE;
+               }
+               /* bottom-left child is currently not maximized, report error */
+               else
+                       return FALSE;
+       }
+}
+
+gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize)
+{
+       if (maximize)
+       {
+               /* see if any child is already maximized */
+               if (!xpaned->maximized[GTK_XPANED_TOP_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_TOP_RIGHT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_LEFT] &&
+                       !xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               {
+                       /* save current position */
+                       gtk_xpaned_save_unmaximized_x (xpaned);
+                       gtk_xpaned_save_unmaximized_y (xpaned);
+
+                       /* set new maximized position */
+                       gtk_xpaned_set_position_x (xpaned, xpaned->min_position.x);
+                       gtk_xpaned_set_position_y (xpaned, xpaned->min_position.y);
+
+                       /* mark maximized flag for bottom-right child */
+                       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = TRUE;
+
+                       return TRUE;
+               }
+               /* already one child maximized, report error */
+               else
+                       return FALSE;
+       }
+       else
+       {
+               /* verify that bottom-right child is really currently maximized */
+               if (xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT])
+               {
+                       /* clear maximized flat for bottom-right child */
+                       xpaned->maximized[GTK_XPANED_BOTTOM_RIGHT] = FALSE;
+
+                       /* restore unmaximized position */
+                       gtk_xpaned_set_position_x (xpaned, gtk_xpaned_fetch_unmaximized_x (xpaned));
+                       gtk_xpaned_set_position_y (xpaned, gtk_xpaned_fetch_unmaximized_y (xpaned));
+
+                       return TRUE;
+               }
+               /* bottom-right child is currently not maximized, report error */
+               else
+                       return FALSE;
+       }
+}
+
+void
+gtk_xpaned_compute_position (GtkXPaned* xpaned,
+                            const GtkAllocation* allocation,
+                            GtkRequisition* top_left_child_req,
+                            GtkRequisition* top_right_child_req,
+                            GtkRequisition* bottom_left_child_req,
+                            GtkRequisition* bottom_right_child_req)
+{
+  GdkPoint old_position;
+  GdkPoint old_min_position;
+       GdkPoint old_max_position;
+       gint handle_size;
+       gint border_width = GTK_CONTAINER (xpaned)->border_width;
+       float fX;
+       float fY;
+
+       g_return_if_fail (GTK_IS_XPANED (xpaned));
+
+       old_position.x = xpaned->top_left_child_size.width;
+       old_position.y = xpaned->top_left_child_size.height;
+       old_min_position.x = xpaned->min_position.x;
+       old_min_position.y = xpaned->min_position.y;
+       old_max_position.x = xpaned->max_position.x;
+       old_max_position.y = xpaned->max_position.y;
+
+       fX = 100.0f * (float) old_position.x / (float) allocation->width;
+       fY = 100.0f * (float) old_position.y / (float) allocation->height;
+
+       xpaned->min_position.x = xpaned->top_left_child_shrink ? 0 : top_left_child_req->width;
+       xpaned->min_position.y = xpaned->top_left_child_shrink ? 0 : top_left_child_req->height;
+
+       gtk_widget_style_get (GTK_WIDGET (xpaned), "handle-size", &handle_size, NULL);
+
+       xpaned->max_position.x = allocation->width - 2 * border_width - handle_size;
+       xpaned->max_position.y = allocation->height - 2 * border_width - handle_size;
+       if (!xpaned->top_left_child_shrink)
+               xpaned->max_position.x = MAX (1, xpaned->max_position.x - top_left_child_req->width);
+       xpaned->max_position.x = MAX (xpaned->min_position.x, xpaned->max_position.x);
+
+       if (!xpaned->position_set)
+       {
+               if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize)
+               {
+                       xpaned->top_left_child_size.width = MAX (0, allocation->width - top_right_child_req->width);
+                       xpaned->top_left_child_size.height = MAX (0, allocation->height - top_right_child_req->height);
+               }
+               else if (!xpaned->top_left_child_resize && xpaned->top_right_child_resize)
+               {
+                       xpaned->top_left_child_size.width = top_left_child_req->width;
+                       xpaned->top_left_child_size.height = top_left_child_req->height;
+               }
+               else if (top_left_child_req->width + top_right_child_req->width != 0)
+               {
+                       xpaned->top_left_child_size.width = allocation->width * ((gdouble)top_left_child_req->width / (top_left_child_req->width + top_right_child_req->width)) + 0.5;
+               }
+               else if (top_left_child_req->height + top_right_child_req->height != 0)
+               {
+                       xpaned->top_left_child_size.height = allocation->height * ((gdouble)top_left_child_req->height / (top_left_child_req->height + top_right_child_req->height)) + 0.5;
+               }
+               else
+               {
+                       xpaned->top_left_child_size.width = allocation->width * 0.5 + 0.5;
+                       xpaned->top_left_child_size.height = allocation->height * 0.5 + 0.5;
+               }
+       }
+       else
+       {
+               /* If the position was set before the initial allocation.
+               ** (paned->last_allocation <= 0) just clamp it and leave it. */
+               if (xpaned->last_allocation.width > 0)
+               {
+                       if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize)
+                       {
+                               xpaned->top_left_child_size.width += allocation->width
+                                                                                                        - xpaned->last_allocation.width;
+
+                               xpaned->top_left_child_size.height += allocation->height
+                                                                                                         - xpaned->last_allocation.height;
+                       }
+                       else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize))
+                       {
+                               xpaned->top_left_child_size.width = allocation->width
+                                                                                                       * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width))
+                                                                                                       + 0.5;
+
+                               xpaned->top_left_child_size.height = allocation->height
+                                                                                                        * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height))
+                                                                                                        + 0.5;
+                       }
+               }
+               if (xpaned->last_allocation.height > 0)
+               {
+                       if (xpaned->top_left_child_resize && !xpaned->top_right_child_resize)
+                       {
+                               xpaned->top_left_child_size.width += allocation->width - xpaned->last_allocation.width;
+                               xpaned->top_left_child_size.height += allocation->height - xpaned->last_allocation.height;
+                       }
+                       else if (!(!xpaned->top_left_child_resize && xpaned->top_right_child_resize))
+                       {
+                               xpaned->top_left_child_size.width = allocation->width * ((gdouble) xpaned->top_left_child_size.width / (xpaned->last_allocation.width)) + 0.5;
+                               xpaned->top_left_child_size.height = allocation->height * ((gdouble) xpaned->top_left_child_size.height / (xpaned->last_allocation.height)) + 0.5;
+                       }
+               }
+
+    }
+
+       xpaned->top_left_child_size.width = CLAMP (xpaned->top_left_child_size.width,
+                                                                                          xpaned->min_position.x,
+                                                                                          xpaned->max_position.x);
+       xpaned->top_left_child_size.height = CLAMP (xpaned->top_left_child_size.height,
+                                                                                               xpaned->min_position.y,
+                                                                                               xpaned->max_position.y);
+
+       xpaned->top_right_child_size.width = CLAMP (xpaned->top_right_child_size.width,
+                                                                                               xpaned->min_position.x,
+                                                                                               xpaned->max_position.x);
+       xpaned->top_right_child_size.height = CLAMP (xpaned->top_right_child_size.height,
+                                                                                                xpaned->min_position.y,
+                                                                                                xpaned->max_position.y);
+
+       xpaned->bottom_left_child_size.width = CLAMP (xpaned->bottom_left_child_size.width,
+                                                                                                 xpaned->min_position.x,
+                                                                                                 xpaned->max_position.x);
+       xpaned->bottom_left_child_size.height = CLAMP (xpaned->bottom_left_child_size.height,
+                                                                                                  xpaned->min_position.y,
+                                                                                                  xpaned->max_position.y);
+
+       xpaned->bottom_right_child_size.width = CLAMP (xpaned->bottom_right_child_size.width,
+                                                                                                  xpaned->min_position.x,
+                                                                                                  xpaned->max_position.x);
+       xpaned->bottom_right_child_size.height = CLAMP (xpaned->bottom_right_child_size.height,
+                                                                                                       xpaned->min_position.y,
+                                                                                                       xpaned->max_position.y);
+
+       gtk_widget_set_child_visible (xpaned->top_left_child, TRUE);
+       gtk_widget_set_child_visible (xpaned->top_right_child, TRUE);
+       gtk_widget_set_child_visible (xpaned->bottom_left_child, TRUE);
+       gtk_widget_set_child_visible (xpaned->bottom_right_child, TRUE);
+
+       g_object_freeze_notify (G_OBJECT (xpaned));
+
+       if (xpaned->top_left_child_size.width != old_position.x)
+               g_object_notify (G_OBJECT (xpaned), "x-position");
+       if (xpaned->top_left_child_size.height != old_position.y)
+               g_object_notify (G_OBJECT (xpaned), "y-position");
+
+       if (xpaned->top_right_child_size.width != old_position.x)
+               g_object_notify (G_OBJECT (xpaned), "x-position");
+       if (xpaned->top_right_child_size.height != old_position.y)
+               g_object_notify (G_OBJECT (xpaned), "y-position");
+
+       if (xpaned->bottom_left_child_size.width != old_position.x)
+               g_object_notify (G_OBJECT (xpaned), "x-position");
+       if (xpaned->bottom_left_child_size.height != old_position.y)
+               g_object_notify (G_OBJECT (xpaned), "y-position");
+
+       if (xpaned->bottom_right_child_size.width != old_position.x)
+               g_object_notify (G_OBJECT (xpaned), "x-position");
+       if (xpaned->bottom_right_child_size.height != old_position.y)
+               g_object_notify (G_OBJECT (xpaned), "y-position");
+
+       if (xpaned->min_position.x != old_min_position.x)
+               g_object_notify (G_OBJECT (xpaned), "min-x-position");
+       if (xpaned->min_position.y != old_min_position.y)
+               g_object_notify (G_OBJECT (xpaned), "min-y-position");
+
+       if (xpaned->max_position.x != old_max_position.x)
+       g_object_notify (G_OBJECT (xpaned), "max-y-position");
+       if (xpaned->max_position.y != old_max_position.y)
+       g_object_notify (G_OBJECT (xpaned), "max-y-position");
+
+       g_object_thaw_notify (G_OBJECT (xpaned));
+
+       xpaned->last_allocation.width = allocation->width;
+       xpaned->last_allocation.height = allocation->height;
+
+       fX = 100.0f * (float) old_position.x / (float) allocation->width;
+       fY = 100.0f * (float) old_position.y / (float) allocation->height;
+}
+
+static void gtk_xpaned_set_saved_focus (GtkXPaned* xpaned, GtkWidget* widget)
+{
+       if (xpaned->priv->saved_focus)
+               g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->saved_focus),
+                                                                         (gpointer *)&(xpaned->priv->saved_focus));
+
+       xpaned->priv->saved_focus = widget;
+
+       if (xpaned->priv->saved_focus)
+               g_object_add_weak_pointer (G_OBJECT (xpaned->priv->saved_focus),
+                                                                  (gpointer *)&(xpaned->priv->saved_focus));
+}
+
+static void gtk_xpaned_set_first_xpaned (GtkXPaned* xpaned,
+                                                                                GtkXPaned* first_xpaned)
+{
+       if (xpaned->priv->first_xpaned)
+               g_object_remove_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned),
+                                                                         (gpointer *)&(xpaned->priv->first_xpaned));
+
+       xpaned->priv->first_xpaned = first_xpaned;
+
+       if (xpaned->priv->first_xpaned)
+               g_object_add_weak_pointer (G_OBJECT (xpaned->priv->first_xpaned),
+                                                                  (gpointer *)&(xpaned->priv->first_xpaned));
+}
+
+static void gtk_xpaned_set_last_top_left_child_focus (GtkXPaned* xpaned,
+                                                                                                         GtkWidget* widget)
+{
+       if (xpaned->last_top_left_child_focus)
+               g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus),
+                                                                         (gpointer *)&(xpaned->last_top_left_child_focus));
+
+       xpaned->last_top_left_child_focus = widget;
+
+       if (xpaned->last_top_left_child_focus)
+               g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_left_child_focus),
+                                                                  (gpointer *)&(xpaned->last_top_left_child_focus));
+}
+
+static void gtk_xpaned_set_last_top_right_child_focus (GtkXPaned* xpaned,
+                                                                                                          GtkWidget *widget)
+{
+       if (xpaned->last_top_right_child_focus)
+               g_object_remove_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus),
+                                                                         (gpointer *)&(xpaned->last_top_right_child_focus));
+
+       xpaned->last_top_right_child_focus = widget;
+
+       if (xpaned->last_top_right_child_focus)
+               g_object_add_weak_pointer (G_OBJECT (xpaned->last_top_right_child_focus),
+                                                                  (gpointer *)&(xpaned->last_top_right_child_focus));
+}
+
+static void gtk_xpaned_set_last_bottom_left_child_focus (GtkXPaned* xpaned,
+                                                                                                                GtkWidget *widget)
+{
+       if (xpaned->last_bottom_left_child_focus)
+               g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus),
+                                                                         (gpointer *)&(xpaned->last_bottom_left_child_focus));
+
+       xpaned->last_bottom_left_child_focus = widget;
+
+       if (xpaned->last_bottom_left_child_focus)
+               g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_left_child_focus),
+                                                                  (gpointer *)&(xpaned->last_bottom_left_child_focus));
+}
+
+static void gtk_xpaned_set_last_bottom_right_child_focus (GtkXPaned* xpaned,
+                                                                                                                GtkWidget *widget)
+{
+       if (xpaned->last_bottom_right_child_focus)
+               g_object_remove_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus),
+                                                                         (gpointer *)&(xpaned->last_bottom_right_child_focus));
+
+       xpaned->last_bottom_right_child_focus = widget;
+
+       if (xpaned->last_bottom_right_child_focus)
+               g_object_add_weak_pointer (G_OBJECT (xpaned->last_bottom_right_child_focus),
+                                                                  (gpointer *)&(xpaned->last_bottom_right_child_focus));
+}
+
+static GtkWidget* xpaned_get_focus_widget (GtkXPaned* xpaned)
+{
+       GtkWidget* toplevel;
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
+       if (GTK_WIDGET_TOPLEVEL (toplevel))
+               return GTK_WINDOW (toplevel)->focus_widget;
+
+       return NULL;
+}
+
+static void gtk_xpaned_set_focus_child (GtkContainer* container,
+                                                                               GtkWidget* focus_child)
+{
+       GtkXPaned* xpaned;
+
+       g_return_if_fail (GTK_IS_XPANED (container));
+
+       xpaned = GTK_XPANED (container);
+
+       if (focus_child == NULL)
+       {
+               GtkWidget* last_focus;
+               GtkWidget* w;
+
+               last_focus = xpaned_get_focus_widget (xpaned);
+
+               if (last_focus)
+               {
+                       /* If there is one or more paned widgets between us and the
+                       * focus widget, we want the topmost of those as last_focus
+                       */
+                       for (w = last_focus; w != GTK_WIDGET (xpaned); w = w->parent)
+                               if (GTK_IS_XPANED (w))
+                                       last_focus = w;
+
+                       if (container->focus_child == xpaned->top_left_child)
+                               gtk_xpaned_set_last_top_left_child_focus (xpaned, last_focus);
+                       else if (container->focus_child == xpaned->top_right_child)
+                               gtk_xpaned_set_last_top_right_child_focus (xpaned, last_focus);
+                       else if (container->focus_child == xpaned->bottom_left_child)
+                               gtk_xpaned_set_last_bottom_left_child_focus (xpaned, last_focus);
+                       else if (container->focus_child == xpaned->bottom_right_child)
+                               gtk_xpaned_set_last_bottom_right_child_focus (xpaned, last_focus);
+               }
+       }
+
+       if (parent_class->set_focus_child)
+               (* parent_class->set_focus_child) (container, focus_child);
+}
+
+static void gtk_xpaned_get_cycle_chain (GtkXPaned* xpaned,
+                                                                               GtkDirectionType direction,
+                                                                               GList** widgets)
+{
+       GtkContainer* container = GTK_CONTAINER (xpaned);
+       GtkWidget* ancestor = NULL;
+       GList* temp_list = NULL;
+       GList* list;
+
+       if (xpaned->in_recursion)
+               return;
+
+       g_assert (widgets != NULL);
+
+       if (xpaned->last_top_left_child_focus &&
+               !gtk_widget_is_ancestor (xpaned->last_top_left_child_focus,
+                                                                GTK_WIDGET (xpaned)))
+       {
+               gtk_xpaned_set_last_top_left_child_focus (xpaned, NULL);
+       }
+
+       if (xpaned->last_top_right_child_focus &&
+               !gtk_widget_is_ancestor (xpaned->last_top_right_child_focus,
+                                                                GTK_WIDGET (xpaned)))
+       {
+               gtk_xpaned_set_last_top_right_child_focus (xpaned, NULL);
+       }
+
+       if (xpaned->last_bottom_left_child_focus &&
+               !gtk_widget_is_ancestor (xpaned->last_bottom_left_child_focus,
+                                                                GTK_WIDGET (xpaned)))
+       {
+               gtk_xpaned_set_last_bottom_left_child_focus (xpaned, NULL);
+       }
+
+       if (xpaned->last_bottom_right_child_focus &&
+               !gtk_widget_is_ancestor (xpaned->last_bottom_right_child_focus,
+                                                                GTK_WIDGET (xpaned)))
+       {
+               gtk_xpaned_set_last_bottom_right_child_focus (xpaned, NULL);
+       }
+
+       if (GTK_WIDGET (xpaned)->parent)
+               ancestor = gtk_widget_get_ancestor (GTK_WIDGET (xpaned)->parent,
+                                                                                       GTK_TYPE_XPANED);
+
+       /* The idea here is that temp_list is a list of widgets we want to cycle
+       * to. The list is prioritized so that the first element is our first
+       * choice, the next our second, and so on.
+       *
+       * We can't just use g_list_reverse(), because we want to try
+       * paned->last_child?_focus before paned->child?, both when we
+       * are going forward and backward.
+       */
+       if (direction == GTK_DIR_TAB_FORWARD)
+       {
+               if (container->focus_child == xpaned->top_left_child)
+               {
+                       temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_right_child);
+                       temp_list = g_list_append (temp_list, ancestor);
+               }
+               else if (container->focus_child == xpaned->top_right_child)
+               {
+                       temp_list = g_list_append (temp_list, ancestor);
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
+               }
+               else if (container->focus_child == xpaned->bottom_left_child)
+               {
+                       temp_list = g_list_append (temp_list, ancestor);
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
+               }
+               else if (container->focus_child == xpaned->bottom_right_child)
+               {
+                       temp_list = g_list_append (temp_list, ancestor);
+                       temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_left_child);
+               }
+               else
+               {
+                       temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_left_child);
+                       temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_right_child);
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
+                       temp_list = g_list_append (temp_list, ancestor);
+               }
+       }
+       else
+       {
+               if (container->focus_child == xpaned->top_left_child)
+               {
+                       temp_list = g_list_append (temp_list, ancestor);
+                       temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_right_child);
+               }
+               else if (container->focus_child == xpaned->top_right_child)
+               {
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
+                       temp_list = g_list_append (temp_list, ancestor);
+               }
+               else if (container->focus_child == xpaned->bottom_right_child)
+               {
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
+                       temp_list = g_list_append (temp_list, ancestor);
+               }
+               else if (container->focus_child == xpaned->top_right_child)
+               {
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
+                       temp_list = g_list_append (temp_list, ancestor);
+               }
+               else
+               {
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_right_child);
+                       temp_list = g_list_append (temp_list, xpaned->last_bottom_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->bottom_left_child);
+                       temp_list = g_list_append (temp_list, xpaned->last_top_right_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_right_child);
+                       temp_list = g_list_append (temp_list, xpaned->last_top_left_child_focus);
+                       temp_list = g_list_append (temp_list, xpaned->top_left_child);
+                       temp_list = g_list_append (temp_list, ancestor);
+               }
+       }
+
+       /* Walk the list and expand all the paned widgets. */
+       for (list = temp_list; list != NULL; list = list->next)
+       {
+               GtkWidget *widget = list->data;
+
+               if (widget)
+               {
+                       if (GTK_IS_XPANED (widget))
+                       {
+                               xpaned->in_recursion = TRUE;
+                               gtk_xpaned_get_cycle_chain (GTK_XPANED (widget),
+                                                                                       direction,
+                                                                                       widgets);
+                               xpaned->in_recursion = FALSE;
+                       }
+                       else
+                       {
+                               *widgets = g_list_append (*widgets, widget);
+                       }
+               }
+       }
+
+       g_list_free (temp_list);
+}
+
+static gboolean gtk_xpaned_cycle_child_focus (GtkXPaned* xpaned,
+                                                                                         gboolean reversed)
+{
+       GList* cycle_chain = NULL;
+       GList* list;
+
+       GtkDirectionType direction = reversed ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
+
+       /* ignore f6 if the handle is focused */
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+               return TRUE;
+
+       /* we can't just let the event propagate up the hierarchy,
+       * because the paned will want to cycle focus _unless_ an
+       * ancestor paned handles the event
+       */
+       gtk_xpaned_get_cycle_chain (xpaned, direction, &cycle_chain);
+
+       for (list = cycle_chain; list != NULL; list = list->next)
+               if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction))
+                       break;
+
+       g_list_free (cycle_chain);
+
+       return TRUE;
+}
+
+static void get_child_xpanes (GtkWidget* widget, GList** xpanes)
+{
+       if (GTK_IS_XPANED (widget))
+       {
+               GtkXPaned* xpaned = GTK_XPANED (widget);
+
+               get_child_xpanes (xpaned->top_left_child, xpanes);
+               *xpanes = g_list_prepend (*xpanes, widget);
+               get_child_xpanes (xpaned->top_right_child, xpanes);
+               *xpanes = g_list_prepend (*xpanes, widget);
+               get_child_xpanes (xpaned->bottom_left_child, xpanes);
+               *xpanes = g_list_prepend (*xpanes, widget);
+               get_child_xpanes (xpaned->bottom_right_child, xpanes);
+       }
+       else if (GTK_IS_CONTAINER (widget))
+       {
+               gtk_container_foreach (GTK_CONTAINER (widget),
+                                                          (GtkCallback)get_child_xpanes,
+                                                          xpanes);
+       }
+}
+
+static GList* get_all_xpanes (GtkXPaned* xpaned)
+{
+       GtkXPaned* topmost = NULL;
+       GList* result = NULL;
+       GtkWidget* w;
+  
+       for (w = GTK_WIDGET (xpaned); w != NULL; w = w->parent)
+       {
+               if (GTK_IS_XPANED (w))
+                       topmost = GTK_XPANED (w);
+       }
+
+       g_assert (topmost);
+
+       get_child_xpanes (GTK_WIDGET (topmost), &result);
+
+       return g_list_reverse (result);
+}
+
+static void gtk_xpaned_find_neighbours (GtkXPaned* xpaned,
+                                                                               GtkXPaned** next,
+                                                                               GtkXPaned** prev)
+{
+       GList* all_xpanes;
+       GList* this_link;
+
+       all_xpanes = get_all_xpanes (xpaned);
+       g_assert (all_xpanes);
+
+       this_link = g_list_find (all_xpanes, xpaned);
+
+       g_assert (this_link);
+
+       if (this_link->next)
+               *next = this_link->next->data;
+       else
+               *next = all_xpanes->data;
+
+       if (this_link->prev)
+               *prev = this_link->prev->data;
+       else
+               *prev = g_list_last (all_xpanes)->data;
+
+       g_list_free (all_xpanes);
+}
+
+static gboolean gtk_xpaned_move_handle (GtkXPaned* xpaned, GtkScrollType scroll)
+{
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+       {
+               GdkPoint old_position;
+               GdkPoint new_position;
+               gint increment;
+
+               enum
+               {
+                       SINGLE_STEP_SIZE = 1,
+                       PAGE_STEP_SIZE   = 75
+               };
+
+               new_position.x = old_position.x = gtk_xpaned_get_position_x (xpaned);
+               new_position.y = old_position.y = gtk_xpaned_get_position_y (xpaned);
+               increment = 0;
+
+               switch (scroll)
+               {
+                       case GTK_SCROLL_STEP_LEFT:
+                       case GTK_SCROLL_STEP_UP:
+                       case GTK_SCROLL_STEP_BACKWARD:
+                               increment = - SINGLE_STEP_SIZE;
+                       break;
+
+                       case GTK_SCROLL_STEP_RIGHT:
+                       case GTK_SCROLL_STEP_DOWN:
+                       case GTK_SCROLL_STEP_FORWARD:
+                               increment = SINGLE_STEP_SIZE;
+                       break;
+
+                       case GTK_SCROLL_PAGE_LEFT:
+                       case GTK_SCROLL_PAGE_UP:
+                       case GTK_SCROLL_PAGE_BACKWARD:
+                               increment = - PAGE_STEP_SIZE;
+                       break;
+
+                       case GTK_SCROLL_PAGE_RIGHT:
+                       case GTK_SCROLL_PAGE_DOWN:
+                       case GTK_SCROLL_PAGE_FORWARD:
+                               increment = PAGE_STEP_SIZE;
+                       break;
+
+                       case GTK_SCROLL_START:
+                               new_position.x = xpaned->min_position.x;
+                               new_position.y = xpaned->min_position.y;
+                       break;
+
+                       case GTK_SCROLL_END:
+                               new_position.x = xpaned->max_position.x;
+                               new_position.y = xpaned->max_position.y;
+                       break;
+
+                       default:
+                       break;
+               }
+
+               if (increment)
+               {
+                       if (is_rtl (xpaned))
+                               increment = -increment;
+
+                       new_position.x = old_position.x + increment;
+                       new_position.y = old_position.y + increment;
+               }
+
+               new_position.x = CLAMP (new_position.x,
+                                                               xpaned->min_position.x,
+                                                               xpaned->max_position.x);
+
+               new_position.y = CLAMP (new_position.y,
+                                                               xpaned->min_position.y,
+                                                               xpaned->max_position.y);
+
+               if (old_position.x != new_position.x)
+                       gtk_xpaned_set_position_x (xpaned, new_position.x);
+
+               if (old_position.y != new_position.y)
+                       gtk_xpaned_set_position_y (xpaned, new_position.y);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void gtk_xpaned_restore_focus (GtkXPaned* xpaned)
+{
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+       {
+               if (xpaned->priv->saved_focus &&
+                       GTK_WIDGET_SENSITIVE (xpaned->priv->saved_focus))
+               {
+                       gtk_widget_grab_focus (xpaned->priv->saved_focus);
+               }
+               else
+               {
+                       /* the saved focus is somehow not available for focusing,
+                       * try
+                       *   1) tabbing into the paned widget
+                       * if that didn't work,
+                       *   2) unset focus for the window if there is one
+                       */
+
+                       if (!gtk_widget_child_focus (GTK_WIDGET (xpaned), GTK_DIR_TAB_FORWARD))
+                       {
+                               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
+             
+                               if (GTK_IS_WINDOW (toplevel))
+                                       gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
+                       }
+               }
+               
+               gtk_xpaned_set_saved_focus (xpaned, NULL);
+               gtk_xpaned_set_first_xpaned (xpaned, NULL);
+    }
+}
+
+static gboolean gtk_xpaned_accept_position (GtkXPaned* xpaned)
+{
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+       {
+               xpaned->original_position.x = -1;
+               xpaned->original_position.y = -1;
+               gtk_xpaned_restore_focus (xpaned);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean gtk_xpaned_cancel_position (GtkXPaned* xpaned)
+{
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+       {
+               if (xpaned->original_position.x != -1)
+               {
+                       gtk_xpaned_set_position_x (xpaned, xpaned->original_position.x);
+                       xpaned->original_position.x = -1;
+               }
+
+               if (xpaned->original_position.y != -1)
+               {
+                       gtk_xpaned_set_position_y (xpaned, xpaned->original_position.y);
+                       xpaned->original_position.y = -1;
+               }
+
+               gtk_xpaned_restore_focus (xpaned);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean gtk_xpaned_cycle_handle_focus (GtkXPaned* xpaned,
+                                                                                          gboolean reversed)
+{
+       GtkXPaned* next;
+       GtkXPaned* prev;
+
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+       {
+               GtkXPaned* focus = NULL;
+
+               if (!xpaned->priv->first_xpaned)
+               {
+                       /* The first_pane has disappeared. As an ad-hoc solution,
+                       * we make the currently focused paned the first_paned. To the
+                       * user this will seem like the paned cycling has been reset.
+                       */
+                       gtk_xpaned_set_first_xpaned (xpaned, xpaned);
+               }
+
+               gtk_xpaned_find_neighbours (xpaned, &next, &prev);
+
+               if (reversed && prev &&
+                       prev != xpaned && xpaned != xpaned->priv->first_xpaned)
+               {
+                       focus = prev;
+               }
+               else if (!reversed &&
+                                next &&
+                                next != xpaned &&
+                                next != xpaned->priv->first_xpaned)
+               {
+                       focus = next;
+               }
+               else
+               {
+                       gtk_xpaned_accept_position (xpaned);
+                       return TRUE;
+               }
+
+               g_assert (focus);
+      
+               gtk_xpaned_set_saved_focus (focus, xpaned->priv->saved_focus);
+               gtk_xpaned_set_first_xpaned (focus, xpaned->priv->first_xpaned);
+      
+               gtk_xpaned_set_saved_focus (xpaned, NULL);
+               gtk_xpaned_set_first_xpaned (xpaned, NULL);
+      
+               gtk_widget_grab_focus (GTK_WIDGET (focus));
+      
+               if (!gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+               {
+                       xpaned->original_position.x = -1;
+                       xpaned->original_position.y = -1;
+                       focus->original_position.x = gtk_xpaned_get_position_x (focus);
+                       focus->original_position.y = gtk_xpaned_get_position_y (focus);
+               }
+       }
+       else
+    {
+               GtkContainer* container = GTK_CONTAINER (xpaned);
+               GtkXPaned* focus;
+               GtkXPaned* first;
+               GtkXPaned* prev;
+               GtkXPaned* next;
+               GtkWidget* toplevel;
+
+               gtk_xpaned_find_neighbours (xpaned, &next, &prev);
+
+               if (container->focus_child == xpaned->top_left_child)
+               {
+                       if (reversed)
+                       {
+                               focus = prev;
+                               first = xpaned;
+                       }
+                       else
+                       {
+                               focus = xpaned;
+                               first = xpaned;
+                       }
+               }
+               else if (container->focus_child == xpaned->top_right_child)
+               {
+                       if (reversed)
+                       {
+                               focus = xpaned;
+                               first = next;
+                       }
+                       else
+                       {
+                               focus = next;
+                               first = next;
+                       }
+               }
+               else
+               {
+                       /* Focus is not inside this xpaned, and we don't have focus.
+                       * Presumably this happened because the application wants us
+                       * to start keyboard navigating.
+                       */
+                       focus = xpaned;
+
+                       if (reversed)
+                               first = xpaned;
+                       else
+                               first = next;
+               }
+
+               toplevel = gtk_widget_get_toplevel (GTK_WIDGET (xpaned));
+
+               if (GTK_IS_WINDOW (toplevel))
+                       gtk_xpaned_set_saved_focus (focus, GTK_WINDOW (toplevel)->focus_widget);
+               gtk_xpaned_set_first_xpaned (focus, first);
+               focus->original_position.x = gtk_xpaned_get_position_x (focus);
+               focus->original_position.y = gtk_xpaned_get_position_y (focus); 
+
+               gtk_widget_grab_focus (GTK_WIDGET (focus));
+       }
+
+       return TRUE;
+}
+
+static gboolean gtk_xpaned_toggle_handle_focus (GtkXPaned* xpaned)
+{
+       /* This function/signal has the wrong name. It is called when you
+       * press Tab or Shift-Tab and what we do is act as if
+       * the user pressed Return and then Tab or Shift-Tab
+       */
+       if (gtk_widget_is_focus (GTK_WIDGET (xpaned)))
+               gtk_xpaned_accept_position (xpaned);
+
+  return FALSE;
+}
+
+/*#define __GTK_XPANED_C__*/
+/*#include "gtkaliasdef.c"*/
diff --git a/lib/gtk-contrib/gtkxpaned.h b/lib/gtk-contrib/gtkxpaned.h
new file mode 100644 (file)
index 0000000..47e752e
--- /dev/null
@@ -0,0 +1,175 @@
+/*******************************************************************************
+**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 
+**      10        20        30        40        50        60        70        80
+**
+**  library for GtkXPaned-widget, a 2x2 grid-like variation of GtkPaned of gtk+
+**  Copyright (C) 2005-2006 Mirco "MacSlow" Müller <macslow@bangang.de>
+**
+**  This library is free software; you can redistribute it and/or
+**  modify it under the terms of the GNU Lesser General Public
+**  License as published by the Free Software Foundation; either
+**  version 2.1 of the License, or (at your option) any later version.
+**
+**  This library 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
+**  Lesser General Public License for more details.
+**
+**  You should have received a copy of the GNU Lesser General Public
+**  License along with this library; if not, write to the Free Software
+**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**
+**  GtkXPaned is based on GtkPaned which was done by...
+**
+**  "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald"
+**
+**  and later modified by...
+**
+**  "the GTK+ Team and others 1997-2000"
+**
+*******************************************************************************/
+
+#ifndef GTK_XPANED_H
+#define GTK_XPANED_H
+
+#include <gtk/gtkcontainer.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_XPANED                  (gtk_xpaned_get_type ())
+#define GTK_XPANED(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_XPANED, GtkXPaned))
+#define GTK_XPANED_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XPANED, GtkXPanedClass))
+#define GTK_IS_XPANED(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_XPANED))
+#define GTK_IS_XPANED_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XPANED))
+#define GTK_XPANED_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XPANED, GtkXPanedClass))
+
+typedef struct _GtkXPaned        GtkXPaned;
+typedef struct _GtkXPanedClass   GtkXPanedClass;
+typedef struct _GtkXPanedPrivate GtkXPanedPrivate;
+
+typedef enum _GtkXPanedChild
+{
+       GTK_XPANED_TOP_LEFT = 0,
+       GTK_XPANED_TOP_RIGHT,
+       GTK_XPANED_BOTTOM_LEFT,
+       GTK_XPANED_BOTTOM_RIGHT
+} GtkXPanedChild;
+       
+struct _GtkXPaned
+{
+       GtkContainer container;
+  
+       GtkWidget* top_left_child;
+       GtkWidget* top_right_child;
+       GtkWidget* bottom_left_child;
+       GtkWidget* bottom_right_child;
+
+       GdkWindow* handle_east;
+       GdkWindow* handle_west;
+       GdkWindow* handle_north;
+       GdkWindow* handle_south;
+       GdkWindow* handle_middle;
+       GdkGC* xor_gc;
+       GdkCursorType cursor_type_east;
+       GdkCursorType cursor_type_west;
+       GdkCursorType cursor_type_north;
+       GdkCursorType cursor_type_south;
+       GdkCursorType cursor_type_middle;
+
+       /*< private >*/
+       GdkRectangle handle_pos_east;
+       GdkRectangle handle_pos_west;
+       GdkRectangle handle_pos_north;
+       GdkRectangle handle_pos_south;
+       GdkRectangle handle_pos_middle;
+       GtkRequisition top_left_child_size;
+       GtkRequisition top_right_child_size;
+       GtkRequisition bottom_left_child_size;
+       GtkRequisition bottom_right_child_size;
+
+       GtkRequisition last_allocation;
+       GdkPoint min_position;
+       GdkPoint max_position;
+       gboolean maximized[4];
+
+       guint position_set : 1;
+       guint in_drag_vert : 1;
+       guint in_drag_horiz : 1;
+       guint in_drag_vert_and_horiz : 1;
+       guint top_left_child_shrink : 1;
+       guint top_left_child_resize : 1;
+       guint top_right_child_shrink : 1;
+       guint top_right_child_resize : 1;
+       guint bottom_left_child_shrink : 1;
+       guint bottom_left_child_resize : 1;
+       guint bottom_right_child_shrink : 1;
+       guint bottom_right_child_resize : 1;
+       guint in_recursion : 1;
+       guint handle_prelit : 1;
+
+       GtkWidget* last_top_left_child_focus;
+       GtkWidget* last_top_right_child_focus;
+       GtkWidget* last_bottom_left_child_focus;
+       GtkWidget* last_bottom_right_child_focus;
+       GtkXPanedPrivate* priv;
+
+       GdkPoint drag_pos;
+       GdkPoint original_position;
+       GdkPoint unmaximized_position;
+};
+
+struct _GtkXPanedClass
+{
+       GtkContainerClass parent_class;
+       gboolean (*cycle_child_focus)   (GtkXPaned* xpaned, gboolean reverse); 
+       gboolean (*toggle_handle_focus) (GtkXPaned* xpaned);
+       gboolean (*move_handle)                 (GtkXPaned* xpaned, GtkScrollType scroll);
+       gboolean (*cycle_handle_focus)  (GtkXPaned* xpaned, gboolean reverse);
+       gboolean (*accept_position)             (GtkXPaned* xpaned);
+       gboolean (*cancel_position)     (GtkXPaned* xpaned);
+};
+
+GType gtk_xpaned_get_type (void) G_GNUC_CONST;
+GtkWidget* gtk_xpaned_new (void);
+void gtk_xpaned_add_top_left (GtkXPaned* xpaned, GtkWidget* child);
+void gtk_xpaned_add_top_right (GtkXPaned* xpaned, GtkWidget* child);
+void gtk_xpaned_add_bottom_left (GtkXPaned* xpaned, GtkWidget* child);
+void gtk_xpaned_add_bottom_right (GtkXPaned* xpaned, GtkWidget* child);
+void gtk_xpaned_pack_top_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink);
+void gtk_xpaned_pack_top_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink);
+void gtk_xpaned_pack_bottom_left (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink);
+void gtk_xpaned_pack_bottom_right (GtkXPaned* xpaned, GtkWidget* child, gboolean resize, gboolean shrink);
+gint gtk_xpaned_get_position_x (GtkXPaned* xpaned);
+gint gtk_xpaned_get_position_y (GtkXPaned* xpaned);
+void gtk_xpaned_set_position_x (GtkXPaned* xpaned, gint xposition);
+void gtk_xpaned_set_position_y (GtkXPaned* xpaned, gint yposition);
+void gtk_xpaned_save_unmaximized_x (GtkXPaned* xpaned);
+void gtk_xpaned_save_unmaximized_y (GtkXPaned* xpaned);
+gint gtk_xpaned_fetch_unmaximized_x (GtkXPaned* xpaned);
+gint gtk_xpaned_fetch_unmaximized_y (GtkXPaned* xpaned);
+GtkWidget* gtk_xpaned_get_top_left_child (GtkXPaned* xpaned);
+GtkWidget* gtk_xpaned_get_top_right_child (GtkXPaned* xpaned);
+GtkWidget* gtk_xpaned_get_bottom_right_child (GtkXPaned* xpaned);
+GtkWidget* gtk_xpaned_get_bottom_left_child (GtkXPaned* xpaned);
+gboolean gtk_xpaned_maximize_top_left (GtkXPaned* xpaned, gboolean maximize);
+gboolean gtk_xpaned_maximize_top_right (GtkXPaned* xpaned, gboolean maximize);
+gboolean gtk_xpaned_maximize_bottom_left (GtkXPaned* xpaned, gboolean maximize);
+gboolean gtk_xpaned_maximize_bottom_right (GtkXPaned* xpaned, gboolean maximize);
+
+/* Internal function */
+#if !defined (GTK_DISABLE_DEPRECATED) || defined (GTK_COMPILATION)
+void gtk_xpaned_compute_position (GtkXPaned* xpaned,
+                                 const GtkAllocation* allocation,
+                                 GtkRequisition* top_left_child_req,
+                                 GtkRequisition* top_right_child_req,
+                                 GtkRequisition* bottom_left_child_req,
+                                 GtkRequisition* bottom_right_child_req);
+#endif /* !GTK_DISABLE_DEPRECATED || GTK_COMPILATION */
+#ifndef GTK_DISABLE_DEPRECATED
+#define        gtk_xpaned_gutter_size(p,s) (void) 0
+#define        gtk_xpaned_set_gutter_size(p,s) (void) 0
+#endif /* GTK_DISABLE_DEPRECATED */
+
+G_END_DECLS
+
+#endif /* GTK_XPANED_H */
diff --git a/lib/gtk-contrib/psppire-sheet.c b/lib/gtk-contrib/psppire-sheet.c
new file mode 100644 (file)
index 0000000..33e7b47
--- /dev/null
@@ -0,0 +1,5591 @@
+/*
+   Copyright (C) 2006, 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
+   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/>.
+
+
+ This file is derived from the gtksheet.c and extensively modified for the
+ requirements of PSPPIRE.  The changes are copyright by the
+ Free Software Foundation.  The copyright notice for the original work is
+ below.
+*/
+
+/* GtkSheet widget for Gtk+.
+ * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
+ *
+ * Based on GtkClist widget by Jay Painter, but major changes.
+ * Memory allocation routines inspired on SC (Spreadsheet Calculator)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:psppiresheet
+ * @short_description: spreadsheet widget for gtk2
+ *
+ * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
+ * cells where you can allocate text. Cell contents can be edited interactively
+ * through a specially designed entry, GtkItemEntry.
+ *
+ */
+#include <config.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtktypeutils.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtkcontainer.h>
+#include <pango/pango.h>
+#include "psppire-sheet.h"
+#include <ui/gui/psppire-marshal.h>
+#include <ui/gui/sheet/psppire-sheetmodel.h>
+#include <ui/gui/sheet/psppire-axis.h>
+#include <libpspp/misc.h>
+
+#include <math.h>
+
+/* sheet flags */
+enum
+  {
+    PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
+    PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
+    PSPPIRE_SHEET_IN_DRAG = 1 << 3,
+    PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
+    PSPPIRE_SHEET_IN_RESIZE = 1 << 5
+  };
+
+#define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
+#define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
+#define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
+
+#define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
+#define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
+#define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
+#define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
+#define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
+
+#define CELL_SPACING 1
+
+#define TIMEOUT_HOVER 300
+#define COLUMN_MIN_WIDTH 10
+#define COLUMN_TITLES_HEIGHT 4
+#define DEFAULT_COLUMN_WIDTH 80
+#define DEFAULT_ROW_HEIGHT 25
+
+static void set_entry_widget_font (PsppireSheet *sheet);
+
+static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
+static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
+static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
+static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
+
+
+static void set_row_height (PsppireSheet *sheet,
+                           gint row,
+                           gint height);
+
+static void destroy_hover_window (PsppireSheetHoverTitle *);
+static PsppireSheetHoverTitle *create_hover_window (void);
+
+static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col);
+
+
+static inline  void
+dispose_string (const PsppireSheet *sheet, gchar *text)
+{
+  PsppireSheetModel *model = psppire_sheet_get_model (sheet);
+
+  if ( ! model )
+    return;
+
+  if (psppire_sheet_model_free_strings (model))
+    g_free (text);
+}
+
+
+/* FIXME: Why bother with these two ? */
+
+/* returns the column index from a pixel location */
+static inline gint
+column_from_xpixel (const PsppireSheet *sheet, gint pixel)
+{
+  return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
+}
+
+static inline gint
+row_from_ypixel (const PsppireSheet *sheet, gint pixel)
+{
+  return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
+}
+
+
+/* Return the lowest row number which is wholly or partially on
+   the visible range of the sheet */
+static inline glong
+min_visible_row (const PsppireSheet *sheet)
+{
+  return row_from_ypixel (sheet, sheet->vadjustment->value);
+}
+
+static inline glong
+min_fully_visible_row (const PsppireSheet *sheet)
+{
+  glong row = min_visible_row (sheet);
+
+  if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
+    row++;
+
+  return row;
+}
+
+static inline glong
+max_visible_row (const PsppireSheet *sheet)
+{
+  return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
+}
+
+
+static inline glong
+max_fully_visible_row (const PsppireSheet *sheet)
+{
+  glong row = max_visible_row (sheet);
+
+  if ( psppire_axis_start_pixel (sheet->vaxis, row)
+       +
+       psppire_axis_unit_size (sheet->vaxis, row)
+       > sheet->vadjustment->value)
+    row--;
+
+  return row;
+}
+
+
+/* Returns the lowest column number which is wholly or partially
+   on the sheet */
+static inline glong
+min_visible_column (const PsppireSheet *sheet)
+{
+  return column_from_xpixel (sheet, sheet->hadjustment->value);
+}
+
+static inline glong
+min_fully_visible_column (const PsppireSheet *sheet)
+{
+  glong col = min_visible_column (sheet);
+
+  if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
+    col++;
+
+  return col;
+}
+
+
+/* Returns the highest column number which is wholly or partially
+   on the sheet */
+static inline glong
+max_visible_column (const PsppireSheet *sheet)
+{
+  return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
+}
+
+static inline glong
+max_fully_visible_column (const PsppireSheet *sheet)
+{
+  glong col = max_visible_column (sheet);
+
+  if ( psppire_axis_start_pixel (sheet->haxis, col)
+       +
+       psppire_axis_unit_size (sheet->haxis, col)
+       > sheet->hadjustment->value)
+    col--;
+
+  return col;
+}
+
+
+
+/* The size of the region (in pixels) around the row/column boundaries
+   where the height/width may be grabbed to change size */
+#define DRAG_WIDTH 6
+
+static gboolean
+on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
+{
+  gint col;
+  gint pixel;
+
+  x += sheet->hadjustment->value;
+
+  if ( x < 0)
+    return FALSE;
+
+  col = column_from_xpixel (sheet, x);
+
+  pixel = x - DRAG_WIDTH / 2;
+  if (pixel < 0)
+    pixel = 0;
+
+  if ( column_from_xpixel (sheet, pixel) < col )
+    {
+      *column = col - 1;
+      return TRUE;
+    }
+
+  if  ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
+    {
+      *column = col;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
+{
+  gint r;
+  gint pixel;
+
+  y += sheet->vadjustment->value;
+
+  if ( y < 0)
+    return FALSE;
+
+  r = row_from_ypixel (sheet, y);
+
+  pixel = y - DRAG_WIDTH / 2;
+  if (pixel < 0)
+    pixel = 0;
+
+  if ( row_from_ypixel (sheet, pixel) < r )
+    {
+      *row = r - 1;
+      return TRUE;
+    }
+
+  if  ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
+    {
+      *row = r;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+static inline gboolean
+POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
+              gint *drag_row, gint *drag_column)
+{
+  gint ydrag, xdrag;
+
+  /* Can't drag if nothing is selected */
+  if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
+       sheet->range.col0 < 0 || sheet->range.coli < 0 )
+    return FALSE;
+
+  *drag_column = column_from_xpixel (sheet, x);
+  *drag_row = row_from_ypixel (sheet, y);
+
+  if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
+      x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
+      psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
+    {
+      ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
+      if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
+       {
+         *drag_row = sheet->range.row0;
+         return TRUE;
+       }
+      ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
+       psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
+      if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
+       {
+         *drag_row = sheet->range.rowi;
+         return TRUE;
+       }
+    }
+
+  if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
+      y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
+      psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
+    {
+      xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
+      if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
+       {
+         *drag_column = sheet->range.col0;
+         return TRUE;
+       }
+      xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
+       psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
+      if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
+       {
+         *drag_column = sheet->range.coli;
+         return TRUE;
+       }
+    }
+
+  return FALSE;
+}
+
+static inline gboolean
+POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
+                gint *drag_row, gint *drag_column)
+{
+  gint xdrag, ydrag;
+
+  /* Can't drag if nothing is selected */
+  if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
+       sheet->range.col0 < 0 || sheet->range.coli < 0 )
+    return FALSE;
+
+  xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
+    psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
+
+  ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
+    psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
+
+  if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED)
+    ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
+
+  if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED)
+    xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
+
+  *drag_column = column_from_xpixel (sheet, x);
+  *drag_row = row_from_ypixel (sheet, y);
+
+  if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
+      y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
+
+  return FALSE;
+}
+
+
+static gboolean
+rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
+                     GdkRectangle *r)
+{
+  g_return_val_if_fail (range, FALSE);
+
+  r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
+  r->x -= round (sheet->hadjustment->value);
+
+  r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
+  r->y -= round (sheet->vadjustment->value);
+
+  r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
+    psppire_axis_start_pixel (sheet->haxis, range->col0) +
+    psppire_axis_unit_size (sheet->haxis, range->coli);
+
+  r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
+    psppire_axis_start_pixel (sheet->vaxis, range->row0) +
+    psppire_axis_unit_size (sheet->vaxis, range->rowi);
+
+  if ( sheet->column_titles_visible)
+    {
+      r->y += sheet->column_title_area.height;
+    }
+
+  if ( sheet->row_titles_visible)
+    {
+      r->x += sheet->row_title_area.width;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
+                    GdkRectangle *r)
+{
+  PsppireSheetRange range;
+  g_return_val_if_fail (row >= 0, FALSE);
+  g_return_val_if_fail (col >= 0, FALSE);
+
+  range.row0 = range.rowi = row;
+  range.col0 = range.coli = col;
+
+  return rectangle_from_range (sheet, &range, r);
+}
+
+
+static void psppire_sheet_class_init            (PsppireSheetClass *klass);
+static void psppire_sheet_init                          (PsppireSheet *sheet);
+static void psppire_sheet_dispose                       (GObject *object);
+static void psppire_sheet_finalize                      (GObject *object);
+static void psppire_sheet_style_set             (GtkWidget *widget,
+                                                 GtkStyle *previous_style);
+static void psppire_sheet_realize                       (GtkWidget *widget);
+static void psppire_sheet_unrealize             (GtkWidget *widget);
+static void psppire_sheet_map                   (GtkWidget *widget);
+static void psppire_sheet_unmap                         (GtkWidget *widget);
+static gint psppire_sheet_expose                        (GtkWidget *widget,
+                                                 GdkEventExpose *event);
+
+static void psppire_sheet_forall                        (GtkContainer *container,
+                                                 gboolean include_internals,
+                                                 GtkCallback callback,
+                                                 gpointer callback_data);
+
+static gboolean psppire_sheet_set_scroll_adjustments  (PsppireSheet *sheet,
+                                                 GtkAdjustment *hadjustment,
+                                                 GtkAdjustment *vadjustment);
+
+static gint psppire_sheet_button_press                  (GtkWidget *widget,
+                                                 GdkEventButton *event);
+static gint psppire_sheet_button_release                (GtkWidget *widget,
+                                                 GdkEventButton *event);
+static gint psppire_sheet_motion                        (GtkWidget *widget,
+                                                 GdkEventMotion *event);
+static gboolean psppire_sheet_crossing_notify           (GtkWidget *widget,
+                                                    GdkEventCrossing *event);
+static gint psppire_sheet_entry_key_press               (GtkWidget *widget,
+                                                 GdkEventKey *key);
+static gboolean psppire_sheet_key_press                 (GtkWidget *widget,
+                                                 GdkEventKey *key);
+static void psppire_sheet_size_request                  (GtkWidget *widget,
+                                                 GtkRequisition *requisition);
+static void psppire_sheet_size_allocate                 (GtkWidget *widget,
+                                                 GtkAllocation *allocation);
+
+static gboolean psppire_sheet_focus_in               (GtkWidget     *widget,
+                                                     GdkEventFocus *event);
+
+/* Sheet queries */
+
+static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
+                                          const PsppireSheetRange *range);
+static gboolean psppire_sheet_cell_isvisible  (PsppireSheet *sheet,
+                                          gint row, gint column);
+/* Drawing Routines */
+
+/* draw cell */
+static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
+
+
+/* draw visible part of range. */
+static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
+
+
+/* highlight the visible part of the selected range */
+static void psppire_sheet_range_draw_selection  (PsppireSheet *sheet,
+                                                 PsppireSheetRange range);
+
+/* Selection */
+
+static void psppire_sheet_real_select_range     (PsppireSheet *sheet,
+                                                 const PsppireSheetRange *range);
+static void psppire_sheet_real_unselect_range   (PsppireSheet *sheet,
+                                                 const PsppireSheetRange *range);
+static void psppire_sheet_extend_selection              (PsppireSheet *sheet,
+                                                 gint row, gint column);
+static void psppire_sheet_new_selection                 (PsppireSheet *sheet,
+                                                 PsppireSheetRange *range);
+static void psppire_sheet_draw_border           (PsppireSheet *sheet,
+                                                 PsppireSheetRange range);
+
+/* Active Cell handling */
+
+static void psppire_sheet_hide_entry_widget             (PsppireSheet *sheet);
+static void change_active_cell  (PsppireSheet *sheet, gint row, gint col);
+static gboolean psppire_sheet_draw_active_cell  (PsppireSheet *sheet);
+static void psppire_sheet_show_entry_widget             (PsppireSheet *sheet);
+static gboolean psppire_sheet_click_cell                (PsppireSheet *sheet,
+                                                 gint row,
+                                                 gint column);
+
+
+/* Scrollbars */
+
+static void adjust_scrollbars                   (PsppireSheet *sheet);
+static void vadjustment_value_changed           (GtkAdjustment *adjustment,
+                                                 gpointer data);
+static void hadjustment_value_changed           (GtkAdjustment *adjustment,
+                                                 gpointer data);
+
+
+static void draw_xor_vline                      (PsppireSheet *sheet);
+static void draw_xor_hline                      (PsppireSheet *sheet);
+static void draw_xor_rectangle                  (PsppireSheet *sheet,
+                                                 PsppireSheetRange range);
+
+/* Sheet Button */
+
+static void create_global_button                (PsppireSheet *sheet);
+static void global_button_clicked               (GtkWidget *widget,
+                                                 gpointer data);
+/* Sheet Entry */
+
+static void create_sheet_entry                  (PsppireSheet *sheet);
+static void psppire_sheet_size_allocate_entry   (PsppireSheet *sheet);
+
+/* Sheet button gadgets */
+
+static void draw_column_title_buttons   (PsppireSheet *sheet);
+static void draw_row_title_buttons      (PsppireSheet *sheet);
+
+
+static void size_allocate_global_button         (PsppireSheet *sheet);
+static void psppire_sheet_button_size_request   (PsppireSheet *sheet,
+                                                 const PsppireSheetButton *button,
+                                                 GtkRequisition *requisition);
+
+static void psppire_sheet_real_cell_clear               (PsppireSheet *sheet,
+                                                 gint row,
+                                                 gint column);
+
+
+/* Signals */
+enum
+  {
+    SELECT_ROW,
+    SELECT_COLUMN,
+    DOUBLE_CLICK_ROW,
+    DOUBLE_CLICK_COLUMN,
+    BUTTON_EVENT_ROW,
+    BUTTON_EVENT_COLUMN,
+    SELECT_RANGE,
+    RESIZE_RANGE,
+    MOVE_RANGE,
+    TRAVERSE,
+    ACTIVATE,
+    LAST_SIGNAL
+  };
+
+static GtkContainerClass *parent_class = NULL;
+static guint sheet_signals[LAST_SIGNAL] = { 0 };
+
+
+GType
+psppire_sheet_get_type ()
+{
+  static GType sheet_type = 0;
+
+  if (!sheet_type)
+    {
+      static const GTypeInfo sheet_info =
+       {
+         sizeof (PsppireSheetClass),
+         NULL,
+         NULL,
+         (GClassInitFunc) psppire_sheet_class_init,
+         NULL,
+         NULL,
+         sizeof (PsppireSheet),
+         0,
+         (GInstanceInitFunc) psppire_sheet_init,
+         NULL,
+       };
+
+      sheet_type =
+       g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
+                               &sheet_info, 0);
+    }
+  return sheet_type;
+}
+
+\f
+
+static PsppireSheetRange*
+psppire_sheet_range_copy (const PsppireSheetRange *range)
+{
+  PsppireSheetRange *new_range;
+
+  g_return_val_if_fail (range != NULL, NULL);
+
+  new_range = g_new (PsppireSheetRange, 1);
+
+  *new_range = *range;
+
+  return new_range;
+}
+
+static void
+psppire_sheet_range_free (PsppireSheetRange *range)
+{
+  g_return_if_fail (range != NULL);
+
+  g_free (range);
+}
+
+GType
+psppire_sheet_range_get_type (void)
+{
+  static GType sheet_range_type = 0;
+
+  if (!sheet_range_type)
+    {
+      sheet_range_type =
+       g_boxed_type_register_static ("PsppireSheetRange",
+                                     (GBoxedCopyFunc) psppire_sheet_range_copy,
+                                     (GBoxedFreeFunc) psppire_sheet_range_free);
+    }
+
+  return sheet_range_type;
+}
+
+static PsppireSheetCell*
+psppire_sheet_cell_copy (const PsppireSheetCell *cell)
+{
+  PsppireSheetCell *new_cell;
+
+  g_return_val_if_fail (cell != NULL, NULL);
+
+  new_cell = g_new (PsppireSheetCell, 1);
+
+  *new_cell = *cell;
+
+  return new_cell;
+}
+
+static void
+psppire_sheet_cell_free (PsppireSheetCell *cell)
+{
+  g_return_if_fail (cell != NULL);
+
+  g_free (cell);
+}
+
+GType
+psppire_sheet_cell_get_type (void)
+{
+  static GType sheet_cell_type = 0;
+
+  if (!sheet_cell_type)
+    {
+      sheet_cell_type =
+       g_boxed_type_register_static ("PsppireSheetCell",
+                                     (GBoxedCopyFunc) psppire_sheet_cell_copy,
+                                     (GBoxedFreeFunc) psppire_sheet_cell_free);
+    }
+
+  return sheet_cell_type;
+}
+\f
+
+/* Properties */
+enum
+  {
+    PROP_0,
+    PROP_VAXIS,
+    PROP_HAXIS,
+    PROP_CELL_PADDING,
+    PROP_MODEL
+  };
+
+static void
+resize_column (PsppireSheet *sheet, gint unit, glong size)
+{
+  PsppireSheetRange range;
+  range.col0 = unit;
+  range.coli = max_visible_column (sheet);
+  range.row0 = min_visible_row (sheet);
+  range.rowi = max_visible_row (sheet);
+
+  redraw_range (sheet, &range);
+
+  draw_column_title_buttons_range (sheet, range.col0, range.coli);
+}
+
+
+static void
+psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
+{
+  if ( sheet->haxis )
+    g_object_unref (sheet->haxis);
+
+  sheet->haxis = a;
+  g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
+
+  if ( sheet->haxis )
+    g_object_ref (sheet->haxis);
+}
+
+static void
+resize_row (PsppireSheet *sheet, gint unit, glong size)
+{
+  PsppireSheetRange range;
+  range.col0 = min_visible_column (sheet);
+  range.coli = max_visible_column (sheet);
+  range.row0 = unit;
+  range.rowi = max_visible_row (sheet);
+
+  redraw_range (sheet, &range);
+
+  draw_row_title_buttons_range (sheet, range.row0, range.rowi);
+}
+
+static void
+psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
+{
+  if ( sheet->vaxis )
+    g_object_unref (sheet->vaxis);
+
+  sheet->vaxis = a;
+
+  g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
+
+  if ( sheet->vaxis )
+    g_object_ref (sheet->vaxis);
+}
+
+static const GtkBorder default_cell_padding = { 3, 3, 2, 2 };
+
+static void
+psppire_sheet_set_property (GObject         *object,
+                       guint            prop_id,
+                       const GValue    *value,
+                       GParamSpec      *pspec)
+
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (object);
+
+  switch (prop_id)
+    {
+    case PROP_CELL_PADDING:
+      if ( sheet->cell_padding)
+       g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
+
+      sheet->cell_padding = g_value_dup_boxed (value);
+
+     if (NULL == sheet->cell_padding)
+       sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
+                                          &default_cell_padding);
+
+     if (sheet->vaxis)
+       g_object_set (sheet->vaxis, "padding",
+                    sheet->cell_padding->top + sheet->cell_padding->bottom,
+                    NULL);
+
+     if (sheet->haxis)
+       g_object_set (sheet->haxis, "padding",
+                    sheet->cell_padding->left + sheet->cell_padding->right,
+                    NULL);
+      break;
+    case PROP_VAXIS:
+      psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
+      g_object_set (sheet->vaxis, "padding",
+                   sheet->cell_padding->top + sheet->cell_padding->bottom,
+                   NULL);
+      break;
+    case PROP_HAXIS:
+      psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
+      g_object_set (sheet->haxis, "padding",
+                   sheet->cell_padding->left + sheet->cell_padding->right,
+                   NULL);
+      break;
+    case PROP_MODEL:
+      psppire_sheet_set_model (sheet, g_value_get_pointer (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+static void
+psppire_sheet_get_property (GObject         *object,
+                       guint            prop_id,
+                       GValue          *value,
+                       GParamSpec      *pspec)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (object);
+
+  switch (prop_id)
+    {
+    case PROP_CELL_PADDING:
+      g_value_set_boxed (value, sheet->cell_padding);
+      break;
+    case PROP_VAXIS:
+      g_value_set_pointer (value, sheet->vaxis);
+      break;
+    case PROP_HAXIS:
+      g_value_set_pointer (value, sheet->haxis);
+      break;
+    case PROP_MODEL:
+      g_value_set_pointer (value, sheet->model);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+static void
+psppire_sheet_class_init (PsppireSheetClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  GParamSpec *haxis_spec ;
+  GParamSpec *vaxis_spec ;
+  GParamSpec *model_spec ;
+  GParamSpec *cell_padding_spec ;
+
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  /**
+   * PsppireSheet::select-row
+   * @sheet: the sheet widget that emitted the signal
+   * @row: the newly selected row index
+   *
+   * A row has been selected.
+   */
+  sheet_signals[SELECT_ROW] =
+    g_signal_new ("select-row",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, select_row),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+
+  /**
+   * PsppireSheet::select - column
+   * @sheet: the sheet widget that emitted the signal
+   * @column: the newly selected column index
+   *
+   * A column has been selected.
+   */
+  sheet_signals[SELECT_COLUMN] =
+    g_signal_new ("select-column",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, select_column),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+
+  /**
+   * PsppireSheet::double-click-row
+   * @sheet: the sheet widget that emitted the signal
+   * @row: the row that was double clicked.
+   *
+   * A row's title button has been double clicked
+   */
+  sheet_signals[DOUBLE_CLICK_ROW] =
+    g_signal_new ("double-click-row",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+
+  /**
+   * PsppireSheet::double-click-column
+   * @sheet: the sheet widget that emitted the signal
+   * @column: the column that was double clicked.
+   *
+   * A column's title button has been double clicked
+   */
+  sheet_signals[DOUBLE_CLICK_COLUMN] =
+    g_signal_new ("double-click-column",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+
+  /**
+   * PsppireSheet::button-event-column
+   * @sheet: the sheet widget that emitted the signal
+   * @column: the column on which the event occured.
+   *
+   * A button event occured on a column title button
+   */
+  sheet_signals[BUTTON_EVENT_COLUMN] =
+    g_signal_new ("button-event-column",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 psppire_marshal_VOID__INT_POINTER,
+                 G_TYPE_NONE,
+                 2,
+                 G_TYPE_INT,
+                 G_TYPE_POINTER
+                 );
+
+
+  /**
+   * PsppireSheet::button-event-row
+   * @sheet: the sheet widget that emitted the signal
+   * @column: the column on which the event occured.
+   *
+   * A button event occured on a row title button
+   */
+  sheet_signals[BUTTON_EVENT_ROW] =
+    g_signal_new ("button-event-row",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 psppire_marshal_VOID__INT_POINTER,
+                 G_TYPE_NONE,
+                 2,
+                 G_TYPE_INT,
+                 G_TYPE_POINTER
+                 );
+
+
+  sheet_signals[SELECT_RANGE] =
+    g_signal_new ("select-range",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, select_range),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__BOXED,
+                 G_TYPE_NONE,
+                 1,
+                 PSPPIRE_TYPE_SHEET_RANGE);
+
+
+  sheet_signals[RESIZE_RANGE] =
+    g_signal_new ("resize-range",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, resize_range),
+                 NULL, NULL,
+                 psppire_marshal_VOID__BOXED_BOXED,
+                 G_TYPE_NONE,
+                 2,
+                 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
+                 );
+
+  sheet_signals[MOVE_RANGE] =
+    g_signal_new ("move-range",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, move_range),
+                 NULL, NULL,
+                 psppire_marshal_VOID__BOXED_BOXED,
+                 G_TYPE_NONE,
+                 2,
+                 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
+                 );
+
+  sheet_signals[TRAVERSE] =
+    g_signal_new ("traverse",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, traverse),
+                 NULL, NULL,
+                 psppire_marshal_BOOLEAN__BOXED_POINTER,
+                 G_TYPE_BOOLEAN, 2,
+                 PSPPIRE_TYPE_SHEET_CELL,
+                 G_TYPE_POINTER);
+
+
+  sheet_signals[ACTIVATE] =
+    g_signal_new ("activate",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, activate),
+                 NULL, NULL,
+                 psppire_marshal_VOID__INT_INT_INT_INT,
+                 G_TYPE_NONE, 4,
+                 G_TYPE_INT, G_TYPE_INT,
+                 G_TYPE_INT, G_TYPE_INT);
+
+  widget_class->set_scroll_adjustments_signal =
+    g_signal_new ("set-scroll-adjustments",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 offsetof (PsppireSheetClass, set_scroll_adjustments),
+                 NULL, NULL,
+                 psppire_marshal_VOID__OBJECT_OBJECT,
+                 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
+
+
+  container_class->add = NULL;
+  container_class->remove = NULL;
+  container_class->forall = psppire_sheet_forall;
+  container_class->set_focus_child = NULL;
+
+  object_class->dispose = psppire_sheet_dispose;
+  object_class->finalize = psppire_sheet_finalize;
+
+  cell_padding_spec =
+    g_param_spec_boxed ("cell-padding",
+                       "Cell Padding",
+                       "The space between a cell's contents and its border",
+                       GTK_TYPE_BORDER,
+                       G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
+
+  vaxis_spec =
+    g_param_spec_pointer ("vertical-axis",
+                         "Vertical Axis",
+                         "A pointer to the PsppireAxis object for the rows",
+                         G_PARAM_READABLE | G_PARAM_WRITABLE );
+
+  haxis_spec =
+    g_param_spec_pointer ("horizontal-axis",
+                         "Horizontal Axis",
+                         "A pointer to the PsppireAxis object for the columns",
+                         G_PARAM_READABLE | G_PARAM_WRITABLE );
+
+  model_spec =
+    g_param_spec_pointer ("model",
+                         "Model",
+                         "A pointer to the data model",
+                         G_PARAM_READABLE | G_PARAM_WRITABLE );
+
+
+  object_class->set_property = psppire_sheet_set_property;
+  object_class->get_property = psppire_sheet_get_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_VAXIS,
+                                   vaxis_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_HAXIS,
+                                   haxis_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_CELL_PADDING,
+                                   cell_padding_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_MODEL,
+                                   model_spec);
+
+
+  widget_class->realize = psppire_sheet_realize;
+  widget_class->unrealize = psppire_sheet_unrealize;
+  widget_class->map = psppire_sheet_map;
+  widget_class->unmap = psppire_sheet_unmap;
+  widget_class->style_set = psppire_sheet_style_set;
+  widget_class->button_press_event = psppire_sheet_button_press;
+  widget_class->button_release_event = psppire_sheet_button_release;
+  widget_class->motion_notify_event = psppire_sheet_motion;
+  widget_class->enter_notify_event = psppire_sheet_crossing_notify;
+  widget_class->leave_notify_event = psppire_sheet_crossing_notify;
+  widget_class->key_press_event = psppire_sheet_key_press;
+  widget_class->expose_event = psppire_sheet_expose;
+  widget_class->size_request = psppire_sheet_size_request;
+  widget_class->size_allocate = psppire_sheet_size_allocate;
+  widget_class->focus_in_event = psppire_sheet_focus_in;
+  widget_class->focus_out_event = NULL;
+
+  klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
+  klass->select_row = NULL;
+  klass->select_column = NULL;
+  klass->select_range = NULL;
+  klass->resize_range = NULL;
+  klass->move_range = NULL;
+  klass->traverse = NULL;
+  klass->activate = NULL;
+  klass->changed = NULL;
+}
+
+static void
+psppire_sheet_init (PsppireSheet *sheet)
+{
+  sheet->model = NULL;
+  sheet->haxis = NULL;
+  sheet->vaxis = NULL;
+
+  sheet->flags = 0;
+  sheet->selection_mode = GTK_SELECTION_NONE;
+  sheet->state = PSPPIRE_SHEET_NORMAL;
+
+  GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
+  GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
+
+  sheet->column_title_window = NULL;
+  sheet->column_title_area.x = 0;
+  sheet->column_title_area.y = 0;
+  sheet->column_title_area.width = 0;
+  sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
+
+  sheet->row_title_window = NULL;
+  sheet->row_title_area.x = 0;
+  sheet->row_title_area.y = 0;
+  sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
+  sheet->row_title_area.height = 0;
+
+
+  sheet->active_cell.row = 0;
+  sheet->active_cell.col = 0;
+  sheet->selection_cell.row = 0;
+  sheet->selection_cell.col = 0;
+
+  sheet->range.row0 = 0;
+  sheet->range.rowi = 0;
+  sheet->range.col0 = 0;
+  sheet->range.coli = 0;
+
+  sheet->state = PSPPIRE_SHEET_NORMAL;
+
+  sheet->sheet_window = NULL;
+  sheet->entry_widget = NULL;
+  sheet->button = NULL;
+
+  sheet->hadjustment = NULL;
+  sheet->vadjustment = NULL;
+
+  sheet->cursor_drag = NULL;
+
+  sheet->xor_gc = NULL;
+  sheet->fg_gc = NULL;
+  sheet->bg_gc = NULL;
+  sheet->x_drag = 0;
+  sheet->y_drag = 0;
+  sheet->show_grid = TRUE;
+
+  sheet->motion_timer = 0;
+
+  sheet->row_titles_visible = TRUE;
+  sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
+
+  sheet->column_titles_visible = TRUE;
+
+
+  /* create sheet entry */
+  sheet->entry_type = GTK_TYPE_ENTRY;
+  create_sheet_entry (sheet);
+
+  /* create global selection button */
+  create_global_button (sheet);
+}
+
+
+/* Cause RANGE to be redrawn. If RANGE is null, then the
+   entire visible range will be redrawn.
+ */
+static void
+redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
+{
+  GdkRectangle rect;
+
+  if ( ! GTK_WIDGET_REALIZED (sheet))
+    return;
+
+  if ( NULL != range )
+    rectangle_from_range (sheet, range, &rect);
+  else
+    {
+      GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
+      gdk_region_get_clipbox (r, &rect);
+
+      if ( sheet->column_titles_visible)
+       {
+         rect.y += sheet->column_title_area.height;
+         rect.height -= sheet->column_title_area.height;
+       }
+
+      if ( sheet->row_titles_visible)
+       {
+         rect.x += sheet->row_title_area.width;
+         rect.width -= sheet->row_title_area.width;
+       }
+    }
+
+  gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
+}
+
+
+/* Callback which occurs whenever columns are inserted / deleted in the model */
+static void
+columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
+                                  gint n_columns,
+                                  gpointer data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+
+  PsppireSheetRange range;
+  gint model_columns = psppire_sheet_model_get_column_count (model);
+
+
+  /* Need to update all the columns starting from the first column and onwards.
+   * Previous column are unchanged, so don't need to be updated.
+   */
+  range.col0 = first_column;
+  range.row0 = 0;
+  range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
+  range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
+
+  adjust_scrollbars (sheet);
+
+  if (sheet->active_cell.col >= model_columns)
+    change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
+
+  draw_column_title_buttons_range (sheet,
+                                  first_column, max_visible_column (sheet));
+
+
+  redraw_range (sheet, &range);
+}
+
+
+
+
+/* Callback which occurs whenever rows are inserted / deleted in the model */
+static void
+rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
+                               gint n_rows, gpointer data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+
+  PsppireSheetRange range;
+
+  gint model_rows = psppire_sheet_model_get_row_count (model);
+
+  /* Need to update all the rows starting from the first row and onwards.
+   * Previous rows are unchanged, so don't need to be updated.
+   */
+  range.row0 = first_row;
+  range.col0 = 0;
+  range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
+  range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
+
+  adjust_scrollbars (sheet);
+
+  if (sheet->active_cell.row >= model_rows)
+    change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
+
+  draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
+
+  redraw_range (sheet, &range);
+}
+
+/*
+  If row0 or rowi are negative, then all rows will be updated.
+  If col0 or coli are negative, then all columns will be updated.
+*/
+static void
+range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
+                      gint rowi, gint coli, gpointer data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+
+  PsppireSheetRange range;
+
+  range.row0 = row0;
+  range.col0 = col0;
+  range.rowi = rowi;
+  range.coli = coli;
+
+  if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
+    {
+      redraw_range (sheet, NULL);
+      adjust_scrollbars (sheet);
+
+      draw_row_title_buttons_range (sheet, min_visible_row (sheet),
+                                      max_visible_row (sheet));
+
+      draw_column_title_buttons_range (sheet, min_visible_column (sheet),
+                                      max_visible_column (sheet));
+
+      return;
+    }
+  else if ( row0 < 0 || rowi < 0 )
+    {
+      range.row0 = min_visible_row (sheet);
+      range.rowi = max_visible_row (sheet);
+    }
+  else if ( col0 < 0 || coli < 0 )
+    {
+      range.col0 = min_visible_column (sheet);
+      range.coli = max_visible_column (sheet);
+    }
+
+  redraw_range (sheet, &range);
+}
+
+
+/**
+ * psppire_sheet_new:
+ * @rows: initial number of rows
+ * @columns: initial number of columns
+ * @title: sheet title
+ * @model: the model to use for the sheet data
+ *
+ * Creates a new sheet widget with the given number of rows and columns.
+ *
+ * Returns: the new sheet widget
+ */
+GtkWidget *
+psppire_sheet_new (PsppireSheetModel *model)
+{
+  GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
+                                   "model", model,
+                                   NULL);
+  return widget;
+}
+
+
+/**
+ * psppire_sheet_set_model
+ * @sheet: the sheet to set the model for
+ * @model: the model to use for the sheet data
+ *
+ * Sets the model for a PsppireSheet
+ *
+ */
+void
+psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
+{
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (sheet->model ) g_object_unref (sheet->model);
+
+  sheet->model = model;
+
+  if ( model)
+    {
+      g_object_ref (model);
+
+      sheet->update_handler_id = g_signal_connect (model, "range_changed",
+                                                  G_CALLBACK (range_update_callback),
+                                                  sheet);
+
+      g_signal_connect (model, "rows_inserted",
+                       G_CALLBACK (rows_inserted_deleted_callback), sheet);
+
+      g_signal_connect (model, "rows_deleted",
+                       G_CALLBACK (rows_inserted_deleted_callback), sheet);
+
+      g_signal_connect (model, "columns_inserted",
+                       G_CALLBACK (columns_inserted_deleted_callback), sheet);
+
+      g_signal_connect (model, "columns_deleted",
+                       G_CALLBACK (columns_inserted_deleted_callback), sheet);
+    }
+}
+
+
+void
+psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
+{
+  gint state;
+
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  state = sheet->state;
+
+  if (sheet->state == PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_hide_entry_widget (sheet);
+
+  sheet->entry_type = entry_type;
+
+  create_sheet_entry (sheet);
+
+  if (state == PSPPIRE_SHEET_NORMAL)
+    {
+      psppire_sheet_show_entry_widget (sheet);
+    }
+
+}
+
+void
+psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (show == sheet->show_grid) return;
+
+  sheet->show_grid = show;
+
+  redraw_range (sheet, NULL);
+}
+
+gboolean
+psppire_sheet_grid_visible (PsppireSheet *sheet)
+{
+  g_return_val_if_fail (sheet != NULL, 0);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
+
+  return sheet->show_grid;
+}
+
+guint
+psppire_sheet_get_columns_count (PsppireSheet *sheet)
+{
+  g_return_val_if_fail (sheet != NULL, 0);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
+
+  return psppire_axis_unit_count (sheet->haxis);
+}
+
+static void set_column_width (PsppireSheet *sheet,
+                             gint column,
+                             gint width);
+
+
+void
+psppire_sheet_show_column_titles (PsppireSheet *sheet)
+{
+  if (sheet->column_titles_visible) return;
+
+  sheet->column_titles_visible = TRUE;
+
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  gdk_window_show (sheet->column_title_window);
+  gdk_window_move_resize (sheet->column_title_window,
+                         sheet->column_title_area.x,
+                         sheet->column_title_area.y,
+                         sheet->column_title_area.width,
+                         sheet->column_title_area.height);
+
+  adjust_scrollbars (sheet);
+
+  if (sheet->vadjustment)
+    g_signal_emit_by_name (sheet->vadjustment,
+                          "value_changed");
+
+  size_allocate_global_button (sheet);
+
+  if ( sheet->row_titles_visible)
+    gtk_widget_show (sheet->button);
+}
+
+
+void
+psppire_sheet_show_row_titles (PsppireSheet *sheet)
+{
+  if (sheet->row_titles_visible) return;
+
+  sheet->row_titles_visible = TRUE;
+
+
+  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    {
+      gdk_window_show (sheet->row_title_window);
+      gdk_window_move_resize (sheet->row_title_window,
+                             sheet->row_title_area.x,
+                             sheet->row_title_area.y,
+                             sheet->row_title_area.width,
+                             sheet->row_title_area.height);
+
+      adjust_scrollbars (sheet);
+    }
+
+  if (sheet->hadjustment)
+    g_signal_emit_by_name (sheet->hadjustment,
+                          "value_changed");
+
+  size_allocate_global_button (sheet);
+
+  if ( sheet->column_titles_visible)
+    gtk_widget_show (sheet->button);
+}
+
+void
+psppire_sheet_hide_column_titles (PsppireSheet *sheet)
+{
+  if (!sheet->column_titles_visible) return;
+
+  sheet->column_titles_visible = FALSE;
+
+  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    {
+      if (sheet->column_title_window)
+       gdk_window_hide (sheet->column_title_window);
+
+      gtk_widget_hide (sheet->button);
+
+      adjust_scrollbars (sheet);
+    }
+
+  if (sheet->vadjustment)
+    g_signal_emit_by_name (sheet->vadjustment,
+                          "value_changed");
+}
+
+void
+psppire_sheet_hide_row_titles (PsppireSheet *sheet)
+{
+  if (!sheet->row_titles_visible) return;
+
+  sheet->row_titles_visible = FALSE;
+
+  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    {
+      if (sheet->row_title_window)
+       gdk_window_hide (sheet->row_title_window);
+
+      gtk_widget_hide (sheet->button);
+
+      adjust_scrollbars (sheet);
+    }
+
+  if (sheet->hadjustment)
+    g_signal_emit_by_name (sheet->hadjustment,
+                          "value_changed");
+}
+
+
+/* Scroll the sheet so that the cell ROW, COLUMN is visible.
+   If {ROW,COL}_ALIGN is zero, then the cell will be placed
+   at the {top,left} of the sheet.  If it's 1, then it'll
+   be placed at the {bottom,right}.
+   ROW or COL may be -1, in which case scrolling in that dimension
+   does not occur.
+ */
+void
+psppire_sheet_moveto (PsppireSheet *sheet,
+                 gint row,
+                 gint col,
+                 gfloat row_align,
+                 gfloat col_align)
+{
+  gint width, height;
+
+  g_return_if_fail (row_align >= 0);
+  g_return_if_fail (col_align >= 0);
+
+  g_return_if_fail (row_align <= 1);
+  g_return_if_fail (col_align <= 1);
+
+  g_return_if_fail (col <
+                   psppire_axis_unit_count (sheet->haxis));
+  g_return_if_fail (row <
+                   psppire_axis_unit_count (sheet->vaxis));
+
+  gdk_drawable_get_size (sheet->sheet_window, &width, &height);
+
+
+  if (row >= 0)
+    {
+      gint y =  psppire_axis_start_pixel (sheet->vaxis, row);
+
+      gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
+    }
+
+
+  if (col >= 0)
+    {
+      gint x =  psppire_axis_start_pixel (sheet->haxis, col);
+
+      gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
+    }
+}
+
+
+void
+psppire_sheet_select_row (PsppireSheet *sheet, gint row)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
+    return;
+
+  if (sheet->state != PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_real_unselect_range (sheet, NULL);
+
+  sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
+  sheet->range.row0 = row;
+  sheet->range.col0 = 0;
+  sheet->range.rowi = row;
+  sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
+  sheet->active_cell.row = row;
+
+  g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
+  psppire_sheet_real_select_range (sheet, NULL);
+}
+
+
+void
+psppire_sheet_select_column (PsppireSheet *sheet, gint column)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
+    return;
+
+  if (sheet->state != PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_real_unselect_range (sheet, NULL);
+
+  sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
+  sheet->range.row0 = 0;
+  sheet->range.col0 = column;
+  sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
+  sheet->range.coli = column;
+  sheet->active_cell.col = column;
+
+  g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
+  psppire_sheet_real_select_range (sheet, NULL);
+}
+
+
+
+
+static gboolean
+psppire_sheet_range_isvisible (const PsppireSheet *sheet,
+                          const PsppireSheetRange *range)
+{
+  g_return_val_if_fail (sheet != NULL, FALSE);
+
+  if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
+    return FALSE;
+
+  if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
+    return FALSE;
+
+  if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
+    return FALSE;
+
+  if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
+    return FALSE;
+
+  if (range->rowi < min_visible_row (sheet))
+    return FALSE;
+
+  if (range->row0 > max_visible_row (sheet))
+    return FALSE;
+
+  if (range->coli < min_visible_column (sheet))
+    return FALSE;
+
+  if (range->col0 > max_visible_column (sheet))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+psppire_sheet_cell_isvisible (PsppireSheet *sheet,
+                         gint row, gint column)
+{
+  PsppireSheetRange range;
+
+  range.row0 = row;
+  range.col0 = column;
+  range.rowi = row;
+  range.coli = column;
+
+  return psppire_sheet_range_isvisible (sheet, &range);
+}
+
+void
+psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
+  g_return_if_fail (range != NULL);
+
+  range->row0 = min_visible_row (sheet);
+  range->col0 = min_visible_column (sheet);
+  range->rowi = max_visible_row (sheet);
+  range->coli = max_visible_column (sheet);
+}
+
+
+static gboolean
+psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
+                                 GtkAdjustment *hadjustment,
+                                 GtkAdjustment *vadjustment)
+{
+  if ( sheet->vadjustment != vadjustment )
+    {
+      if (sheet->vadjustment)
+       g_object_unref (sheet->vadjustment);
+      sheet->vadjustment = vadjustment;
+
+      if ( vadjustment)
+       {
+         g_object_ref (vadjustment);
+
+         g_signal_connect (sheet->vadjustment, "value_changed",
+                           G_CALLBACK (vadjustment_value_changed),
+                           sheet);
+       }
+    }
+
+  if ( sheet->hadjustment != hadjustment )
+    {
+      if (sheet->hadjustment)
+       g_object_unref (sheet->hadjustment);
+
+      sheet->hadjustment = hadjustment;
+
+      if ( hadjustment)
+       {
+         g_object_ref (hadjustment);
+
+         g_signal_connect (sheet->hadjustment, "value_changed",
+                           G_CALLBACK (hadjustment_value_changed),
+                           sheet);
+       }
+    }
+  return TRUE;
+}
+
+static void
+psppire_sheet_finalize (GObject *object)
+{
+  PsppireSheet *sheet;
+
+  g_return_if_fail (object != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (object));
+
+  sheet = PSPPIRE_SHEET (object);
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (*G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+static void
+psppire_sheet_dispose  (GObject *object)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (object);
+
+  g_return_if_fail (object != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (object));
+
+  if ( sheet->dispose_has_run )
+    return ;
+
+  sheet->dispose_has_run = TRUE;
+
+  if ( sheet->cell_padding)
+    g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
+
+  if (sheet->model) g_object_unref (sheet->model);
+  if (sheet->vaxis) g_object_unref (sheet->vaxis);
+  if (sheet->haxis) g_object_unref (sheet->haxis);
+
+  g_object_unref (sheet->button);
+  sheet->button = NULL;
+
+  /* unref adjustments */
+  if (sheet->hadjustment)
+    {
+      g_signal_handlers_disconnect_matched (sheet->hadjustment,
+                                           G_SIGNAL_MATCH_DATA,
+                                           0, 0, 0, 0,
+                                           sheet);
+
+      g_object_unref (sheet->hadjustment);
+      sheet->hadjustment = NULL;
+    }
+
+  if (sheet->vadjustment)
+    {
+      g_signal_handlers_disconnect_matched (sheet->vadjustment,
+                                           G_SIGNAL_MATCH_DATA,
+                                           0, 0, 0, 0,
+                                           sheet);
+
+      g_object_unref (sheet->vadjustment);
+
+      sheet->vadjustment = NULL;
+    }
+
+  if (G_OBJECT_CLASS (parent_class)->dispose)
+    (*G_OBJECT_CLASS (parent_class)->dispose) (object);
+}
+
+static void
+psppire_sheet_style_set (GtkWidget *widget,
+                    GtkStyle *previous_style)
+{
+  PsppireSheet *sheet;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (widget));
+
+  if (GTK_WIDGET_CLASS (parent_class)->style_set)
+    (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
+
+  sheet = PSPPIRE_SHEET (widget);
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      gtk_style_set_background (widget->style, widget->window, widget->state);
+    }
+
+  set_entry_widget_font (sheet);
+}
+
+#define BORDER_WIDTH 3
+
+static void
+psppire_sheet_realize (GtkWidget *widget)
+{
+  PsppireSheet *sheet;
+  GdkWindowAttr attributes;
+  const gint attributes_mask =
+    GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
+
+  GdkGCValues values;
+  GdkColormap *colormap;
+  GdkDisplay *display;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (widget));
+
+  sheet = PSPPIRE_SHEET (widget);
+
+  colormap = gtk_widget_get_colormap (widget);
+  display = gtk_widget_get_display (widget);
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = colormap;
+
+  attributes.event_mask = gtk_widget_get_events (widget);
+  attributes.event_mask |= (GDK_EXPOSURE_MASK |
+                           GDK_BUTTON_PRESS_MASK |
+                           GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
+                           GDK_ENTER_NOTIFY_MASK |
+                           GDK_LEAVE_NOTIFY_MASK |
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_POINTER_MOTION_HINT_MASK);
+
+  attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
+
+  /* main window */
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
+
+  gdk_window_set_user_data (widget->window, sheet);
+
+  widget->style = gtk_style_attach (widget->style, widget->window);
+
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+
+  gdk_color_parse ("white", &sheet->color[BG_COLOR]);
+  gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
+                           TRUE);
+  gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
+  gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
+                           TRUE);
+
+  attributes.x = 0;
+  attributes.y = 0;
+  attributes.width = sheet->column_title_area.width;
+  attributes.height = sheet->column_title_area.height;
+
+
+  /* column - title window */
+  sheet->column_title_window =
+    gdk_window_new (widget->window, &attributes, attributes_mask);
+  gdk_window_set_user_data (sheet->column_title_window, sheet);
+  gtk_style_set_background (widget->style, sheet->column_title_window,
+                           GTK_STATE_NORMAL);
+
+
+  attributes.x = 0;
+  attributes.y = 0;
+  attributes.width = sheet->row_title_area.width;
+  attributes.height = sheet->row_title_area.height;
+
+  /* row - title window */
+  sheet->row_title_window = gdk_window_new (widget->window,
+                                           &attributes, attributes_mask);
+  gdk_window_set_user_data (sheet->row_title_window, sheet);
+  gtk_style_set_background (widget->style, sheet->row_title_window,
+                           GTK_STATE_NORMAL);
+
+  /* sheet - window */
+  attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
+
+  attributes.x = 0;
+  attributes.y = 0;
+
+  sheet->sheet_window = gdk_window_new (widget->window,
+                                       &attributes, attributes_mask);
+  gdk_window_set_user_data (sheet->sheet_window, sheet);
+
+  gdk_cursor_unref (attributes.cursor);
+
+  gdk_window_set_background (sheet->sheet_window, &widget->style->white);
+  gdk_window_show (sheet->sheet_window);
+
+  /* GCs */
+  sheet->fg_gc = gdk_gc_new (widget->window);
+  sheet->bg_gc = gdk_gc_new (widget->window);
+
+  values.foreground = widget->style->white;
+  values.function = GDK_INVERT;
+  values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+  values.line_width = BORDER_WIDTH;
+
+  sheet->xor_gc = gdk_gc_new_with_values (widget->window,
+                                         &values,
+                                         GDK_GC_FOREGROUND |
+                                         GDK_GC_FUNCTION |
+                                         GDK_GC_SUBWINDOW |
+                                         GDK_GC_LINE_WIDTH
+                                         );
+
+
+  gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
+  gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
+
+  gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
+  gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
+
+
+  sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
+
+  if (sheet->column_titles_visible)
+    gdk_window_show (sheet->column_title_window);
+  if (sheet->row_titles_visible)
+    gdk_window_show (sheet->row_title_window);
+
+  sheet->hover_window = create_hover_window ();
+
+  draw_row_title_buttons (sheet);
+  draw_column_title_buttons (sheet);
+
+  psppire_sheet_update_primary_selection (sheet);
+
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+}
+
+static void
+create_global_button (PsppireSheet *sheet)
+{
+  sheet->button = gtk_button_new_with_label (" ");
+
+  GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
+
+  g_object_ref_sink (sheet->button);
+
+  g_signal_connect (sheet->button,
+                   "pressed",
+                   G_CALLBACK (global_button_clicked),
+                   sheet);
+}
+
+static void
+size_allocate_global_button (PsppireSheet *sheet)
+{
+  GtkAllocation allocation;
+
+  if (!sheet->column_titles_visible) return;
+  if (!sheet->row_titles_visible) return;
+
+  gtk_widget_size_request (sheet->button, NULL);
+
+  allocation.x = 0;
+  allocation.y = 0;
+  allocation.width = sheet->row_title_area.width;
+  allocation.height = sheet->column_title_area.height;
+
+  gtk_widget_size_allocate (sheet->button, &allocation);
+}
+
+static void
+global_button_clicked (GtkWidget *widget, gpointer data)
+{
+  psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
+}
+
+
+static void
+psppire_sheet_unrealize (GtkWidget *widget)
+{
+  PsppireSheet *sheet;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (widget));
+
+  sheet = PSPPIRE_SHEET (widget);
+
+  gdk_cursor_unref (sheet->cursor_drag);
+  sheet->cursor_drag = NULL;
+
+  gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
+                           sheet->color, n_COLORS);
+
+  g_object_unref (sheet->xor_gc);
+  g_object_unref (sheet->fg_gc);
+  g_object_unref (sheet->bg_gc);
+
+  destroy_hover_window (sheet->hover_window);
+
+  gdk_window_destroy (sheet->sheet_window);
+  gdk_window_destroy (sheet->column_title_window);
+  gdk_window_destroy (sheet->row_title_window);
+
+  gtk_widget_unparent (sheet->entry_widget);
+  if (sheet->button != NULL)
+    gtk_widget_unparent (sheet->button);
+
+  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+psppire_sheet_map (GtkWidget *widget)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (widget));
+
+  if (!GTK_WIDGET_MAPPED (widget))
+    {
+      GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+
+      gdk_window_show (widget->window);
+      gdk_window_show (sheet->sheet_window);
+
+      if (sheet->column_titles_visible)
+       {
+         draw_column_title_buttons (sheet);
+         gdk_window_show (sheet->column_title_window);
+       }
+      if (sheet->row_titles_visible)
+       {
+         draw_row_title_buttons (sheet);
+         gdk_window_show (sheet->row_title_window);
+       }
+
+      if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
+         && sheet->active_cell.row >= 0
+         && sheet->active_cell.col >= 0 )
+       {
+         gtk_widget_show (sheet->entry_widget);
+         gtk_widget_map (sheet->entry_widget);
+       }
+
+      if (!GTK_WIDGET_MAPPED (sheet->button))
+       {
+         gtk_widget_show (sheet->button);
+         gtk_widget_map (sheet->button);
+       }
+
+      redraw_range (sheet, NULL);
+      change_active_cell (sheet,
+                    sheet->active_cell.row,
+                    sheet->active_cell.col);
+    }
+}
+
+static void
+psppire_sheet_unmap (GtkWidget *widget)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+
+  if (!GTK_WIDGET_MAPPED (widget))
+    return;
+
+  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
+
+  gdk_window_hide (sheet->sheet_window);
+  if (sheet->column_titles_visible)
+    gdk_window_hide (sheet->column_title_window);
+  if (sheet->row_titles_visible)
+    gdk_window_hide (sheet->row_title_window);
+  gdk_window_hide (widget->window);
+
+  if (GTK_WIDGET_MAPPED (sheet->entry_widget))
+    gtk_widget_unmap (sheet->entry_widget);
+
+  if (GTK_WIDGET_MAPPED (sheet->button))
+    gtk_widget_unmap (sheet->button);
+}
+
+/* get cell attributes of the given cell */
+/* TRUE means that the cell is currently allocated */
+static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
+                                             gint row, gint col,
+                                             PsppireSheetCellAttr *attributes);
+
+
+
+static void
+psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
+{
+  PangoLayout *layout;
+  PangoRectangle text;
+  PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
+  gint font_height;
+
+  gchar *label;
+
+  PsppireSheetCellAttr attributes;
+  GdkRectangle area;
+
+  g_return_if_fail (sheet != NULL);
+
+  /* bail now if we aren't yet drawable */
+  if (!GTK_WIDGET_DRAWABLE (sheet)) return;
+
+  if (row < 0 ||
+      row >= psppire_axis_unit_count (sheet->vaxis))
+    return;
+
+  if (col < 0 ||
+      col >= psppire_axis_unit_count (sheet->haxis))
+    return;
+
+  psppire_sheet_get_attributes (sheet, row, col, &attributes);
+
+  /* select GC for background rectangle */
+  gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
+  gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
+
+  rectangle_from_cell (sheet, row, col, &area);
+
+  gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
+
+  if (sheet->show_grid)
+    {
+      gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
+
+      gdk_draw_rectangle (sheet->sheet_window,
+                         sheet->bg_gc,
+                         FALSE,
+                         area.x, area.y,
+                         area.width, area.height);
+    }
+
+
+  label = psppire_sheet_cell_get_text (sheet, row, col);
+  if (NULL == label)
+    return;
+
+
+  layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
+  dispose_string (sheet, label);
+
+
+  pango_layout_set_font_description (layout, font_desc);
+
+  pango_layout_get_pixel_extents (layout, NULL, &text);
+
+  gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
+
+  font_height = pango_font_description_get_size (font_desc);
+  if ( !pango_font_description_get_size_is_absolute (font_desc))
+    font_height /= PANGO_SCALE;
+
+
+  if ( sheet->cell_padding )
+    {
+      area.x += sheet->cell_padding->left;
+      area.width -= sheet->cell_padding->right
+       + sheet->cell_padding->left;
+
+      area.y += sheet->cell_padding->top;
+      area.height -= sheet->cell_padding->bottom
+       +
+       sheet->cell_padding->top;
+    }
+
+  /* Centre the text vertically */
+  area.y += (area.height - font_height) / 2.0;
+
+  switch (attributes.justification)
+    {
+    case GTK_JUSTIFY_RIGHT:
+      area.x += area.width - text.width;
+      break;
+    case GTK_JUSTIFY_CENTER:
+      area.x += (area.width - text.width) / 2.0;
+      break;
+    case GTK_JUSTIFY_LEFT:
+      /* Do nothing */
+      break;
+    default:
+      g_critical ("Unhandled justification %d in column %d\n",
+                attributes.justification, col);
+      break;
+    }
+
+  gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
+                  area.x,
+                  area.y,
+                  layout);
+
+  gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
+  g_object_unref (layout);
+}
+
+
+static void
+draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
+{
+  PsppireSheetRange range;
+  GdkRectangle area;
+  gint y, x;
+  gint i, j;
+
+  PsppireSheetRange drawing_range;
+
+  gdk_region_get_clipbox (region, &area);
+
+  y = area.y + sheet->vadjustment->value;
+  x = area.x + sheet->hadjustment->value;
+
+  if ( sheet->column_titles_visible)
+    y -= sheet->column_title_area.height;
+
+  if ( sheet->row_titles_visible)
+    x -= sheet->row_title_area.width;
+
+  maximize_int (&x, 0);
+  maximize_int (&y, 0);
+
+  range.row0 = row_from_ypixel (sheet, y);
+  range.rowi = row_from_ypixel (sheet, y + area.height);
+
+  range.col0 = column_from_xpixel (sheet, x);
+  range.coli = column_from_xpixel (sheet, x + area.width);
+
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_SHEET (sheet));
+
+  if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
+  if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
+
+
+  drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
+  drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
+  drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
+  drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
+
+  g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
+  g_return_if_fail (drawing_range.coli >= drawing_range.col0);
+
+  for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
+    {
+      for (j = drawing_range.col0; j <= drawing_range.coli; j++)
+       psppire_sheet_cell_draw (sheet, i, j);
+    }
+
+  if (sheet->state != PSPPIRE_SHEET_NORMAL &&
+      psppire_sheet_range_isvisible (sheet, &sheet->range))
+    psppire_sheet_range_draw_selection (sheet, drawing_range);
+
+
+  if (sheet->state == GTK_STATE_NORMAL &&
+      sheet->active_cell.row >= drawing_range.row0 &&
+      sheet->active_cell.row <= drawing_range.rowi &&
+      sheet->active_cell.col >= drawing_range.col0 &&
+      sheet->active_cell.col <= drawing_range.coli)
+    psppire_sheet_show_entry_widget (sheet);
+}
+
+
+static void
+psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
+{
+  GdkRectangle area;
+  gint i, j;
+  PsppireSheetRange aux;
+
+  if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
+      range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
+    return;
+
+  if (!psppire_sheet_range_isvisible (sheet, &range)) return;
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
+
+  aux = range;
+
+  range.col0 = MAX (sheet->range.col0, range.col0);
+  range.coli = MIN (sheet->range.coli, range.coli);
+  range.row0 = MAX (sheet->range.row0, range.row0);
+  range.rowi = MIN (sheet->range.rowi, range.rowi);
+
+  range.col0 = MAX (range.col0, min_visible_column (sheet));
+  range.coli = MIN (range.coli, max_visible_column (sheet));
+  range.row0 = MAX (range.row0, min_visible_row (sheet));
+  range.rowi = MIN (range.rowi, max_visible_row (sheet));
+
+  for (i = range.row0; i <= range.rowi; i++)
+    {
+      for (j = range.col0; j <= range.coli; j++)
+       {
+         if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
+           {
+             rectangle_from_cell (sheet, i, j, &area);
+
+             if (i == sheet->range.row0)
+               {
+                 area.y = area.y + 2;
+                 area.height = area.height - 2;
+               }
+             if (i == sheet->range.rowi) area.height = area.height - 3;
+             if (j == sheet->range.col0)
+               {
+                 area.x = area.x + 2;
+                 area.width = area.width - 2;
+               }
+             if (j == sheet->range.coli) area.width = area.width - 3;
+
+             if (i != sheet->active_cell.row || j != sheet->active_cell.col)
+               {
+                 gdk_draw_rectangle (sheet->sheet_window,
+                                     sheet->xor_gc,
+                                     TRUE,
+                                     area.x + 1, area.y + 1,
+                                     area.width, area.height);
+               }
+           }
+
+       }
+    }
+
+  psppire_sheet_draw_border (sheet, sheet->range);
+}
+
+static inline gint
+safe_strcmp (const gchar *s1, const gchar *s2)
+{
+  if ( !s1 && !s2) return 0;
+  if ( !s1) return -1;
+  if ( !s2) return +1;
+  return strcmp (s1, s2);
+}
+
+static void
+psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
+                   GtkJustification justification,
+                   const gchar *text)
+{
+  PsppireSheetModel *model ;
+  gchar *old_text ;
+
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (col >= psppire_axis_unit_count (sheet->haxis)
+      || row >= psppire_axis_unit_count (sheet->vaxis))
+    return;
+
+  if (col < 0 || row < 0) return;
+
+  model = psppire_sheet_get_model (sheet);
+
+  old_text = psppire_sheet_model_get_string (model, row, col);
+
+  if (0 != safe_strcmp (old_text, text))
+    {
+      g_signal_handler_block    (sheet->model, sheet->update_handler_id);
+      psppire_sheet_model_set_string (model, text, row, col);
+      g_signal_handler_unblock  (sheet->model, sheet->update_handler_id);
+    }
+
+  if ( psppire_sheet_model_free_strings (model))
+    g_free (old_text);
+}
+
+
+void
+psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
+{
+  PsppireSheetRange range;
+
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+  if (column >= psppire_axis_unit_count (sheet->haxis) ||
+      row >= psppire_axis_unit_count (sheet->vaxis)) return;
+
+  if (column < 0 || row < 0) return;
+
+  range.row0 = row;
+  range.rowi = row;
+  range.col0 = min_visible_column (sheet);
+  range.coli = max_visible_column (sheet);
+
+  psppire_sheet_real_cell_clear (sheet, row, column);
+
+  redraw_range (sheet, &range);
+}
+
+static void
+psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
+{
+  PsppireSheetModel *model = psppire_sheet_get_model (sheet);
+
+  gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
+
+  if (old_text && strlen (old_text) > 0 )
+    {
+      psppire_sheet_model_datum_clear (model, row, column);
+    }
+
+  dispose_string (sheet, old_text);
+}
+
+gchar *
+psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
+{
+  PsppireSheetModel *model;
+  g_return_val_if_fail (sheet != NULL, NULL);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
+
+  if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
+    return NULL;
+  if (col < 0 || row < 0) return NULL;
+
+  model = psppire_sheet_get_model (sheet);
+
+  if ( !model )
+    return NULL;
+
+  return psppire_sheet_model_get_string (model, row, col);
+}
+
+
+static GtkStateType
+psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
+{
+  gint state;
+  PsppireSheetRange *range;
+
+  g_return_val_if_fail (sheet != NULL, 0);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
+  if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
+  if (col < 0 || row < 0) return 0;
+
+  state = sheet->state;
+  range = &sheet->range;
+
+  switch (state)
+    {
+    case PSPPIRE_SHEET_NORMAL:
+      return GTK_STATE_NORMAL;
+      break;
+    case PSPPIRE_SHEET_ROW_SELECTED:
+      if (row >= range->row0 && row <= range->rowi)
+       return GTK_STATE_SELECTED;
+      break;
+    case PSPPIRE_SHEET_COLUMN_SELECTED:
+      if (col >= range->col0 && col <= range->coli)
+       return GTK_STATE_SELECTED;
+      break;
+    case PSPPIRE_SHEET_RANGE_SELECTED:
+      if (row >= range->row0 && row <= range->rowi && \
+         col >= range->col0 && col <= range->coli)
+       return GTK_STATE_SELECTED;
+      break;
+    }
+  return GTK_STATE_NORMAL;
+}
+
+/* Convert X, Y (in pixels) to *ROW, *COLUMN
+   If the function returns FALSE, then the results will be unreliable.
+*/
+static gboolean
+psppire_sheet_get_pixel_info (PsppireSheet *sheet,
+                         gint x,
+                         gint y,
+                         gint *row,
+                         gint *column)
+{
+  gint trow, tcol;
+  *row = -G_MAXINT;
+  *column = -G_MAXINT;
+
+  g_return_val_if_fail (sheet != NULL, 0);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
+
+  /* bounds checking, return false if the user clicked
+     on a blank area */
+  if (y < 0)
+    return FALSE;
+
+  if (x < 0)
+    return FALSE;
+
+  if ( sheet->column_titles_visible)
+    y -= sheet->column_title_area.height;
+
+  y += sheet->vadjustment->value;
+
+  if ( y < 0 && sheet->column_titles_visible)
+    {
+      trow = -1;
+    }
+  else
+    {
+      trow = row_from_ypixel (sheet, y);
+      if (trow > psppire_axis_unit_count (sheet->vaxis))
+       return FALSE;
+    }
+
+  *row = trow;
+
+  if ( sheet->row_titles_visible)
+    x -= sheet->row_title_area.width;
+
+  x += sheet->hadjustment->value;
+
+  if ( x < 0 && sheet->row_titles_visible)
+    {
+      tcol = -1;
+    }
+  else
+    {
+      tcol = column_from_xpixel (sheet, x);
+      if (tcol > psppire_axis_unit_count (sheet->haxis))
+       return FALSE;
+    }
+
+  *column = tcol;
+
+  return TRUE;
+}
+
+gboolean
+psppire_sheet_get_cell_area (PsppireSheet *sheet,
+                        gint row,
+                        gint column,
+                        GdkRectangle *area)
+{
+  g_return_val_if_fail (sheet != NULL, 0);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
+
+  if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
+    return FALSE;
+
+  area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
+  area->y = (row == -1)    ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
+
+  area->width= (column == -1) ? sheet->row_title_area.width
+    : psppire_axis_unit_size (sheet->haxis, column);
+
+  area->height= (row == -1) ? sheet->column_title_area.height
+    : psppire_axis_unit_size (sheet->vaxis, row);
+
+  return TRUE;
+}
+
+void
+psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (row < -1 || col < -1)
+    return;
+
+  if (row >= psppire_axis_unit_count (sheet->vaxis)
+      ||
+      col >= psppire_axis_unit_count (sheet->haxis))
+    return;
+
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  if ( row == -1 || col == -1)
+    {
+      psppire_sheet_hide_entry_widget (sheet);
+      return;
+    }
+
+  change_active_cell (sheet, row, col);
+}
+
+void
+psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if ( row ) *row = sheet->active_cell.row;
+  if (column) *column = sheet->active_cell.col;
+}
+
+static void
+entry_load_text (PsppireSheet *sheet)
+{
+  gint row, col;
+  const char *text;
+  GtkJustification justification;
+  PsppireSheetCellAttr attributes;
+
+  if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
+  if (sheet->state != GTK_STATE_NORMAL) return;
+
+  row = sheet->active_cell.row;
+  col = sheet->active_cell.col;
+
+  if (row < 0 || col < 0) return;
+
+  text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
+
+  if (text && strlen (text) > 0)
+    {
+      psppire_sheet_get_attributes (sheet, row, col, &attributes);
+      justification = attributes.justification;
+      psppire_sheet_set_cell (sheet, row, col, justification, text);
+    }
+}
+
+
+static void
+psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
+{
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  if (sheet->active_cell.row < 0 ||
+      sheet->active_cell.col < 0) return;
+
+  gtk_widget_hide (sheet->entry_widget);
+  gtk_widget_unmap (sheet->entry_widget);
+
+  GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
+}
+
+static void
+change_active_cell (PsppireSheet *sheet, gint row, gint col)
+{
+  gint old_row, old_col;
+
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (row < 0 || col < 0)
+    return;
+
+  if ( row > psppire_axis_unit_count (sheet->vaxis)
+       || col > psppire_axis_unit_count (sheet->haxis))
+    return;
+
+  if (sheet->state != PSPPIRE_SHEET_NORMAL)
+    {
+      sheet->state = PSPPIRE_SHEET_NORMAL;
+      psppire_sheet_real_unselect_range (sheet, NULL);
+    }
+
+  old_row = sheet->active_cell.row;
+  old_col = sheet->active_cell.col;
+
+  /* Erase the old cell */
+  psppire_sheet_draw_active_cell (sheet);
+
+  entry_load_text (sheet);
+
+  sheet->range.row0 = row;
+  sheet->range.col0 = col;
+  sheet->range.rowi = row;
+  sheet->range.coli = col;
+  sheet->active_cell.row = row;
+  sheet->active_cell.col = col;
+  sheet->selection_cell.row = row;
+  sheet->selection_cell.col = col;
+
+  PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+
+  GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
+
+  psppire_sheet_draw_active_cell (sheet);
+  psppire_sheet_show_entry_widget (sheet);
+
+  GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
+
+  g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
+                row, col, old_row, old_col);
+
+}
+
+static void
+psppire_sheet_show_entry_widget (PsppireSheet *sheet)
+{
+  GtkEntry *sheet_entry;
+  PsppireSheetCellAttr attributes;
+
+  gint row, col;
+
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  row = sheet->active_cell.row;
+  col = sheet->active_cell.col;
+
+  /* Don't show the active cell, if there is no active cell: */
+  if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
+    return;
+
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
+  if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
+  if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
+
+  GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
+
+  sheet_entry = psppire_sheet_get_entry (sheet);
+
+  psppire_sheet_get_attributes (sheet, row, col, &attributes);
+
+  if (GTK_IS_ENTRY (sheet_entry))
+    {
+      gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
+      const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
+
+      if ( ! text )
+       text = g_strdup ("");
+
+      if (strcmp (old_text, text) != 0)
+       gtk_entry_set_text (sheet_entry, text);
+      
+      dispose_string (sheet, text);
+
+       {
+         switch (attributes.justification)
+           {
+           case GTK_JUSTIFY_RIGHT:
+             gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
+             break;
+           case GTK_JUSTIFY_CENTER:
+             gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
+             break;
+           case GTK_JUSTIFY_LEFT:
+           default:
+             gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
+             break;
+           }
+       }
+    }
+
+  psppire_sheet_size_allocate_entry (sheet);
+
+  gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
+                           psppire_sheet_model_is_editable (sheet->model,
+                                                      row, col));
+  gtk_widget_map (sheet->entry_widget);
+}
+
+static gboolean
+psppire_sheet_draw_active_cell (PsppireSheet *sheet)
+{
+  gint row, col;
+  PsppireSheetRange range;
+
+  row = sheet->active_cell.row;
+  col = sheet->active_cell.col;
+
+  if (row < 0 || col < 0) return FALSE;
+
+  if (!psppire_sheet_cell_isvisible (sheet, row, col))
+    return FALSE;
+
+  range.col0 = range.coli = col;
+  range.row0 = range.rowi = row;
+
+  psppire_sheet_draw_border (sheet, range);
+
+  return FALSE;
+}
+
+
+
+static void
+psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
+{
+  gint i, j, mask1, mask2;
+  gint state, selected;
+  gint x, y, width, height;
+  PsppireSheetRange new_range, aux_range;
+
+  g_return_if_fail (sheet != NULL);
+
+  if (range == NULL) range=&sheet->range;
+
+  new_range=*range;
+
+  range->row0 = MIN (range->row0, sheet->range.row0);
+  range->rowi = MAX (range->rowi, sheet->range.rowi);
+  range->col0 = MIN (range->col0, sheet->range.col0);
+  range->coli = MAX (range->coli, sheet->range.coli);
+
+  range->row0 = MAX (range->row0, min_visible_row (sheet));
+  range->rowi = MIN (range->rowi, max_visible_row (sheet));
+  range->col0 = MAX (range->col0, min_visible_column (sheet));
+  range->coli = MIN (range->coli, max_visible_column (sheet));
+
+  aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
+  aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
+  aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
+  aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
+
+  for (i = range->row0; i <= range->rowi; i++)
+    {
+      for (j = range->col0; j <= range->coli; j++)
+       {
+
+         state = psppire_sheet_cell_get_state (sheet, i, j);
+         selected= (i <= new_range.rowi && i >= new_range.row0 &&
+                    j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
+
+         if (state == GTK_STATE_SELECTED && selected &&
+             (i == sheet->range.row0 || i == sheet->range.rowi ||
+              j == sheet->range.col0 || j == sheet->range.coli ||
+              i == new_range.row0 || i == new_range.rowi ||
+              j == new_range.col0 || j == new_range.coli))
+           {
+
+             mask1 = i == sheet->range.row0 ? 1 : 0;
+             mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
+             mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
+             mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
+
+             mask2 = i == new_range.row0 ? 1 : 0;
+             mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
+             mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
+             mask2 = j == new_range.coli ? mask2 + 8 : mask2;
+
+             if (mask1 != mask2)
+               {
+                 x = psppire_axis_start_pixel (sheet->haxis, j);
+                 y = psppire_axis_start_pixel (sheet->vaxis, i);
+                 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
+                   psppire_axis_unit_size (sheet->haxis, j);
+                 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
+
+                 if (i == sheet->range.row0)
+                   {
+                     y = y - 3;
+                     height = height + 3;
+                   }
+                 if (i == sheet->range.rowi) height = height + 3;
+                 if (j == sheet->range.col0)
+                   {
+                     x = x - 3;
+                     width = width + 3;
+                   }
+                 if (j == sheet->range.coli) width = width + 3;
+
+                 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
+                   {
+                     x = psppire_axis_start_pixel (sheet->haxis, j);
+                     y = psppire_axis_start_pixel (sheet->vaxis, i);
+                     width = psppire_axis_start_pixel (sheet->haxis, j)- x+
+                       psppire_axis_unit_size (sheet->haxis, j);
+
+                     height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
+
+                     if (i == new_range.row0)
+                       {
+                         y = y+2;
+                         height = height - 2;
+                       }
+                     if (i == new_range.rowi) height = height - 3;
+                     if (j == new_range.col0)
+                       {
+                         x = x+2;
+                         width = width - 2;
+                       }
+                     if (j == new_range.coli) width = width - 3;
+
+                     gdk_draw_rectangle (sheet->sheet_window,
+                                         sheet->xor_gc,
+                                         TRUE,
+                                         x + 1, y + 1,
+                                         width, height);
+                   }
+               }
+           }
+       }
+    }
+
+  for (i = range->row0; i <= range->rowi; i++)
+    {
+      for (j = range->col0; j <= range->coli; j++)
+       {
+
+         state = psppire_sheet_cell_get_state (sheet, i, j);
+         selected= (i <= new_range.rowi && i >= new_range.row0 &&
+                    j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
+
+         if (state == GTK_STATE_SELECTED && !selected)
+           {
+
+             x = psppire_axis_start_pixel (sheet->haxis, j);
+             y = psppire_axis_start_pixel (sheet->vaxis, i);
+             width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
+             height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
+
+             if (i == sheet->range.row0)
+               {
+                 y = y - 3;
+                 height = height + 3;
+               }
+             if (i == sheet->range.rowi) height = height + 3;
+             if (j == sheet->range.col0)
+               {
+                 x = x - 3;
+                 width = width + 3;
+               }
+             if (j == sheet->range.coli) width = width + 3;
+
+           }
+       }
+    }
+
+  for (i = range->row0; i <= range->rowi; i++)
+    {
+      for (j = range->col0; j <= range->coli; j++)
+       {
+
+         state = psppire_sheet_cell_get_state (sheet, i, j);
+         selected= (i <= new_range.rowi && i >= new_range.row0 &&
+                    j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
+
+         if (state != GTK_STATE_SELECTED && selected &&
+             (i != sheet->active_cell.row || j != sheet->active_cell.col))
+           {
+
+             x = psppire_axis_start_pixel (sheet->haxis, j);
+             y = psppire_axis_start_pixel (sheet->vaxis, i);
+             width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
+             height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
+
+             if (i == new_range.row0)
+               {
+                 y = y+2;
+                 height = height - 2;
+               }
+             if (i == new_range.rowi) height = height - 3;
+             if (j == new_range.col0)
+               {
+                 x = x+2;
+                 width = width - 2;
+               }
+             if (j == new_range.coli) width = width - 3;
+
+             gdk_draw_rectangle (sheet->sheet_window,
+                                 sheet->xor_gc,
+                                 TRUE,
+                                 x + 1, y + 1,
+                                 width, height);
+
+           }
+
+       }
+    }
+
+  for (i = aux_range.row0; i <= aux_range.rowi; i++)
+    {
+      for (j = aux_range.col0; j <= aux_range.coli; j++)
+       {
+         state = psppire_sheet_cell_get_state (sheet, i, j);
+
+         mask1 = i == sheet->range.row0 ? 1 : 0;
+         mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
+         mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
+         mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
+
+         mask2 = i == new_range.row0 ? 1 : 0;
+         mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
+         mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
+         mask2 = j == new_range.coli ? mask2 + 8 : mask2;
+         if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
+           {
+             x = psppire_axis_start_pixel (sheet->haxis, j);
+             y = psppire_axis_start_pixel (sheet->vaxis, i);
+             width = psppire_axis_unit_size (sheet->haxis, j);
+             height = psppire_axis_unit_size (sheet->vaxis, i);
+             if (mask2 & 1)
+               gdk_draw_rectangle (sheet->sheet_window,
+                                   sheet->xor_gc,
+                                   TRUE,
+                                   x + 1, y - 1,
+                                   width, 3);
+
+
+             if (mask2 & 2)
+               gdk_draw_rectangle (sheet->sheet_window,
+                                   sheet->xor_gc,
+                                   TRUE,
+                                   x + 1, y + height - 1,
+                                   width, 3);
+
+             if (mask2 & 4)
+               gdk_draw_rectangle (sheet->sheet_window,
+                                   sheet->xor_gc,
+                                   TRUE,
+                                   x - 1, y + 1,
+                                   3, height);
+
+
+             if (mask2 & 8)
+               gdk_draw_rectangle (sheet->sheet_window,
+                                   sheet->xor_gc,
+                                   TRUE,
+                                   x + width - 1, y + 1,
+                                   3, height);
+           }
+       }
+    }
+
+  *range = new_range;
+}
+
+
+
+static void
+psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
+{
+  GdkRectangle area;
+
+  rectangle_from_range (sheet, &new_range, &area);
+
+  gdk_draw_rectangle (sheet->sheet_window,
+                     sheet->xor_gc,
+                     FALSE,
+                     area.x, area.y,
+                     area.width, area.height);
+}
+
+
+static void
+psppire_sheet_real_select_range (PsppireSheet *sheet,
+                            const PsppireSheetRange *range)
+{
+  gint state;
+
+  g_return_if_fail (sheet != NULL);
+
+  if (range == NULL) range = &sheet->range;
+
+  memcpy (&sheet->range, range, sizeof (*range));
+
+  if (range->row0 < 0 || range->rowi < 0) return;
+  if (range->col0 < 0 || range->coli < 0) return;
+
+  state = sheet->state;
+
+#if 0
+  if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
+      range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
+    {
+      psppire_sheet_new_selection (sheet, &sheet->range);
+    }
+  else
+    {
+      psppire_sheet_range_draw_selection (sheet, sheet->range);
+    }
+#endif
+
+  psppire_sheet_update_primary_selection (sheet);
+
+  g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
+}
+
+
+void
+psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
+{
+  g_return_if_fail (sheet != NULL);
+  *range = sheet->range;
+}
+
+
+void
+psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
+{
+  g_return_if_fail (sheet != NULL);
+
+  if (range == NULL) range=&sheet->range;
+
+  if (range->row0 < 0 || range->rowi < 0) return;
+  if (range->col0 < 0 || range->coli < 0) return;
+
+
+  if (sheet->state != PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_real_unselect_range (sheet, NULL);
+
+  sheet->range.row0 = range->row0;
+  sheet->range.rowi = range->rowi;
+  sheet->range.col0 = range->col0;
+  sheet->range.coli = range->coli;
+  sheet->active_cell.row = range->row0;
+  sheet->active_cell.col = range->col0;
+  sheet->selection_cell.row = range->rowi;
+  sheet->selection_cell.col = range->coli;
+
+  sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
+  psppire_sheet_real_select_range (sheet, NULL);
+}
+
+void
+psppire_sheet_unselect_range (PsppireSheet *sheet)
+{
+  if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  psppire_sheet_real_unselect_range (sheet, NULL);
+  sheet->state = GTK_STATE_NORMAL;
+
+  change_active_cell (sheet,
+                sheet->active_cell.row, sheet->active_cell.col);
+}
+
+
+static void
+psppire_sheet_real_unselect_range (PsppireSheet *sheet,
+                              const PsppireSheetRange *range)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
+
+  if ( range == NULL)
+    range = &sheet->range;
+
+  if (range->row0 < 0 || range->rowi < 0) return;
+  if (range->col0 < 0 || range->coli < 0) return;
+
+  g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
+  g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
+
+  sheet->range.row0 = -1;
+  sheet->range.rowi = -1;
+  sheet->range.col0 = -1;
+  sheet->range.coli = -1;
+}
+
+
+static gint
+psppire_sheet_expose (GtkWidget *widget,
+                 GdkEventExpose *event)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (!GTK_WIDGET_DRAWABLE (widget))
+    return FALSE;
+
+  /* exposure events on the sheet */
+  if (event->window == sheet->row_title_window &&
+      sheet->row_titles_visible)
+    {
+      draw_row_title_buttons_range (sheet,
+                                   min_visible_row (sheet),
+                                   max_visible_row (sheet));
+    }
+
+  if (event->window == sheet->column_title_window &&
+      sheet->column_titles_visible)
+    {
+      draw_column_title_buttons_range (sheet,
+                                      min_visible_column (sheet),
+                                      max_visible_column (sheet));
+    }
+
+  if (event->window == sheet->sheet_window)
+    {
+      draw_sheet_region (sheet, event->region);
+
+#if 0
+      if (sheet->state != PSPPIRE_SHEET_NORMAL)
+       {
+         if (psppire_sheet_range_isvisible (sheet, &sheet->range))
+           psppire_sheet_range_draw (sheet, &sheet->range);
+
+         if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
+           psppire_sheet_range_draw (sheet, &sheet->drag_range);
+
+         if (psppire_sheet_range_isvisible (sheet, &sheet->range))
+           psppire_sheet_range_draw_selection (sheet, sheet->range);
+         if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
+           draw_xor_rectangle (sheet, sheet->drag_range);
+       }
+#endif
+
+      if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
+       {
+         GdkRectangle rect;
+         PsppireSheetRange range;
+         range.row0 = range.rowi =  sheet->active_cell.row;
+         range.col0 = range.coli =  sheet->active_cell.col;
+
+         rectangle_from_range (sheet, &range, &rect);
+
+         if (GDK_OVERLAP_RECTANGLE_OUT !=
+             gdk_region_rect_in (event->region, &rect))
+           {
+             psppire_sheet_draw_active_cell (sheet);
+           }
+       }
+
+    }
+
+  (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
+
+  return FALSE;
+}
+
+
+static gboolean
+psppire_sheet_button_press (GtkWidget *widget,
+                       GdkEventButton *event)
+{
+  PsppireSheet *sheet;
+  GdkModifierType mods;
+  gint x, y;
+  gint  row, column;
+  gboolean veto;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  sheet = PSPPIRE_SHEET (widget);
+
+  /* Cancel any pending tooltips */
+  if (sheet->motion_timer)
+    {
+      g_source_remove (sheet->motion_timer);
+      sheet->motion_timer = 0;
+    }
+
+  gtk_widget_get_pointer (widget, &x, &y);
+  psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
+
+
+  if (event->window == sheet->column_title_window)
+    {
+      sheet->x_drag = event->x;
+      g_signal_emit (sheet,
+                    sheet_signals[BUTTON_EVENT_COLUMN], 0,
+                    column, event);
+
+      if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
+       {
+         if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
+           g_signal_emit (sheet,
+                          sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
+       }
+    }
+  else if (event->window == sheet->row_title_window)
+    {
+      g_signal_emit (sheet,
+                    sheet_signals[BUTTON_EVENT_ROW], 0,
+                    row, event);
+
+      if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
+       {
+         if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
+           g_signal_emit (sheet,
+                          sheet_signals[DOUBLE_CLICK_ROW], 0, row);
+       }
+    }
+
+  gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
+
+  if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
+
+
+  /* press on resize windows */
+  if (event->window == sheet->column_title_window)
+    {
+      sheet->x_drag = event->x;
+
+      if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
+       {
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
+         gdk_pointer_grab (sheet->column_title_window, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK |
+                           GDK_BUTTON1_MOTION_MASK |
+                           GDK_BUTTON_RELEASE_MASK,
+                           NULL, NULL, event->time);
+
+         draw_xor_vline (sheet);
+         return TRUE;
+       }
+    }
+
+  if (event->window == sheet->row_title_window)
+    {
+      sheet->y_drag = event->y;
+
+      if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
+       {
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
+         gdk_pointer_grab (sheet->row_title_window, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK |
+                           GDK_BUTTON1_MOTION_MASK |
+                           GDK_BUTTON_RELEASE_MASK,
+                           NULL, NULL, event->time);
+
+         draw_xor_hline (sheet);
+         return TRUE;
+       }
+    }
+
+  /* the sheet itself does not handle other than single click events */
+  if (event->type != GDK_BUTTON_PRESS) return FALSE;
+
+  /* selections on the sheet */
+  if (event->window == sheet->sheet_window)
+    {
+      gtk_widget_get_pointer (widget, &x, &y);
+      psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
+      gdk_pointer_grab (sheet->sheet_window, FALSE,
+                       GDK_POINTER_MOTION_HINT_MASK |
+                       GDK_BUTTON1_MOTION_MASK |
+                       GDK_BUTTON_RELEASE_MASK,
+                       NULL, NULL, event->time);
+      gtk_grab_add (GTK_WIDGET (sheet));
+
+      if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
+         sheet->selection_mode != GTK_SELECTION_NONE &&
+         sheet->cursor_drag->type == GDK_SIZING &&
+         !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
+       {
+         if (sheet->state == GTK_STATE_NORMAL)
+           {
+             row = sheet->active_cell.row;
+             column = sheet->active_cell.col;
+             sheet->active_cell.row = row;
+             sheet->active_cell.col = column;
+             sheet->drag_range = sheet->range;
+             sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
+             psppire_sheet_select_range (sheet, &sheet->drag_range);
+           }
+         sheet->x_drag = x;
+         sheet->y_drag = y;
+         if (row > sheet->range.rowi) row--;
+         if (column > sheet->range.coli) column--;
+         sheet->drag_cell.row = row;
+         sheet->drag_cell.col = column;
+         sheet->drag_range = sheet->range;
+         draw_xor_rectangle (sheet, sheet->drag_range);
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
+       }
+      else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
+              !PSPPIRE_SHEET_IN_SELECTION (sheet)
+              && ! PSPPIRE_SHEET_IN_DRAG (sheet)
+              && sheet->active_cell.row >= 0
+              && sheet->active_cell.col >= 0
+              )
+       {
+         if (sheet->state == GTK_STATE_NORMAL)
+           {
+             row = sheet->active_cell.row;
+             column = sheet->active_cell.col;
+             sheet->active_cell.row = row;
+             sheet->active_cell.col = column;
+             sheet->drag_range = sheet->range;
+             sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
+             psppire_sheet_select_range (sheet, &sheet->drag_range);
+           }
+         sheet->x_drag = x;
+         sheet->y_drag = y;
+         if (row < sheet->range.row0) row++;
+         if (row > sheet->range.rowi) row--;
+         if (column < sheet->range.col0) column++;
+         if (column > sheet->range.coli) column--;
+         sheet->drag_cell.row = row;
+         sheet->drag_cell.col = column;
+         sheet->drag_range = sheet->range;
+         draw_xor_rectangle (sheet, sheet->drag_range);
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
+       }
+      else
+       {
+         veto = psppire_sheet_click_cell (sheet, row, column);
+         if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+       }
+    }
+
+  if (event->window == sheet->column_title_window)
+    {
+      gtk_widget_get_pointer (widget, &x, &y);
+      if ( sheet->row_titles_visible)
+       x -= sheet->row_title_area.width;
+
+      x += sheet->hadjustment->value;
+
+      column = column_from_xpixel (sheet, x);
+
+      if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
+       {
+         veto = psppire_sheet_click_cell (sheet, -1, column);
+         gtk_grab_add (GTK_WIDGET (sheet));
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+       }
+    }
+
+  if (event->window == sheet->row_title_window)
+    {
+      gtk_widget_get_pointer (widget, &x, &y);
+      if ( sheet->column_titles_visible)
+       y -= sheet->column_title_area.height;
+
+      y += sheet->vadjustment->value;
+
+      row = row_from_ypixel (sheet, y);
+      if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
+       {
+         veto = psppire_sheet_click_cell (sheet, row, -1);
+         gtk_grab_add (GTK_WIDGET (sheet));
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+       }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
+{
+  PsppireSheetCell cell;
+  gboolean forbid_move;
+
+  cell.row = row;
+  cell.col = column;
+
+  if (row >= psppire_axis_unit_count (sheet->vaxis)
+      || column >= psppire_axis_unit_count (sheet->haxis))
+    {
+      return FALSE;
+    }
+
+  g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
+                &sheet->active_cell,
+                &cell,
+                &forbid_move);
+
+  if (forbid_move)
+    {
+      if (sheet->state == GTK_STATE_NORMAL)
+       return FALSE;
+
+      row = sheet->active_cell.row;
+      column = sheet->active_cell.col;
+
+      change_active_cell (sheet, row, column);
+      return FALSE;
+    }
+
+  if (row == -1 && column >= 0)
+    {
+      psppire_sheet_select_column (sheet, column);
+      return TRUE;
+    }
+
+  if (column == -1 && row >= 0)
+    {
+      psppire_sheet_select_row (sheet, row);
+      return TRUE;
+    }
+
+  if (row == -1 && column == -1)
+    {
+      sheet->range.row0 = 0;
+      sheet->range.col0 = 0;
+      sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
+      sheet->range.coli =
+       psppire_axis_unit_count (sheet->haxis) - 1;
+      sheet->active_cell.row = 0;
+      sheet->active_cell.col = 0;
+      psppire_sheet_select_range (sheet, NULL);
+      return TRUE;
+    }
+
+  if (sheet->state != PSPPIRE_SHEET_NORMAL)
+    {
+      sheet->state = PSPPIRE_SHEET_NORMAL;
+      psppire_sheet_real_unselect_range (sheet, NULL);
+    }
+  else
+    {
+      change_active_cell (sheet, row, column);
+    }
+
+  sheet->active_cell.row = row;
+  sheet->active_cell.col = column;
+  sheet->selection_cell.row = row;
+  sheet->selection_cell.col = column;
+  sheet->range.row0 = row;
+  sheet->range.col0 = column;
+  sheet->range.rowi = row;
+  sheet->range.coli = column;
+  sheet->state = PSPPIRE_SHEET_NORMAL;
+  PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+
+  gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
+
+  return TRUE;
+}
+
+static gint
+psppire_sheet_button_release (GtkWidget *widget,
+                         GdkEventButton *event)
+{
+  GdkDisplay *display = gtk_widget_get_display (widget);
+
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+
+  /* release on resize windows */
+  if (PSPPIRE_SHEET_IN_XDRAG (sheet))
+    {
+      gint width;
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+
+      gdk_display_pointer_ungrab (display, event->time);
+      draw_xor_vline (sheet);
+
+      width = event->x -
+       psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
+       + sheet->hadjustment->value;
+
+      set_column_width (sheet, sheet->drag_cell.col, width);
+
+      return TRUE;
+    }
+
+  if (PSPPIRE_SHEET_IN_YDRAG (sheet))
+    {
+      gint height;
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+
+      gdk_display_pointer_ungrab (display, event->time);
+      draw_xor_hline (sheet);
+
+      height = event->y -
+       psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
+       sheet->vadjustment->value;
+
+      set_row_height (sheet, sheet->drag_cell.row, height);
+
+      return TRUE;
+    }
+
+  if (PSPPIRE_SHEET_IN_DRAG (sheet))
+    {
+      PsppireSheetRange old_range;
+      draw_xor_rectangle (sheet, sheet->drag_range);
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
+      gdk_display_pointer_ungrab (display, event->time);
+
+      psppire_sheet_real_unselect_range (sheet, NULL);
+
+      sheet->active_cell.row = sheet->active_cell.row +
+       (sheet->drag_range.row0 - sheet->range.row0);
+      sheet->active_cell.col = sheet->active_cell.col +
+       (sheet->drag_range.col0 - sheet->range.col0);
+      sheet->selection_cell.row = sheet->selection_cell.row +
+       (sheet->drag_range.row0 - sheet->range.row0);
+      sheet->selection_cell.col = sheet->selection_cell.col +
+       (sheet->drag_range.col0 - sheet->range.col0);
+      old_range = sheet->range;
+      sheet->range = sheet->drag_range;
+      sheet->drag_range = old_range;
+      g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
+                    &sheet->drag_range, &sheet->range);
+      psppire_sheet_select_range (sheet, &sheet->range);
+    }
+
+  if (PSPPIRE_SHEET_IN_RESIZE (sheet))
+    {
+      PsppireSheetRange old_range;
+      draw_xor_rectangle (sheet, sheet->drag_range);
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
+      gdk_display_pointer_ungrab (display, event->time);
+
+      psppire_sheet_real_unselect_range (sheet, NULL);
+
+      sheet->active_cell.row = sheet->active_cell.row +
+       (sheet->drag_range.row0 - sheet->range.row0);
+      sheet->active_cell.col = sheet->active_cell.col +
+       (sheet->drag_range.col0 - sheet->range.col0);
+      if (sheet->drag_range.row0 < sheet->range.row0)
+       sheet->selection_cell.row = sheet->drag_range.row0;
+      if (sheet->drag_range.rowi >= sheet->range.rowi)
+       sheet->selection_cell.row = sheet->drag_range.rowi;
+      if (sheet->drag_range.col0 < sheet->range.col0)
+       sheet->selection_cell.col = sheet->drag_range.col0;
+      if (sheet->drag_range.coli >= sheet->range.coli)
+       sheet->selection_cell.col = sheet->drag_range.coli;
+      old_range = sheet->range;
+      sheet->range = sheet->drag_range;
+      sheet->drag_range = old_range;
+
+      if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
+      g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
+                    &sheet->drag_range, &sheet->range);
+      psppire_sheet_select_range (sheet, &sheet->range);
+    }
+
+  if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
+    {
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+      gdk_display_pointer_ungrab (display, event->time);
+      change_active_cell (sheet, sheet->active_cell.row,
+                              sheet->active_cell.col);
+    }
+
+  if (PSPPIRE_SHEET_IN_SELECTION)
+    gdk_display_pointer_ungrab (display, event->time);
+  gtk_grab_remove (GTK_WIDGET (sheet));
+
+  PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+
+  return TRUE;
+}
+
+\f
+
+
+
+/* Shamelessly lifted from gtktooltips */
+static gboolean
+psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
+{
+  GtkRequisition req;
+
+  gtk_widget_size_request (tip_window, &req);
+  gtk_paint_flat_box (tip_window->style, tip_window->window,
+                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+                     NULL, GTK_WIDGET(tip_window), "tooltip",
+                     0, 0, req.width, req.height);
+
+  return FALSE;
+}
+
+static void
+destroy_hover_window (PsppireSheetHoverTitle *h)
+{
+  gtk_widget_destroy (h->window);
+  g_free (h);
+}
+
+static PsppireSheetHoverTitle *
+create_hover_window (void)
+{
+  PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
+
+  hw->window = gtk_window_new (GTK_WINDOW_POPUP);
+
+#if GTK_CHECK_VERSION (2, 9, 0)
+  gtk_window_set_type_hint (GTK_WINDOW (hw->window),
+                           GDK_WINDOW_TYPE_HINT_TOOLTIP);
+#endif
+
+  gtk_widget_set_app_paintable (hw->window, TRUE);
+  gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
+  gtk_widget_set_name (hw->window, "gtk-tooltips");
+  gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
+
+  g_signal_connect (hw->window,
+                   "expose_event",
+                   G_CALLBACK (psppire_sheet_subtitle_paint_window),
+                   NULL);
+
+  hw->label = gtk_label_new (NULL);
+
+
+  gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
+  gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
+
+  gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
+
+  gtk_widget_show (hw->label);
+
+  g_signal_connect (hw->window,
+                   "destroy",
+                   G_CALLBACK (gtk_widget_destroyed),
+                   &hw->window);
+
+  return hw;
+}
+
+#define HOVER_WINDOW_Y_OFFSET 2
+
+static void
+show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
+{
+  gint x, y;
+  gint px, py;
+  gint width;
+
+  if ( ! subtitle )
+    return;
+
+  gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
+                     subtitle);
+
+
+  sheet->hover_window->row = row;
+  sheet->hover_window->column = column;
+
+  gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
+
+  gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
+
+  gtk_widget_show (sheet->hover_window->window);
+
+  width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
+
+  if (row == -1 )
+    {
+      x += px;
+      x -= width / 2;
+      y += sheet->column_title_area.y;
+      y += sheet->column_title_area.height;
+      y += HOVER_WINDOW_Y_OFFSET;
+    }
+
+  if ( column == -1 )
+    {
+      y += py;
+      x += sheet->row_title_area.x;
+      x += sheet->row_title_area.width * 2 / 3.0;
+    }
+
+  gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
+                  x, y);
+}
+
+static gboolean
+motion_timeout_callback (gpointer data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+  gint x, y;
+  gint row, column;
+  gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
+
+  if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
+    {
+      if (sheet->row_title_under && row >= 0)
+       {
+         gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
+
+         show_subtitle (sheet, row, -1, text);
+         g_free (text);
+       }
+
+      if (sheet->column_title_under && column >= 0)
+       {
+         gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
+                                                          column);
+
+         show_subtitle (sheet, -1, column, text);
+
+         g_free (text);
+       }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+psppire_sheet_motion (GtkWidget *widget,  GdkEventMotion *event)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+  GdkModifierType mods;
+  GdkCursorType new_cursor;
+  gint x, y;
+  gint row, column;
+  GdkDisplay *display;
+
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  display = gtk_widget_get_display (widget);
+
+  /* selections on the sheet */
+  x = event->x;
+  y = event->y;
+
+  if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
+    {
+      if ( sheet->motion_timer > 0 )
+       g_source_remove (sheet->motion_timer);
+      sheet->motion_timer =
+       g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
+    }
+  else
+    {
+      gint row, column;
+      gint wx, wy;
+      gtk_widget_get_pointer (widget, &wx, &wy);
+
+      if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
+       {
+         if ( row != sheet->hover_window->row ||
+              column != sheet->hover_window->column)
+           {
+             gtk_widget_hide (sheet->hover_window->window);
+           }
+       }
+    }
+
+  if (event->window == sheet->column_title_window)
+    {
+      if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
+         on_column_boundary (sheet, x, &column))
+       {
+         new_cursor = GDK_SB_H_DOUBLE_ARROW;
+         if (new_cursor != sheet->cursor_drag->type)
+           {
+             gdk_cursor_unref (sheet->cursor_drag);
+             sheet->cursor_drag =
+               gdk_cursor_new_for_display (display, new_cursor);
+
+             gdk_window_set_cursor (sheet->column_title_window,
+                                    sheet->cursor_drag);
+           }
+       }
+      else
+       {
+         new_cursor = GDK_TOP_LEFT_ARROW;
+         if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
+             new_cursor != sheet->cursor_drag->type)
+           {
+             gdk_cursor_unref (sheet->cursor_drag);
+             sheet->cursor_drag =
+               gdk_cursor_new_for_display (display, new_cursor);
+             gdk_window_set_cursor (sheet->column_title_window,
+                                    sheet->cursor_drag);
+           }
+       }
+    }
+  else if (event->window == sheet->row_title_window)
+    {
+      if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
+         on_row_boundary (sheet, y, &row))
+       {
+         new_cursor = GDK_SB_V_DOUBLE_ARROW;
+         if (new_cursor != sheet->cursor_drag->type)
+           {
+             gdk_cursor_unref (sheet->cursor_drag);
+             sheet->cursor_drag =
+               gdk_cursor_new_for_display (display, new_cursor);
+             gdk_window_set_cursor (sheet->row_title_window,
+                                    sheet->cursor_drag);
+           }
+       }
+      else
+       {
+         new_cursor = GDK_TOP_LEFT_ARROW;
+         if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
+             new_cursor != sheet->cursor_drag->type)
+           {
+             gdk_cursor_unref (sheet->cursor_drag);
+             sheet->cursor_drag =
+               gdk_cursor_new_for_display (display, new_cursor);
+             gdk_window_set_cursor (sheet->row_title_window,
+                                    sheet->cursor_drag);
+           }
+       }
+    }
+
+  new_cursor = GDK_PLUS;
+  if ( event->window == sheet->sheet_window &&
+       !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
+       !PSPPIRE_SHEET_IN_DRAG (sheet) &&
+       !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
+       !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
+       new_cursor != sheet->cursor_drag->type)
+    {
+      gdk_cursor_unref (sheet->cursor_drag);
+      sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
+      gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
+    }
+
+  new_cursor = GDK_TOP_LEFT_ARROW;
+  if ( event->window == sheet->sheet_window &&
+       ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
+         PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
+       (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
+       PSPPIRE_SHEET_IN_DRAG (sheet)) &&
+       new_cursor != sheet->cursor_drag->type)
+    {
+      gdk_cursor_unref (sheet->cursor_drag);
+      sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
+      gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
+    }
+
+  new_cursor = GDK_SIZING;
+  if ( event->window == sheet->sheet_window &&
+       sheet->selection_mode != GTK_SELECTION_NONE &&
+       !PSPPIRE_SHEET_IN_DRAG (sheet) &&
+       (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
+       PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
+       new_cursor != sheet->cursor_drag->type)
+    {
+      gdk_cursor_unref (sheet->cursor_drag);
+      sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
+      gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
+    }
+
+
+  gdk_window_get_pointer (widget->window, &x, &y, &mods);
+  if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
+
+  if (PSPPIRE_SHEET_IN_XDRAG (sheet))
+    {
+      if (event->x != sheet->x_drag)
+       {
+         draw_xor_vline (sheet);
+         sheet->x_drag = event->x;
+         draw_xor_vline (sheet);
+       }
+
+      return TRUE;
+    }
+
+  if (PSPPIRE_SHEET_IN_YDRAG (sheet))
+    {
+      if (event->y != sheet->y_drag)
+       {
+         draw_xor_hline (sheet);
+         sheet->y_drag = event->y;
+         draw_xor_hline (sheet);
+       }
+
+      return TRUE;
+    }
+
+  if (PSPPIRE_SHEET_IN_DRAG (sheet))
+    {
+      PsppireSheetRange aux;
+      column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
+      row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
+      if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
+      if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
+      sheet->x_drag = x;
+      sheet->y_drag = y;
+      aux = sheet->range;
+      if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
+         aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
+       {
+         aux = sheet->drag_range;
+         sheet->drag_range.row0 = sheet->range.row0 + row;
+         sheet->drag_range.col0 = sheet->range.col0 + column;
+         sheet->drag_range.rowi = sheet->range.rowi + row;
+         sheet->drag_range.coli = sheet->range.coli + column;
+         if (aux.row0 != sheet->drag_range.row0 ||
+             aux.col0 != sheet->drag_range.col0)
+           {
+             draw_xor_rectangle (sheet, aux);
+             draw_xor_rectangle (sheet, sheet->drag_range);
+           }
+       }
+      return TRUE;
+    }
+
+  if (PSPPIRE_SHEET_IN_RESIZE (sheet))
+    {
+      PsppireSheetRange aux;
+      gint v_h, current_col, current_row, col_threshold, row_threshold;
+      v_h = 1;
+      if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
+         abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
+
+      current_col = column_from_xpixel (sheet, x);
+      current_row = row_from_ypixel (sheet, y);
+      column = current_col - sheet->drag_cell.col;
+      row = current_row - sheet->drag_cell.row;
+
+      /*use half of column width resp. row height as threshold to
+       expand selection*/
+      col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
+       psppire_axis_unit_size (sheet->haxis, current_col) / 2;
+      if (column > 0)
+       {
+         if (x < col_threshold)
+           column -= 1;
+       }
+      else if (column < 0)
+       {
+         if (x > col_threshold)
+           column +=1;
+       }
+      row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
+       psppire_axis_unit_size (sheet->vaxis, current_row)/2;
+      if (row > 0)
+       {
+         if (y < row_threshold)
+           row -= 1;
+       }
+      else if (row < 0)
+       {
+         if (y > row_threshold)
+           row +=1;
+       }
+
+      if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
+      if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
+      sheet->x_drag = x;
+      sheet->y_drag = y;
+      aux = sheet->range;
+
+      if (v_h == 1)
+       column = 0;
+      else
+       row = 0;
+
+      if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
+         aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
+       {
+         aux = sheet->drag_range;
+         sheet->drag_range = sheet->range;
+
+         if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
+         if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
+         if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
+         if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
+
+         if (aux.row0 != sheet->drag_range.row0 ||
+             aux.rowi != sheet->drag_range.rowi ||
+             aux.col0 != sheet->drag_range.col0 ||
+             aux.coli != sheet->drag_range.coli)
+           {
+             draw_xor_rectangle (sheet, aux);
+             draw_xor_rectangle (sheet, sheet->drag_range);
+           }
+       }
+      return TRUE;
+    }
+
+  psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
+
+  if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
+      column == sheet->active_cell.col) return TRUE;
+
+  if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
+    psppire_sheet_extend_selection (sheet, row, column);
+
+  return TRUE;
+}
+
+static gboolean
+psppire_sheet_crossing_notify (GtkWidget *widget,
+                          GdkEventCrossing *event)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+
+  if (event->window == sheet->column_title_window)
+    sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
+  else if (event->window == sheet->row_title_window)
+    sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
+
+  return TRUE;
+}
+
+
+static gboolean
+psppire_sheet_focus_in (GtkWidget     *w,
+                       GdkEventFocus *event)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (w);
+
+  gtk_widget_grab_focus (sheet->entry_widget);
+
+  return TRUE;
+}
+
+
+static void
+psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
+{
+  PsppireSheetRange range;
+  gint state;
+  gint r, c;
+
+  if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
+    return;
+
+  if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
+
+  gtk_widget_grab_focus (GTK_WIDGET (sheet));
+
+  if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
+
+  state = sheet->state;
+
+  switch (sheet->state)
+    {
+    case PSPPIRE_SHEET_ROW_SELECTED:
+      column = psppire_axis_unit_count (sheet->haxis) - 1;
+      break;
+    case PSPPIRE_SHEET_COLUMN_SELECTED:
+      row = psppire_axis_unit_count (sheet->vaxis) - 1;
+      break;
+    case PSPPIRE_SHEET_NORMAL:
+      sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
+      r = sheet->active_cell.row;
+      c = sheet->active_cell.col;
+      sheet->range.col0 = c;
+      sheet->range.row0 = r;
+      sheet->range.coli = c;
+      sheet->range.rowi = r;
+      psppire_sheet_range_draw_selection (sheet, sheet->range);
+    case PSPPIRE_SHEET_RANGE_SELECTED:
+      sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
+    }
+
+  sheet->selection_cell.row = row;
+  sheet->selection_cell.col = column;
+
+  range.col0 = MIN (column, sheet->active_cell.col);
+  range.coli = MAX (column, sheet->active_cell.col);
+  range.row0 = MIN (row, sheet->active_cell.row);
+  range.rowi = MAX (row, sheet->active_cell.row);
+
+  if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
+      range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
+      state == PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_real_select_range (sheet, &range);
+
+}
+
+static gint
+psppire_sheet_entry_key_press (GtkWidget *widget,
+                          GdkEventKey *key)
+{
+  gboolean focus;
+  g_signal_emit_by_name (widget, "key_press_event", key, &focus);
+  return focus;
+}
+
+
+/* Number of rows in a step-increment */
+#define ROWS_PER_STEP 1
+
+
+static void
+page_vertical (PsppireSheet *sheet, GtkScrollType dir)
+{
+  gint old_row = sheet->active_cell.row ;
+  glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
+
+  gint new_row;
+
+  vpixel -= psppire_axis_start_pixel (sheet->vaxis,
+                                    min_visible_row (sheet));
+
+  switch ( dir)
+    {
+    case GTK_SCROLL_PAGE_DOWN:
+      gtk_adjustment_set_value (sheet->vadjustment,
+                               sheet->vadjustment->value +
+                               sheet->vadjustment->page_increment);
+      break;
+    case GTK_SCROLL_PAGE_UP:
+      gtk_adjustment_set_value (sheet->vadjustment,
+                               sheet->vadjustment->value -
+                               sheet->vadjustment->page_increment);
+
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+
+  vpixel += psppire_axis_start_pixel (sheet->vaxis,
+                                    min_visible_row (sheet));
+
+  new_row =  row_from_ypixel (sheet, vpixel);
+
+  change_active_cell (sheet, new_row,
+                          sheet->active_cell.col);
+}
+
+
+static void
+step_sheet (PsppireSheet *sheet, GtkScrollType dir)
+{
+  gint current_row = sheet->active_cell.row;
+  gint current_col = sheet->active_cell.col;
+  PsppireSheetCell new_cell ;
+  gboolean forbidden = FALSE;
+
+  new_cell.row = current_row;
+  new_cell.col = current_col;
+
+  switch ( dir)
+    {
+    case GTK_SCROLL_STEP_DOWN:
+      new_cell.row++;
+      break;
+    case GTK_SCROLL_STEP_UP:
+      new_cell.row--;
+      break;
+    case GTK_SCROLL_STEP_RIGHT:
+      new_cell.col++;
+      break;
+    case GTK_SCROLL_STEP_LEFT:
+      new_cell.col--;
+      break;
+    case GTK_SCROLL_STEP_FORWARD:
+      new_cell.col++;
+      if (new_cell.col >=
+         psppire_sheet_model_get_column_count (sheet->model))
+       {
+         new_cell.col = 0;
+         new_cell.row++;
+       }
+      break;
+    case GTK_SCROLL_STEP_BACKWARD:
+      new_cell.col--;
+      if (new_cell.col < 0)
+       {
+         new_cell.col =
+           psppire_sheet_model_get_column_count (sheet->model) - 1;
+         new_cell.row--;
+       }
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
+                &sheet->active_cell,
+                &new_cell,
+                &forbidden);
+
+  if (forbidden)
+    return;
+
+
+  maximize_int (&new_cell.row, 0);
+  maximize_int (&new_cell.col, 0);
+
+  minimize_int (&new_cell.row,
+               psppire_axis_unit_count (sheet->vaxis) - 1);
+
+  minimize_int (&new_cell.col,
+               psppire_axis_unit_count (sheet->haxis) - 1);
+
+  change_active_cell (sheet, new_cell.row, new_cell.col);
+
+
+  if ( new_cell.col > max_fully_visible_column (sheet))
+    {
+      glong hpos  =
+       psppire_axis_start_pixel (sheet->haxis,
+                                   new_cell.col + 1);
+      hpos -= sheet->hadjustment->page_size;
+
+      gtk_adjustment_set_value (sheet->hadjustment,
+                               hpos);
+    }
+  else if ( new_cell.col < min_fully_visible_column (sheet))
+    {
+      glong hpos  =
+       psppire_axis_start_pixel (sheet->haxis,
+                                   new_cell.col);
+
+      gtk_adjustment_set_value (sheet->hadjustment,
+                               hpos);
+    }
+
+
+  if ( new_cell.row > max_fully_visible_row (sheet))
+    {
+      glong vpos  =
+       psppire_axis_start_pixel (sheet->vaxis,
+                                   new_cell.row + 1);
+      vpos -= sheet->vadjustment->page_size;
+
+      gtk_adjustment_set_value (sheet->vadjustment,
+                               vpos);
+    }
+  else if ( new_cell.row < min_fully_visible_row (sheet))
+    {
+      glong vpos  =
+       psppire_axis_start_pixel (sheet->vaxis,
+                                   new_cell.row);
+
+      gtk_adjustment_set_value (sheet->vadjustment,
+                               vpos);
+    }
+
+  gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
+}
+
+
+static gboolean
+psppire_sheet_key_press (GtkWidget *widget,
+                    GdkEventKey *key)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (widget);
+
+  PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+
+  switch (key->keyval)
+    {
+    case GDK_Tab:
+      step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
+      break;
+    case GDK_Right:
+      step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
+      break;
+    case GDK_ISO_Left_Tab:
+      step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
+      break;
+    case GDK_Left:
+      step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
+      break;
+    case GDK_Return:
+    case GDK_Down:
+      step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
+      break;
+    case GDK_Up:
+      step_sheet (sheet, GTK_SCROLL_STEP_UP);
+      break;
+
+    case GDK_Page_Down:
+      page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
+      break;
+    case GDK_Page_Up:
+      page_vertical (sheet, GTK_SCROLL_PAGE_UP);
+      break;
+
+    case GDK_Home:
+      gtk_adjustment_set_value (sheet->vadjustment,
+                               sheet->vadjustment->lower);
+
+      change_active_cell (sheet,  0,
+                              sheet->active_cell.col);
+
+      break;
+
+    case GDK_End:
+      gtk_adjustment_set_value (sheet->vadjustment,
+                               sheet->vadjustment->upper -
+                               sheet->vadjustment->page_size -
+                               sheet->vadjustment->page_increment);
+
+      /*
+       change_active_cellx (sheet,
+       psppire_axis_unit_count (sheet->vaxis) - 1,
+       sheet->active_cell.col);
+      */
+      break;
+    case GDK_Delete:
+      psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
+      break;
+    default:
+      return FALSE;
+      break;
+    }
+
+  return TRUE;
+}
+
+static void
+psppire_sheet_size_request (GtkWidget *widget,
+                       GtkRequisition *requisition)
+{
+  PsppireSheet *sheet;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (widget));
+  g_return_if_fail (requisition != NULL);
+
+  sheet = PSPPIRE_SHEET (widget);
+
+  requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
+  requisition->height = 3 * DEFAULT_ROW_HEIGHT;
+
+  /* compute the size of the column title area */
+  if (sheet->column_titles_visible)
+    requisition->height += sheet->column_title_area.height;
+
+  /* compute the size of the row title area */
+  if (sheet->row_titles_visible)
+    requisition->width += sheet->row_title_area.width;
+}
+
+
+static void
+psppire_sheet_size_allocate (GtkWidget *widget,
+                        GtkAllocation *allocation)
+{
+  PsppireSheet *sheet;
+  GtkAllocation sheet_allocation;
+  gint border_width;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (widget));
+  g_return_if_fail (allocation != NULL);
+
+  sheet = PSPPIRE_SHEET (widget);
+  widget->allocation = *allocation;
+  border_width = GTK_CONTAINER (widget)->border_width;
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (widget->window,
+                           allocation->x + border_width,
+                           allocation->y + border_width,
+                           allocation->width - 2 * border_width,
+                           allocation->height - 2 * border_width);
+
+  sheet_allocation.x = 0;
+  sheet_allocation.y = 0;
+  sheet_allocation.width = allocation->width - 2 * border_width;
+  sheet_allocation.height = allocation->height - 2 * border_width;
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (sheet->sheet_window,
+                           sheet_allocation.x,
+                           sheet_allocation.y,
+                           sheet_allocation.width,
+                           sheet_allocation.height);
+
+  /* position the window which holds the column title buttons */
+  sheet->column_title_area.x = 0;
+  sheet->column_title_area.y = 0;
+  sheet->column_title_area.width = sheet_allocation.width ;
+
+
+  /* position the window which holds the row title buttons */
+  sheet->row_title_area.x = 0;
+  sheet->row_title_area.y = 0;
+  sheet->row_title_area.height = sheet_allocation.height;
+
+  if (sheet->row_titles_visible)
+    sheet->column_title_area.x += sheet->row_title_area.width;
+
+  if (sheet->column_titles_visible)
+    sheet->row_title_area.y += sheet->column_title_area.height;
+
+  if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
+    gdk_window_move_resize (sheet->column_title_window,
+                           sheet->column_title_area.x,
+                           sheet->column_title_area.y,
+                           sheet->column_title_area.width,
+                           sheet->column_title_area.height);
+
+
+  if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
+    gdk_window_move_resize (sheet->row_title_window,
+                           sheet->row_title_area.x,
+                           sheet->row_title_area.y,
+                           sheet->row_title_area.width,
+                           sheet->row_title_area.height);
+
+  size_allocate_global_button (sheet);
+
+  if (sheet->haxis)
+    {
+      gint width = sheet->column_title_area.width;
+
+      if ( sheet->row_titles_visible)
+       width -= sheet->row_title_area.width;
+
+      g_object_set (sheet->haxis,
+                   "minimum-extent", width,
+                   NULL);
+    }
+
+
+  if (sheet->vaxis)
+    {
+      gint height = sheet->row_title_area.height;
+
+      if ( sheet->column_titles_visible)
+       height -= sheet->column_title_area.height;
+
+      g_object_set (sheet->vaxis,
+                   "minimum-extent", height,
+                   NULL);
+    }
+
+
+  /* set the scrollbars adjustments */
+  adjust_scrollbars (sheet);
+}
+
+static void
+draw_column_title_buttons (PsppireSheet *sheet)
+{
+  gint x, width;
+
+  if (!sheet->column_titles_visible) return;
+  if (!GTK_WIDGET_REALIZED (sheet))
+    return;
+
+  gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
+  x = 0;
+
+  if (sheet->row_titles_visible)
+    {
+      x = sheet->row_title_area.width;
+    }
+
+  if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
+    {
+      sheet->column_title_area.width = width;
+      sheet->column_title_area.x = x;
+      gdk_window_move_resize (sheet->column_title_window,
+                             sheet->column_title_area.x,
+                             sheet->column_title_area.y,
+                             sheet->column_title_area.width,
+                             sheet->column_title_area.height);
+    }
+
+  if (max_visible_column (sheet) ==
+      psppire_axis_unit_count (sheet->haxis) - 1)
+    gdk_window_clear_area (sheet->column_title_window,
+                          0, 0,
+                          sheet->column_title_area.width,
+                          sheet->column_title_area.height);
+
+  if (!GTK_WIDGET_DRAWABLE (sheet)) return;
+
+  draw_column_title_buttons_range (sheet, min_visible_column (sheet), 
+                                  max_visible_column (sheet));
+}
+
+static void
+draw_row_title_buttons (PsppireSheet *sheet)
+{
+  gint y = 0;
+  gint height;
+
+  if (!sheet->row_titles_visible) return;
+  if (!GTK_WIDGET_REALIZED (sheet))
+    return;
+
+  gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
+
+  if (sheet->column_titles_visible)
+    {
+      y = sheet->column_title_area.height;
+    }
+
+  if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
+    {
+      sheet->row_title_area.y = y;
+      sheet->row_title_area.height = height;
+      gdk_window_move_resize (sheet->row_title_window,
+                             sheet->row_title_area.x,
+                             sheet->row_title_area.y,
+                             sheet->row_title_area.width,
+                             sheet->row_title_area.height);
+    }
+
+  if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
+    gdk_window_clear_area (sheet->row_title_window,
+                          0, 0,
+                          sheet->row_title_area.width,
+                          sheet->row_title_area.height);
+
+  if (!GTK_WIDGET_DRAWABLE (sheet)) return;
+
+  draw_row_title_buttons_range (sheet, min_visible_row (sheet),
+                               max_visible_row (sheet));
+}
+
+
+static void
+psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
+{
+  GtkAllocation entry_alloc;
+  PsppireSheetCellAttr attributes = { 0 };
+  GtkEntry *sheet_entry;
+
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
+  if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
+
+  sheet_entry = psppire_sheet_get_entry (sheet);
+
+  if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
+                                  sheet->active_cell.col,
+                                  &attributes) )
+    return ;
+
+  if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
+    {
+      GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
+
+      style->bg[GTK_STATE_NORMAL] = attributes.background;
+      style->fg[GTK_STATE_NORMAL] = attributes.foreground;
+      style->text[GTK_STATE_NORMAL] = attributes.foreground;
+      style->bg[GTK_STATE_ACTIVE] = attributes.background;
+      style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
+      style->text[GTK_STATE_ACTIVE] = attributes.foreground;
+    }
+
+  rectangle_from_cell (sheet, sheet->active_cell.row,
+                      sheet->active_cell.col, &entry_alloc);
+
+  entry_alloc.width -= BORDER_WIDTH ;
+  entry_alloc.height -= BORDER_WIDTH ;
+  entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
+  entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
+
+
+  gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
+                              entry_alloc.height);
+  gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
+}
+
+
+/* Copy the sheet's font to the entry widget */
+static void
+set_entry_widget_font (PsppireSheet *sheet)
+{
+  GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
+
+  pango_font_description_free (style->font_desc);
+  style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
+
+  gtk_widget_modify_style (sheet->entry_widget, style);
+}
+
+static void
+create_sheet_entry (PsppireSheet *sheet)
+{
+  if (sheet->entry_widget)
+    {
+      gtk_widget_unparent (sheet->entry_widget);
+    }
+
+  sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
+  g_object_ref_sink (sheet->entry_widget);
+
+  gtk_widget_size_request (sheet->entry_widget, NULL);
+
+  if ( GTK_IS_ENTRY (sheet->entry_widget))
+    {
+      g_object_set (sheet->entry_widget,
+                   "has-frame", FALSE,
+                   NULL);
+    }
+
+  if (GTK_WIDGET_REALIZED (sheet))
+    {
+      gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
+      gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
+      gtk_widget_realize (sheet->entry_widget);
+    }
+
+  g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
+                           G_CALLBACK (psppire_sheet_entry_key_press),
+                           sheet);
+
+  set_entry_widget_font (sheet);
+
+  gtk_widget_show (sheet->entry_widget);
+}
+
+
+/* Finds the last child widget that happens to be of type GtkEntry */
+static void
+find_entry (GtkWidget *w, gpointer user_data)
+{
+  GtkWidget **entry = user_data;
+  if ( GTK_IS_ENTRY (w))
+    {
+      *entry = w;
+    }
+}
+
+
+GtkEntry *
+psppire_sheet_get_entry (PsppireSheet *sheet)
+{
+  GtkWidget *w = sheet->entry_widget;
+
+  g_return_val_if_fail (sheet != NULL, NULL);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
+  g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
+
+  while (! GTK_IS_ENTRY (w))
+    {
+      GtkWidget *entry = NULL;
+
+      if (GTK_IS_CONTAINER (w))
+       {
+         gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
+
+         if (NULL == entry)
+           break;
+
+         w = entry;
+       }
+    }
+
+  return GTK_ENTRY (w);
+}
+
+
+static void
+draw_button (PsppireSheet *sheet, GdkWindow *window,
+                      PsppireSheetButton *button, gboolean is_sensitive,
+                      GdkRectangle allocation)
+{
+  GtkShadowType shadow_type;
+  gint text_width = 0, text_height = 0;
+  PangoAlignment align = PANGO_ALIGN_LEFT;
+
+  gboolean rtl ;
+
+  gint state = 0;
+
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (button != NULL);
+
+
+  rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
+
+  gdk_window_clear_area (window,
+                        allocation.x, allocation.y,
+                        allocation.width, allocation.height);
+
+  gtk_widget_ensure_style (sheet->button);
+
+  gtk_paint_box (sheet->button->style, window,
+                GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+                &allocation, GTK_WIDGET (sheet->button),
+                "buttondefault",
+                allocation.x, allocation.y,
+                allocation.width, allocation.height);
+
+  state = button->state;
+  if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
+
+  if (state == GTK_STATE_ACTIVE)
+    shadow_type = GTK_SHADOW_IN;
+  else
+    shadow_type = GTK_SHADOW_OUT;
+
+  if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
+    gtk_paint_box (sheet->button->style, window,
+                  button->state, shadow_type,
+                  &allocation, GTK_WIDGET (sheet->button),
+                  "button",
+                  allocation.x, allocation.y,
+                  allocation.width, allocation.height);
+
+  if ( button->overstruck)
+    {
+      GdkPoint points[2] = {
+       {allocation.x,  allocation.y},
+       {allocation.x + allocation.width,
+        allocation.y + allocation.height}
+      };
+
+      gtk_paint_polygon (sheet->button->style,
+                        window,
+                        button->state,
+                        shadow_type,
+                        NULL,
+                        GTK_WIDGET (sheet),
+                        "button",
+                        points,
+                        2,
+                        TRUE);
+    }
+
+  if (button->label_visible)
+    {
+      text_height = DEFAULT_ROW_HEIGHT -
+       2 * COLUMN_TITLES_HEIGHT;
+
+      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
+                                &allocation);
+      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
+                                &allocation);
+
+      allocation.y += 2 * sheet->button->style->ythickness;
+
+      if (button->label && strlen (button->label) > 0)
+       {
+         PangoRectangle rect;
+         gchar *line = button->label;
+
+         PangoLayout *layout = NULL;
+         gint real_x = allocation.x;
+         gint real_y = allocation.y;
+
+         layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
+         pango_layout_get_extents (layout, NULL, &rect);
+
+         text_width = PANGO_PIXELS (rect.width);
+         switch (button->justification)
+           {
+           case GTK_JUSTIFY_LEFT:
+             real_x = allocation.x + COLUMN_TITLES_HEIGHT;
+             align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+             break;
+           case GTK_JUSTIFY_RIGHT:
+             real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
+             align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
+             break;
+           case GTK_JUSTIFY_CENTER:
+           default:
+             real_x = allocation.x + (allocation.width - text_width)/2;
+             align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+             pango_layout_set_justify (layout, TRUE);
+           }
+         pango_layout_set_alignment (layout, align);
+         gtk_paint_layout (GTK_WIDGET (sheet)->style,
+                           window,
+                           state,
+                           FALSE,
+                           &allocation,
+                           GTK_WIDGET (sheet),
+                           "label",
+                           real_x, real_y,
+                           layout);
+         g_object_unref (layout);
+       }
+
+      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
+                                NULL);
+      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
+
+    }
+
+  psppire_sheet_button_free (button);
+}
+
+
+/* Draw the column title buttons FIRST through to LAST */
+static void
+draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
+{
+  GdkRectangle rect;
+  gint col;
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
+
+  if (!sheet->column_titles_visible) return;
+
+  g_return_if_fail (first >= min_visible_column (sheet));
+  g_return_if_fail (last <= max_visible_column (sheet));
+
+  rect.y = 0;
+  rect.height = sheet->column_title_area.height;
+  rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
+  rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
+    + psppire_axis_unit_size (sheet->haxis, last);
+
+  rect.x -= sheet->hadjustment->value;
+
+  minimize_int (&rect.width, sheet->column_title_area.width);
+  maximize_int (&rect.x, 0);
+
+  gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
+
+  for (col = first ; col <= last ; ++col)
+    {
+      GdkRectangle allocation;
+      gboolean is_sensitive = FALSE;
+
+      PsppireSheetButton *
+       button = psppire_sheet_model_get_column_button (sheet->model, col);
+      allocation.y = 0;
+      allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
+       + CELL_SPACING;
+      allocation.x -= sheet->hadjustment->value;
+
+      allocation.height = sheet->column_title_area.height;
+      allocation.width = psppire_axis_unit_size (sheet->haxis, col);
+      is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
+
+      draw_button (sheet, sheet->column_title_window,
+                  button, is_sensitive, allocation);
+    }
+
+  gdk_window_end_paint (sheet->column_title_window);
+}
+
+
+static void
+draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
+{
+  GdkRectangle rect;
+  gint row;
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
+
+  if (!sheet->row_titles_visible) return;
+
+  g_return_if_fail (first >= min_visible_row (sheet));
+  g_return_if_fail (last <= max_visible_row (sheet));
+
+  rect.x = 0;
+  rect.width = sheet->row_title_area.width;
+  rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
+  rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
+    + psppire_axis_unit_size (sheet->vaxis, last);
+
+  rect.y -= sheet->vadjustment->value;
+
+  minimize_int (&rect.height, sheet->row_title_area.height);
+  maximize_int (&rect.y, 0);
+
+  gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
+  for (row = first; row <= last; ++row)
+    {
+      GdkRectangle allocation;
+
+      gboolean is_sensitive = FALSE;
+
+      PsppireSheetButton *button =
+       psppire_sheet_model_get_row_button (sheet->model, row);
+      allocation.x = 0;
+      allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
+       + CELL_SPACING;
+      allocation.y -= sheet->vadjustment->value;
+
+      allocation.width = sheet->row_title_area.width;
+      allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
+      is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
+
+      draw_button (sheet, sheet->row_title_window,
+                  button, is_sensitive, allocation);
+    }
+
+  gdk_window_end_paint (sheet->row_title_window);
+}
+
+/* SCROLLBARS
+ *
+ * functions:
+ * adjust_scrollbars
+ * vadjustment_value_changed
+ * hadjustment_value_changed */
+
+
+static void
+update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
+{
+  double position =
+    (adj->value + adj->page_size)
+    /
+    (adj->upper - adj->lower);
+
+  const glong last_item = psppire_axis_unit_count (axis) - 1;
+
+  if (isnan (position) || position < 0)
+    position = 0;
+
+  adj->upper =
+    psppire_axis_start_pixel (axis, last_item)
+    +
+    psppire_axis_unit_size (axis, last_item)
+    ;
+
+  adj->lower = 0;
+  adj->page_size = page_size;
+
+#if 0
+  adj->value = position * (adj->upper - adj->lower) - adj->page_size;
+
+  if ( adj->value < adj->lower)
+    adj->value = adj->lower;
+#endif
+
+  gtk_adjustment_changed (adj);
+}
+
+
+static void
+adjust_scrollbars (PsppireSheet *sheet)
+{
+  gint width, height;
+
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  gdk_drawable_get_size (sheet->sheet_window, &width, &height);
+
+  if ( sheet->row_titles_visible)
+    width -= sheet->row_title_area.width;
+
+  if (sheet->column_titles_visible)
+    height -= sheet->column_title_area.height;
+
+  if (sheet->vadjustment)
+    {
+      glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
+
+      sheet->vadjustment->step_increment =
+       ROWS_PER_STEP *
+       psppire_axis_unit_size (sheet->vaxis, last_row);
+
+      sheet->vadjustment->page_increment =
+       height -
+       sheet->column_title_area.height -
+       psppire_axis_unit_size (sheet->vaxis, last_row);
+
+      update_adjustment (sheet->vadjustment, sheet->vaxis, height);
+    }
+
+  if (sheet->hadjustment)
+    {
+      gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
+      sheet->hadjustment->step_increment = 1;
+
+      sheet->hadjustment->page_increment = width;
+
+      sheet->hadjustment->upper =
+       psppire_axis_start_pixel (sheet->haxis, last_col)
+       +
+       psppire_axis_unit_size (sheet->haxis, last_col)
+       ;
+
+      update_adjustment (sheet->hadjustment, sheet->haxis, width);
+    }
+}
+
+/* Subtracts the region of WIDGET from REGION */
+static void
+subtract_widget_region (GdkRegion *region, GtkWidget *widget)
+{
+  GdkRectangle rect;
+  GdkRectangle intersect;
+  GdkRegion *region2;
+
+  gdk_region_get_clipbox (region, &rect);
+  gtk_widget_intersect (widget,
+                       &rect,
+                       &intersect);
+
+  region2 = gdk_region_rectangle (&intersect);
+  gdk_region_subtract (region, region2);
+  gdk_region_destroy (region2);
+}
+
+static void
+vadjustment_value_changed (GtkAdjustment *adjustment,
+                          gpointer data)
+{
+  GdkRegion *region;
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+
+  g_return_if_fail (adjustment != NULL);
+
+  if ( ! GTK_WIDGET_REALIZED (sheet)) return;
+
+  gtk_widget_hide (sheet->entry_widget);
+
+  region =
+    gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
+
+  subtract_widget_region (region, sheet->button);
+  gdk_window_begin_paint_region (sheet->sheet_window, region);
+
+  draw_sheet_region (sheet, region);
+
+  draw_row_title_buttons (sheet);
+  psppire_sheet_draw_active_cell (sheet);
+
+  gdk_window_end_paint (sheet->sheet_window);
+  gdk_region_destroy (region);
+}
+
+
+static void
+hadjustment_value_changed (GtkAdjustment *adjustment,
+                          gpointer data)
+{
+  GdkRegion *region;
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+
+  g_return_if_fail (adjustment != NULL);
+
+  if ( ! GTK_WIDGET_REALIZED (sheet)) return;
+
+  gtk_widget_hide (sheet->entry_widget);
+
+
+  region =
+    gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
+
+  subtract_widget_region (region, sheet->button);
+  gdk_window_begin_paint_region (sheet->sheet_window, region);
+
+  draw_sheet_region (sheet, region);
+
+  draw_column_title_buttons (sheet);
+
+  psppire_sheet_draw_active_cell (sheet);
+
+  gdk_window_end_paint (sheet->sheet_window);
+
+  gdk_region_destroy (region);
+}
+
+
+/* COLUMN RESIZING */
+static void
+draw_xor_vline (PsppireSheet *sheet)
+{
+  gint height;
+  gint xpos = sheet->x_drag;
+  gdk_drawable_get_size (sheet->sheet_window,
+                        NULL, &height);
+
+  if (sheet->row_titles_visible)
+    xpos += sheet->row_title_area.width;
+
+  gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
+                xpos,
+                sheet->column_title_area.height,
+                xpos,
+                height + CELL_SPACING);
+}
+
+/* ROW RESIZING */
+static void
+draw_xor_hline (PsppireSheet *sheet)
+
+{
+  gint width;
+  gint ypos = sheet->y_drag;
+
+  gdk_drawable_get_size (sheet->sheet_window,
+                        &width, NULL);
+
+
+  if (sheet->column_titles_visible)
+    ypos += sheet->column_title_area.height;
+
+  gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
+                sheet->row_title_area.width,
+                ypos,
+                width + CELL_SPACING,
+                ypos);
+}
+
+/* SELECTED RANGE */
+static void
+draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
+{
+  gint i = 0;
+  GdkRectangle clip_area, area;
+  GdkGCValues values;
+
+  area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
+  area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
+  area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
+    psppire_axis_unit_size (sheet->haxis, range.coli);
+  area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
+    psppire_axis_unit_size (sheet->vaxis, range.rowi);
+
+  clip_area.x = sheet->row_title_area.width;
+  clip_area.y = sheet->column_title_area.height;
+
+  gdk_drawable_get_size (sheet->sheet_window,
+                        &clip_area.width, &clip_area.height);
+
+  if (!sheet->row_titles_visible) clip_area.x = 0;
+  if (!sheet->column_titles_visible) clip_area.y = 0;
+
+  if (area.x < 0)
+    {
+      area.width = area.width + area.x;
+      area.x = 0;
+    }
+  if (area.width > clip_area.width) area.width = clip_area.width + 10;
+  if (area.y < 0)
+    {
+      area.height = area.height + area.y;
+      area.y = 0;
+    }
+  if (area.height > clip_area.height) area.height = clip_area.height + 10;
+
+  clip_area.x--;
+  clip_area.y--;
+  clip_area.width += 3;
+  clip_area.height += 3;
+
+  gdk_gc_get_values (sheet->xor_gc, &values);
+
+  gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
+
+  gdk_draw_rectangle (sheet->sheet_window,
+                     sheet->xor_gc,
+                     FALSE,
+                     area.x + i, area.y + i,
+                     area.width - 2 * i, area.height - 2 * i);
+
+
+  gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
+
+  gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
+}
+
+
+static void
+set_column_width (PsppireSheet *sheet,
+                 gint column,
+                 gint width)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
+    return;
+
+  if ( width <= 0)
+    return;
+
+  psppire_axis_resize (sheet->haxis, column,
+                      width - sheet->cell_padding->left -
+                      sheet->cell_padding->right);
+
+  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    {
+      draw_column_title_buttons (sheet);
+      adjust_scrollbars (sheet);
+      psppire_sheet_size_allocate_entry (sheet);
+      redraw_range (sheet, NULL);
+    }
+}
+
+static void
+set_row_height (PsppireSheet *sheet,
+               gint row,
+               gint height)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
+    return;
+
+  if (height <= 0)
+    return;
+
+  psppire_axis_resize (sheet->vaxis, row,
+                      height - sheet->cell_padding->top -
+                      sheet->cell_padding->bottom);
+
+  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
+    {
+      draw_row_title_buttons (sheet);
+      adjust_scrollbars (sheet);
+      psppire_sheet_size_allocate_entry (sheet);
+      redraw_range (sheet, NULL);
+    }
+}
+
+static gboolean
+psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
+                         PsppireSheetCellAttr *attr)
+{
+  GdkColor *fg, *bg;
+  const GtkJustification *j ;
+  GdkColormap *colormap;
+
+  g_return_val_if_fail (sheet != NULL, FALSE);
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
+
+  if (row < 0 || col < 0) return FALSE;
+
+  attr->foreground = GTK_WIDGET (sheet)->style->black;
+  attr->background = sheet->color[BG_COLOR];
+
+  attr->border.width = 0;
+  attr->border.line_style = GDK_LINE_SOLID;
+  attr->border.cap_style = GDK_CAP_NOT_LAST;
+  attr->border.join_style = GDK_JOIN_MITER;
+  attr->border.mask = 0;
+  attr->border.color = GTK_WIDGET (sheet)->style->black;
+
+  colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
+  fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
+  if ( fg )
+    {
+      gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
+      attr->foreground = *fg;
+    }
+
+  bg = psppire_sheet_model_get_background (sheet->model, row, col);
+  if ( bg )
+    {
+      gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
+      attr->background = *bg;
+    }
+
+  attr->justification =
+    psppire_sheet_model_get_column_justification (sheet->model, col);
+
+  j = psppire_sheet_model_get_justification (sheet->model, row, col);
+  if (j)
+    attr->justification = *j;
+
+  return TRUE;
+}
+
+static void
+psppire_sheet_button_size_request       (PsppireSheet *sheet,
+                                 const PsppireSheetButton *button,
+                                 GtkRequisition *button_requisition)
+{
+  GtkRequisition requisition;
+  GtkRequisition label_requisition;
+
+  label_requisition.height = DEFAULT_ROW_HEIGHT;
+  label_requisition.width = COLUMN_MIN_WIDTH;
+
+  requisition.height = DEFAULT_ROW_HEIGHT;
+  requisition.width = COLUMN_MIN_WIDTH;
+
+
+  *button_requisition = requisition;
+  button_requisition->width = MAX (requisition.width, label_requisition.width);
+  button_requisition->height = MAX (requisition.height, label_requisition.height);
+
+}
+
+static void
+psppire_sheet_forall (GtkContainer *container,
+                 gboolean include_internals,
+                 GtkCallback callback,
+                 gpointer callback_data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (container);
+
+  g_return_if_fail (callback != NULL);
+
+  if (sheet->button && sheet->button->parent)
+    (* callback) (sheet->button, callback_data);
+
+  if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
+    (* callback) (sheet->entry_widget, callback_data);
+}
+
+
+PsppireSheetModel *
+psppire_sheet_get_model (const PsppireSheet *sheet)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
+
+  return sheet->model;
+}
+
+
+PsppireSheetButton *
+psppire_sheet_button_new (void)
+{
+  PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
+
+  button->state = GTK_STATE_NORMAL;
+  button->label = NULL;
+  button->label_visible = TRUE;
+  button->justification = GTK_JUSTIFY_FILL;
+  button->overstruck = FALSE;
+
+  return button;
+}
+
+
+void
+psppire_sheet_button_free (PsppireSheetButton *button)
+{
+  if (!button) return ;
+
+  g_free (button->label);
+  g_free (button);
+}
+
+static void
+append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
+{
+  gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
+
+  if ( NULL == celltext)
+    return;
+
+  g_string_append (string, celltext);
+  g_free (celltext);
+}
+
+
+static GString *
+range_to_text (const PsppireSheet *sheet)
+{
+  gint r, c;
+  GString *string;
+
+  if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
+    return NULL;
+
+  string = g_string_sized_new (80);
+
+  for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
+    {
+      for (c = sheet->range.col0; c < sheet->range.coli; ++c)
+       {
+         append_cell_text (string, sheet, r, c);
+         g_string_append (string, "\t");
+       }
+      append_cell_text (string, sheet, r, c);
+      if ( r < sheet->range.rowi)
+       g_string_append (string, "\n");
+    }
+
+  return string;
+}
+
+static GString *
+range_to_html (const PsppireSheet *sheet)
+{
+  gint r, c;
+  GString *string;
+
+  if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
+    return NULL;
+
+  string = g_string_sized_new (480);
+
+  g_string_append (string, "<html>\n");
+  g_string_append (string, "<body>\n");
+  g_string_append (string, "<table>\n");
+  for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
+    {
+      g_string_append (string, "<tr>\n");
+      for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
+       {
+         g_string_append (string, "<td>");
+         append_cell_text (string, sheet, r, c);
+         g_string_append (string, "</td>\n");
+       }
+      g_string_append (string, "</tr>\n");
+    }
+  g_string_append (string, "</table>\n");
+  g_string_append (string, "</body>\n");
+  g_string_append (string, "</html>\n");
+
+  return string;
+}
+
+enum {
+  SELECT_FMT_NULL,
+  SELECT_FMT_TEXT,
+  SELECT_FMT_HTML
+};
+
+static void
+primary_get_cb (GtkClipboard     *clipboard,
+               GtkSelectionData *selection_data,
+               guint             info,
+               gpointer          data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+  GString *string = NULL;
+
+  switch (info)
+    {
+    case SELECT_FMT_TEXT:
+      string = range_to_text (sheet);
+      break;
+    case SELECT_FMT_HTML:
+      string = range_to_html (sheet);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  gtk_selection_data_set (selection_data, selection_data->target,
+                         8,
+                         (const guchar *) string->str, string->len);
+  g_string_free (string, TRUE);
+}
+
+static void
+primary_clear_cb (GtkClipboard *clipboard,
+                 gpointer      data)
+{
+  PsppireSheet *sheet = PSPPIRE_SHEET (data);
+  if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
+    return;
+
+  psppire_sheet_real_unselect_range (sheet, NULL);
+}
+
+static void
+psppire_sheet_update_primary_selection (PsppireSheet *sheet)
+{
+  static const GtkTargetEntry targets[] = {
+    { "UTF8_STRING",   0, SELECT_FMT_TEXT },
+    { "STRING",        0, SELECT_FMT_TEXT },
+    { "TEXT",          0, SELECT_FMT_TEXT },
+    { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
+    { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
+    { "text/plain",    0, SELECT_FMT_TEXT },
+    { "text/html",     0, SELECT_FMT_HTML }
+  };
+
+  GtkClipboard *clipboard;
+
+  if (!GTK_WIDGET_REALIZED (sheet))
+    return;
+
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
+                                       GDK_SELECTION_PRIMARY);
+
+  if (psppire_sheet_range_isvisible (sheet, &sheet->range))
+    {
+      if (!gtk_clipboard_set_with_owner (clipboard, targets,
+                                        G_N_ELEMENTS (targets),
+                                        primary_get_cb, primary_clear_cb,
+                                        G_OBJECT (sheet)))
+       primary_clear_cb (clipboard, sheet);
+    }
+  else
+    {
+      if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
+       gtk_clipboard_clear (clipboard);
+    }
+}
diff --git a/lib/gtk-contrib/psppire-sheet.h b/lib/gtk-contrib/psppire-sheet.h
new file mode 100644 (file)
index 0000000..27c2ed5
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+   Copyright (C) 2006, 2008 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
+   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/>.
+
+
+ This file is derived from the gtksheet.c and extensively modified for the
+ requirements of PSPPIRE.  The changes are copyright by the
+ Free Software Foundation.  The copyright notice for the original work is
+ below.
+
+
+ GtkSheet widget for Gtk+.
+ * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
+ *
+ * Based on GtkClist widget by Jay Painter, but major changes.
+ * Memory allocation routines inspired on SC (Spreadsheet Calculator)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __PSPPIRE_SHEET_H__
+#define __PSPPIRE_SHEET_H__
+
+#include <gtk/gtk.h>
+
+#include "gtkextra-sheet.h"
+#include <ui/gui/sheet/psppire-sheetmodel.h>
+#include <ui/gui/sheet/psppire-axis.h>
+
+G_BEGIN_DECLS
+
+/* sheet->state */
+enum
+{
+  PSPPIRE_SHEET_NORMAL,
+  PSPPIRE_SHEET_ROW_SELECTED,
+  PSPPIRE_SHEET_COLUMN_SELECTED,
+  PSPPIRE_SHEET_RANGE_SELECTED
+};
+
+
+#define PSPPIRE_TYPE_SHEET_RANGE (psppire_sheet_range_get_type ())
+#define PSPPIRE_TYPE_SHEET_CELL (psppire_sheet_cell_get_type ())
+#define PSPPIRE_TYPE_SHEET (psppire_sheet_get_type ())
+
+#define PSPPIRE_SHEET(obj)          GTK_CHECK_CAST (obj, psppire_sheet_get_type (), PsppireSheet)
+#define PSPPIRE_SHEET_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, psppire_sheet_get_type (), PsppireSheetClass)
+#define PSPPIRE_IS_SHEET(obj)       GTK_CHECK_TYPE (obj, psppire_sheet_get_type ())
+
+
+typedef struct _PsppireSheetClass PsppireSheetClass;
+typedef struct _PsppireSheetCellAttr     PsppireSheetCellAttr;
+
+typedef struct _PsppireSheetHoverTitle PsppireSheetHoverTitle;
+
+
+struct _PsppireSheetCellAttr
+{
+  GtkJustification justification;
+  GdkColor foreground;
+  GdkColor background;
+  PsppireSheetCellBorder border;
+};
+
+struct _PsppireSheetHoverTitle
+{
+  GtkWidget *window;
+  GtkWidget *label;
+  gint row, column;
+};
+
+enum
+  {
+    BG_COLOR,
+    GRID_COLOR,
+    n_COLORS
+  };
+
+struct _PsppireSheet
+{
+  GtkBin parent;
+
+  gboolean dispose_has_run;
+  PsppireAxis *haxis;
+  PsppireAxis *vaxis;
+
+  guint16 flags;
+
+  PsppireSheetModel *model;
+
+  GtkSelectionMode selection_mode;
+
+  /* Component colors */
+  GdkColor color[n_COLORS];
+  gboolean show_grid;
+
+  /* active cell */
+  PsppireSheetCell active_cell;
+
+  /* The GtkEntry used for editing the cells */
+  GtkWidget *entry_widget;
+
+  /* The type of entry_widget */
+  GtkType entry_type;
+
+  /* expanding selection */
+  PsppireSheetCell selection_cell;
+
+  /* global selection button */
+  GtkWidget *button;
+
+  /* sheet state */
+  gint state;
+
+  /* selected range */
+  PsppireSheetRange range;
+
+  /* The space between a cell's contents and its border */
+  GtkBorder *cell_padding;
+
+  /* the scrolling window and its height and width to
+   * make things a little speedier */
+  GdkWindow *sheet_window;
+
+  /* border shadow style */
+  GtkShadowType shadow_type;
+
+  /* Column Titles */
+  GdkRectangle column_title_area;
+  GdkWindow *column_title_window;
+  gboolean column_titles_visible;
+  /* TRUE if the cursor is over the column title window */
+  gboolean column_title_under;
+
+  /* Row Titles */
+  GdkRectangle row_title_area;
+  GdkWindow *row_title_window;
+  gboolean row_titles_visible;
+  /* TRUE if the cursor is over the row title window */
+  gboolean row_title_under;
+
+  /*scrollbars*/
+  GtkAdjustment *hadjustment;
+  GtkAdjustment *vadjustment;
+
+  /* xor GC for the verticle drag line */
+  GdkGC *xor_gc;
+
+  /* gc for drawing unselected cells */
+  GdkGC *fg_gc;
+  GdkGC *bg_gc;
+
+  /* cursor used to indicate dragging */
+  GdkCursor *cursor_drag;
+
+  /* the current x-pixel location of the xor-drag vline */
+  gint x_drag;
+
+  /* the current y-pixel location of the xor-drag hline */
+  gint y_drag;
+
+  /* current cell being dragged */
+  PsppireSheetCell drag_cell;
+  /* current range being dragged */
+  PsppireSheetRange drag_range;
+
+  /* Used for the subtitle (popups) */
+  gint motion_timer;
+  PsppireSheetHoverTitle *hover_window;
+
+  gulong update_handler_id;
+};
+
+struct _PsppireSheetClass
+{
+  GtkBinClass parent_class;
+
+ gboolean (*set_scroll_adjustments) (PsppireSheet *sheet,
+                                GtkAdjustment *hadjustment,
+                                GtkAdjustment *vadjustment);
+
+ void (*select_row)            (PsppireSheet *sheet, gint row);
+
+ void (*select_column)                 (PsppireSheet *sheet, gint column);
+
+ void (*select_range)          (PsppireSheet *sheet, PsppireSheetRange *range);
+
+ void (*resize_range)          (PsppireSheet *sheet,
+                               PsppireSheetRange *old_range,
+                               PsppireSheetRange *new_range);
+
+ void (*move_range)                    (PsppireSheet *sheet,
+                               PsppireSheetRange *old_range,
+                               PsppireSheetRange *new_range);
+
+ gboolean (*traverse)          (PsppireSheet *sheet,
+                               gint row, gint column,
+                               gint *new_row, gint *new_column);
+
+ gboolean (*activate)          (PsppireSheet *sheet,
+                               gint row, gint column);
+
+ void (*changed)               (PsppireSheet *sheet,
+                               gint row, gint column);
+};
+
+GType psppire_sheet_get_type (void);
+GtkType psppire_sheet_range_get_type (void);
+
+
+/* create a new sheet */
+GtkWidget * psppire_sheet_new (PsppireSheetModel *model);
+
+/* create a new sheet with custom entry */
+GtkWidget *
+psppire_sheet_new_with_custom_entry (GtkType entry_type);
+
+/* Change entry */
+void psppire_sheet_change_entry                (PsppireSheet *sheet, GtkType entry_type);
+
+GtkEntry *psppire_sheet_get_entry    (PsppireSheet *sheet);
+
+
+void psppire_sheet_get_selected_range (PsppireSheet *sheet,
+                                        PsppireSheetRange *range);
+
+void psppire_sheet_show_grid     (PsppireSheet *sheet,
+                                        gboolean show);
+
+gboolean psppire_sheet_grid_visible   (PsppireSheet *sheet);
+
+
+/* scroll the viewing area of the sheet to the given column
+ * and row; row_align and col_align are between 0-1 representing the
+ * location the row should appear on the screen, 0.0 being top or left,
+ * 1.0 being bottom or right; if row or column is negative then there
+ * is no change */
+void psppire_sheet_moveto (PsppireSheet *sheet,
+                 gint row,
+                 gint column,
+                 gfloat row_align,
+                  gfloat col_align);
+
+
+void psppire_sheet_show_row_titles             (PsppireSheet *sheet);
+void psppire_sheet_hide_row_titles             (PsppireSheet *sheet);
+void psppire_sheet_show_column_titles       (PsppireSheet *sheet);
+void psppire_sheet_hide_column_titles  (PsppireSheet *sheet);
+
+/* select the row. The range is then highlighted, and the bounds are stored
+ * in sheet->range  */
+void psppire_sheet_select_row    (PsppireSheet * sheet,  gint row);
+
+/* select the column. The range is then highlighted, and the bounds are stored
+ * in sheet->range  */
+void psppire_sheet_select_column (PsppireSheet * sheet,  gint column);
+
+/* highlight the selected range and store bounds in sheet->range */
+void psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range);
+
+void psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range);
+
+
+/* obvious */
+void psppire_sheet_unselect_range              (PsppireSheet *sheet);
+
+/* set active cell where the entry will be displayed */
+void psppire_sheet_set_active_cell (PsppireSheet *sheet,
+                               gint row, gint column);
+
+/* Sets *ROW and *COLUMN to be the coordinates of the active cell.
+   ROW and/or COLUMN may be null if the caller is not interested in their
+   values */
+void psppire_sheet_get_active_cell (PsppireSheet *sheet,
+                                       gint *row, gint *column);
+
+/* get cell contents */
+gchar *psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col);
+
+
+void psppire_sheet_set_model (PsppireSheet *sheet,
+                                  PsppireSheetModel *model);
+
+PsppireSheetModel * psppire_sheet_get_model (const PsppireSheet *sheet);
+
+
+G_END_DECLS
+
+
+#endif /* __PSPPIRE_SHEET_H__ */
+
+
diff --git a/lib/gtksheet/COPYING.LESSER b/lib/gtksheet/COPYING.LESSER
deleted file mode 100644 (file)
index 8add30a..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-                 GNU LESSER GENERAL PUBLIC LICENSE
-                      Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-\f
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-\f
-                 GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-  
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-\f
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-\f
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-\f
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-\f
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-\f
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-\f
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-                           NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-                    END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library 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
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
-
-
diff --git a/lib/gtksheet/OChangeLog b/lib/gtksheet/OChangeLog
deleted file mode 100644 (file)
index b5ba1c8..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-2008-05-08  Ben Pfaff  <blp@gnu.org>
-
-       Patch #6506.  Reviewed by John Darrington.
-
-       * gtksheet.c (gtk_sheet_unrealize): Don't call gtk_widget_unparent
-       on sheet->button if it's null.
-
-2008-05-06  Ben Pfaff  <blp@gnu.org>
-
-       * gtksheet.c (gtk_sheet_dispose): Set the sheet's entry_container
-       and button members to NULL after unref'ing them, so that a later
-       call to gtk_sheet_for_all will not try to dereference a dangling
-       pointer.
-
-2008-03-06 John Darrington <john@darrington.wattle.id.au>
-
-       * gsheet-row-iface.c gsheet-row-iface.h: Delete unused, unneccesary
-       gpointer variable from the interface.
-
-       * gtksheet.c: Update to match new gsheet-row-iface
-
-2008-02-27 John Darrington <john@darrington.wattle.id.au>
-       * gtksheet.c gtksheet.h: Corrected some leaks and other problems
-       related to de-allocating the sheet.
-
-2008-02-27 John Darrington <john@darrington.wattle.id.au>
-       * gtksheet.c: (gtk_sheet_expose) Don't queue a redraw on the entry
-       widget.  Fixes bug #21073
-
-2008-02-20 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c gtksheet.h: Removed some unused signals.
-       Made the models properties of the widget.
-
-2008-02-08 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c: Removed the sheet_locked feature, which we never
-       used, and interfered with the editability of the entry widget.
-
-       * gtksheet.c: Add one to the row to which we scroll. Seems like
-       the best way to cope with granularity problems.
-       
-21 Septempber 2007 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c (range_update_callback): Scroll to cell 0,0 if the
-       current position is outside the model's range.
-
-24 July 2007 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c gtksheet.h: Removed the `clip' feature, which IMO 
-       is a croc, and we're unlikely to use.  In its place, added a primary 
-       selection which supports text and html targets.
-
-16 July 2007 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c gtksheet.h: Removed some legacy functions called from 
-       gtk_sheet_finalize which caused unnecessary delays when shutting down.
-
-12 July 2007 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c gtksheet.h: Removed view member and replaced with 
-        function call.  Removed hadjustment_changed and vadjustment_changed 
-        functions which did nothing.  Added some whitespace arount != 
-        operators.
-
-09 July 2007 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c gtksheet.h (gtk_sheet_get_active_cell): Allowed row,
-       column  to be NULL.
-
-07 July 2007 John Darrington <john@darrington.wattle.id.au>
-        
-       * gsheet-column-iface.c gsheet-column-iface.h gsheet-row-iface.c
-       gsheet-row-iface.h gtksheet.c gtksheet.h: Added a "subtitle"
-       feature on row/column titles, which shows tooltip-like popups.  
-
-03 July 2007 John Darrington <john@darrington.wattle.id.au>
-
-       * gtksheet.c gtksheet.h: Removed the autoscroll-on-select feature 
-       that was causing us grief.
-
-28 June 2007 John Darrington <john@darrington.wattle.id.au>
-
-        * gtksheet.c: Removed some features that we dont use, to get better 
-       speed.
-
-Sat Feb 17 17:36:56 2007  Ben Pfaff  <blp@gnu.org>
-
-       * gsheet-column-iface.c gsheet-hetero-column.c gsheet-row-iface.c
-       gsheet-uniform-column.c gsheet-uniform-row.c gsheetmodel.c
-       gtkextra-marshal.c gtkextra.c gtkiconlist.c gtkitementry.c
-       gtksheet.c: Add "#include <config.h>".
-
-Mon Jun 19 18:03:21 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-       * gsheet-column-iface.c gsheet-column-iface.h
-       gsheet-hetero-column.c gsheet-row-iface.c gsheet-row-iface.h
-       gsheet-uniform-column.c gsheet-uniform-row.c gtksheet.c
-       gtksheet.h:  Fixed some warnings.  Corrected errors updating
-               row/column titles
-       
-Di Mai 30 19:51:19 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gtksheet.c gtksheet.h: constness. Removed dependence on glib2.10
-
-Sat May 27 16:29:36 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gtksheet.c: Removed call to gtk_entry_set_text, which caused warnings 
-       and was unnecessary.
-
-Thu May 25 17:58:51 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gsheet-column-iface.c gsheet-column-iface.h gsheet-hetero-column.c
-    gsheet-row-iface.c gsheet-row-iface.h gsheet-uniform-row.c
-    gtksheet-extra.h gtksheet.c:  Plugged memory leaks.  Rationalised the way
-    that GtkSheetButtons are created.
-
-Sat May 20 21:02:03 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gsheetmodel.c gsheetmodel.h: Added columns-inserted and columns-deleted 
-    signals.  Added g_sheet_get_{row,column}_count functions.
-
-    * gtksheet.c gtksheet.h: Allowed -1 to be passed to
-    gtk_sheet_set_active_cell to indicate no active cell.
-
-Mon May 15 16:10:49 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gtksheet.c: Removed code which rendered the title buttons a second 
-    time.  Cut and Paste error ?
-
-Sat May 13 07:58:32 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-        * gsheetmodel.c gsheetmodel.h gtksheet.c gtksheeet.h: Added
-       free_strings flag to tell the sheet whether to free the string
-       data passed from the model.
-
-Thu May 11 22:20:04 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gtksheet.c, gtksheet.h: Fixed broken deallocation of sheet->pixmap.
-
-Thu May  4 17:55:48 WST 2006 John Darrington <john@darrington.wattle.id.au>
-
-    * gtksheet.c: Added callback on inserted rows.
-
-Sat Jan 28 08:48:08 2006 UTC John Darrington <john@darrington.wattle.id.au>
-
-    * Separated the data out of the GtkSheet.  The gtksheet should now be
-    regarded as a way of looking into the data.  The data is represented by a
-    GSheetModel and the rows and columns by  GSheetRow and GSheetColumn.
diff --git a/lib/gtksheet/README b/lib/gtksheet/README
deleted file mode 100644 (file)
index ebfce1d..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-This is not part of the GNU PSPP program, but is used with GNU PSPP.
-
-This directory contains a version of the GtkSheet widget from the gtk-extra 
-project at http://gtkextra.sourceforge.net  The version found here has 
-major modifications developed for the needs of PSPP.  Every effort has been
-made to keep GtkSheet application independent. Thus, it should be possible
-to use this modified software for other applications.  However, the API is
-substantially different from the original.
-
-Files in this directory ONLY are licensed under the GNU Lesser General Public 
-License.  See COPYING.LGPL
diff --git a/lib/gtksheet/automake.mk b/lib/gtksheet/automake.mk
deleted file mode 100644 (file)
index babcb2a..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-## Process this file with automake to produce Makefile.in  -*- makefile -*-
-
-noinst_LIBRARIES += lib/gtksheet/libgtksheet.a
-
-lib_gtksheet_libgtksheet_a_CFLAGS = $(GTK_CFLAGS) -Wall
-
-
-lib_gtksheet_libgtksheet_a_SOURCES = \
-       lib/gtksheet/gsheet-column-iface.c \
-       lib/gtksheet/gsheet-column-iface.h \
-       lib/gtksheet/gsheet-hetero-column.c \
-       lib/gtksheet/gsheet-hetero-column.h \
-       lib/gtksheet/gsheetmodel.c \
-       lib/gtksheet/gsheetmodel.h \
-       lib/gtksheet/gsheet-row-iface.c \
-       lib/gtksheet/gsheet-row-iface.h \
-       lib/gtksheet/gsheet-uniform-column.c \
-       lib/gtksheet/gsheet-uniform-column.h \
-       lib/gtksheet/gsheet-uniform-row.c \
-       lib/gtksheet/gsheet-uniform-row.h \
-       lib/gtksheet/gtkextra.c \
-       lib/gtksheet/gtkextrafeatures.h \
-       lib/gtksheet/gtkextra-marshal.c \
-       lib/gtksheet/gtkextra-marshal.h \
-       lib/gtksheet/gtkextra-sheet.h \
-       lib/gtksheet/gtkitementry.h \
-       lib/gtksheet/gtkitementry.c \
-       lib/gtksheet/gtksheet.c \
-       lib/gtksheet/gtksheet.h 
-
-EXTRA_DIST += lib/gtksheet/OChangeLog
diff --git a/lib/gtksheet/gsheet-column-iface.c b/lib/gtksheet/gsheet-column-iface.c
deleted file mode 100644 (file)
index 1cd7996..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-/* GSheetColumn --- an abstract model of the column geometry of a
-   GSheet widget.
-
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <config.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gprintf.h>
-#include <gobject/gvaluecollector.h>
-#include "gsheet-column-iface.h"
-#include "gtkextra-marshal.h"
-#include "gtkextra-sheet.h"
-
-enum {
-  COLUMNS_CHANGED,
-  LAST_SIGNAL
-};
-
-static guint sheet_column_signals[LAST_SIGNAL];
-
-
-
-static void      g_sheet_column_base_init   (gpointer g_class);
-
-
-GType
-g_sheet_column_get_type (void)
-{
-  static GType sheet_column_type = 0;
-
-  if (! sheet_column_type)
-    {
-      static const GTypeInfo sheet_column_info =
-
-      {
-        sizeof (GSheetColumnIface), /* class_size */
-       g_sheet_column_base_init,   /* base_init */
-       NULL,           /* base_finalize */
-       NULL,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       0,
-       0,              /* n_preallocs */
-       NULL
-      };
-
-      sheet_column_type =
-       g_type_register_static (G_TYPE_INTERFACE, "GSheetColumn",
-                               &sheet_column_info, 0);
-
-      g_assert(sheet_column_type);
-
-      g_type_interface_add_prerequisite (sheet_column_type, G_TYPE_OBJECT);
-    }
-
-  return sheet_column_type;
-}
-
-
-static void
-g_sheet_column_base_init (gpointer g_class)
-{
-  static gboolean initialized = FALSE;
-
-  if (! initialized)
-    {
-
-      sheet_column_signals[COLUMNS_CHANGED] =
-       g_signal_new ("columns_changed",
-                     G_TYPE_SHEET_COLUMN,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetColumnIface, columns_changed),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT,
-                     G_TYPE_NONE, 2,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-
-      initialized = TRUE;
-    }
-}
-
-
-void
-g_sheet_column_set_width (GSheetColumn *column, glong col, gint size)
-{
-  g_return_if_fail (G_IS_SHEET_COLUMN (column));
-
-  if ((G_SHEET_COLUMN_GET_IFACE (column)->set_width) )
-    (G_SHEET_COLUMN_GET_IFACE (column)->set_width) (column, col, size);
-}
-
-
-gint
-g_sheet_column_get_width (const GSheetColumn *column, glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), -1);
-
-  g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_width);
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_width) (column, col);
-}
-
-
-
-gboolean
-g_sheet_column_get_visibility(const GSheetColumn *column,
-                                           glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE);
-
-  g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_visibility);
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_visibility) (column,
-                                                                 col);
-
-}
-
-gboolean
-g_sheet_column_get_sensitivity(const GSheetColumn *column,
-                                            glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE);
-
-  g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_sensitivity);
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_sensitivity) (column,
-                                                                  col);
-
-}
-
-
-GtkSheetButton *
-g_sheet_column_get_button(const GSheetColumn *column,
-                             glong col)
-{
-  GtkSheetButton *button = gtk_sheet_button_new();
-
-  GSheetColumnIface *iface = G_SHEET_COLUMN_GET_IFACE (column);
-
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE);
-
-  if ( iface->get_button_label)
-    button->label = iface->get_button_label(column, col);
-
-  return button;
-}
-
-GtkJustification
-g_sheet_column_get_justification(const GSheetColumn *column,
-                                    glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), FALSE);
-
-  g_assert (G_SHEET_COLUMN_GET_IFACE (column)->get_justification);
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_justification) (column, col);
-}
-
-gchar *
-g_sheet_column_get_subtitle (const GSheetColumn *column, glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), NULL);
-
-  if  ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_subtitle)
-    return NULL;
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_subtitle) (column, col);
-}
-
-
-
-gint
-g_sheet_column_get_left_text_column (const GSheetColumn *column,
-                                        glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), -1);
-
-  if  ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_left_text_column)
-    return col;
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_left_text_column) (column, col);
-
-}
-
-gint
-g_sheet_column_get_right_text_column (const GSheetColumn *column,
-                                         glong col)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (column), -1);
-
-  if  ( ! G_SHEET_COLUMN_GET_IFACE (column)->get_right_text_column)
-    return col;
-
-  return (G_SHEET_COLUMN_GET_IFACE (column)->get_right_text_column) (column, col);
-
-}
-
-void
-g_sheet_column_set_left_text_column (const GSheetColumn *column,
-                                        glong col, gint i)
-{
-  g_return_if_fail (G_IS_SHEET_COLUMN (column));
-
-  if  ( G_SHEET_COLUMN_GET_IFACE (column)->set_left_text_column)
-    (G_SHEET_COLUMN_GET_IFACE (column)->set_left_text_column) (column, col, i);
-
-}
-
-
-void
-g_sheet_column_set_right_text_column (const GSheetColumn *column,
-                                         glong col, gint i)
-{
-  g_return_if_fail (G_IS_SHEET_COLUMN (column));
-
-  if  ( G_SHEET_COLUMN_GET_IFACE (column)->set_right_text_column)
-    (G_SHEET_COLUMN_GET_IFACE (column)->set_right_text_column) (column, col, i);
-}
-
-glong
-g_sheet_column_get_column_count(const GSheetColumn *geo)
-{
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (geo), -1);
-
-  g_assert  ( G_SHEET_COLUMN_GET_IFACE (geo)->get_column_count);
-
-  return (G_SHEET_COLUMN_GET_IFACE (geo)->get_column_count) (geo);
-}
-
-gint
-g_sheet_column_start_pixel(const GSheetColumn *geo, glong col)
-{
-  gint i;
-  gint start_pixel = 0;
-
-  g_return_val_if_fail (G_IS_SHEET_COLUMN (geo), -1);
-  g_return_val_if_fail (col <
-                       g_sheet_column_get_column_count(geo),-1);
-
-  for ( i = 0 ; i < col ; ++i )
-    {
-      if ( g_sheet_column_get_visibility(geo, i))
-       start_pixel += g_sheet_column_get_width(geo, i);
-    }
-
-  return start_pixel;
-
-}
-
-
-
-void
-g_sheet_column_columns_changed(GSheetColumn *geo,
-                                glong first, glong n_columns)
-{
-  g_return_if_fail (G_IS_SHEET_COLUMN (geo));
-
-  g_signal_emit (geo, sheet_column_signals[COLUMNS_CHANGED], 0,
-                first, n_columns);
-}
-
-
-
-
diff --git a/lib/gtksheet/gsheet-column-iface.h b/lib/gtksheet/gsheet-column-iface.h
deleted file mode 100644 (file)
index 5503393..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/* GSheetColumn --- an abstract model of the column geometry of a
- * GSheet widget.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef __GSHEET_COLUMN_IFACE_H
-#define __GSHEET_COLUMN_IFACE_H
-
-#include <glib-object.h>
-#include <gdk/gdk.h>
-#include <gtk/gtk.h>
-
-#include "gtkextra-sheet.h"
-
-
-G_BEGIN_DECLS
-
-#define G_TYPE_SHEET_COLUMN            (g_sheet_column_get_type ())
-#define G_SHEET_COLUMN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SHEET_COLUMN, GSheetColumn))
-#define G_IS_SHEET_COLUMN(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SHEET_COLUMN))
-#define G_SHEET_COLUMN_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SHEET_COLUMN, GSheetColumnIface))
-
-
-typedef struct _GSheetColumn        GSheetColumn;
-typedef struct _GSheetColumnIface   GSheetColumnIface;
-struct _GSheetColumnIface
-{
-  GTypeInterface g_iface;
-
-
-  /* Signals */
-  void         (* columns_changed)     (GSheetColumn *geo,
-                                     glong col, glong n_columns);
-
-  /* Virtual Table */
-  gint (* get_width) (const GSheetColumn *gcolumn, glong col);
-  void (* set_width) (GSheetColumn *gcolumn, glong col, gint width);
-
-  gboolean (* get_visibility) (const GSheetColumn *gcolumn, glong col);
-  gboolean (* get_sensitivity) (const GSheetColumn *gcolumn, glong col);
-  const GtkSheetButton * (* get_button) (const GSheetColumn *gcolumn, glong col);
-  GtkJustification (* get_justification) (const GSheetColumn *gcolumn, glong col);
-
-  gint  (*get_left_text_column) (const GSheetColumn *gcolumn,
-                                glong col);
-
-  gint  (*get_right_text_column) (const GSheetColumn *gcolumn,
-                                 glong col);
-
-  void (* set_left_text_column) (const GSheetColumn *gcolumn,
-                                glong col, gint i);
-
-  void (* set_right_text_column) (const GSheetColumn *gcolumn,
-                                 glong col, gint i);
-
-  glong  (* get_column_count) (const GSheetColumn *geo);
-
-
-  GtkStateType  (*get_button_state)(const GSheetColumn *geo, glong col);
-  gchar * (*get_button_label)(const GSheetColumn *geo, glong col);
-  gchar * (*get_subtitle)(const GSheetColumn *geo, glong col);
-
-  gboolean      (*get_button_visibility)(const GSheetColumn *geo,
-                                       glong col);
-  const GtkSheetChild * (*get_button_child)(const GSheetColumn *geo,
-                                          glong col);
-  GtkJustification * (*get_button_justification)(const GSheetColumn *geo,
-                                               glong col);
-};
-
-
-inline GType g_sheet_column_get_type   (void) G_GNUC_CONST;
-
-
-inline gint  g_sheet_column_get_width (const GSheetColumn *gcolumn,
-                                      glong col);
-
-
-inline void  g_sheet_column_set_width (GSheetColumn *gcolumn,
-                                      glong col, gint size);
-
-
-inline gboolean  g_sheet_column_get_visibility (const GSheetColumn *gcolumn,
-                                           glong col);
-
-inline gboolean  g_sheet_column_get_sensitivity (const GSheetColumn *gcolumn,
-                                            glong col);
-
-
-inline GtkSheetButton *g_sheet_column_get_button (const GSheetColumn *gcolumn,
-                                            glong col);
-
-gchar *g_sheet_column_get_subtitle (const GSheetColumn *, glong);
-
-inline GtkJustification g_sheet_column_get_justification (const GSheetColumn *gcolumn, glong col);
-
-
-inline gint  g_sheet_column_get_left_text_column (const GSheetColumn *gcolumn,
-                                       glong col);
-
-inline gint  g_sheet_column_get_right_text_column (const GSheetColumn *gcolumn,
-                                       glong col);
-
-inline void g_sheet_column_set_left_text_column (const GSheetColumn *gcolumn,
-                                       glong col, gint i);
-
-
-inline void g_sheet_column_set_right_text_column (const GSheetColumn *gcolumn,
-                                       glong col, gint i);
-
-
-inline glong  g_sheet_column_get_column_count (const GSheetColumn *geo);
-
-inline gint  g_sheet_column_start_pixel (const GSheetColumn *geo, glong col);
-
-inline void g_sheet_column_columns_changed (GSheetColumn *geo,
-                                          glong first, glong n_columns);
-
-G_END_DECLS
-
-#endif /* __G_SHEET_COLUMN_IFACE_H__ */
diff --git a/lib/gtksheet/gsheet-hetero-column.c b/lib/gtksheet/gsheet-hetero-column.c
deleted file mode 100644 (file)
index ecc06cc..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/* gsheet-hetero-column.c
- * PSPPIRE --- A Graphical User Interface for PSPP
- * Copyright (C) 2006  Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-#include <config.h>
-
-#include "gsheet-column-iface.h"
-#include "gsheet-hetero-column.h"
-#include <string.h>
-
-
-static void  g_sheet_hetero_column_init       (GSheetHeteroColumn      *hg);
-static void  g_sheet_hetero_column_class_init (GSheetHeteroColumnClass *class);
-static void  g_sheet_hetero_column_finalize   (GObject           *object);
-
-static void g_sheet_column_init (GSheetColumnIface *iface);
-
-
-static GObjectClass *parent_class = NULL;
-
-GType
-g_sheet_hetero_column_get_type (void)
-{
-  static GType hetero_column_type = 0;
-
-  if (!hetero_column_type)
-    {
-      static const GTypeInfo hetero_column_info =
-      {
-       sizeof (GSheetHeteroColumnClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-        (GClassInitFunc) g_sheet_hetero_column_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-        sizeof (GSheetHeteroColumn),
-       0,
-        (GInstanceInitFunc) g_sheet_hetero_column_init,
-      };
-
-      static const GInterfaceInfo column_info =
-      {
-       (GInterfaceInitFunc) g_sheet_column_init,
-       NULL,
-       NULL
-      };
-
-      hetero_column_type =
-       g_type_register_static (G_TYPE_OBJECT, "g_sheet_hetero_column",
-                               &hetero_column_info, 0);
-
-      g_type_add_interface_static (hetero_column_type,
-                                  G_TYPE_SHEET_COLUMN,
-                                  &column_info);
-    }
-
-  return hetero_column_type;
-}
-
-
-static GtkSheetButton default_button;
-
-
-
-/**
- * g_sheet_hetero_column_new:
- * @width: The size of columns in this hetero column
- *
- * Return value: a new #g_sheet_hetero_column
- **/
-GObject *
-g_sheet_hetero_column_new (gint default_width, gint n_columns)
-{
-  gint i;
-  GSheetHeteroColumn *hg;
-  GObject *retval;
-
-  retval = g_object_new (G_TYPE_SHEET_HETERO_COLUMN, NULL);
-
-  hg = G_SHEET_HETERO_COLUMN (retval);
-  hg->n_columns = n_columns;
-  hg->default_width = default_width;
-  hg->col = g_new0 (struct GSheetHeteroColumnUnit, n_columns);
-
-  for (i = 0 ; i < hg->n_columns; ++i )
-    {
-      hg->col[i].button = default_button;
-    }
-
-  return retval;
-}
-
-static gint
-g_sheet_hetero_column_get_width (const GSheetColumn *geom, glong i)
-{
-  GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geom);
-
-  g_return_val_if_fail (i < hg->n_columns, -1);
-
-  return hg->col[i].width;
-}
-
-static gboolean
-g_sheet_hetero_column_get_sensitivity (const GSheetColumn *geom, glong u)
-{
-  return TRUE;
-}
-
-
-static gboolean
-g_sheet_hetero_column_get_visibility (const GSheetColumn *geom, glong u)
-{
-  return TRUE;
-}
-
-
-
-static gchar *
-g_sheet_hetero_column_get_button_label (const GSheetColumn *geom, glong u)
-{
-  GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geom);
-
-  return g_locale_to_utf8 (hg->col[u].button.label, -1, 0, 0, 0);
-}
-
-
-static GtkJustification
-g_sheet_hetero_column_get_justification (const GSheetColumn *geom, glong u)
-{
-  return GTK_JUSTIFY_FILL;
-}
-
-
-
-static glong
-g_sheet_hetero_column_get_column_count (const GSheetColumn *geom)
-{
-  GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geom);
-
-  return hg->n_columns;
-}
-
-static void
-g_sheet_hetero_column_class_init (GSheetHeteroColumnClass *class)
-{
-  GObjectClass *object_class;
-
-  parent_class = g_type_class_peek_parent (class);
-  object_class = (GObjectClass*) class;
-
-  object_class->finalize = g_sheet_hetero_column_finalize;
-
-  default_button.label=NULL;
-  default_button.child=NULL;
-  default_button.state=GTK_STATE_NORMAL;
-  default_button.justification=GTK_JUSTIFY_CENTER;
-  default_button.label_visible = TRUE;
-}
-
-
-static void
-g_sheet_hetero_column_init (GSheetHeteroColumn *o)
-{
-}
-
-static void
-g_sheet_hetero_column_finalize (GObject           *object)
-{
-  GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (object);
-
-  g_free (hg->col);
-}
-
-static void
-hetero_column_set_width (GSheetColumn *geo, glong i, gint size)
-{
-  GSheetHeteroColumn *hg = G_SHEET_HETERO_COLUMN (geo);
-
-  g_return_if_fail (i < hg->n_columns);
-
-  hg->col[i].width = size;
-}
-
-
-
-static void
-g_sheet_column_init (GSheetColumnIface *iface)
-{
-  iface->get_width = g_sheet_hetero_column_get_width ;
-  iface->set_width = hetero_column_set_width ;
-  iface->get_sensitivity = g_sheet_hetero_column_get_sensitivity ;
-  iface->get_visibility = g_sheet_hetero_column_get_visibility ;
-  iface->get_justification = g_sheet_hetero_column_get_justification;
-  iface->get_column_count = g_sheet_hetero_column_get_column_count;
-
-  iface->get_button_label = g_sheet_hetero_column_get_button_label;
-}
-
-
-void
-g_sheet_hetero_column_set_button_label (GSheetHeteroColumn *geo,
-                                             glong i, const gchar *label)
-{
-  g_return_if_fail (i < geo->n_columns);
-
-  g_free (geo->col[i].button.label);
-  geo->col[i].button.label = g_malloc (strlen (label) + 1);
-
-  g_stpcpy (geo->col[i].button.label, label);
-}
-
-
-
-
-void
-g_sheet_hetero_column_set_width (GSheetHeteroColumn *geo, glong i, gint size)
-{
-  GSheetColumn *iface = G_SHEET_COLUMN (geo);
-
-  hetero_column_set_width (iface, i, size);
-}
-
-
-
diff --git a/lib/gtksheet/gsheet-hetero-column.h b/lib/gtksheet/gsheet-hetero-column.h
deleted file mode 100644 (file)
index 507bc20..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* GtkSheet widget for Gtk+.
- * Copyright (C) 2006 Free Software Foundation
-
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef __G_SHEET_HETERO_COLUMN_H__
-#define __G_SHEET_HETERO_COLUMN_H__
-
-#include <glib-object.h>
-#include <glib.h>
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-#define G_TYPE_SHEET_HETERO_COLUMN (g_sheet_hetero_column_get_type ())
-
-#define G_SHEET_HETERO_COLUMN(obj)    G_TYPE_CHECK_INSTANCE_CAST (obj, G_TYPE_SHEET_HETERO_COLUMN, GSheetHeteroColumn )
-#define G_SHEET_HETERO_COLUMN_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, g_sheet_hetero_column_get_type (), GSheetHeteroColumnClass)
-#define G_IS_SHEET_HETERO_COLUMN(obj)  G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_SHEET_HETERO_COLUMN)
-
-
-  struct GSheetHeteroColumnUnit
-  {
-    GtkSheetButton button;
-
-    gint width;
-    gboolean is_sensitive;
-    gboolean is_visible;
-  };
-
-
-  struct _GSheetHeteroColumn{
-    GObject parent;
-
-    gint n_columns;
-    gint default_width;
-
-    struct GSheetHeteroColumnUnit *col;
-
-  };
-
-  struct _GSheetHeteroColumnClass
-  {
-    GObjectClass parent_class;
-  };
-
-
-
-
-  /* create a new column */
-  GObject * g_sheet_hetero_column_new (gint default_width, gint n_columns);
-
-  GType g_sheet_hetero_column_get_type (void);
-
-
-  typedef struct _GSheetHeteroColumn GSheetHeteroColumn;
-  typedef struct _GSheetHeteroColumnClass GSheetHeteroColumnClass;
-
-
-  void g_sheet_hetero_column_set_button_label (GSheetHeteroColumn *geo,
-                                               glong i, const gchar *label);
-
-  void g_sheet_hetero_column_set_width (GSheetHeteroColumn *geo,
-                                            glong i, gint size);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* __G_SHEET_HETERO_COLUMN_H__ */
-
-
diff --git a/lib/gtksheet/gsheet-row-iface.c b/lib/gtksheet/gsheet-row-iface.c
deleted file mode 100644 (file)
index 1512737..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-/* GSheetRow --- an abstract model of the row geometry of a
- * GSheet widget.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-#include <config.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gprintf.h>
-#include <gobject/gvaluecollector.h>
-#include "gsheet-row-iface.h"
-#include "gtkextra-marshal.h"
-
-
-enum {
-  ROWS_CHANGED,
-  LAST_SIGNAL
-};
-
-static guint sheet_row_signals[LAST_SIGNAL];
-
-
-
-static void      g_sheet_row_base_init   (gpointer g_class);
-
-
-GType
-g_sheet_row_get_type (void)
-{
-  static GType sheet_row_type = 0;
-
-  if (! sheet_row_type)
-    {
-      static const GTypeInfo sheet_row_info =
-
-      {
-        sizeof (GSheetRowIface), /* class_size */
-       g_sheet_row_base_init,   /* base_init */
-       NULL,           /* base_finalize */
-       NULL,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       0,
-       0,              /* n_preallocs */
-       NULL
-      };
-
-      sheet_row_type =
-       g_type_register_static (G_TYPE_INTERFACE, "GSheetRow",
-                               &sheet_row_info, 0);
-
-      g_type_interface_add_prerequisite (sheet_row_type, G_TYPE_OBJECT);
-    }
-
-  return sheet_row_type;
-}
-
-
-static GtkSheetButton default_button;
-
-static void
-g_sheet_row_base_init (gpointer g_class)
-{
-  static gboolean initialized = FALSE;
-
-  if (! initialized)
-    {
-
-      sheet_row_signals[ROWS_CHANGED] =
-       g_signal_new ("rows_changed",
-                     G_TYPE_SHEET_ROW,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetRowIface, rows_changed),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT,
-                     G_TYPE_NONE, 2,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-
-      default_button.state = GTK_STATE_NORMAL;
-      default_button.label = NULL;
-      default_button.label_visible = TRUE;
-      default_button.child = NULL;
-      default_button.justification = GTK_JUSTIFY_FILL;
-
-      initialized = TRUE;
-    }
-}
-
-void
-g_sheet_row_set_height (GSheetRow *row_geo,
-                       glong row, gint size)
-{
-  g_return_if_fail (G_IS_SHEET_ROW (row_geo));
-
-  if ((G_SHEET_ROW_GET_IFACE (row_geo)->set_height) )
-    (G_SHEET_ROW_GET_IFACE (row_geo)->set_height) (row_geo, row,
-                                                       size);
-}
-
-
-gint
-g_sheet_row_get_height     (const GSheetRow *row_geo,
-                           glong row)
-{
-  g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), -1);
-
-  g_assert (G_SHEET_ROW_GET_IFACE (row_geo)->get_height);
-
-  return (G_SHEET_ROW_GET_IFACE (row_geo)->get_height) (row_geo, row);
-}
-
-
-
-gboolean
-g_sheet_row_get_visibility(const GSheetRow *row_geo,
-                          glong row)
-{
-  g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), FALSE);
-
-  g_assert (G_SHEET_ROW_GET_IFACE (row_geo)->get_visibility);
-
-  return (G_SHEET_ROW_GET_IFACE (row_geo)->get_visibility) (row_geo,
-                                                                 row);
-
-}
-
-gboolean
-g_sheet_row_get_sensitivity(const GSheetRow *row_geo,
-                           glong row)
-{
-  g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), FALSE);
-
-  g_assert (G_SHEET_ROW_GET_IFACE (row_geo)->get_sensitivity);
-
-  return (G_SHEET_ROW_GET_IFACE (row_geo)->get_sensitivity) (row_geo,
-                                                            row);
-
-}
-
-
-GtkSheetButton *
-g_sheet_row_get_button(const GSheetRow *row_geo,
-                      glong row)
-{
-  GtkSheetButton *button  = gtk_sheet_button_new();
-
-  GSheetRowIface *iface = G_SHEET_ROW_GET_IFACE (row_geo);
-
-  g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), FALSE);
-
-  if ( iface->get_button_label)
-    button->label = iface->get_button_label(row_geo, row);
-
-  return button;
-}
-
-gchar *
-g_sheet_row_get_subtitle (const GSheetRow *row_geo, glong row)
-{
-  g_return_val_if_fail (G_IS_SHEET_ROW (row_geo), NULL);
-
-  if ( ! G_SHEET_ROW_GET_IFACE (row_geo)->get_subtitle )
-    return NULL;
-
-  return (G_SHEET_ROW_GET_IFACE (row_geo)->get_subtitle) (row_geo, row);
-}
-
-
-
-
-glong
-g_sheet_row_get_row_count (const GSheetRow *geo)
-{
-  g_return_val_if_fail (G_IS_SHEET_ROW (geo), -1);
-
-  g_assert  ( G_SHEET_ROW_GET_IFACE (geo)->get_row_count);
-
-  return (G_SHEET_ROW_GET_IFACE (geo)->get_row_count) (geo);
-}
-
-/**
- * g_sheet_row_start_pixel:
- * @geo: the row model
- * @row: the row number
- * @sheet: pointer to the sheet
- *
- * Returns the top y pixel for ROW.
- * Instances may override this method in order to achieve time and/or memory
- * optmisation.
- *
- * Returns: the y coordinate of the top of the row.
- */
-
-gint
-g_sheet_row_start_pixel(const GSheetRow *geo, glong row)
-{
-  gint i;
-  gint start_pixel = 0;
-
-  g_return_val_if_fail (G_IS_SHEET_ROW (geo), -1);
-  g_return_val_if_fail (row >= 0, -1);
-  g_return_val_if_fail (row <
-                       g_sheet_row_get_row_count(geo),-1);
-
-  if ( G_SHEET_ROW_GET_IFACE(geo)->top_ypixel)
-    return (G_SHEET_ROW_GET_IFACE(geo)->top_ypixel)(geo, row);
-
-  for ( i = 0 ; i < row ; ++i )
-    {
-      if ( g_sheet_row_get_visibility(geo, i))
-       start_pixel += g_sheet_row_get_height(geo, i);
-    }
-
-  return start_pixel;
-}
-
-
-glong
-g_sheet_row_pixel_to_row (const GSheetRow *geo, gint pixel)
-{
-  gint i, cy;
-  g_return_val_if_fail (G_IS_SHEET_ROW (geo), -1);
-  g_return_val_if_fail (pixel >= 0, -1) ;
-
-  if ( G_SHEET_ROW_GET_IFACE(geo)->pixel_to_row)
-    return (G_SHEET_ROW_GET_IFACE(geo)->pixel_to_row)(geo, pixel);
-
-  cy = 0;
-  for (i = 0; i < g_sheet_row_get_row_count (geo); ++i )
-    {
-      if (pixel >= cy  &&
-         pixel <= (cy + g_sheet_row_get_height (geo, i)) &&
-         g_sheet_row_get_visibility (geo, i))
-       return i;
-
-      if(g_sheet_row_get_visibility (geo, i))
-       cy += g_sheet_row_get_height (geo, i);
-    }
-
-  /* no match */
-  return g_sheet_row_get_row_count (geo) - 1;
-}
-
-
-
-void
-g_sheet_row_rows_deleted (GSheetRow *geo,
-                                glong first, glong n_rows)
-{
-  g_return_if_fail (G_IS_SHEET_ROW (geo));
-
-  g_signal_emit (geo, sheet_row_signals[ROWS_CHANGED], 0,
-                first, n_rows);
-}
diff --git a/lib/gtksheet/gsheet-row-iface.h b/lib/gtksheet/gsheet-row-iface.h
deleted file mode 100644 (file)
index 921f369..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* GSheetRow --- an abstract model of the row geometry of a
- * GSheet widget.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef __G_SHEET_ROW_IFACE_H__
-#define __G_SHEET_ROW_IFACE_H__
-
-#include <glib-object.h>
-#include <gdk/gdk.h>
-#include <gtk/gtk.h>
-
-#include "gtkextra-sheet.h"
-
-
-G_BEGIN_DECLS
-
-#define G_TYPE_SHEET_ROW            (g_sheet_row_get_type ())
-#define G_SHEET_ROW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SHEET_ROW, GSheetRow))
-#define G_IS_SHEET_ROW(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SHEET_ROW))
-#define G_SHEET_ROW_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SHEET_ROW, GSheetRowIface))
-
-
-
-
-typedef struct _GSheetRow        GSheetRow;
-typedef struct _GSheetRowIface   GSheetRowIface;
-
-struct _GSheetRowIface
-{
-  GTypeInterface g_iface;
-
-
-  /* Signals */
-  void         (* rows_changed)     (GSheetRow *geo,
-                                     glong row, glong n_rows);
-
-  /* Virtual Table */
-  gint (* get_height) (const GSheetRow *grow, glong row);
-  void (* set_height) (GSheetRow *grow, glong row, gint height);
-
-  gboolean (* get_visibility) (const GSheetRow *grow, glong row);
-
-  gboolean (* get_sensitivity) (const GSheetRow *grow, glong row);
-
-  const GtkSheetButton * (* get_button) (const GSheetRow *grow, glong row);
-
-  glong  (* get_row_count) (const GSheetRow *geo);
-
-  GtkStateType  (*get_button_state) (const GSheetRow *geo, glong row);
-
-  gchar * (*get_button_label) (const GSheetRow *geo, glong row);
-
-  gchar * (*get_subtitle) (const GSheetRow *geo, glong row);
-
-  gboolean  (*get_button_visibility) (const GSheetRow *geo,
-                                         glong row);
-
-  const GtkSheetChild * (*get_button_child) (const GSheetRow *geo,
-                                            glong row);
-
-  guint (*top_ypixel) (const GSheetRow *geo, glong row);
-  glong (*pixel_to_row) (const GSheetRow *geo, guint pixel);
-};
-
-
-GType g_sheet_row_get_type   (void) G_GNUC_CONST;
-
-
-gint  g_sheet_row_get_height (const GSheetRow *grow,
-                             glong row);
-
-
-void  g_sheet_row_set_height (GSheetRow *grow,
-                             glong row, gint size);
-
-
-gboolean  g_sheet_row_get_visibility (const GSheetRow *grow,
-                                     glong row);
-
-gboolean  g_sheet_row_get_sensitivity (const GSheetRow *grow,
-                                      glong row);
-
-
-GtkSheetButton *g_sheet_row_get_button (const GSheetRow *grow,
-                                       glong row);
-
-
-glong  g_sheet_row_get_row_count (const GSheetRow *geo);
-
-/* Return the top pixel of row ROW */
-gint  g_sheet_row_start_pixel (const GSheetRow *geo, glong row);
-
-
-/* Return the row contained by pixel PIXEL */
-glong  g_sheet_row_pixel_to_row (const GSheetRow *geo, gint pixel);
-
-
-void g_sheet_row_rows_deleted (GSheetRow *geo,
-                                     glong first, glong n_rows);
-
-
-gchar *g_sheet_row_get_subtitle (const GSheetRow *row_geo, glong row);
-
-
-G_END_DECLS
-
-#endif /* __G_SHEET_ROW_IFACE_H__ */
diff --git a/lib/gtksheet/gsheet-uniform-column.c b/lib/gtksheet/gsheet-uniform-column.c
deleted file mode 100644 (file)
index 5093da2..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/* gsheet-uniform-column.c
- *
- * PSPPIRE --- A Graphical User Interface for PSPP
- * Copyright (C) 2006  Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <config.h>
-
-#include "gsheet-column-iface.h"
-#include "gsheet-uniform-column.h"
-
-
-static void  g_sheet_uniform_column_init       (GSheetUniformColumn      *ug);
-static void  g_sheet_uniform_column_class_init (GSheetUniformColumnClass *class);
-static void  g_sheet_uniform_column_finalize   (GObject           *object);
-
-static void g_sheet_column_init (GSheetColumnIface *iface);
-
-
-static GObjectClass *parent_class = NULL;
-
-GType
-g_sheet_uniform_column_get_type (void)
-{
-  static GType uniform_column_type = 0;
-
-  if (!uniform_column_type)
-    {
-      static const GTypeInfo uniform_column_info =
-      {
-       sizeof (GSheetUniformColumnClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-        (GClassInitFunc) g_sheet_uniform_column_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-        sizeof (GSheetUniformColumn),
-       0,
-        (GInstanceInitFunc) g_sheet_uniform_column_init,
-      };
-
-      static const GInterfaceInfo column_info =
-      {
-       (GInterfaceInitFunc) g_sheet_column_init,
-       NULL,
-       NULL
-      };
-
-      uniform_column_type =
-       g_type_register_static (G_TYPE_OBJECT, "g_sheet_uniform_column",
-                               &uniform_column_info, 0);
-
-      g_type_add_interface_static (uniform_column_type,
-                                  G_TYPE_SHEET_COLUMN,
-                                  &column_info);
-    }
-
-  return uniform_column_type;
-}
-
-
-/**
- * g_sheet_uniform_column_new:
- * @width: The size of columns in this uniform column
- *
- * Return value: a new #g_sheet_uniform_column
- **/
-GObject *
-g_sheet_uniform_column_new (gint width, gint n_columns)
-{
-  GSheetUniformColumn *ug;
-  GObject *retval;
-
-  retval = g_object_new (G_TYPE_SHEET_UNIFORM_COLUMN, NULL);
-
-  ug = G_SHEET_UNIFORM_COLUMN(retval);
-  ug->n_columns = n_columns;
-  ug->width = width;
-  ug->is_visible = TRUE;
-  ug->is_sensitive = FALSE;
-
-  return retval;
-}
-
-static gint
-g_sheet_uniform_column_get_width (const GSheetColumn *geom, glong u)
-{
-  GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom);
-
-  return ug->width;
-}
-
-static gboolean
-g_sheet_uniform_column_get_sensitivity (const GSheetColumn *geom, glong u)
-{
-  GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom);
-
-  return ug->is_sensitive;
-}
-
-
-static gboolean
-g_sheet_uniform_column_get_visibility (const GSheetColumn *geom, glong u)
-{
-  GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom);
-
-  return ug->is_visible;
-}
-
-
-static gchar *
-g_sheet_uniform_column_get_button_label (const GSheetColumn *geom, glong u)
-{
-  gchar *label = g_strdup_printf ("%ld", u);
-
-  return label;
-}
-
-
-static GtkJustification
-g_sheet_uniform_column_get_justification (const GSheetColumn *geom, glong u)
-{
-  return GTK_JUSTIFY_FILL;
-}
-
-
-
-static glong
-g_sheet_uniform_column_get_column_count (const GSheetColumn *geom)
-{
-  GSheetUniformColumn *ug = G_SHEET_UNIFORM_COLUMN (geom);
-
-  return ug->n_columns;
-}
-
-static void
-g_sheet_uniform_column_class_init (GSheetUniformColumnClass *class)
-{
-  GObjectClass *object_class;
-
-  parent_class = g_type_class_peek_parent (class);
-  object_class = (GObjectClass*) class;
-
-  object_class->finalize = g_sheet_uniform_column_finalize;
-
-}
-
-
-static void
-g_sheet_uniform_column_init (GSheetUniformColumn *o)
-{
-}
-
-static void
-g_sheet_uniform_column_finalize (GObject *object)
-{
-}
-
-
-static void
-g_sheet_column_init (GSheetColumnIface *iface)
-{
-  iface->get_width = g_sheet_uniform_column_get_width ;
-  iface->get_sensitivity = g_sheet_uniform_column_get_sensitivity ;
-  iface->get_visibility = g_sheet_uniform_column_get_visibility ;
-  iface->get_justification = g_sheet_uniform_column_get_justification;
-  iface->get_column_count = g_sheet_uniform_column_get_column_count;
-  iface->get_button_label = g_sheet_uniform_column_get_button_label;
-}
-
diff --git a/lib/gtksheet/gsheet-uniform-column.h b/lib/gtksheet/gsheet-uniform-column.h
deleted file mode 100644 (file)
index e56037b..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/* GtkSheet widget for Gtk+.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef  __G_SHEET_UNIFORM_COLUMN_H__
-#define  __G_SHEET_UNIFORM_COLUMN_H__
-
-
-#include <glib-object.h>
-#include <glib.h>
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-#define G_TYPE_SHEET_UNIFORM_COLUMN (g_sheet_uniform_column_get_type ())
-
-#define G_SHEET_UNIFORM_COLUMN(obj)    G_TYPE_CHECK_INSTANCE_CAST (obj, G_TYPE_SHEET_UNIFORM_COLUMN, GSheetUniformColumn )
-#define G_SHEET_UNIFORM_COLUMN_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, g_sheet_uniform_column_get_type (), GSheetUniformColumnClass)
-#define G_IS_SHEET_UNIFORM_COLUMN(obj)  G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_SHEET_UNIFORM_COLUMN)
-
-
-  struct _GSheetUniformColumn{
-    GObject parent;
-
-    gint n_columns;
-    gint width;
-    gboolean is_sensitive;
-    gboolean is_visible;
-  };
-
-  struct _GSheetUniformColumnClass
-  {
-    GObjectClass parent_class;
-  };
-
-  /* create a new column */
-  GObject * g_sheet_uniform_column_new (gint width, gint n_columns);
-
-  GType g_sheet_uniform_column_get_type (void);
-
-
-  typedef struct _GSheetUniformColumn GSheetUniformColumn;
-  typedef struct _GSheetUniformColumnClass GSheetUniformColumnClass;
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* __G_SHEET_UNIFORM_COLUMN_H__ */
-
-
diff --git a/lib/gtksheet/gsheet-uniform-row.c b/lib/gtksheet/gsheet-uniform-row.c
deleted file mode 100644 (file)
index 7ab9b60..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/* gsheet-uniform-row.c
- *
- *  PSPPIRE --- A Graphical User Interface for PSPP
- * Copyright (C) 2006  Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <config.h>
-
-#include "gsheet-row-iface.h"
-#include "gsheet-uniform-row.h"
-
-
-static void  g_sheet_uniform_row_init       (GSheetUniformRow      *ug);
-static void  g_sheet_uniform_row_class_init (GSheetUniformRowClass *class);
-static void  g_sheet_uniform_row_finalize   (GObject           *object);
-
-static void g_sheet_row_init (GSheetRowIface *iface);
-
-
-static GObjectClass *parent_class = NULL;
-
-GType
-g_sheet_uniform_row_get_type (void)
-{
-  static GType uniform_row_type = 0;
-
-  if (!uniform_row_type)
-    {
-      static const GTypeInfo uniform_row_info =
-      {
-       sizeof (GSheetUniformRowClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-        (GClassInitFunc) g_sheet_uniform_row_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-        sizeof (GSheetUniformRow),
-       0,
-        (GInstanceInitFunc) g_sheet_uniform_row_init,
-      };
-
-      static const GInterfaceInfo row_info =
-      {
-       (GInterfaceInitFunc) g_sheet_row_init,
-       NULL,
-       NULL
-      };
-
-      uniform_row_type =
-       g_type_register_static (G_TYPE_OBJECT, "g_sheet_uniform_row",
-                               &uniform_row_info, 0);
-
-      g_type_add_interface_static (uniform_row_type,
-                                  G_TYPE_SHEET_ROW,
-                                  &row_info);
-    }
-
-  return uniform_row_type;
-}
-
-
-/**
- * g_sheet_uniform_row_new:
- * @height: The size of rows in this uniform row
- *
- * Return value: a new #g_sheet_uniform_row
- **/
-GObject *
-g_sheet_uniform_row_new (gint height, gint n_rows)
-{
-  GSheetUniformRow *ug;
-  GObject *retval;
-
-  retval = g_object_new (G_TYPE_SHEET_UNIFORM_ROW, NULL);
-
-  ug = G_SHEET_UNIFORM_ROW(retval);
-  ug->n_rows = n_rows;
-  ug->height = height;
-  ug->is_visible = TRUE;
-
-  return retval;
-}
-
-static gint
-g_sheet_uniform_row_get_height (const GSheetRow *geom, glong u)
-{
-  GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geom);
-
-  return ug->height;
-}
-
-static gboolean
-g_sheet_uniform_row_get_sensitivity (const GSheetRow *geom, glong u)
-{
-  GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geom);
-
-  return (u < ug->n_rows);
-}
-
-
-static gboolean
-g_sheet_uniform_row_get_visibility (const GSheetRow *geom, glong u)
-{
-  GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW (geom);
-
-  return ug->is_visible;
-}
-
-
-static gchar *
-g_sheet_uniform_row_get_button_label (const GSheetRow *geom, glong u)
-{
-  gchar *label = g_strdup_printf("%ld", u);
-
-  return label;
-}
-
-
-
-static glong
-g_sheet_uniform_row_get_row_count (const GSheetRow *geom)
-{
-  GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geom);
-
-  return ug->n_rows;
-}
-
-
-static void
-g_sheet_uniform_row_class_init (GSheetUniformRowClass *class)
-{
-  GObjectClass *object_class;
-
-  parent_class = g_type_class_peek_parent (class);
-  object_class = (GObjectClass*) class;
-
-  object_class->finalize = g_sheet_uniform_row_finalize;
-
-}
-
-
-static void
-g_sheet_uniform_row_init (GSheetUniformRow *o)
-{
-}
-
-static void
-g_sheet_uniform_row_finalize (GObject *object)
-{
-}
-
-
-static guint
-g_sheet_uniform_row_top_ypixel (const GSheetRow *geo, glong row)
-{
-  GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geo);
-
-  return row * ug->height;
-}
-
-static glong
-g_sheet_uniform_row_pixel_to_row (const GSheetRow *geo, guint pixel)
-{
-  GSheetUniformRow *ug = G_SHEET_UNIFORM_ROW(geo);
-
-  gint row = pixel / ug->height;
-
-  if (row >= g_sheet_uniform_row_get_row_count(geo))
-    row = g_sheet_uniform_row_get_row_count(geo) - 1;
-
-  return row;
-}
-
-
-
-static void
-g_sheet_row_init (GSheetRowIface *iface)
-{
-  iface->get_height = g_sheet_uniform_row_get_height;
-  iface->get_sensitivity = g_sheet_uniform_row_get_sensitivity ;
-  iface->get_visibility = g_sheet_uniform_row_get_visibility;
-  iface->get_row_count = g_sheet_uniform_row_get_row_count;
-  iface->get_button_label = g_sheet_uniform_row_get_button_label;
-  iface->top_ypixel = g_sheet_uniform_row_top_ypixel;
-  iface->pixel_to_row = g_sheet_uniform_row_pixel_to_row;
-}
-
diff --git a/lib/gtksheet/gsheet-uniform-row.h b/lib/gtksheet/gsheet-uniform-row.h
deleted file mode 100644 (file)
index 845dbf6..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/* GtkSheet widget for Gtk+.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef __G_SHEET_UNIFORM_ROW_H__
-#define __G_SHEET_UNIFORM_ROW_H__
-
-#include <glib-object.h>
-#include <glib.h>
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-#define G_TYPE_SHEET_UNIFORM_ROW (g_sheet_uniform_row_get_type ())
-
-#define G_SHEET_UNIFORM_ROW(obj)    G_TYPE_CHECK_INSTANCE_CAST (obj, G_TYPE_SHEET_UNIFORM_ROW, GSheetUniformRow )
-#define G_SHEET_UNIFORM_ROW_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, g_sheet_uniform_row_get_type (), GSheetUniformRowClass)
-#define G_IS_SHEET_UNIFORM_ROW(obj)  G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_SHEET_UNIFORM_ROW)
-
-
-  struct _GSheetUniformRow{
-    GObject parent;
-
-    gint n_rows;
-    gint height;
-    gboolean is_visible;
-  };
-
-  struct _GSheetUniformRowClass
-  {
-    GObjectClass parent_class;
-  };
-
-  /* create a new row */
-  GObject * g_sheet_uniform_row_new (gint height, gint n_rows);
-
-  GType g_sheet_uniform_row_get_type (void);
-
-
-  typedef struct _GSheetUniformRow GSheetUniformRow;
-  typedef struct _GSheetUniformRowClass GSheetUniformRowClass;
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* __G_SHEET_UNIFORM_ROW_H__ */
-
-
diff --git a/lib/gtksheet/gsheetmodel.c b/lib/gtksheet/gsheetmodel.c
deleted file mode 100644 (file)
index 0d1a3f5..0000000
+++ /dev/null
@@ -1,510 +0,0 @@
-/* GSheetModel --- an abstract model for the GSheet widget.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <config.h>
-
-#include <glib.h>
-#include "gsheetmodel.h"
-#include "gtkextra-marshal.h"
-
-enum {
-  RANGE_CHANGED,
-  ROWS_INSERTED,
-  ROWS_DELETED,
-  COLUMNS_INSERTED,
-  COLUMNS_DELETED,
-  LAST_SIGNAL
-};
-
-static guint sheet_model_signals[LAST_SIGNAL] = { 0 };
-
-
-static void      g_sheet_model_base_init   (gpointer           g_class);
-
-
-GType
-g_sheet_model_get_type (void)
-{
-  static GType sheet_model_type = 0;
-
-  if (! sheet_model_type)
-    {
-      static const GTypeInfo sheet_model_info =
-      {
-        sizeof (GSheetModelIface), /* class_size */
-       g_sheet_model_base_init,   /* base_init */
-       NULL,           /* base_finalize */
-       NULL,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       0,
-       0,              /* n_preallocs */
-       NULL
-      };
-
-      sheet_model_type =
-       g_type_register_static (G_TYPE_INTERFACE, "GSheetModel",
-                               &sheet_model_info, 0);
-
-      g_type_interface_add_prerequisite (sheet_model_type, G_TYPE_OBJECT);
-    }
-
-  return sheet_model_type;
-}
-
-static void
-g_sheet_model_base_init (gpointer g_class)
-{
-  static gboolean initialized = FALSE;
-
-  if (! initialized)
-    {
-      sheet_model_signals[RANGE_CHANGED] =
-       g_signal_new ("range_changed",
-                     G_TYPE_SHEET_MODEL,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetModelIface, range_changed),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT_INT_INT,
-                     G_TYPE_NONE, 4,
-                     G_TYPE_INT,
-                     G_TYPE_INT,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-
-
-      sheet_model_signals[ROWS_INSERTED] =
-       g_signal_new ("rows_inserted",
-                     G_TYPE_SHEET_MODEL,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetModelIface, rows_inserted),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT,
-                     G_TYPE_NONE, 2,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-
-      sheet_model_signals[ROWS_DELETED] =
-       g_signal_new ("rows_deleted",
-                     G_TYPE_SHEET_MODEL,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetModelIface, rows_deleted),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT,
-                     G_TYPE_NONE, 2,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-      sheet_model_signals[COLUMNS_INSERTED] =
-       g_signal_new ("columns_inserted",
-                     G_TYPE_SHEET_MODEL,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetModelIface, columns_inserted),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT,
-                     G_TYPE_NONE, 2,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-
-      sheet_model_signals[COLUMNS_DELETED] =
-       g_signal_new ("columns_deleted",
-                     G_TYPE_SHEET_MODEL,
-                     G_SIGNAL_RUN_LAST,
-                     G_STRUCT_OFFSET (GSheetModelIface, columns_deleted),
-                     NULL, NULL,
-                     gtkextra_VOID__INT_INT,
-                     G_TYPE_NONE, 2,
-                     G_TYPE_INT,
-                     G_TYPE_INT);
-
-
-      initialized = TRUE;
-    }
-}
-
-
-/**
- * g_sheet_model_free_strings
- * @sheet_model: A #GSheetModel
- *
- * Returns: True if strings obtained with get_string should be freed by the
- * sheet when no longer required.
- **/
-gboolean
-g_sheet_model_free_strings (const GSheetModel *sheet_model)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), FALSE);
-
-  return G_SHEET_MODEL_GET_IFACE (sheet_model)->free_strings;
-}
-
-
-/**
- * g_sheet_model_get_string:
- * @sheet_model: A #GSheetModel
- * @row: The row of the cell to be retrieved.
- * @column: The column of the cell to be retrieved.
- *
- * Retrieves the datum at location ROW, COLUMN in the form of a string.
- * Returns: The string representation of the datum, or NULL on error.
- **/
-gchar *
-g_sheet_model_get_string (const GSheetModel *sheet_model,
-                         glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), 0);
-
-  g_assert (G_SHEET_MODEL_GET_IFACE (sheet_model)->get_string);
-
-  return (G_SHEET_MODEL_GET_IFACE (sheet_model)->get_string) (sheet_model, row, column);
-}
-
-/**
- * g_sheet_model_set_string
- * @sheet_model: A #GSheetModel
- * @text: The text describing the datum to be set.
- * @row: The row of the cell to be cleared.
- * @column: The column of the cell to be cleared.
- *
- * Sets the datum at a location from a string.
- * Returns: TRUE if the datum was changed, FALSE otherwise.
- **/
-gboolean
-g_sheet_model_set_string      (GSheetModel *sheet_model,
-                                const gchar *text,
-                                glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), FALSE);
-
-  g_assert (G_SHEET_MODEL_GET_IFACE (sheet_model)->set_string);
-
-  return G_SHEET_MODEL_GET_IFACE (sheet_model)->set_string (sheet_model,
-                                                           text, row, column);
-}
-
-
-
-/**
- * g_sheet_model_datum_clear:
- * @sheet_model: A #GSheetModel
- * @row: The row of the cell to be cleared.
- * @column: The column of the cell to be cleared.
- *
- * Called when the datum at a location is to be cleared.
- * Returns: TRUE if the datum was cleared, FALSE otherwise.
- **/
-gboolean
-g_sheet_model_datum_clear    (GSheetModel *sheet_model,
-                               glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (sheet_model), FALSE);
-
-  g_assert (G_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum);
-
-  return G_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum (sheet_model,
-                                                               row, column);
-}
-
-
-/**
- * g_sheet_model_range_changed:
- * @sheet_model: A #GSheetModel
- * @range: The #GSheetRange range of cells which have changed.
- *
- * Emits the "range_changed" signal on @sheet_model.
- **/
-void
-g_sheet_model_range_changed (GSheetModel *sheet_model,
-                              glong row0, glong col0,
-                              glong rowi, glong coli)
-{
-  g_return_if_fail (G_IS_SHEET_MODEL (sheet_model));
-
-  g_signal_emit (sheet_model, sheet_model_signals[RANGE_CHANGED], 0,
-                row0, col0, rowi, coli);
-}
-
-
-
-
-/**
- * g_sheet_model_rows_inserted:
- * @sheet_model: A #GSheetModel
- * @row: The row before which the new rows should be inserted.
- * @n_rows: The number of rows to insert.
- *
- * Emits the "rows_inserted" signal on @sheet_model.
- **/
-void
-g_sheet_model_rows_inserted (GSheetModel *sheet_model,
-                              glong row, glong n_rows)
-{
-  g_return_if_fail (G_IS_SHEET_MODEL (sheet_model));
-
-  g_signal_emit (sheet_model, sheet_model_signals[ROWS_INSERTED], 0,
-                row, n_rows);
-}
-
-
-/**
- * g_sheet_model_columns_inserted:
- * @sheet_model: A #GSheetModel
- * @column: The column before which the new columns should be inserted.
- * @n_columns: The number of columns to insert.
- *
- * Emits the "columns_inserted" signal on @sheet_model.
- **/
-void
-g_sheet_model_columns_inserted (GSheetModel *sheet_model,
-                              glong column, glong n_columns)
-{
-  g_return_if_fail (G_IS_SHEET_MODEL (sheet_model));
-
-  g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_INSERTED], 0,
-                column, n_columns);
-}
-
-
-
-
-/**
- * g_sheet_model_rows_deleted:
- * @sheet_model: A #GSheetModel
- * @row: The first row to be deleted.
- * @n_rows: The number of rows to delete.
- *
- * Emits the "rows_deleted" signal on @sheet_model.
- **/
-void
-g_sheet_model_rows_deleted (GSheetModel *sheet_model,
-                              glong row, glong n_rows)
-{
-  g_return_if_fail (G_IS_SHEET_MODEL (sheet_model));
-
-  g_signal_emit (sheet_model, sheet_model_signals[ROWS_DELETED], 0,
-                row, n_rows);
-}
-
-
-
-/**
- * g_sheet_model_columns_deleted:
- * @sheet_model: A #GSheetModel
- * @column: The first column to be deleted.
- * @n_columns: The number of columns to delete.
- *
- * Emits the "columns_deleted" signal on @sheet_model.
- **/
-void
-g_sheet_model_columns_deleted (GSheetModel *sheet_model,
-                              glong column, glong n_columns)
-{
-  g_return_if_fail (G_IS_SHEET_MODEL (sheet_model));
-
-  g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_DELETED], 0,
-                column, n_columns);
-}
-
-
-
-
-
-/**
- * g_sheet_model_is_editable:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns: TRUE if the cell is editable, FALSE otherwise
- **/
-gboolean
-g_sheet_model_is_editable (const GSheetModel *model,
-                            glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), TRUE);
-
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->is_editable )
-    return TRUE;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->is_editable (model,
-                                                         row, column);
-}
-
-/**
- * g_sheet_model_is_visible:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns: TRUE if the cell is visible, FALSE otherwise
- **/
-gboolean
-g_sheet_model_is_visible (const GSheetModel *model,
-                         glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), TRUE);
-
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->is_visible )
-    return TRUE;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->is_visible (model,
-                                                       row, column);
-}
-
-
-/**
- * g_sheet_model_get_foreground:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns the foreground colour of the cell at @row, @column
- * Returns: the foreground colour, or NULL on error.
- **/
-const GdkColor *
-g_sheet_model_get_foreground (const GSheetModel *model,
-                               glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL);
-
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_foreground )
-    return NULL;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_foreground (model,
-                                                           row, column);
-}
-
-/**
- * g_sheet_model_get_background:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns the background colour of the cell at @row, @column
- * Returns: the background colour, or NULL on error.
- **/
-const GdkColor *
-g_sheet_model_get_background (const GSheetModel *model,
-                               glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL);
-
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_background )
-    return NULL;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_background (model,
-                                                           row, column);
-}
-
-/**
- * g_sheet_model_get_justification:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns the justification of the cell at @row, @column
- * Returns: the justification, or NULL on error.
- **/
-const GtkJustification *
-g_sheet_model_get_justification (const GSheetModel *model,
-                                  glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL);
-
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_justification)
-    return NULL;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_justification (model,
-                                                              row, column);
-}
-
-/**
- * g_sheet_model_get_font_desc:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns the font description of the cell at @row, @column
- * Returns: the font description, or NULL on error.
- **/
-const PangoFontDescription *
-g_sheet_model_get_font_desc(const GSheetModel *model,
-                             glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL);
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_font_desc)
-    return NULL;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_font_desc (model,
-                                                          row, column);
-}
-
-/**
- * g_sheet_model_get_cell_border:
- * @sheet_model: A #GSheetModel
- * @row: The row
- * @column: The column
- *
- * Returns the cell border of the cell at @row, @column
- * Returns: the cell border, or NULL on error.
- **/
-const GtkSheetCellBorder *
-g_sheet_model_get_cell_border (const GSheetModel *model,
-                                glong row, glong column)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), NULL);
-  if ( ! G_SHEET_MODEL_GET_IFACE (model)->get_cell_border)
-    return NULL;
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_cell_border (model,
-                                                          row, column);
-}
-
-
-
-/**
- * g_sheet_model_get_column_count:
- * @model: A #GSheetModel
- *
- * Returns the total number of columns represented by the model
- **/
-glong
-g_sheet_model_get_column_count (const GSheetModel *model)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), -1);
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_column_count (model);
-}
-
-/**
- * g_sheet_model_get_row_count:
- * @model: A #GSheetModel
- *
- * Returns the total number of rows represented by the model
- **/
-gint
-g_sheet_model_get_row_count(const GSheetModel *model)
-{
-  g_return_val_if_fail (G_IS_SHEET_MODEL (model), -1);
-
-
-  return G_SHEET_MODEL_GET_IFACE (model)->get_row_count (model);
-}
diff --git a/lib/gtksheet/gsheetmodel.h b/lib/gtksheet/gsheetmodel.h
deleted file mode 100644 (file)
index 6d60e03..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/* GSheetModel --- an abstract model for the GtkSheet widget.
- * Copyright (C) 2006 Free Software Foundation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef __G_SHEET_MODEL_H__
-#define __G_SHEET_MODEL_H__
-
-
-/* This file provides an abstract interface or the data displayed by the
-   GtkSheet widget */
-
-#include <glib-object.h>
-#include <gdk/gdk.h>
-#include <gtk/gtk.h>
-
-
-G_BEGIN_DECLS
-
-#define G_TYPE_SHEET_MODEL            (g_sheet_model_get_type ())
-#define G_SHEET_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SHEET_MODEL, GSheetModel))
-#define G_IS_SHEET_MODEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SHEET_MODEL))
-#define G_SHEET_MODEL_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SHEET_MODEL, GSheetModelIface))
-
-typedef enum
-{
-  GTK_SHEET_LEFT_BORDER     = 1 << 0,
-  GTK_SHEET_RIGHT_BORDER    = 1 << 1,
-  GTK_SHEET_TOP_BORDER      = 1 << 2,
-  GTK_SHEET_BOTTOM_BORDER   = 1 << 3
-} GtkSheetBorderType ;
-
-
-typedef struct _GSheetModel        GSheetModel; /* Dummy typedef */
-typedef struct _GSheetModelIface   GSheetModelIface;
-typedef struct _GtkSheetRange GtkSheetRange;
-typedef struct _GtkSheetCellBorder     GtkSheetCellBorder;
-
-struct _GtkSheetRange
-{
-  glong row0,col0; /* upper-left cell */
-  glong rowi,coli; /* lower-right cell */
-};
-
-struct _GtkSheetCellBorder
-{
-  GtkSheetBorderType mask;
-  guint width;
-  GdkLineStyle line_style;
-  GdkCapStyle cap_style;
-  GdkJoinStyle join_style;
-  GdkColor color;
-};
-
-
-
-struct _GSheetModelIface
-{
-  GTypeInterface g_iface;
-
-  gboolean free_strings;
-
-  /* Signals */
-  void         (* range_changed)    (GSheetModel *sheet_model,
-                                    glong row0, glong col0,
-                                    glong rowi, glong coli);
-
-  void         (* rows_inserted)    (GSheetModel *sheet_model,
-                                    glong row, glong n_rows);
-
-  void         (* rows_deleted)     (GSheetModel *sheet_model,
-                                    glong row, glong n_rows);
-
-  void         (* columns_inserted)    (GSheetModel *sheet_model,
-                                    glong column, glong n_columns);
-
-  void         (* columns_deleted)     (GSheetModel *sheet_model,
-                                    glong column, glong n_columns);
-
-
-
-
-  /* Virtual Table */
-
-  gchar *      (* get_string)      (const GSheetModel *sheet_model,
-                                              glong row, glong column);
-
-  gboolean  (* set_string) (GSheetModel *sheet_model,
-                           const gchar *s, glong row, glong column);
-
-  gboolean  (* clear_datum) (GSheetModel *sheet_model,
-                            glong row, glong column);
-
-  gboolean (* is_visible) (const GSheetModel *sheet_model, glong row, glong column);
-  gboolean (* is_editable) (const GSheetModel *sheet_model, glong row, glong column);
-
-  const GdkColor *  (* get_foreground) (const GSheetModel *sheet_model,
-                                   glong row, glong column);
-
-  const GdkColor *  (* get_background) (const GSheetModel *sheet_model,
-                                   glong row, glong column);
-
-  const GtkJustification *  (* get_justification) (const GSheetModel *sheet_model,
-                                                  glong row, glong column);
-
-  const PangoFontDescription *  (* get_font_desc) (const GSheetModel *sheet_model,
-                                                  glong row, glong column);
-
-  const GtkSheetCellBorder *  (* get_cell_border) (const GSheetModel *sheet_model,
-                                                  glong row, glong column);
-
-
-  glong (*get_column_count) (const GSheetModel *model);
-
-  glong (*get_row_count) (const GSheetModel *model);
-
-};
-
-
-
-GType              g_sheet_model_get_type   (void) G_GNUC_CONST;
-
-
-inline  gchar * g_sheet_model_get_string (const GSheetModel *sheet_model,
-                                              glong row, glong column);
-
-inline gboolean  g_sheet_model_set_string (GSheetModel *sheet_model,
-                                     const gchar *s,
-                                     glong row, glong column);
-
-inline gboolean g_sheet_model_datum_clear    (GSheetModel *sheet_model,
-                                        glong row, glong column);
-
-
-inline void g_sheet_model_range_changed (GSheetModel *sheet_model,
-                                   glong row0, glong col0,
-                                   glong rowi, glong coli);
-
-inline void g_sheet_model_rows_deleted (GSheetModel *sheet_model,
-                                  glong row, glong n_rows);
-
-inline void g_sheet_model_rows_inserted (GSheetModel *sheet_model,
-                                   glong row, glong n_rows);
-
-inline void g_sheet_model_columns_inserted (GSheetModel *sheet_model,
-                                           glong column, glong n_columns);
-
-inline void g_sheet_model_columns_deleted (GSheetModel *sheet_model,
-                                          glong column, glong n_columns);
-
-
-inline gboolean g_sheet_model_is_editable (const GSheetModel *model,
-                                     glong row, glong column);
-
-inline gboolean g_sheet_model_is_visible
-                   (const GSheetModel *model, glong row, glong column);
-
-
-inline const GdkColor *g_sheet_model_get_foreground
-                   (const GSheetModel *model, glong row, glong column);
-
-inline const GdkColor *g_sheet_model_get_background
-                   (const GSheetModel *model, glong row, glong column);
-
-
-inline const GtkJustification *g_sheet_model_get_justification
-                   (const GSheetModel *model, glong row, glong column);
-
-
-inline const PangoFontDescription *g_sheet_model_get_font_desc
-                   (const GSheetModel *model, glong row, glong column);
-
-inline const GtkSheetCellBorder * g_sheet_model_get_cell_border
-                   (const GSheetModel *model, glong row, glong column);
-
-inline  gboolean g_sheet_model_free_strings (const GSheetModel *sheet_model);
-
-inline glong g_sheet_model_get_column_count (const GSheetModel *sheet_model);
-
-inline  gint g_sheet_model_get_row_count (const GSheetModel *sheet_model);
-
-G_END_DECLS
-
-#endif /* __G_SHEET_MODEL_H__ */
diff --git a/lib/gtksheet/gtkextra-marshal.c b/lib/gtksheet/gtkextra-marshal.c
deleted file mode 100644 (file)
index f8c8448..0000000
+++ /dev/null
@@ -1,893 +0,0 @@
-#include <config.h>
-
-#include       <glib-object.h>
-
-
-#ifdef G_ENABLE_DEBUG
-#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
-#define g_marshal_value_peek_char(v)     g_value_get_char (v)
-#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
-#define g_marshal_value_peek_int(v)      g_value_get_int (v)
-#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
-#define g_marshal_value_peek_long(v)     g_value_get_long (v)
-#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
-#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
-#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
-#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
-#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
-#define g_marshal_value_peek_float(v)    g_value_get_float (v)
-#define g_marshal_value_peek_double(v)   g_value_get_double (v)
-#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
-#define g_marshal_value_peek_param(v)    g_value_get_param (v)
-#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
-#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
-#define g_marshal_value_peek_object(v)   g_value_get_object (v)
-#else /* !G_ENABLE_DEBUG */
-/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
- *          Do not access GValues directly in your code. Instead, use the
- *          g_value_get_*() functions
- */
-#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
-#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
-#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
-#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
-#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
-#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
-#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
-#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
-#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
-#define g_marshal_value_peek_enum(v)     (v)->data[0].v_int
-#define g_marshal_value_peek_flags(v)    (v)->data[0].v_uint
-#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
-#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
-#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
-#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
-#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
-#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
-#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
-#endif /* !G_ENABLE_DEBUG */
-
-
-/* BOOL:INT,INT,POINTER,POINTER (gtkextra-marshal.list:1) */
-void
-gtkextra_BOOLEAN__INT_INT_POINTER_POINTER (GClosure     *closure,
-                                           GValue       *return_value,
-                                           guint         n_param_values,
-                                           const GValue *param_values,
-                                           gpointer      invocation_hint,
-                                           gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER) (gpointer     data1,
-                                                                     gint         arg_1,
-                                                                     gint         arg_2,
-                                                                     gpointer     arg_3,
-                                                                     gpointer     arg_4,
-                                                                     gpointer     data2);
-  register GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 5);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__INT_INT_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_int (param_values + 1),
-                       g_marshal_value_peek_int (param_values + 2),
-                       g_marshal_value_peek_pointer (param_values + 3),
-                       g_marshal_value_peek_pointer (param_values + 4),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:BOXED,POINTER (gtkextra-marshal.list:2) */
-void
-gtkextra_BOOLEAN__BOXED_POINTER (GClosure     *closure,
-                                 GValue       *return_value,
-                                 guint         n_param_values,
-                                 const GValue *param_values,
-                                 gpointer      invocation_hint,
-                                 gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_POINTER) (gpointer     data1,
-                                                           gpointer     arg_1,
-                                                           gpointer     arg_2,
-                                                           gpointer     data2);
-  register GMarshalFunc_BOOLEAN__BOXED_POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_boxed (param_values + 1),
-                       g_marshal_value_peek_pointer (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:BOXED,STRING (gtkextra-marshal.list:3) */
-void
-gtkextra_BOOLEAN__BOXED_STRING (GClosure     *closure,
-                                GValue       *return_value,
-                                guint         n_param_values,
-                                const GValue *param_values,
-                                gpointer      invocation_hint,
-                                gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_STRING) (gpointer     data1,
-                                                          gpointer     arg_1,
-                                                          gpointer     arg_2,
-                                                          gpointer     data2);
-  register GMarshalFunc_BOOLEAN__BOXED_STRING callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__BOXED_STRING) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_boxed (param_values + 1),
-                       g_marshal_value_peek_string (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:BOXED,BOXED (gtkextra-marshal.list:4) */
-void
-gtkextra_BOOLEAN__BOXED_BOXED (GClosure     *closure,
-                               GValue       *return_value,
-                               guint         n_param_values,
-                               const GValue *param_values,
-                               gpointer      invocation_hint,
-                               gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_BOXED) (gpointer     data1,
-                                                         gpointer     arg_1,
-                                                         gpointer     arg_2,
-                                                         gpointer     data2);
-  register GMarshalFunc_BOOLEAN__BOXED_BOXED callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_boxed (param_values + 1),
-                       g_marshal_value_peek_boxed (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:BOXED,DOUBLE,DOUBLE (gtkextra-marshal.list:5) */
-void
-gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE (GClosure     *closure,
-                                       GValue       *return_value,
-                                       guint         n_param_values,
-                                       const GValue *param_values,
-                                       gpointer      invocation_hint,
-                                       gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE) (gpointer     data1,
-                                                                 gpointer     arg_1,
-                                                                 gdouble      arg_2,
-                                                                 gdouble      arg_3,
-                                                                 gpointer     data2);
-  register GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 4);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__BOXED_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_boxed (param_values + 1),
-                       g_marshal_value_peek_double (param_values + 2),
-                       g_marshal_value_peek_double (param_values + 3),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:POINTER,POINTER (gtkextra-marshal.list:6) */
-void
-gtkextra_BOOLEAN__POINTER_POINTER (GClosure     *closure,
-                                   GValue       *return_value,
-                                   guint         n_param_values,
-                                   const GValue *param_values,
-                                   gpointer      invocation_hint,
-                                   gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer     data1,
-                                                             gpointer     arg_1,
-                                                             gpointer     arg_2,
-                                                             gpointer     data2);
-  register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_pointer (param_values + 1),
-                       g_marshal_value_peek_pointer (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:POINTER,BOXED (gtkextra-marshal.list:7) */
-void
-gtkextra_BOOLEAN__POINTER_BOXED (GClosure     *closure,
-                                 GValue       *return_value,
-                                 guint         n_param_values,
-                                 const GValue *param_values,
-                                 gpointer      invocation_hint,
-                                 gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_BOXED) (gpointer     data1,
-                                                           gpointer     arg_1,
-                                                           gpointer     arg_2,
-                                                           gpointer     data2);
-  register GMarshalFunc_BOOLEAN__POINTER_BOXED callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__POINTER_BOXED) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_pointer (param_values + 1),
-                       g_marshal_value_peek_boxed (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:POINTER,STRING (gtkextra-marshal.list:8) */
-void
-gtkextra_BOOLEAN__POINTER_STRING (GClosure     *closure,
-                                  GValue       *return_value,
-                                  guint         n_param_values,
-                                  const GValue *param_values,
-                                  gpointer      invocation_hint,
-                                  gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_STRING) (gpointer     data1,
-                                                            gpointer     arg_1,
-                                                            gpointer     arg_2,
-                                                            gpointer     data2);
-  register GMarshalFunc_BOOLEAN__POINTER_STRING callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__POINTER_STRING) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_pointer (param_values + 1),
-                       g_marshal_value_peek_string (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:POINTER (gtkextra-marshal.list:9) */
-void
-gtkextra_BOOLEAN__POINTER (GClosure     *closure,
-                           GValue       *return_value,
-                           guint         n_param_values,
-                           const GValue *param_values,
-                           gpointer      invocation_hint,
-                           gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer     data1,
-                                                     gpointer     arg_1,
-                                                     gpointer     data2);
-  register GMarshalFunc_BOOLEAN__POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 2);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_pointer (param_values + 1),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:BOXED (gtkextra-marshal.list:10) */
-void
-gtkextra_BOOLEAN__BOXED (GClosure     *closure,
-                         GValue       *return_value,
-                         guint         n_param_values,
-                         const GValue *param_values,
-                         gpointer      invocation_hint,
-                         gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer     data1,
-                                                   gpointer     arg_1,
-                                                   gpointer     data2);
-  register GMarshalFunc_BOOLEAN__BOXED callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 2);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_boxed (param_values + 1),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* BOOL:INT,INT (gtkextra-marshal.list:11) */
-void
-gtkextra_BOOLEAN__INT_INT (GClosure     *closure,
-                           GValue       *return_value,
-                           guint         n_param_values,
-                           const GValue *param_values,
-                           gpointer      invocation_hint,
-                           gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__INT_INT) (gpointer     data1,
-                                                     gint         arg_1,
-                                                     gint         arg_2,
-                                                     gpointer     data2);
-  register GMarshalFunc_BOOLEAN__INT_INT callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__INT_INT) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_int (param_values + 1),
-                       g_marshal_value_peek_int (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* VOID:INT (gtkextra-marshal.list:12) */
-
-/* VOID:INT,STRING (gtkextra-marshal.list:13) */
-void
-gtkextra_VOID__INT_STRING (GClosure     *closure,
-                           GValue       *return_value,
-                           guint         n_param_values,
-                           const GValue *param_values,
-                           gpointer      invocation_hint,
-                           gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__INT_STRING) (gpointer     data1,
-                                                 gint         arg_1,
-                                                 gpointer     arg_2,
-                                                 gpointer     data2);
-  register GMarshalFunc_VOID__INT_STRING callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__INT_STRING) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_int (param_values + 1),
-            g_marshal_value_peek_string (param_values + 2),
-            data2);
-}
-
-/* VOID:BOXED (gtkextra-marshal.list:14) */
-
-/* VOID:VOID (gtkextra-marshal.list:15) */
-
-/* VOID:BOOL (gtkextra-marshal.list:16) */
-
-/* VOID:POINTER (gtkextra-marshal.list:17) */
-
-/* VOID:INT,INT (gtkextra-marshal.list:18) */
-void
-gtkextra_VOID__INT_INT (GClosure     *closure,
-                        GValue       *return_value,
-                        guint         n_param_values,
-                        const GValue *param_values,
-                        gpointer      invocation_hint,
-                        gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer     data1,
-                                              gint         arg_1,
-                                              gint         arg_2,
-                                              gpointer     data2);
-  register GMarshalFunc_VOID__INT_INT callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_int (param_values + 1),
-            g_marshal_value_peek_int (param_values + 2),
-            data2);
-}
-
-
-/* VOID:INT,INT,INT,INT (Added by JMD 1/1/2006) */
-void
-gtkextra_VOID__INT_INT_INT_INT (GClosure     *closure,
-                        GValue       *return_value,
-                        guint         n_param_values,
-                        const GValue *param_values,
-                        gpointer      invocation_hint,
-                        gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__INT_INT_INT_INT) (gpointer     data1,
-                                              gint         arg_1,
-                                              gint         arg_2,
-                                              gint         arg_3,
-                                              gint         arg_4,
-                                              gpointer     data2);
-  register GMarshalFunc_VOID__INT_INT_INT_INT callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 5);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_int (param_values + 1),
-            g_marshal_value_peek_int (param_values + 2),
-            g_marshal_value_peek_int (param_values + 3),
-            g_marshal_value_peek_int (param_values + 4),
-            data2);
-}
-
-
-/* VOID:INT,POINTER (gtkextra-marshal.list:19) */
-void
-gtkextra_VOID__INT_POINTER (GClosure     *closure,
-                            GValue       *return_value,
-                            guint         n_param_values,
-                            const GValue *param_values,
-                            gpointer      invocation_hint,
-                            gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__INT_POINTER) (gpointer     data1,
-                                                  gint         arg_1,
-                                                  gpointer     arg_2,
-                                                  gpointer     data2);
-  register GMarshalFunc_VOID__INT_POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__INT_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_int (param_values + 1),
-            g_marshal_value_peek_pointer (param_values + 2),
-            data2);
-}
-
-/* VOID:INT,BOXED (gtkextra-marshal.list:20) */
-void
-gtkextra_VOID__INT_BOXED (GClosure     *closure,
-                          GValue       *return_value,
-                          guint         n_param_values,
-                          const GValue *param_values,
-                          gpointer      invocation_hint,
-                          gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__INT_BOXED) (gpointer     data1,
-                                                gint         arg_1,
-                                                gpointer     arg_2,
-                                                gpointer     data2);
-  register GMarshalFunc_VOID__INT_BOXED callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__INT_BOXED) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_int (param_values + 1),
-            g_marshal_value_peek_boxed (param_values + 2),
-            data2);
-}
-
-/* VOID:POINTER,POINTER (gtkextra-marshal.list:21) */
-void
-gtkextra_VOID__POINTER_POINTER (GClosure     *closure,
-                                GValue       *return_value,
-                                guint         n_param_values,
-                                const GValue *param_values,
-                                gpointer      invocation_hint,
-                                gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer     data1,
-                                                      gpointer     arg_1,
-                                                      gpointer     arg_2,
-                                                      gpointer     data2);
-  register GMarshalFunc_VOID__POINTER_POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_pointer (param_values + 1),
-            g_marshal_value_peek_pointer (param_values + 2),
-            data2);
-}
-
-/* VOID:BOXED,POINTER (gtkextra-marshal.list:22) */
-void
-gtkextra_VOID__BOXED_POINTER (GClosure     *closure,
-                              GValue       *return_value,
-                              guint         n_param_values,
-                              const GValue *param_values,
-                              gpointer      invocation_hint,
-                              gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer     data1,
-                                                    gpointer     arg_1,
-                                                    gpointer     arg_2,
-                                                    gpointer     data2);
-  register GMarshalFunc_VOID__BOXED_POINTER callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_boxed (param_values + 1),
-            g_marshal_value_peek_pointer (param_values + 2),
-            data2);
-}
-
-/* VOID:BOXED,BOXED (gtkextra-marshal.list:23) */
-void
-gtkextra_VOID__BOXED_BOXED (GClosure     *closure,
-                            GValue       *return_value,
-                            guint         n_param_values,
-                            const GValue *param_values,
-                            gpointer      invocation_hint,
-                            gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer     data1,
-                                                  gpointer     arg_1,
-                                                  gpointer     arg_2,
-                                                  gpointer     data2);
-  register GMarshalFunc_VOID__BOXED_BOXED callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_boxed (param_values + 1),
-            g_marshal_value_peek_boxed (param_values + 2),
-            data2);
-}
-
-/* VOID:OBJECT,OBJECT (gtkextra-marshal.list:24) */
-void
-gtkextra_VOID__OBJECT_OBJECT (GClosure     *closure,
-                              GValue       *return_value,
-                              guint         n_param_values,
-                              const GValue *param_values,
-                              gpointer      invocation_hint,
-                              gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer     data1,
-                                                    gpointer     arg_1,
-                                                    gpointer     arg_2,
-                                                    gpointer     data2);
-  register GMarshalFunc_VOID__OBJECT_OBJECT callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_object (param_values + 1),
-            g_marshal_value_peek_object (param_values + 2),
-            data2);
-}
-
-/* VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE (gtkextra-marshal.list:25) */
-void
-gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure     *closure,
-                                            GValue       *return_value,
-                                            guint         n_param_values,
-                                            const GValue *param_values,
-                                            gpointer      invocation_hint,
-                                            gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (gpointer     data1,
-                                                                  gdouble      arg_1,
-                                                                  gdouble      arg_2,
-                                                                  gdouble      arg_3,
-                                                                  gdouble      arg_4,
-                                                                  gpointer     data2);
-  register GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 5);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_double (param_values + 1),
-            g_marshal_value_peek_double (param_values + 2),
-            g_marshal_value_peek_double (param_values + 3),
-            g_marshal_value_peek_double (param_values + 4),
-            data2);
-}
-
diff --git a/lib/gtksheet/gtkextra-marshal.h b/lib/gtksheet/gtkextra-marshal.h
deleted file mode 100644 (file)
index ea9ed5e..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-
-#ifndef __gtkextra_MARSHAL_H__
-#define __gtkextra_MARSHAL_H__
-
-#include       <glib-object.h>
-
-G_BEGIN_DECLS
-
-/* BOOL:INT,INT,POINTER,POINTER (gtkextra-marshal.list:1) */
-extern void gtkextra_BOOLEAN__INT_INT_POINTER_POINTER (GClosure     *closure,
-                                                       GValue       *return_value,
-                                                       guint         n_param_values,
-                                                       const GValue *param_values,
-                                                       gpointer      invocation_hint,
-                                                       gpointer      marshal_data);
-#define gtkextra_BOOL__INT_INT_POINTER_POINTER gtkextra_BOOLEAN__INT_INT_POINTER_POINTER
-
-/* BOOL:BOXED,POINTER (gtkextra-marshal.list:2) */
-extern void gtkextra_BOOLEAN__BOXED_POINTER (GClosure     *closure,
-                                             GValue       *return_value,
-                                             guint         n_param_values,
-                                             const GValue *param_values,
-                                             gpointer      invocation_hint,
-                                             gpointer      marshal_data);
-#define gtkextra_BOOL__BOXED_POINTER   gtkextra_BOOLEAN__BOXED_POINTER
-
-/* BOOL:BOXED,STRING (gtkextra-marshal.list:3) */
-extern void gtkextra_BOOLEAN__BOXED_STRING (GClosure     *closure,
-                                            GValue       *return_value,
-                                            guint         n_param_values,
-                                            const GValue *param_values,
-                                            gpointer      invocation_hint,
-                                            gpointer      marshal_data);
-#define gtkextra_BOOL__BOXED_STRING    gtkextra_BOOLEAN__BOXED_STRING
-
-/* BOOL:BOXED,BOXED (gtkextra-marshal.list:4) */
-extern void gtkextra_BOOLEAN__BOXED_BOXED (GClosure     *closure,
-                                           GValue       *return_value,
-                                           guint         n_param_values,
-                                           const GValue *param_values,
-                                           gpointer      invocation_hint,
-                                           gpointer      marshal_data);
-#define gtkextra_BOOL__BOXED_BOXED     gtkextra_BOOLEAN__BOXED_BOXED
-
-/* BOOL:BOXED,DOUBLE,DOUBLE (gtkextra-marshal.list:5) */
-extern void gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE (GClosure     *closure,
-                                                   GValue       *return_value,
-                                                   guint         n_param_values,
-                                                   const GValue *param_values,
-                                                   gpointer      invocation_hint,
-                                                   gpointer      marshal_data);
-#define gtkextra_BOOL__BOXED_DOUBLE_DOUBLE     gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE
-
-/* BOOL:POINTER,POINTER (gtkextra-marshal.list:6) */
-extern void gtkextra_BOOLEAN__POINTER_POINTER (GClosure     *closure,
-                                               GValue       *return_value,
-                                               guint         n_param_values,
-                                               const GValue *param_values,
-                                               gpointer      invocation_hint,
-                                               gpointer      marshal_data);
-#define gtkextra_BOOL__POINTER_POINTER gtkextra_BOOLEAN__POINTER_POINTER
-
-/* BOOL:POINTER,BOXED (gtkextra-marshal.list:7) */
-extern void gtkextra_BOOLEAN__POINTER_BOXED (GClosure     *closure,
-                                             GValue       *return_value,
-                                             guint         n_param_values,
-                                             const GValue *param_values,
-                                             gpointer      invocation_hint,
-                                             gpointer      marshal_data);
-#define gtkextra_BOOL__POINTER_BOXED   gtkextra_BOOLEAN__POINTER_BOXED
-
-/* BOOL:POINTER,STRING (gtkextra-marshal.list:8) */
-extern void gtkextra_BOOLEAN__POINTER_STRING (GClosure     *closure,
-                                              GValue       *return_value,
-                                              guint         n_param_values,
-                                              const GValue *param_values,
-                                              gpointer      invocation_hint,
-                                              gpointer      marshal_data);
-#define gtkextra_BOOL__POINTER_STRING  gtkextra_BOOLEAN__POINTER_STRING
-
-/* BOOL:POINTER (gtkextra-marshal.list:9) */
-extern void gtkextra_BOOLEAN__POINTER (GClosure     *closure,
-                                       GValue       *return_value,
-                                       guint         n_param_values,
-                                       const GValue *param_values,
-                                       gpointer      invocation_hint,
-                                       gpointer      marshal_data);
-#define gtkextra_BOOL__POINTER gtkextra_BOOLEAN__POINTER
-
-/* BOOL:BOXED (gtkextra-marshal.list:10) */
-extern void gtkextra_BOOLEAN__BOXED (GClosure     *closure,
-                                     GValue       *return_value,
-                                     guint         n_param_values,
-                                     const GValue *param_values,
-                                     gpointer      invocation_hint,
-                                     gpointer      marshal_data);
-#define gtkextra_BOOL__BOXED   gtkextra_BOOLEAN__BOXED
-
-/* BOOL:INT,INT (gtkextra-marshal.list:11) */
-extern void gtkextra_BOOLEAN__INT_INT (GClosure     *closure,
-                                       GValue       *return_value,
-                                       guint         n_param_values,
-                                       const GValue *param_values,
-                                       gpointer      invocation_hint,
-                                       gpointer      marshal_data);
-#define gtkextra_BOOL__INT_INT gtkextra_BOOLEAN__INT_INT
-
-/* VOID:INT (gtkextra-marshal.list:12) */
-#define gtkextra_VOID__INT     g_cclosure_marshal_VOID__INT
-
-/* VOID:INT,STRING (gtkextra-marshal.list:13) */
-extern void gtkextra_VOID__INT_STRING (GClosure     *closure,
-                                       GValue       *return_value,
-                                       guint         n_param_values,
-                                       const GValue *param_values,
-                                       gpointer      invocation_hint,
-                                       gpointer      marshal_data);
-
-/* VOID:BOXED (gtkextra-marshal.list:14) */
-#define gtkextra_VOID__BOXED   g_cclosure_marshal_VOID__BOXED
-
-/* VOID:VOID (gtkextra-marshal.list:15) */
-#define gtkextra_VOID__VOID    g_cclosure_marshal_VOID__VOID
-
-/* VOID:BOOL (gtkextra-marshal.list:16) */
-#define gtkextra_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN
-#define gtkextra_VOID__BOOL    gtkextra_VOID__BOOLEAN
-
-/* VOID:POINTER (gtkextra-marshal.list:17) */
-#define gtkextra_VOID__POINTER g_cclosure_marshal_VOID__POINTER
-
-/* VOID:INT,INT (gtkextra-marshal.list:18) */
-extern void gtkextra_VOID__INT_INT (GClosure     *closure,
-                                    GValue       *return_value,
-                                    guint         n_param_values,
-                                    const GValue *param_values,
-                                    gpointer      invocation_hint,
-                                    gpointer      marshal_data);
-
-/* VOID:INT,INT,INT,INT (Added by JMD 1/1/26) */
-extern void gtkextra_VOID__INT_INT_INT_INT (GClosure     *closure,
-                                           GValue       *return_value,
-                                           guint         n_param_values,
-                                           const GValue *param_values,
-                                           gpointer      invocation_hint,
-                                           gpointer      marshal_data);
-
-
-/* VOID:INT,POINTER (gtkextra-marshal.list:19) */
-extern void gtkextra_VOID__INT_POINTER (GClosure     *closure,
-                                        GValue       *return_value,
-                                        guint         n_param_values,
-                                        const GValue *param_values,
-                                        gpointer      invocation_hint,
-                                        gpointer      marshal_data);
-
-/* VOID:INT,BOXED (gtkextra-marshal.list:20) */
-extern void gtkextra_VOID__INT_BOXED (GClosure     *closure,
-                                      GValue       *return_value,
-                                      guint         n_param_values,
-                                      const GValue *param_values,
-                                      gpointer      invocation_hint,
-                                      gpointer      marshal_data);
-
-/* VOID:POINTER,POINTER (gtkextra-marshal.list:21) */
-extern void gtkextra_VOID__POINTER_POINTER (GClosure     *closure,
-                                            GValue       *return_value,
-                                            guint         n_param_values,
-                                            const GValue *param_values,
-                                            gpointer      invocation_hint,
-                                            gpointer      marshal_data);
-
-/* VOID:BOXED,POINTER (gtkextra-marshal.list:22) */
-extern void gtkextra_VOID__BOXED_POINTER (GClosure     *closure,
-                                          GValue       *return_value,
-                                          guint         n_param_values,
-                                          const GValue *param_values,
-                                          gpointer      invocation_hint,
-                                          gpointer      marshal_data);
-
-/* VOID:BOXED,BOXED (gtkextra-marshal.list:23) */
-extern void gtkextra_VOID__BOXED_BOXED (GClosure     *closure,
-                                        GValue       *return_value,
-                                        guint         n_param_values,
-                                        const GValue *param_values,
-                                        gpointer      invocation_hint,
-                                        gpointer      marshal_data);
-
-/* VOID:OBJECT,OBJECT (gtkextra-marshal.list:24) */
-extern void gtkextra_VOID__OBJECT_OBJECT (GClosure     *closure,
-                                          GValue       *return_value,
-                                          guint         n_param_values,
-                                          const GValue *param_values,
-                                          gpointer      invocation_hint,
-                                          gpointer      marshal_data);
-
-/* VOID:DOUBLE,DOUBLE,DOUBLE,DOUBLE (gtkextra-marshal.list:25) */
-extern void gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE (GClosure     *closure,
-                                                        GValue       *return_value,
-                                                        guint         n_param_values,
-                                                        const GValue *param_values,
-                                                        gpointer      invocation_hint,
-                                                        gpointer      marshal_data);
-
-G_END_DECLS
-
-#endif /* __gtkextra_MARSHAL_H__ */
-
diff --git a/lib/gtksheet/gtkextra-sheet.h b/lib/gtksheet/gtkextra-sheet.h
deleted file mode 100644 (file)
index 0a5fb70..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/* This version of GtkSheet has been heavily modified, for the specific
- *  requirements of PSPPIRE.
- *
- * GtkSheet widget for Gtk+.
- * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
- *
- * Based on GtkClist widget by Jay Painter, but major changes.
- * Memory allocation routines inspired on SC (Spreadsheet Calculator)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-
-#ifndef __GTK_EXTRA_SHEET_H__
-#define __GTK_EXTRA_SHEET_H__
-
-
-struct _GtkSheet ;
-
-typedef struct _GtkSheet GtkSheet;
-
-
-struct _GtkSheetChild
-{
-  GtkWidget *widget;
-  gint x,y ;
-  gboolean attached_to_cell;
-  gboolean floating;
-  gint row, col;
-  guint16 xpadding;
-  guint16 ypadding;
-  gboolean xexpand;
-  gboolean yexpand;
-  gboolean xshrink;
-  gboolean yshrink;
-  gboolean xfill;
-  gboolean yfill;
-};
-
-typedef struct _GtkSheetChild GtkSheetChild;
-
-
-
-struct _GtkSheetButton
-{
-  GtkStateType state;
-  gchar *label;
-
-  gboolean label_visible;
-  GtkSheetChild *child;
-
-  GtkJustification justification;
-};
-
-typedef struct _GtkSheetButton GtkSheetButton;
-
-
-
-GtkSheetButton * gtk_sheet_button_new(void);
-
-inline void gtk_sheet_button_free(GtkSheetButton *button);
-
-
-#endif /* __GTK_EXTRA_SHEET_H__ */
-
-
diff --git a/lib/gtksheet/gtkextra.c b/lib/gtksheet/gtkextra.c
deleted file mode 100644 (file)
index 4d79268..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/* gtkextra
- * Copyright 1999-2001 Adrian E. Feiguin <feiguin@ifir.edu.ar>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <config.h>
-
-#include <string.h>
-#include <gtk/gtk.h>
-#include "gtkextrafeatures.h"
-#include <gobject/gvaluecollector.h>
-
-const guint gtkextra_major_version = GTKEXTRA_MAJOR_VERSION;
-const guint gtkextra_minor_version = GTKEXTRA_MINOR_VERSION;
-const guint gtkextra_micro_version = GTKEXTRA_MICRO_VERSION;
-const guint gtkextra_binary_age = GTKEXTRA_BINARY_AGE;
-const guint gtkextra_interface_age = GTKEXTRA_INTERFACE_AGE;
-
-gchar *
-gtkextra_check_version (guint required_major,
-                        guint required_minor,
-                        guint required_micro)
-{
-  if (required_major > GTKEXTRA_MAJOR_VERSION)
-    return "GtkExtra version too old (major mismatch)";
-  if (required_major < GTKEXTRA_MAJOR_VERSION)
-    return "GtkExtra version too new (major mismatch)";
-  if (required_minor > GTKEXTRA_MINOR_VERSION)
-    return "GtkExtra version too old (minor mismatch)";
-  if (required_minor < GTKEXTRA_MINOR_VERSION)
-    return "GtkExtra version too new (minor mismatch)";
-  if (required_micro < GTKEXTRA_MICRO_VERSION - GTKEXTRA_BINARY_AGE)
-    return "GtkExtra version too new (micro mismatch)";
-  if (required_micro > GTKEXTRA_MICRO_VERSION)
-    return "GtkExtra version too old (micro mismatch)";
-  return NULL;
-}
-
-/*
-void
-_gtkextra_signal_test(GtkObject *object, guint signal_id, gint arg1, gint arg2, gboolean *default_ret)
-{
-  gboolean result;
-  GValue ret = { 0, };
-  GValue instance_and_param[3] = { { 0, }, {0, }, {0, } };
-
-  g_value_init(instance_and_param + 0, GTK_OBJECT_TYPE(object));
-  g_value_set_instance(instance_and_param + 0, G_OBJECT(object));
-
-  g_value_init(instance_and_param + 1, G_TYPE_INT);
-  g_value_set_int(instance_and_param + 1, arg1);
-
-  g_value_init(instance_and_param + 2, G_TYPE_INT);
-  g_value_set_int(instance_and_param + 2, arg2);
-
-  g_value_init(&ret, G_TYPE_BOOLEAN);
-  g_value_set_boolean(&ret, *default_ret);
-
-  g_signal_emitv(instance_and_param, signal_id, 0, &ret);
-  *default_ret = g_value_get_boolean(&ret);
-
-  g_value_unset(instance_and_param + 0);
-  g_value_unset(instance_and_param + 1);
-  g_value_unset(instance_and_param + 2);
-}
-*/
-
-void
-_gtkextra_signal_emit(GtkObject *object, guint signal_id, ...)
-{
-  gboolean *result;
-  GValue ret = { 0, };
-  GValue instance_and_params [10] = { {0, }, };
-  va_list var_args;
-  GSignalQuery query;
-  gchar *error;
-  int i;
-
-  va_start (var_args, signal_id);
-
-  g_value_init(instance_and_params + 0, GTK_OBJECT_TYPE(object));
-  g_value_set_instance (instance_and_params + 0, G_OBJECT(object));
-
-  g_signal_query(signal_id, &query);
-
-  for (i = 0; i < query.n_params; i++)
-    {
-      gboolean static_scope = query.param_types[i]&~G_SIGNAL_TYPE_STATIC_SCOPE;
-      g_value_init(instance_and_params + i + 1, query.param_types[i]);
-
-
-      G_VALUE_COLLECT (instance_and_params + i + 1,
-                       var_args,
-                       static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
-                       &error);
-
-      if (error)
-        {
-          g_warning ("%s: %s", G_STRLOC, error);
-          g_free (error);
-          while (i-- > 0)
-            g_value_unset (instance_and_params + i);
-
-          va_end (var_args);
-          return;
-        }
-
-
-    }
-
-  g_value_init(&ret, query.return_type);
-  result = va_arg(var_args,gboolean *);
-  g_value_set_boolean(&ret, *result);
-  g_signal_emitv(instance_and_params, signal_id, 0, &ret);
-  *result = g_value_get_boolean(&ret);
-  g_value_unset (&ret);
-
-  for (i = 0; i < query.n_params; i++)
-    g_value_unset (instance_and_params + 1 + i);
-  g_value_unset (instance_and_params + 0);
-
-  va_end (var_args);
-}
diff --git a/lib/gtksheet/gtkextrafeatures.h b/lib/gtksheet/gtkextrafeatures.h
deleted file mode 100644 (file)
index 8b526a8..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/* gtkextra - set of widgets for gtk+
- * Copyright 1999-2001  Adrian E. Feiguin <feiguin@ifir.edu.ar>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef GTK_EXTRA_FEATURES_H
-#define GTK_EXTRA_FEATURES_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-/* GtkExtra version.
- */
-
-#define GTKEXTRA_MAJOR_VERSION                 (2)
-#define GTKEXTRA_MINOR_VERSION                 (1)
-#define GTKEXTRA_MICRO_VERSION                 (1)
-#define GTKEXTRA_BINARY_AGE                    (0)
-#define GTKEXTRA_INTERFACE_AGE                 (0)
-#define GTKEXTRA_CHECK_VERSION(major,minor,micro)    \
-   (GTKEXTRA_MAJOR_VERSION > (major) || \
-    (GTKEXTRA_MAJOR_VERSION == (major) && GTKEXTRA_MINOR_VERSION > (minor)) || \
-    (GTKEXTRA_MAJOR_VERSION == (major) && GTKEXTRA_MINOR_VERSION == (minor) && \
-     GTKEXTRA_MICRO_VERSION >= (micro)))
-
-
-extern const guint gtkextra_major_version;
-extern const guint gtkextra_minor_version;
-extern const guint gtkextra_micro_version;
-extern const guint gtkextra_binary_age;
-extern const guint gtkextra_interface_age;
-gchar* gtkextra_check_version (guint required_major,
-                               guint required_minor,
-                               guint required_micro);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-#endif /* GTK_EXTRA_FEATURES_H */
diff --git a/lib/gtksheet/gtkitementry.c b/lib/gtksheet/gtkitementry.c
deleted file mode 100644 (file)
index efc22e2..0000000
+++ /dev/null
@@ -1,2406 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#include <config.h>
-
-#include <string.h>
-
-#include <pango/pango.h>
-
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-#include "gtkitementry.h"
-
-#define MIN_ENTRY_WIDTH  150
-#define DRAW_TIMEOUT     20
-#define INNER_BORDER     0
-
-/* Initial size of buffer, in bytes */
-#define MIN_SIZE 16
-
-/* Maximum size of text buffer, in bytes */
-#define MAX_SIZE G_MAXUSHORT
-
-typedef enum {
-  CURSOR_STANDARD,
-  CURSOR_DND
-} CursorType;
-
-/* GObject, GtkObject methods
- */
-static void   gtk_item_entry_class_init           (GtkItemEntryClass        *klass);
-static void   gtk_item_entry_init                 (GtkItemEntry         *entry);
-static void   gtk_item_entry_editable_init (GtkEditableClass *iface);
-
-/* GtkWidget methods
- */
-static void   gtk_entry_realize              (GtkWidget        *widget);
-static void   gtk_entry_size_request         (GtkWidget        *widget,
-                                             GtkRequisition   *requisition);
-static void   gtk_entry_size_allocate        (GtkWidget        *widget,
-                                             GtkAllocation    *allocation);
-static void   gtk_entry_draw_frame           (GtkWidget        *widget);
-static gint   gtk_entry_expose               (GtkWidget        *widget,
-                                             GdkEventExpose   *event);
-static void   gtk_entry_grab_focus           (GtkWidget        *widget);
-static void   gtk_entry_style_set            (GtkWidget        *widget,
-                                             GtkStyle         *previous_style);
-static void   gtk_entry_direction_changed    (GtkWidget        *widget,
-                                             GtkTextDirection  previous_dir);
-static void   gtk_entry_state_changed        (GtkWidget        *widget,
-                                             GtkStateType      previous_state);
-
-/* GtkEditable method implementations
- */
-static void     gtk_entry_insert_text          (GtkEditable *editable,
-                                               const gchar *new_text,
-                                               gint         new_text_length,
-                                               gint        *position);
-static void     gtk_entry_delete_text          (GtkEditable *editable,
-                                               gint         start_pos,
-                                               gint         end_pos);
-
-static void     gtk_entry_real_set_position    (GtkEditable *editable,
-                                                gint         position);
-static gint     gtk_entry_get_position         (GtkEditable *editable);
-
-/* Default signal handlers
- */
-static void gtk_entry_real_insert_text   (GtkEditable     *editable,
-                                         const gchar     *new_text,
-                                         gint             new_text_length,
-                                         gint            *position);
-static void gtk_entry_real_delete_text   (GtkEditable     *editable,
-                                         gint             start_pos,
-                                         gint             end_pos);
-static void gtk_entry_move_cursor        (GtkEntry        *entry,
-                                         GtkMovementStep  step,
-                                         gint             count,
-                                         gboolean         extend_selection);
-static void gtk_entry_insert_at_cursor   (GtkEntry        *entry,
-                                         const gchar     *str);
-static void gtk_entry_delete_from_cursor (GtkEntry        *entry,
-                                         GtkDeleteType    type,
-                                         gint             count);
-
-/* IM Context Callbacks
- */
-static void     gtk_entry_commit_cb               (GtkIMContext *context,
-                                                   const gchar  *str,
-                                                   GtkEntry     *entry);
-static void     gtk_entry_preedit_changed_cb      (GtkIMContext *context,
-                                                   GtkEntry     *entry);
-static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
-                                                   GtkEntry     *entry);
-static gboolean gtk_entry_delete_surrounding_cb   (GtkIMContext *context,
-                                                   gint          offset,
-                                                   gint          n_chars,
-                                                   GtkEntry     *entry);
-
-/* Internal routines
- */
-static void         gtk_entry_enter_text               (GtkEntry       *entry,
-                                                        const gchar    *str);
-static void         gtk_entry_set_positions            (GtkEntry       *entry,
-                                                        gint            current_pos,
-                                                        gint            selection_bound);
-static void         gtk_entry_draw_text                (GtkEntry       *entry);
-static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
-                                                       CursorType      type);
-static PangoLayout *gtk_entry_ensure_layout            (GtkEntry       *entry,
-                                                        gboolean        include_preedit);
-static void         gtk_entry_queue_draw               (GtkEntry       *entry);
-static void         gtk_entry_reset_im_context         (GtkEntry       *entry);
-static void         gtk_entry_recompute                (GtkEntry       *entry);
-static void         gtk_entry_get_cursor_locations     (GtkEntry       *entry,
-                                                       CursorType      type,
-                                                       gint           *strong_x,
-                                                       gint           *weak_x);
-static void         gtk_entry_adjust_scroll            (GtkEntry       *entry);
-static gint         gtk_entry_move_visually            (GtkEntry       *editable,
-                                                       gint            start,
-                                                       gint            count);
-static gint         gtk_entry_move_logically           (GtkEntry       *entry,
-                                                       gint            start,
-                                                       gint            count);
-static gint         gtk_entry_move_forward_word        (GtkEntry       *entry,
-                                                       gint            start);
-static gint         gtk_entry_move_backward_word       (GtkEntry       *entry,
-                                                       gint            start);
-static void         gtk_entry_delete_whitespace        (GtkEntry       *entry);
-static char *       gtk_entry_get_public_chars         (GtkEntry       *entry,
-                                                       gint            start,
-                                                       gint            end);
-static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
-static void         gtk_entry_state_changed            (GtkWidget      *widget,
-                                                       GtkStateType    previous_state);
-static void         gtk_entry_check_cursor_blink       (GtkEntry       *entry);
-static void         gtk_entry_pend_cursor_blink        (GtkEntry       *entry);
-static void         get_text_area_size                 (GtkEntry       *entry,
-                                                       gint           *x,
-                                                       gint           *y,
-                                                       gint           *width,
-                                                       gint           *height);
-static void         get_widget_window_size             (GtkEntry       *entry,
-                                                       gint           *x,
-                                                       gint           *y,
-                                                       gint           *width,
-                                                       gint           *height);
-
-static GtkEntryClass *parent_class = NULL;
-
-GtkType
-gtk_item_entry_get_type (void)
-{
-  static GtkType item_entry_type = 0;
-
-  if (!item_entry_type)
-    {
-      static const GtkTypeInfo item_entry_info =
-      {
-       "GtkItemEntry",
-       sizeof (GtkItemEntry),
-       sizeof (GtkItemEntryClass),
-       (GtkClassInitFunc) gtk_item_entry_class_init,
-       (GtkObjectInitFunc) gtk_item_entry_init,
-       /* reserved_1 */ NULL,
-       /* reserved_2 */ NULL,
-        (GtkClassInitFunc) NULL,
-      };
-
-      static const GInterfaceInfo item_editable_info =
-      {
-        (GInterfaceInitFunc) gtk_item_entry_editable_init,    /* interface_init */
-        NULL,                                            /* interface_finalize */
-        NULL                                             /* interface_data */
-      };
-
-
-      item_entry_type = gtk_type_unique (GTK_TYPE_ENTRY, &item_entry_info);
-
-      g_type_add_interface_static (item_entry_type,
-                                   GTK_TYPE_EDITABLE,
-                                   &item_editable_info);
-
-    }
-
-  return item_entry_type;
-}
-
-static void
-gtk_item_entry_class_init (GtkItemEntryClass *class)
-{
-  GtkObjectClass *object_class;
-  GtkWidgetClass *widget_class;
-  GtkEntryClass *entry_class;
-
-  object_class = (GtkObjectClass*) class;
-  widget_class = (GtkWidgetClass*) class;
-  parent_class = gtk_type_class (GTK_TYPE_ENTRY);
-  entry_class = (GtkEntryClass *) class;
-
-  widget_class->realize = gtk_entry_realize;
-  widget_class->size_request = gtk_entry_size_request;
-  widget_class->size_allocate = gtk_entry_size_allocate;
-  widget_class->expose_event = gtk_entry_expose;
-  widget_class->grab_focus = gtk_entry_grab_focus;
-  widget_class->style_set = gtk_entry_style_set;
-  widget_class->direction_changed = gtk_entry_direction_changed;
-  widget_class->state_changed = gtk_entry_state_changed;
-
-  entry_class->move_cursor = gtk_entry_move_cursor;
-  entry_class->insert_at_cursor = gtk_entry_insert_at_cursor;
-  entry_class->delete_from_cursor = gtk_entry_delete_from_cursor;
-
-}
-
-static void
-gtk_item_entry_editable_init (GtkEditableClass *iface)
-{
-  iface->do_insert_text = gtk_entry_insert_text;
-  iface->do_delete_text = gtk_entry_delete_text;
-  iface->insert_text = gtk_entry_real_insert_text;
-  iface->delete_text = gtk_entry_real_delete_text;
-  iface->set_position = gtk_entry_real_set_position;
-  iface->get_position = gtk_entry_get_position;
-}
-
-static void
-gtk_item_entry_init (GtkItemEntry *entry)
-{
-  entry->justification = GTK_JUSTIFY_LEFT;
-  entry->text_max_size = 0;
-  GTK_ENTRY(entry)->has_frame = FALSE;
-
-  g_object_unref(G_OBJECT(GTK_ENTRY(entry)->im_context));
-
-  GTK_ENTRY(entry)->im_context = gtk_im_multicontext_new ();
-
-  g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "commit",
-                    G_CALLBACK (gtk_entry_commit_cb), entry);
-  g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "preedit_changed",
-                    G_CALLBACK (gtk_entry_preedit_changed_cb), entry);
-  g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "retrieve_surrounding",
-                    G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry);
-  g_signal_connect (G_OBJECT (GTK_ENTRY(entry)->im_context), "delete_surrounding",
-                    G_CALLBACK (gtk_entry_delete_surrounding_cb), entry);
-
-}
-
-static void
-gtk_entry_realize (GtkWidget *widget)
-{
-  GtkEntry *entry;
-  GtkEditable *editable;
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-
-  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
-  entry = GTK_ENTRY (widget);
-  editable = GTK_EDITABLE (widget);
-
-  attributes.window_type = GDK_WINDOW_CHILD;
-
-  get_widget_window_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height);
-
-  attributes.wclass = GDK_INPUT_OUTPUT;
-  attributes.visual = gtk_widget_get_visual (widget);
-  attributes.colormap = gtk_widget_get_colormap (widget);
-  attributes.event_mask = gtk_widget_get_events (widget);
-  attributes.event_mask |= (GDK_EXPOSURE_MASK |
-                            GDK_BUTTON_PRESS_MASK |
-                            GDK_BUTTON_RELEASE_MASK |
-                            GDK_BUTTON1_MOTION_MASK |
-                            GDK_BUTTON3_MOTION_MASK |
-                            GDK_POINTER_MOTION_HINT_MASK |
-                            GDK_POINTER_MOTION_MASK |
-                            GDK_ENTER_NOTIFY_MASK |
-                            GDK_LEAVE_NOTIFY_MASK);
-  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-
-  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
-  gdk_window_set_user_data (widget->window, entry);
-
-  get_text_area_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height);
-
-  attributes.cursor = gdk_cursor_new (GDK_XTERM);
-  attributes_mask |= GDK_WA_CURSOR;
-
-  entry->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
-  gdk_window_set_user_data (entry->text_area, entry);
-
-  gdk_cursor_unref (attributes.cursor);
-
-  widget->style = gtk_style_attach (widget->style, widget->window);
-
-  gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]);
-  gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
-
-  gdk_window_show (entry->text_area);
-
-  gtk_im_context_set_client_window (entry->im_context, entry->text_area);
-
-  gtk_entry_adjust_scroll (entry);
-}
-
-static void
-get_borders (GtkEntry *entry,
-             gint     *xborder,
-             gint     *yborder)
-{
-  GtkWidget *widget = GTK_WIDGET (entry);
-  gint focus_width;
-  gboolean interior_focus;
-
-  gtk_widget_style_get (widget,
-                       "interior-focus", &interior_focus,
-                       "focus-line-width", &focus_width,
-                       NULL);
-
-  if (entry->has_frame)
-    {
-      *xborder = widget->style->xthickness;
-      *yborder = widget->style->ythickness;
-    }
-  else
-    {
-      *xborder = 0;
-      *yborder = 0;
-    }
-
-  if (!interior_focus)
-    {
-      *xborder += focus_width;
-      *yborder += focus_width;
-    }
-
-}
-
-static void
-gtk_entry_size_request (GtkWidget      *widget,
-                       GtkRequisition *requisition)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-  PangoFontMetrics *metrics;
-  gint xborder, yborder;
-  PangoContext *context;
-
-  context = gtk_widget_get_pango_context (widget);
-  metrics = pango_context_get_metrics (context,
-                                      widget->style->font_desc,
-                                      pango_context_get_language (context));
-
-  entry->ascent = pango_font_metrics_get_ascent (metrics);
-  entry->descent = pango_font_metrics_get_descent (metrics);
-
-  get_borders (entry, &xborder, &yborder);
-
-  xborder += INNER_BORDER;
-  yborder += INNER_BORDER;
-
-  if (entry->width_chars < 0)
-    requisition->width = MIN_ENTRY_WIDTH + xborder * 2;
-  else
-    {
-      gint char_width = pango_font_metrics_get_approximate_char_width (metrics);
-      requisition->width = PANGO_PIXELS (char_width) * entry->width_chars + xborder * 2;
-    }
-
-  requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2;
-
-  pango_font_metrics_unref (metrics);
-}
-
-static void
-get_text_area_size (GtkEntry *entry,
-                    gint     *x,
-                    gint     *y,
-                    gint     *width,
-                    gint     *height)
-{
-  gint xborder, yborder;
-  GtkRequisition requisition;
-  GtkWidget *widget = GTK_WIDGET (entry);
-
-  gtk_widget_get_child_requisition (widget, &requisition);
-
-  get_borders (entry, &xborder, &yborder);
-
-  if (x)
-    *x = xborder;
-
-  if (y)
-    *y = yborder;
-
-  if (width)
-    *width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
-
-  if (height)
-    *height = requisition.height - yborder * 2;
-}
-
-static void
-get_widget_window_size (GtkEntry *entry,
-                        gint     *x,
-                        gint     *y,
-                        gint     *width,
-                        gint     *height)
-{
-  GtkRequisition requisition;
-  GtkWidget *widget = GTK_WIDGET (entry);
-
-  gtk_widget_get_child_requisition (widget, &requisition);
-
-  if (x)
-    *x = widget->allocation.x;
-
-  if (y)
-    {
-      if (entry->is_cell_renderer)
-       *y = widget->allocation.y;
-      else
-       *y = widget->allocation.y + (widget->allocation.height - requisition.height) / 2;
-    }
-
-  if (width)
-    *width = widget->allocation.width;
-
-  if (height)
-    {
-      if (entry->is_cell_renderer)
-       *height = widget->allocation.height;
-      else
-       *height = requisition.height;
-    }
-}
-
-static void
-gtk_entry_size_allocate (GtkWidget     *widget,
-                        GtkAllocation *allocation)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-  GtkItemEntry *ientry = GTK_ITEM_ENTRY (widget);
-
-  if(ientry->text_max_size > 0)
-    allocation->width = MIN(ientry->text_max_size, allocation->width);
-
-  widget->allocation = *allocation;
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      /* We call gtk_widget_get_child_requisition, since we want (for
-       * backwards compatibility reasons) the realization here to
-       * be affected by the usize of the entry, if set
-       */
-      gint x, y, width, height;
-
-      get_widget_window_size (entry, &x, &y, &width, &height);
-
-      gdk_window_move_resize (widget->window,
-                              allocation->x, allocation->y, allocation->width, allocation->height);
-
-      get_text_area_size (entry, &x, &y, &width, &height);
-
-      gdk_window_move_resize (entry->text_area,
-                              0, allocation->height - height, allocation->width, height);
-
-      gtk_entry_recompute (entry);
-    }
-}
-
-static void
-gtk_entry_draw_frame (GtkWidget *widget)
-{
-}
-
-static gint
-gtk_entry_expose (GtkWidget      *widget,
-                 GdkEventExpose *event)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-
-  if (widget->window == event->window)
-    gtk_entry_draw_frame (widget);
-  else if (entry->text_area == event->window)
-    {
-      gint area_width, area_height;
-
-      get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
-
-      gdk_draw_rectangle (entry->text_area,
-                          widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
-                          TRUE,
-                          0, 0, area_width, area_height);
-
-      if ((entry->visible || entry->invisible_char != 0) &&
-         GTK_WIDGET_HAS_FOCUS (widget) &&
-         entry->selection_bound == entry->current_pos && entry->cursor_visible)
-       gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
-
-      if (entry->dnd_position != -1)
-       gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND);
-
-      gtk_entry_draw_text (GTK_ENTRY (widget));
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_entry_grab_focus (GtkWidget        *widget)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-  gboolean select_on_focus;
-
-  GTK_WIDGET_CLASS (parent_class)->grab_focus (widget);
-
-  g_object_get (G_OBJECT (gtk_settings_get_default ()),
-               "gtk-entry-select-on-focus",
-               &select_on_focus,
-               NULL);
-
-  if (select_on_focus && entry->editable && !entry->in_click)
-    gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
-}
-
-static void
-gtk_entry_direction_changed (GtkWidget        *widget,
-                            GtkTextDirection  previous_dir)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-
-  gtk_entry_recompute (entry);
-
-  GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
-}
-
-static void
-gtk_entry_state_changed (GtkWidget      *widget,
-                        GtkStateType    previous_state)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
-      gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
-    }
-
-  if (!GTK_WIDGET_IS_SENSITIVE (widget))
-    {
-      /* Clear any selection */
-      gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
-    }
-
-  gtk_widget_queue_clear (widget);
-}
-
-/* GtkEditable method implementations
- */
-static void
-gtk_entry_insert_text (GtkEditable *editable,
-                      const gchar *new_text,
-                      gint         new_text_length,
-                      gint        *position)
-{
-  GtkEntry *entry = GTK_ENTRY (editable);
-  gchar buf[64];
-  gchar *text;
-
-  if (*position < 0 || *position > entry->text_length)
-    *position = entry->text_length;
-
-  g_object_ref (G_OBJECT (editable));
-
-  if (new_text_length <= 63)
-    text = buf;
-  else
-    text = g_new (gchar, new_text_length + 1);
-
-  text[new_text_length] = '\0';
-  strncpy (text, new_text, new_text_length);
-
-  g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position);
-
-  if (new_text_length > 63)
-    g_free (text);
-
-  g_object_unref (G_OBJECT (editable));
-}
-
-static void
-gtk_entry_delete_text (GtkEditable *editable,
-                      gint         start_pos,
-                      gint         end_pos)
-{
-  GtkEntry *entry = GTK_ENTRY (editable);
-
-  if (end_pos < 0 || end_pos > entry->text_length)
-    end_pos = entry->text_length;
-  if (start_pos < 0)
-    start_pos = 0;
-  if (start_pos > end_pos)
-    start_pos = end_pos;
-
-  g_object_ref (G_OBJECT (editable));
-
-  g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
-
-  g_object_unref (G_OBJECT (editable));
-}
-
-static void
-gtk_entry_style_set    (GtkWidget      *widget,
-                        GtkStyle       *previous_style)
-{
-  GtkEntry *entry = GTK_ENTRY (widget);
-
-  if (previous_style && GTK_WIDGET_REALIZED (widget))
-    {
-      gtk_entry_recompute (entry);
-
-      gdk_window_set_background (widget->window, &widget->style->bg[GTK_WIDGET_STATE(widget)]);
-      gdk_window_set_background (entry->text_area, &widget->style->bg[GTK_WIDGET_STATE (widget)]);
-    }
-}
-
-static void
-gtk_entry_real_set_position (GtkEditable *editable,
-                             gint         position)
-{
-  GtkEntry *entry = GTK_ENTRY (editable);
-
-  if (position < 0 || position > entry->text_length)
-    position = entry->text_length;
-
-  if (position != entry->current_pos ||
-      position != entry->selection_bound)
-    {
-      gtk_entry_reset_im_context (entry);
-      gtk_entry_set_positions (entry, position, position);
-    }
-}
-
-static gint
-gtk_entry_get_position (GtkEditable *editable)
-{
-  return GTK_ENTRY (editable)->current_pos;
-}
-
-
-/* Default signal handlers
- */
-static void
-gtk_entry_real_insert_text (GtkEditable *editable,
-                           const gchar *new_text,
-                           gint         new_text_length,
-                           gint        *position)
-{
-  gint index;
-  gint n_chars;
-
-  GtkEntry *entry = GTK_ENTRY (editable);
-
-  if (new_text_length < 0)
-    new_text_length = strlen (new_text);
-
-  n_chars = g_utf8_strlen (new_text, new_text_length);
-  if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
-    {
-      gdk_beep ();
-      n_chars = entry->text_max_length - entry->text_length;
-      new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
-    }
-
-  if (new_text_length + entry->n_bytes + 1 > entry->text_size)
-    {
-      while (new_text_length + entry->n_bytes + 1 > entry->text_size)
-       {
-         if (entry->text_size == 0)
-           entry->text_size = MIN_SIZE;
-         else
-           {
-             if (2 * (guint)entry->text_size < MAX_SIZE &&
-                 2 * (guint)entry->text_size > entry->text_size)
-               entry->text_size *= 2;
-             else
-               {
-                 entry->text_size = MAX_SIZE;
-                 if (new_text_length > (gint)entry->text_size - (gint)entry->n_bytes - 1)
-                   {
-                     new_text_length = (gint)entry->text_size - (gint)entry->n_bytes - 1;
-                     new_text_length = g_utf8_find_prev_char (new_text, new_text + new_text_length + 1) - new_text;
-                     n_chars = g_utf8_strlen (new_text, new_text_length);
-                   }
-                 break;
-               }
-           }
-       }
-
-      entry->text = g_realloc (entry->text, entry->text_size);
-    }
-
-  index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
-
-  g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
-  memcpy (entry->text + index, new_text, new_text_length);
-
-  entry->n_bytes += new_text_length;
-  entry->text_length += n_chars;
-
-  /* NUL terminate for safety and convenience */
-  entry->text[entry->n_bytes] = '\0';
-
-  if (entry->current_pos > *position)
-    entry->current_pos += n_chars;
-
-  if (entry->selection_bound > *position)
-    entry->selection_bound += n_chars;
-
-  *position += n_chars;
-
-  gtk_entry_recompute (entry);
-
-  g_signal_emit_by_name (editable, "changed");
-  g_object_notify (G_OBJECT (editable), "text");
-}
-
-static void
-gtk_entry_real_delete_text (GtkEditable *editable,
-                           gint         start_pos,
-                           gint         end_pos)
-{
-  GtkEntry *entry = GTK_ENTRY (editable);
-
-  if (start_pos < 0)
-    start_pos = 0;
-  if (end_pos < 0 || end_pos > entry->text_length)
-    end_pos = entry->text_length;
-
-  if (start_pos < end_pos)
-    {
-      gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text;
-      gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text;
-
-      g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index);
-      entry->text_length -= (end_pos - start_pos);
-      entry->n_bytes -= (end_index - start_index);
-
-      if (entry->current_pos > start_pos)
-       entry->current_pos -= MIN (entry->current_pos, end_pos) - start_pos;
-
-      if (entry->selection_bound > start_pos)
-       entry->selection_bound -= MIN (entry->selection_bound, end_pos) - start_pos;
-      /* We might have deleted the selection
-       */
-      gtk_entry_update_primary_selection (entry);
-
-      gtk_entry_recompute (entry);
-
-      g_signal_emit_by_name (editable, "changed");
-      g_object_notify (G_OBJECT (editable), "text");
-    }
-}
-
-/* Compute the X position for an offset that corresponds to the "more important
- * cursor position for that offset. We use this when trying to guess to which
- * end of the selection we should go to when the user hits the left or
- * right arrow key.
- */
-static gint
-get_better_cursor_x (GtkEntry *entry,
-                    gint      offset)
-{
-  GtkTextDirection keymap_direction =
-    (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
-    GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
-  GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
-  gboolean split_cursor;
-
-  PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
-  gint index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text;
-
-  PangoRectangle strong_pos, weak_pos;
-
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
-               "gtk-split-cursor", &split_cursor,
-               NULL);
-
-  pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
-
-  if (split_cursor)
-    return strong_pos.x / PANGO_SCALE;
-  else
-    return (keymap_direction == widget_direction) ? strong_pos.x / PANGO_SCALE : weak_pos.x / PANGO_SCALE;
-}
-
-static void
-gtk_entry_move_cursor (GtkEntry       *entry,
-                      GtkMovementStep step,
-                      gint            count,
-                      gboolean        extend_selection)
-{
-  gint new_pos = entry->current_pos;
-
-  gtk_entry_reset_im_context (entry);
-
-  if (entry->current_pos != entry->selection_bound && !extend_selection)
-    {
-      /* If we have a current selection and aren't extending it, move to the
-       * start/or end of the selection as appropriate
-       */
-      switch (step)
-       {
-       case GTK_MOVEMENT_VISUAL_POSITIONS:
-         {
-           gint current_x = get_better_cursor_x (entry, entry->current_pos);
-           gint bound_x = get_better_cursor_x (entry, entry->selection_bound);
-
-           if (count < 0)
-             new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound;
-           else
-             new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound;
-
-           break;
-         }
-       case GTK_MOVEMENT_LOGICAL_POSITIONS:
-       case GTK_MOVEMENT_WORDS:
-         if (count < 0)
-           new_pos = MIN (entry->current_pos, entry->selection_bound);
-         else
-           new_pos = MAX (entry->current_pos, entry->selection_bound);
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
-       case GTK_MOVEMENT_PARAGRAPH_ENDS:
-       case GTK_MOVEMENT_BUFFER_ENDS:
-         new_pos = count < 0 ? 0 : entry->text_length;
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINES:
-       case GTK_MOVEMENT_PARAGRAPHS:
-       case GTK_MOVEMENT_PAGES:
-         break;
-       default:
-         break;
-       }
-    }
-  else
-    {
-      switch (step)
-       {
-       case GTK_MOVEMENT_LOGICAL_POSITIONS:
-         new_pos = gtk_entry_move_logically (entry, new_pos, count);
-         break;
-       case GTK_MOVEMENT_VISUAL_POSITIONS:
-         new_pos = gtk_entry_move_visually (entry, new_pos, count);
-         break;
-       case GTK_MOVEMENT_WORDS:
-         while (count > 0)
-           {
-             new_pos = gtk_entry_move_forward_word (entry, new_pos);
-             count--;
-           }
-         while (count < 0)
-           {
-             new_pos = gtk_entry_move_backward_word (entry, new_pos);
-             count++;
-           }
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
-       case GTK_MOVEMENT_PARAGRAPH_ENDS:
-       case GTK_MOVEMENT_BUFFER_ENDS:
-         new_pos = count < 0 ? 0 : entry->text_length;
-         break;
-       case GTK_MOVEMENT_DISPLAY_LINES:
-       case GTK_MOVEMENT_PARAGRAPHS:
-       case GTK_MOVEMENT_PAGES:
-         break;
-       default:
-         break;
-       }
-    }
-
-  if (extend_selection)
-    gtk_editable_select_region (GTK_EDITABLE (entry), entry->selection_bound, new_pos);
-  else
-    gtk_editable_set_position (GTK_EDITABLE (entry), new_pos);
-
-  gtk_entry_pend_cursor_blink (entry);
-}
-
-static void
-gtk_entry_insert_at_cursor (GtkEntry    *entry,
-                           const gchar *str)
-{
-  GtkEditable *editable = GTK_EDITABLE (entry);
-  gint pos = entry->current_pos;
-
-  if (entry->editable)
-    {
-      gtk_entry_reset_im_context (entry);
-
-      gtk_editable_insert_text (editable, str, -1, &pos);
-      gtk_editable_set_position (editable, pos);
-    }
-}
-
-static void
-gtk_entry_delete_from_cursor (GtkEntry       *entry,
-                             GtkDeleteType   type,
-                             gint            count)
-{
-  GtkEditable *editable = GTK_EDITABLE (entry);
-  gint start_pos = entry->current_pos;
-  gint end_pos = entry->current_pos;
-
-  gtk_entry_reset_im_context (entry);
-
-  if (!entry->editable)
-    return;
-
-  if (entry->selection_bound != entry->current_pos)
-    {
-      gtk_editable_delete_selection (editable);
-      return;
-    }
-
-  switch (type)
-    {
-    case GTK_DELETE_CHARS:
-      end_pos = gtk_entry_move_logically (entry, entry->current_pos, count);
-      gtk_editable_delete_text (editable, MIN (start_pos, end_pos), MAX (start_pos, end_pos));
-      break;
-    case GTK_DELETE_WORDS:
-      if (count < 0)
-       {
-         /* Move to end of current word, or if not on a word, end of previous word */
-         end_pos = gtk_entry_move_backward_word (entry, end_pos);
-         end_pos = gtk_entry_move_forward_word (entry, end_pos);
-       }
-      else if (count > 0)
-       {
-         /* Move to beginning of current word, or if not on a word, begining of next word */
-         start_pos = gtk_entry_move_forward_word (entry, start_pos);
-         start_pos = gtk_entry_move_backward_word (entry, start_pos);
-       }
-
-      /* Fall through */
-    case GTK_DELETE_WORD_ENDS:
-      while (count < 0)
-       {
-         start_pos = gtk_entry_move_backward_word (entry, start_pos);
-         count++;
-       }
-      while (count > 0)
-       {
-         end_pos = gtk_entry_move_forward_word (entry, end_pos);
-         count--;
-       }
-      gtk_editable_delete_text (editable, start_pos, end_pos);
-      break;
-    case GTK_DELETE_DISPLAY_LINE_ENDS:
-    case GTK_DELETE_PARAGRAPH_ENDS:
-      if (count < 0)
-       gtk_editable_delete_text (editable, 0, entry->current_pos);
-      else
-       gtk_editable_delete_text (editable, entry->current_pos, -1);
-      break;
-    case GTK_DELETE_DISPLAY_LINES:
-    case GTK_DELETE_PARAGRAPHS:
-      gtk_editable_delete_text (editable, 0, -1);
-      break;
-    case GTK_DELETE_WHITESPACE:
-      gtk_entry_delete_whitespace (entry);
-      break;
-    }
-
-  gtk_entry_pend_cursor_blink (entry);
-}
-
-/* IM Context Callbacks
- */
-
-static void
-gtk_entry_commit_cb (GtkIMContext *context,
-                     const gchar  *str,
-                     GtkEntry     *entry)
-{
-  gtk_entry_enter_text (entry, str);
-}
-
-static void
-gtk_entry_preedit_changed_cb (GtkIMContext *context,
-                              GtkEntry     *entry)
-{
-  gchar *preedit_string;
-  gint cursor_pos;
-
-  gtk_im_context_get_preedit_string (entry->im_context,
-                                     &preedit_string, NULL,
-                                     &cursor_pos);
-  entry->preedit_length = strlen (preedit_string);
-  cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
-  entry->preedit_cursor = cursor_pos;
-  g_free (preedit_string);
-
-  gtk_entry_recompute (entry);
-}
-
-static gboolean
-gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
-                               GtkEntry     *entry)
-{
-  gtk_im_context_set_surrounding (context,
-                                  entry->text,
-                                  entry->n_bytes,
-                                  g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text);
-
-  return TRUE;
-}
-
-static gboolean
-gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
-                                 gint          offset,
-                                 gint          n_chars,
-                                 GtkEntry     *entry)
-{
-  gtk_editable_delete_text (GTK_EDITABLE (entry),
-                            entry->current_pos + offset,
-                            entry->current_pos + offset + n_chars);
-
-  return TRUE;
-}
-
-
-/* Internal functions
- */
-
-/* Used for im_commit_cb and inserting Unicode chars */
-static void
-gtk_entry_enter_text (GtkEntry       *entry,
-                      const gchar    *str)
-{
-  GtkEditable *editable = GTK_EDITABLE (entry);
-  gint tmp_pos;
-
-  if (gtk_editable_get_selection_bounds (editable, NULL, NULL))
-    gtk_editable_delete_selection (editable);
-  else
-    {
-      if (entry->overwrite_mode)
-        gtk_entry_delete_from_cursor (entry, GTK_DELETE_CHARS, 1);
-    }
-
-  tmp_pos = entry->current_pos;
-  gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
-  gtk_editable_set_position (editable, tmp_pos);
-}
-
-/* All changes to entry->current_pos and entry->selection_bound
- * should go through this function.
- */
-static void
-gtk_entry_set_positions (GtkEntry *entry,
-                         gint      current_pos,
-                         gint      selection_bound)
-{
-  gboolean changed = FALSE;
-
-  g_object_freeze_notify (G_OBJECT (entry));
-
-  if (current_pos != -1 &&
-      entry->current_pos != current_pos)
-    {
-      entry->current_pos = current_pos;
-      changed = TRUE;
-
-      g_object_notify (G_OBJECT (entry), "cursor_position");
-    }
-
-  if (selection_bound != -1 &&
-      entry->selection_bound != selection_bound)
-    {
-      entry->selection_bound = selection_bound;
-      changed = TRUE;
-
-      g_object_notify (G_OBJECT (entry), "selection_bound");
-    }
-
-  g_object_thaw_notify (G_OBJECT (entry));
-
-  if (changed)
-    gtk_entry_recompute (entry);
-}
-
-static void
-gtk_entry_reset_layout (GtkEntry *entry)
-{
-  if (entry->cached_layout)
-    {
-      g_object_unref (G_OBJECT (entry->cached_layout));
-      entry->cached_layout = NULL;
-    }
-}
-
-static void
-update_im_cursor_location (GtkEntry *entry)
-{
-  GdkRectangle area;
-  gint strong_x;
-  gint strong_xoffset;
-  gint x, y, area_width, area_height;
-
-  gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL)
-;
-  get_text_area_size (entry, &x, &y, &area_width, &area_height);
-
-  strong_xoffset = strong_x - entry->scroll_offset;
-  if (strong_xoffset < 0)
-    {
-      strong_xoffset = 0;
-    }
-  else if (strong_xoffset > area_width)
-    {
-      strong_xoffset = area_width;
-    }
-  area.x = x + strong_xoffset;
-  area.y = y + area_height;
-  area.width = area_width;
-  area.height = area_height;
-
-  gtk_im_context_set_cursor_location (entry->im_context, &area);
-}
-
-static gboolean
-recompute_idle_func (gpointer data)
-{
-  GtkEntry *entry;
-
-  GDK_THREADS_ENTER ();
-
-  entry = GTK_ENTRY (data);
-
-  gtk_entry_adjust_scroll (entry);
-  gtk_entry_queue_draw (entry);
-
-  entry->recompute_idle = FALSE;
-
-  update_im_cursor_location (entry);
-
-  GDK_THREADS_LEAVE ();
-
-  return FALSE;
-}
-
-static void
-gtk_entry_recompute (GtkEntry *entry)
-{
-  gtk_entry_reset_layout (entry);
-  gtk_entry_check_cursor_blink (entry);
-
-
-  if (!entry->recompute_idle)
-    {
-      entry->recompute_idle = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 15, /* between resize and redraw */
-                                              recompute_idle_func, entry, NULL);
-    }
-}
-
-static void
-append_char (GString *str,
-             gunichar ch,
-             gint     count)
-{
-  gint i;
-  gint char_len;
-  gchar buf[7];
-
-  char_len = g_unichar_to_utf8 (ch, buf);
-
-  i = 0;
-  while (i < count)
-    {
-      g_string_append_len (str, buf, char_len);
-      ++i;
-    }
-}
-
-static PangoLayout *
-gtk_entry_create_layout (GtkEntry *entry,
-                        gboolean  include_preedit)
-{
-  PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (entry), NULL);
-  PangoAttrList *tmp_attrs = pango_attr_list_new ();
-
-  gchar *preedit_string = NULL;
-  gint preedit_length = 0;
-  PangoAttrList *preedit_attrs = NULL;
-
-  pango_layout_set_single_paragraph_mode (layout, TRUE);
-
-  if (include_preedit)
-    {
-      gtk_im_context_get_preedit_string (entry->im_context,
-                                        &preedit_string, &preedit_attrs, NULL);
-      preedit_length = entry->preedit_length;
-    }
-
-  if (preedit_length)
-    {
-      GString *tmp_string = g_string_new (NULL);
-
-      gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text;
-
-      if (entry->visible)
-        {
-          g_string_prepend_len (tmp_string, entry->text, entry->n_bytes);
-          g_string_insert (tmp_string, cursor_index, preedit_string);
-        }
-      else
-        {
-          gint ch_len;
-          gint preedit_len_chars;
-          gunichar invisible_char;
-
-          ch_len = g_utf8_strlen (entry->text, entry->n_bytes);
-          preedit_len_chars = g_utf8_strlen (preedit_string, -1);
-          ch_len += preedit_len_chars;
-
-          if (entry->invisible_char != 0)
-            invisible_char = entry->invisible_char;
-          else
-            invisible_char = ' '; /* just pick a char */
-
-          append_char (tmp_string, invisible_char, ch_len);
-
-          /* Fix cursor index to point to invisible char corresponding
-           * to the preedit, fix preedit_length to be the length of
-           * the invisible chars representing the preedit
-           */
-          cursor_index =
-            g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) -
-            tmp_string->str;
-          preedit_length =
-            preedit_len_chars *
-            g_unichar_to_utf8 (invisible_char, NULL);
-        }
-
-      pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
-
-      pango_attr_list_splice (tmp_attrs, preedit_attrs,
-                             cursor_index, preedit_length);
-
-      g_string_free (tmp_string, TRUE);
-    }
-  else
-    {
-      if (entry->visible)
-        {
-          pango_layout_set_text (layout, entry->text, entry->n_bytes);
-        }
-      else
-        {
-          GString *str = g_string_new (NULL);
-          gunichar invisible_char;
-
-          if (entry->invisible_char != 0)
-            invisible_char = entry->invisible_char;
-          else
-            invisible_char = ' '; /* just pick a char */
-
-          append_char (str, invisible_char, entry->text_length);
-          pango_layout_set_text (layout, str->str, str->len);
-          g_string_free (str, TRUE);
-        }
-    }
-
-  pango_layout_set_attributes (layout, tmp_attrs);
-
-  if (preedit_string)
-    g_free (preedit_string);
-  if (preedit_attrs)
-    pango_attr_list_unref (preedit_attrs);
-
-  pango_attr_list_unref (tmp_attrs);
-
-  return layout;
-}
-
-static PangoLayout *
-gtk_entry_ensure_layout (GtkEntry *entry,
-                         gboolean  include_preedit)
-{
-  if (entry->preedit_length > 0 &&
-      !include_preedit != !entry->cache_includes_preedit)
-    gtk_entry_reset_layout (entry);
-
-  if (!entry->cached_layout)
-    {
-      entry->cached_layout = gtk_entry_create_layout (entry, include_preedit);
-      entry->cache_includes_preedit = include_preedit;
-    }
-
-  return entry->cached_layout;
-}
-
-static void
-get_layout_position (GtkEntry *entry,
-                     gint     *x,
-                     gint     *y)
-{
-  PangoLayout *layout;
-  PangoRectangle logical_rect;
-  gint area_width, area_height;
-  gint y_pos;
-  PangoLayoutLine *line;
-
-  layout = gtk_entry_ensure_layout (entry, TRUE);
-
-  get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
-
-  area_height = PANGO_SCALE * (area_height);
-
-  line = pango_layout_get_lines (layout)->data;
-  pango_layout_line_get_extents (line, NULL, &logical_rect);
-
-  /* Align primarily for locale's ascent/descent */
-
-  y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
-           entry->ascent + logical_rect.y);
-
-
-  /* Now see if we need to adjust to fit in actual drawn string */
-
-  if (logical_rect.height > area_height)
-    y_pos = (area_height - logical_rect.height) / 2;
-  else if (y_pos < 0)
-    y_pos = 0;
-  else if (y_pos + logical_rect.height > area_height)
-    y_pos = area_height - logical_rect.height;
-
-  y_pos = y_pos / PANGO_SCALE;
-
-  if (x)
-    *x = - entry->scroll_offset;
-
-  if (y)
-    *y = y_pos;
-}
-
-static void
-gtk_entry_draw_text (GtkEntry *entry)
-{
-  GtkWidget *widget;
-  PangoLayoutLine *line;
-
-  if (!entry->visible && entry->invisible_char == 0)
-    return;
-
-  if (GTK_WIDGET_DRAWABLE (entry))
-    {
-      PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
-      gint area_width, area_height;
-
-      gint x, y;
-      gint start_pos, end_pos;
-
-      widget = GTK_WIDGET (entry);
-
-      get_layout_position (entry, &x, &y);
-
-      get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
-
-
-      gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state],
-                       x, y,
-                      layout);
-
-
-      if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos))
-       {
-         gint *ranges;
-         gint n_ranges, i;
-          PangoRectangle logical_rect;
-         const gchar *text = pango_layout_get_text (layout);
-         gint start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
-         gint end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
-         GdkRegion *clip_region = gdk_region_new ();
-         GdkGC *text_gc;
-         GdkGC *selection_gc;
-
-          line = pango_layout_get_lines (layout)->data;
-
-         pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
-
-          pango_layout_get_extents (layout, NULL, &logical_rect);
-
-         if (GTK_WIDGET_HAS_FOCUS (entry))
-           {
-             selection_gc = widget->style->base_gc [GTK_STATE_SELECTED];
-             text_gc = widget->style->text_gc [GTK_STATE_SELECTED];
-           }
-         else
-           {
-             selection_gc = widget->style->base_gc [GTK_STATE_ACTIVE];
-             text_gc = widget->style->text_gc [GTK_STATE_ACTIVE];
-           }
-
-         for (i=0; i < n_ranges; i++)
-           {
-             GdkRectangle rect;
-
-             rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
-             rect.y = y;
-             rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
-             rect.height = logical_rect.height / PANGO_SCALE;
-
-             gdk_draw_rectangle (entry->text_area, selection_gc, TRUE,
-                                 rect.x, rect.y, rect.width, rect.height);
-
-             gdk_region_union_with_rect (clip_region, &rect);
-           }
-
-         gdk_gc_set_clip_region (text_gc, clip_region);
-         gdk_draw_layout (entry->text_area, text_gc,
-                          x, y,
-                          layout);
-         gdk_gc_set_clip_region (text_gc, NULL);
-
-         gdk_region_destroy (clip_region);
-         g_free (ranges);
-       }
-    }
-}
-
-/*
- * From _gtk_get_insertion_cursor_gc
- */
-
-typedef struct _CursorInfo CursorInfo;
-
-struct _CursorInfo
-{
-  GType for_type;
-  GdkGC *primary_gc;
-  GdkGC *secondary_gc;
-};
-
-static GdkGC *
-make_cursor_gc (GtkWidget *widget,
-               const gchar *property_name,
-               GdkColor *fallback)
-{
-  GdkGCValues gc_values;
-  GdkGCValuesMask gc_values_mask;
-  GdkColor *cursor_color;
-
-  gtk_widget_style_get (widget, property_name, &cursor_color, NULL);
-
-  gc_values_mask = GDK_GC_FOREGROUND;
-  if (cursor_color)
-    {
-      gc_values.foreground = *cursor_color;
-      gdk_color_free (cursor_color);
-    }
-  else
-    gc_values.foreground = *fallback;
-
-  gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground);
-  return gtk_gc_get (widget->style->depth, widget->style->colormap,
-    &gc_values, gc_values_mask);
-}
-
-static GdkGC *
-_gtkextra_get_insertion_cursor_gc (GtkWidget *widget,
-                                  gboolean   is_primary)
-{
-  CursorInfo *cursor_info;
-
-  cursor_info = g_object_get_data (G_OBJECT (widget->style), "gtk-style-cursor-info");
-  if (!cursor_info)
-    {
-      cursor_info = g_new (CursorInfo, 1);
-      g_object_set_data (G_OBJECT (widget->style), "gtk-style-cursor-info", cursor_info);
-      cursor_info->primary_gc = NULL;
-      cursor_info->secondary_gc = NULL;
-      cursor_info->for_type = G_TYPE_INVALID;
-    }
-
-  /* We have to keep track of the type because gtk_widget_style_get()
-   * can return different results when called on the same property and
-   * same style but for different widgets. :-(. That is,
-   * GtkEntry::cursor-color = "red" in a style will modify the cursor
-   * color for entries but not for text view.
-   */
-  if (cursor_info->for_type != G_OBJECT_TYPE (widget))
-    {
-      cursor_info->for_type = G_OBJECT_TYPE (widget);
-      if (cursor_info->primary_gc)
-       {
-         gtk_gc_release (cursor_info->primary_gc);
-         cursor_info->primary_gc = NULL;
-       }
-      if (cursor_info->secondary_gc)
-       {
-         gtk_gc_release (cursor_info->secondary_gc);
-         cursor_info->secondary_gc = NULL;
-       }
-    }
-
-  if (is_primary)
-    {
-      if (!cursor_info->primary_gc)
-       cursor_info->primary_gc = make_cursor_gc (widget,
-                                                 "cursor-color",
-                                                 &widget->style->black);
-
-      return g_object_ref (cursor_info->primary_gc);
-    }
-  else
-    {
-      static GdkColor gray = { 0, 0x8888, 0x8888, 0x8888 };
-
-      if (!cursor_info->secondary_gc)
-       cursor_info->secondary_gc = make_cursor_gc (widget,
-                                                   "secondary-cursor-color",
-                                                   &gray);
-
-      return g_object_ref (cursor_info->secondary_gc);
-    }
-}
-
-/*
- * From _gtk_draw_insertion_cursor
- */
-static void
-_gtkextra_draw_insertion_cursor (GtkWidget *widget,
-                                GdkDrawable *drawable,
-                                GdkGC *gc,
-                                GdkRectangle *location,
-                                GtkTextDirection direction,
-                                gboolean draw_arrow)
-{
-  gint stem_width;
-  gint arrow_width;
-  gint x, y;
-  gint i;
-  gfloat cursor_aspect_ratio;
-  gint offset;
-
-  g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
-
-  gtk_widget_style_get (widget, "cursor-aspect-ratio", &cursor_aspect_ratio, NULL);
-
-  stem_width = location->height * cursor_aspect_ratio + 1;
-  arrow_width = stem_width + 1;
-
-  /* put (stem_width % 2) on the proper side of the cursor */
-  if (direction == GTK_TEXT_DIR_LTR)
-    offset = stem_width / 2;
-  else
-    offset = stem_width - stem_width / 2;
-
-  for (i = 0; i < stem_width; i++)
-    gdk_draw_line (drawable, gc,
-                  location->x + i - offset, location->y,
-                  location->x + i - offset, location->y + location->height - 1);
-
-  if (draw_arrow)
-    {
-      if (direction == GTK_TEXT_DIR_RTL)
-        {
-          x = location->x - offset - 1;
-          y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
-
-          for (i = 0; i < arrow_width; i++)
-            {
-              gdk_draw_line (drawable, gc,
-                             x, y + i + 1,
-                             x, y + 2 * arrow_width - i - 1);
-              x --;
-            }
-        }
-      else if (direction == GTK_TEXT_DIR_LTR)
-        {
-          x = location->x + stem_width - offset;
-          y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
-
-          for (i = 0; i < arrow_width; i++)
-            {
-              gdk_draw_line (drawable, gc,
-                             x, y + i + 1,
-                             x, y + 2 * arrow_width - i - 1);
-              x++;
-            }
-        }
-    }
-}
-
-static void
-gtk_entry_draw_cursor (GtkEntry  *entry,
-                      CursorType type)
-{
-  GtkTextDirection keymap_direction =
-    (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
-    GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
-  GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (entry));
-
-  if (GTK_WIDGET_DRAWABLE (entry) && GTK_ENTRY(entry)->cursor_visible)
-    {
-      GtkWidget *widget = GTK_WIDGET (entry);
-      GdkRectangle cursor_location;
-      gboolean split_cursor;
-
-      gint xoffset = INNER_BORDER - entry->scroll_offset;
-      gint strong_x, weak_x;
-      gint text_area_height;
-      GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
-      GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
-      gint x1 = 0;
-      gint x2 = 0;
-      GdkGC *gc;
-
-      gdk_window_get_size (entry->text_area, NULL, &text_area_height);
-
-      gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
-
-      g_object_get (gtk_widget_get_settings (widget),
-                   "gtk-split-cursor", &split_cursor,
-                   NULL);
-
-      dir1 = widget_direction;
-
-      if (split_cursor)
-       {
-         x1 = strong_x;
-
-         if (weak_x != strong_x)
-           {
-             dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
-             x2 = weak_x;
-           }
-       }
-      else
-       {
-         if (keymap_direction == widget_direction)
-           x1 = strong_x;
-         else
-           x1 = weak_x;
-       }
-
-      cursor_location.x = xoffset + x1;
-      cursor_location.y = INNER_BORDER;
-      cursor_location.width = 0;
-      cursor_location.height = text_area_height - 2 * INNER_BORDER ;
-
-      gc = _gtkextra_get_insertion_cursor_gc (widget, TRUE);
-      _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc,
-                                 &cursor_location, dir1,
-                                  dir2 != GTK_TEXT_DIR_NONE);
-      g_object_unref (gc);
-
-      if (dir2 != GTK_TEXT_DIR_NONE)
-       {
-         cursor_location.x = xoffset + x2;
-         gc = _gtkextra_get_insertion_cursor_gc (widget, FALSE);
-         _gtkextra_draw_insertion_cursor (widget, entry->text_area, gc,
-                                     &cursor_location, dir2,
-                                      TRUE);
-         g_object_unref (gc);
-       }
-    }
-}
-
-static void
-gtk_entry_queue_draw (GtkEntry *entry)
-{
-  if (GTK_WIDGET_REALIZED (entry))
-    gdk_window_invalidate_rect (entry->text_area, NULL, FALSE);
-}
-
-static void
-gtk_entry_reset_im_context (GtkEntry *entry)
-{
-  if (entry->need_im_reset)
-    {
-      entry->need_im_reset = 0;
-      gtk_im_context_reset (entry->im_context);
-    }
-}
-
-static void
-gtk_entry_get_cursor_locations (GtkEntry   *entry,
-                               CursorType  type,
-                               gint       *strong_x,
-                               gint       *weak_x)
-{
-  PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
-  const gchar *text;
-  PangoRectangle strong_pos, weak_pos;
-  gint index;
-
-  if (type == CURSOR_STANDARD)
-    {
-      text = pango_layout_get_text (layout);
-      index = g_utf8_offset_to_pointer (text, entry->current_pos + entry->preedit_cursor) - text;
-    }
-  else /* type == CURSOR_DND */
-    {
-      index = g_utf8_offset_to_pointer (entry->text, entry->dnd_position) - entry->text;
-      if (entry->dnd_position > entry->current_pos)
-       index += entry->preedit_length;
-    }
-
-  pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
-
-  if (strong_x)
-    *strong_x = strong_pos.x / PANGO_SCALE;
-
-  if (weak_x)
-    *weak_x = weak_pos.x / PANGO_SCALE;
-}
-
-static void
-gtk_entry_adjust_scroll (GtkEntry *entry)
-{
-  gint min_offset, max_offset;
-  gint text_area_width;
-  gint strong_x, weak_x;
-  PangoLayout *layout;
-  PangoLayoutLine *line;
-  PangoRectangle logical_rect;
-  GtkItemEntry *item_entry;
-  gint text_width;
-
-  if (!GTK_WIDGET_REALIZED (entry))
-    return;
-
-  item_entry = GTK_ITEM_ENTRY(entry);
-
-  gdk_window_get_size (entry->text_area, &text_area_width, NULL);
-  text_area_width -= 2 * INNER_BORDER;
-
-  layout = gtk_entry_ensure_layout (entry, TRUE);
-  line = pango_layout_get_lines (layout)->data;
-
-  pango_layout_line_get_extents (line, NULL, &logical_rect);
-  text_width = logical_rect.width / PANGO_SCALE + 2; /* 2 for cursor */
-
-  gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
-
-  /* Display as much text as we can */
-
-  if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_LTR)
-    {
-      entry->scroll_offset = 0;
-      switch(item_entry->justification){
-
-        case GTK_JUSTIFY_FILL:
-        case GTK_JUSTIFY_LEFT:
-
-/* LEFT JUSTIFICATION */
-
-          strong_x -= entry->scroll_offset;
-          if (strong_x < 0)
-            entry->scroll_offset += strong_x;
-          else if (strong_x > text_area_width){
-            if(item_entry->text_max_size != 0 &&
-               text_area_width + 2 <= item_entry->text_max_size){
-               GtkAllocation allocation;
-               allocation = GTK_WIDGET(entry)->allocation;
-               allocation.width += text_width - text_area_width;
-               entry->scroll_offset = 0;
-               gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation);
-            }else{
-               entry->scroll_offset += (strong_x - text_area_width) + 1;
-            }
-          }
-
-          break;
-
-        case GTK_JUSTIFY_RIGHT:
-
-    /* RIGHT JUSTIFICATION FOR NUMBERS */
-          if(entry->text){
-
-            entry->scroll_offset=  -(text_area_width - text_width) + 1;
-            if(entry->scroll_offset > 0){
-              if(item_entry->text_max_size != 0 &&
-                text_area_width + 2 <= item_entry->text_max_size){
-                GtkAllocation allocation;
-                allocation = GTK_WIDGET(entry)->allocation;
-                allocation.x -= text_width - text_area_width;
-                allocation.width += text_width - text_area_width;
-                entry->scroll_offset = 0;
-                gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation);
-              }
-              else
-              {
-                entry->scroll_offset= -(text_area_width - strong_x) + 1;
-                if(entry->scroll_offset < 0) entry->scroll_offset = 0;
-              }
-            }
-          }
-          else
-            entry->scroll_offset=0;
-
-          break;
-        case GTK_JUSTIFY_CENTER:
-
-          if(entry->text){
-
-            entry->scroll_offset=  -(text_area_width - text_width)/2;
-            if(entry->scroll_offset > 0){
-              if(item_entry->text_max_size != 0 &&
-                          text_area_width+1<=item_entry->text_max_size){
-                GtkAllocation allocation;
-                allocation = GTK_WIDGET(entry)->allocation;
-                allocation.x += (text_area_width/2 - text_width/2);
-                allocation.width += text_width - text_area_width;
-                entry->scroll_offset = 0;
-                gtk_entry_size_allocate(GTK_WIDGET(entry), &allocation);
-              }
-              else
-              {
-                entry->scroll_offset= -(text_area_width - strong_x) + 1;
-                if(entry->scroll_offset < 0) entry->scroll_offset = 0;
-              }
-            }
-          }
-          else
-            entry->scroll_offset=0;
-
-          break;
-
-      }
-
-    }
-  else
-    {
-      max_offset = text_width - text_area_width;
-      min_offset = MIN (0, max_offset);
-      entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
-    }
-
-  g_object_notify (G_OBJECT (entry), "scroll_offset");
-}
-
-static gint
-gtk_entry_move_visually (GtkEntry *entry,
-                        gint      start,
-                        gint      count)
-{
-  gint index;
-  PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
-  const gchar *text;
-
-  text = pango_layout_get_text (layout);
-
-  index = g_utf8_offset_to_pointer (text, start) - text;
-
-  while (count != 0)
-    {
-      int new_index, new_trailing;
-      gboolean split_cursor;
-      gboolean strong;
-
-      g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)),
-                   "gtk-split-cursor", &split_cursor,
-                   NULL);
-
-      if (split_cursor)
-       strong = TRUE;
-      else
-       {
-         GtkTextDirection keymap_direction =
-           (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
-           GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
-
-         strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (entry));
-       }
-
-      if (count > 0)
-       {
-         pango_layout_move_cursor_visually (layout, strong, index, 0, 1, &new_index, &new_trailing);
-         count--;
-       }
-      else
-       {
-         pango_layout_move_cursor_visually (layout, strong, index, 0, -1, &new_index, &new_trailing);
-         count++;
-       }
-
-      if (new_index < 0 || new_index == G_MAXINT)
-       break;
-
-      index = new_index;
-
-      while (new_trailing--)
-       index = g_utf8_next_char (entry->text + new_index) - entry->text;
-    }
-
-  return g_utf8_pointer_to_offset (text, text + index);
-}
-
-static gint
-gtk_entry_move_logically (GtkEntry *entry,
-                         gint      start,
-                         gint      count)
-{
-  gint new_pos = start;
-
-  /* Prevent any leak of information */
-  if (!entry->visible)
-    {
-      new_pos = CLAMP (start + count, 0, entry->text_length);
-    }
-  else if (entry->text)
-    {
-      PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
-      PangoLogAttr *log_attrs;
-      gint n_attrs;
-
-      pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
-
-      while (count > 0 && new_pos < entry->text_length)
-       {
-         do
-           new_pos++;
-         while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position);
-
-         count--;
-       }
-      while (count < 0 && new_pos > 0)
-       {
-         do
-           new_pos--;
-         while (new_pos > 0 && !log_attrs[new_pos].is_cursor_position);
-
-         count++;
-       }
-
-      g_free (log_attrs);
-    }
-
-  return new_pos;
-}
-
-static gint
-gtk_entry_move_forward_word (GtkEntry *entry,
-                            gint      start)
-{
-  gint new_pos = start;
-
-  /* Prevent any leak of information */
-  if (!entry->visible)
-    {
-      new_pos = entry->text_length;
-    }
-  else if (entry->text && (new_pos < entry->text_length))
-    {
-      PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
-      PangoLogAttr *log_attrs;
-      gint n_attrs;
-
-      pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
-
-      /* Find the next word end */
-      new_pos++;
-      while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
-       new_pos++;
-
-      g_free (log_attrs);
-    }
-
-  return new_pos;
-}
-
-
-static gint
-gtk_entry_move_backward_word (GtkEntry *entry,
-                             gint      start)
-{
-  gint new_pos = start;
-
-  /* Prevent any leak of information */
-  if (!entry->visible)
-    {
-      new_pos = 0;
-    }
-  else if (entry->text && start > 0)
-    {
-      PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
-      PangoLogAttr *log_attrs;
-      gint n_attrs;
-
-      pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
-
-      new_pos = start - 1;
-
-      /* Find the previous word beginning */
-      while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
-       new_pos--;
-
-      g_free (log_attrs);
-    }
-
-  return new_pos;
-}
-
-static void
-gtk_entry_delete_whitespace (GtkEntry *entry)
-{
-  PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE);
-  PangoLogAttr *log_attrs;
-  gint n_attrs;
-  gint start, end;
-
-  pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
-
-  start = end = entry->current_pos;
-
-  while (start > 0 && log_attrs[start-1].is_white)
-    start--;
-
-  while (end < n_attrs && log_attrs[end].is_white)
-    end++;
-
-  g_free (log_attrs);
-
-  if (start != end)
-    gtk_editable_delete_text (GTK_EDITABLE (entry), start, end);
-}
-
-
-/*
- * Like gtk_editable_get_chars, but if the editable is not
- * visible, return asterisks; also convert result to UTF-8.
- */
-static char *
-gtk_entry_get_public_chars (GtkEntry *entry,
-                           gint      start,
-                           gint      end)
-{
-  if (end < 0)
-    end = entry->text_length;
-
-  if (entry->visible)
-    return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end);
-  else
-    {
-      gchar *str;
-      gint i;
-      gint n_chars = end - start;
-
-      str = g_malloc (n_chars + 1);
-      for (i = 0; i < n_chars; i++)
-       str[i] = '*';
-      str[i] = '\0';
-
-      return str;
-    }
-
-}
-
-static void
-primary_get_cb (GtkClipboard     *clipboard,
-               GtkSelectionData *selection_data,
-               guint             info,
-               gpointer          data)
-{
-  GtkEntry *entry = GTK_ENTRY (data);
-  gint start, end;
-
-  if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
-    {
-      gchar *str = gtk_entry_get_public_chars (entry, start, end);
-      gtk_selection_data_set_text (selection_data, str, -1);
-      g_free (str);
-    }
-}
-
-static void
-primary_clear_cb (GtkClipboard *clipboard,
-                 gpointer      data)
-{
-  GtkEntry *entry = GTK_ENTRY (data);
-
-  gtk_editable_select_region (GTK_EDITABLE (entry), entry->current_pos, entry->current_pos);
-}
-
-static void
-gtk_entry_update_primary_selection (GtkEntry *entry)
-{
-  static const GtkTargetEntry targets[] = {
-    { "UTF8_STRING", 0, 0 },
-    { "STRING", 0, 0 },
-    { "TEXT",   0, 0 },
-    { "COMPOUND_TEXT", 0, 0 }
-  };
-
-  GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
-  gint start, end;
-
-  if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
-    {
-      if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
-                                        primary_get_cb, primary_clear_cb, G_OBJECT (entry)))
-       primary_clear_cb (clipboard, entry);
-    }
-  else
-    {
-      if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
-       gtk_clipboard_clear (clipboard);
-    }
-}
-
-/* Public API
- */
-
-GtkWidget*
-gtk_item_entry_new (void)
-{
-  GtkWidget *widget;
-
-  widget = GTK_WIDGET (gtk_type_new (GTK_TYPE_ITEM_ENTRY));
-  return widget;
-}
-
-GtkWidget*
-gtk_item_entry_new_with_max_length (gint max)
-{
-  GtkItemEntry *entry;
-
-  entry = gtk_type_new (GTK_TYPE_ITEM_ENTRY);
-  gtk_entry_set_max_length(GTK_ENTRY(entry), max);
-
-  return GTK_WIDGET (entry);
-}
-
-void
-gtk_item_entry_set_text (GtkItemEntry    *entry,
-                        const gchar *text,
-                         GtkJustification justification)
-{
-  gint tmp_pos;
-
-  g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
-  g_return_if_fail (text != NULL);
-
-  entry->justification = justification;
-
-  /* Actually setting the text will affect the cursor and selection;
-   * if the contents don't actually change, this will look odd to the user.
-   */
-  if (strcmp (GTK_ENTRY(entry)->text, text) == 0)
-    return;
-
-  if (GTK_ENTRY(entry)->recompute_idle){
-    g_source_remove (GTK_ENTRY(entry)->recompute_idle);
-    GTK_ENTRY(entry)->recompute_idle = 0;
-  }
-  if (GTK_ENTRY(entry)->blink_timeout){
-    g_source_remove (GTK_ENTRY(entry)->blink_timeout);
-    GTK_ENTRY(entry)->blink_timeout = 0;
-  }
-
-  gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
-
-  tmp_pos = 0;
-  gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
-}
-
-/**
- * gtk_entry_get_layout_offsets:
- * @entry: a #GtkEntry
- * @x: location to store X offset of layout, or %NULL
- * @y: location to store Y offset of layout, or %NULL
- *
- *
- * Obtains the position of the #PangoLayout used to render text
- * in the entry, in widget coordinates. Useful if you want to line
- * up the text in an entry with some other text, e.g. when using the
- * entry to implement editable cells in a sheet widget.
- *
- * Also useful to convert mouse events into coordinates inside the
- * #PangoLayout, e.g. to take some action if some part of the entry text
- * is clicked.
- *
- * Note that as the user scrolls around in the entry the offsets will
- * change; you'll need to connect to the "notify::scroll_offset"
- * signal to track this. Remember when using the #PangoLayout
- * functions you need to convert to and from pixels using
- * PANGO_PIXELS() or #PANGO_SCALE.
- *
- * Keep in mind that the layout text may contain a preedit string, so
- * gtk_entry_layout_index_to_text_index() and
- * gtk_entry_text_index_to_layout_index() are needed to convert byte
- * indices in the layout to byte indices in the entry contents.
- *
- **/
-void
-gtk_item_entry_get_layout_offsets (GtkItemEntry *entry,
-                                   gint     *x,
-                                   gint     *y)
-{
-  gint text_area_x, text_area_y;
-
-  g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
-
-  /* this gets coords relative to text area */
-  get_layout_position (GTK_ENTRY(entry), x, y);
-
-  /* convert to widget coords */
-  get_text_area_size (GTK_ENTRY(entry), &text_area_x, &text_area_y, NULL, NULL);
-
-  if (x)
-    *x += text_area_x;
-
-  if (y)
-    *y += text_area_y;
-}
-
-void
-gtk_item_entry_set_justification(GtkItemEntry *entry, GtkJustification just)
-{
-  g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
-
-  entry->justification = just;
-}
-
-
-/* We display the cursor when
- *
- *  - the selection is empty, AND
- *  - the widget has focus
- */
-
-#define CURSOR_ON_MULTIPLIER 0.66
-#define CURSOR_OFF_MULTIPLIER 0.34
-#define CURSOR_PEND_MULTIPLIER 1.0
-
-static gboolean
-cursor_blinks (GtkEntry *entry)
-{
-  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
-  gboolean blink;
-
-  if (GTK_WIDGET_HAS_FOCUS (entry) &&
-      entry->selection_bound == entry->current_pos)
-    {
-      g_object_get (G_OBJECT (settings), "gtk-cursor-blink", &blink, NULL);
-      return blink;
-    }
-  else
-    return FALSE;
-}
-
-static gint
-get_cursor_time (GtkEntry *entry)
-{
-  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (entry));
-  gint time;
-
-  g_object_get (G_OBJECT (settings), "gtk-cursor-blink-time", &time, NULL);
-
-  return time;
-}
-
-static void
-show_cursor (GtkEntry *entry)
-{
-  if (!entry->cursor_visible)
-    {
-      entry->cursor_visible = TRUE;
-
-      if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
-       gtk_widget_queue_draw (GTK_WIDGET (entry));
-    }
-}
-
-static void
-hide_cursor (GtkEntry *entry)
-{
-  if (entry->cursor_visible)
-    {
-      entry->cursor_visible = FALSE;
-
-      if (GTK_WIDGET_HAS_FOCUS (entry) && entry->selection_bound == entry->current_pos)
-       gtk_widget_queue_draw (GTK_WIDGET (entry));
-    }
-}
-
-/*
- * Blink!
- */
-static gint
-blink_cb (gpointer data)
-{
-  GtkEntry *entry;
-
-  GDK_THREADS_ENTER ();
-
-  entry = GTK_ENTRY (data);
-
-  g_assert (GTK_WIDGET_HAS_FOCUS (entry));
-  g_assert (entry->selection_bound == entry->current_pos);
-
-  if (entry->cursor_visible)
-    {
-      hide_cursor (entry);
-      entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_OFF_MULTIPLIER,
-                                             blink_cb,
-                                             entry);
-    }
-  else
-    {
-      show_cursor (entry);
-      entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
-                                             blink_cb,
-                                             entry);
-    }
-
-  GDK_THREADS_LEAVE ();
-
-  /* Remove ourselves */
-  return FALSE;
-}
-
-static void
-gtk_entry_check_cursor_blink (GtkEntry *entry)
-{
-  if (cursor_blinks (entry))
-    {
-      if (!entry->blink_timeout)
-       {
-         entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_ON_MULTIPLIER,
-                                                 blink_cb,
-                                                 entry);
-         show_cursor (entry);
-       }
-    }
-  else
-    {
-      if (entry->blink_timeout)
-       {
-         gtk_timeout_remove (entry->blink_timeout);
-         entry->blink_timeout = 0;
-       }
-
-      entry->cursor_visible = TRUE;
-    }
-
-}
-
-static void
-gtk_entry_pend_cursor_blink (GtkEntry *entry)
-{
-  if (cursor_blinks (entry))
-    {
-      if (entry->blink_timeout != 0)
-       gtk_timeout_remove (entry->blink_timeout);
-
-      entry->blink_timeout = gtk_timeout_add (get_cursor_time (entry) * CURSOR_PEND_MULTIPLIER,
-                                             blink_cb,
-                                             entry);
-      show_cursor (entry);
-    }
-}
-
-void
-gtk_item_entry_set_cursor_visible(GtkItemEntry *entry, gboolean visible)
-{
-  g_return_if_fail (GTK_IS_ITEM_ENTRY (entry));
-
-  GTK_ENTRY(entry)->cursor_visible = visible;
-}
-
-gboolean
-gtk_item_entry_get_cursor_visible(GtkItemEntry *entry)
-{
-  g_return_val_if_fail (GTK_IS_ITEM_ENTRY (entry), FALSE);
-
-  return(GTK_ENTRY(entry)->cursor_visible);
-}
diff --git a/lib/gtksheet/gtkitementry.h b/lib/gtksheet/gtkitementry.h
deleted file mode 100644 (file)
index 11fab7a..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/* GtkItemEntry - widget for gtk+
- * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GtkItemEntry widget by Adrian E. Feiguin
- * Based on GtkEntry widget
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __GTK_ITEM_ENTRY_H__
-#define __GTK_ITEM_ENTRY_H__
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-#define GTK_TYPE_ITEM_ENTRY            (gtk_item_entry_get_type ())
-#define GTK_ITEM_ENTRY(obj)            (GTK_CHECK_CAST (obj, gtk_item_entry_get_type (), GtkItemEntry))
-#define GTK_ITEM_ENTRY_CLASS(klass)    (GTK_CHECK_CLASS_CAST (klass, gtk_item_entry_get_type (), GtkItemEntryClass))
-#define GTK_IS_ITEM_ENTRY(obj)         (GTK_CHECK_TYPE (obj, gtk_item_entry_get_type ()))
-#define GTK_IS_ITEM_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY))
-
-
-typedef struct _GtkItemEntry       GtkItemEntry;
-typedef struct _GtkItemEntryClass  GtkItemEntryClass;
-
-struct _GtkItemEntry
-{
-  GtkEntry parent;
-
-  gint text_max_size;
-
-  GtkJustification justification;
-};
-
-struct _GtkItemEntryClass
-{
-  GtkEntryClass parent_class;
-};
-
-GtkType    gtk_item_entry_get_type       (void);
-GtkWidget* gtk_item_entry_new            (void);
-GtkWidget* gtk_item_entry_new_with_max_length (gint   max);
-void       gtk_item_entry_set_text            (GtkItemEntry *item_entry,
-                                               const gchar *text,
-                                               GtkJustification justification);
-
-void       gtk_item_entry_set_justification (GtkItemEntry        *item_entry,
-                                            GtkJustification   justification);
-
-void       gtk_item_entry_set_cursor_visible   (GtkItemEntry *entry,
-                                                gboolean visible);
-gboolean   gtk_item_entry_get_cursor_visible   (GtkItemEntry *entry);
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-#endif /* __GTK_ITEM_ENTRY_H__ */
diff --git a/lib/gtksheet/gtksheet.c b/lib/gtksheet/gtksheet.c
deleted file mode 100644 (file)
index 942b023..0000000
+++ /dev/null
@@ -1,8005 +0,0 @@
-/*
- * Copyright (C) 2006, 2008 Free Software Foundation
- *
- * This version of GtkSheet has been *heavily* modified, for the specific
- * requirements of PSPPIRE.  The changes are copyright by the
- * Free Software Foundation.  The copyright notice for the original work is
- * below.
- */
-
-/* GtkSheet widget for Gtk+.
- * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
- *
- * Based on GtkClist widget by Jay Painter, but major changes.
- * Memory allocation routines inspired on SC (Spreadsheet Calculator)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/**
- * SECTION:gtksheet
- * @short_description: spreadsheet widget for gtk2
- *
- * GtkSheet is a matrix widget for GTK+. It consists of an scrollable grid of
- * cells where you can allocate text. Cell contents can be edited interactively
- * through a specially designed entry, GtkItemEntry. It is also a container
- * subclass, allowing you to display buttons, curves, pixmaps and any other
- * widgets in it.
- *
- * You can also set many attributes as: border, foreground and background color,
- * text justification, and more.
- *
- * The testgtksheet program shows how easy is to create a spreadsheet-like GUI
- * using this widget.
- */
-#include <config.h>
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtksignal.h>
-#include <gtk/gtklabel.h>
-#include <gtk/gtkbutton.h>
-#include <gtk/gtkadjustment.h>
-#include <gtk/gtktable.h>
-#include <gtk/gtkbox.h>
-#include <gtk/gtkmain.h>
-#include <gtk/gtktypeutils.h>
-#include <gtk/gtkentry.h>
-#include <gtk/gtkcontainer.h>
-#include <gtk/gtkpixmap.h>
-#include <pango/pango.h>
-#include "gtkitementry.h"
-#include "gtksheet.h"
-#include "gtkextra-marshal.h"
-#include "gsheetmodel.h"
-
-/* sheet flags */
-enum
-  {
-    GTK_SHEET_IS_FROZEN = 1 << 1,
-    GTK_SHEET_IN_XDRAG = 1 << 2,
-    GTK_SHEET_IN_YDRAG = 1 << 3,
-    GTK_SHEET_IN_DRAG = 1 << 4,
-    GTK_SHEET_IN_SELECTION = 1 << 5,
-    GTK_SHEET_IN_RESIZE = 1 << 6,
-    GTK_SHEET_REDRAW_PENDING = 1 << 7,
-  };
-
-#define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags)
-#define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag))
-#define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~ (flag))
-
-#define GTK_SHEET_IS_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_FROZEN)
-#define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG)
-#define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG)
-#define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG)
-#define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION)
-#define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE)
-#define GTK_SHEET_REDRAW_PENDING(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_REDRAW_PENDING)
-
-#define CELL_SPACING 1
-#define DRAG_WIDTH 6
-#define TIMEOUT_HOVER 300
-#define COLUMN_MIN_WIDTH 10
-#define CELLOFFSET 4
-#define DEFAULT_COLUMN_WIDTH 80
-
-static void gtk_sheet_update_primary_selection (GtkSheet *sheet);
-static void gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column);
-
-static void gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row);
-
-
-static gboolean gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col);
-
-static inline  void
-dispose_string (const GtkSheet *sheet, gchar *text)
-{
-  GSheetModel *model = gtk_sheet_get_model (sheet);
-
-  if ( ! model )
-    return;
-
-  if (g_sheet_model_free_strings (model))
-    g_free (text);
-}
-
-static inline
-guint DEFAULT_ROW_HEIGHT (GtkWidget *widget)
-{
-  if (!widget->style->font_desc) return 24;
-  else
-    {
-      PangoContext *context = gtk_widget_get_pango_context (widget);
-      PangoFontMetrics *metrics =
-       pango_context_get_metrics (context,
-                                  widget->style->font_desc,
-                                  pango_context_get_language (context));
-
-      guint val = pango_font_metrics_get_descent (metrics) +
-       pango_font_metrics_get_ascent (metrics);
-
-      pango_font_metrics_unref (metrics);
-
-      return PANGO_PIXELS (val) + 2 * CELLOFFSET;
-    }
-}
-
-static inline
-guint DEFAULT_FONT_ASCENT (GtkWidget *widget)
-{
-  if (!widget->style->font_desc) return 12;
-  else
-    {
-      PangoContext *context = gtk_widget_get_pango_context (widget);
-      PangoFontMetrics *metrics =
-       pango_context_get_metrics (context,
-                                  widget->style->font_desc,
-                                  pango_context_get_language (context));
-      guint val = pango_font_metrics_get_ascent (metrics);
-      pango_font_metrics_unref (metrics);
-      return PANGO_PIXELS (val);
-    }
-}
-
-static inline
-guint STRING_WIDTH (GtkWidget *widget,
-                   const PangoFontDescription *font, const gchar *text)
-{
-  PangoRectangle rect;
-  PangoLayout *layout;
-
-  layout = gtk_widget_create_pango_layout (widget, text);
-  pango_layout_set_font_description (layout, font);
-
-  pango_layout_get_extents (layout, NULL, &rect);
-
-  g_object_unref (layout);
-  return PANGO_PIXELS (rect.width);
-}
-
-static inline
-guint DEFAULT_FONT_DESCENT (GtkWidget *widget)
-{
-  if (!widget->style->font_desc) return 12;
-  else
-    {
-      PangoContext *context = gtk_widget_get_pango_context (widget);
-      PangoFontMetrics *metrics =
-       pango_context_get_metrics (context,
-                                  widget->style->font_desc,
-                                  pango_context_get_language (context));
-      guint val = pango_font_metrics_get_descent (metrics);
-      pango_font_metrics_unref (metrics);
-      return PANGO_PIXELS (val);
-    }
-}
-
-
-static gint
-yyy_row_is_visible (const GtkSheet *sheet, gint row)
-{
-  GSheetRow *row_geo = sheet->row_geometry;
-
-  return g_sheet_row_get_visibility (row_geo, row);
-}
-
-
-static gint
-yyy_row_is_sensitive (const GtkSheet *sheet, gint row)
-{
-  GSheetRow *row_geo = sheet->row_geometry;
-
-  return g_sheet_row_get_sensitivity (row_geo, row);
-}
-
-
-
-static inline gint
-yyy_row_count (const GtkSheet *sheet)
-{
-  GSheetRow *row_geo = sheet->row_geometry;
-
-  return g_sheet_row_get_row_count (row_geo);
-}
-
-static inline gint
-yyy_row_height (const GtkSheet *sheet, gint row)
-{
-  GSheetRow *row_geo = sheet->row_geometry;
-
-  return g_sheet_row_get_height (row_geo, row);
-}
-
-static gint
-yyy_row_top_ypixel (const GtkSheet *sheet, gint row)
-{
-  GSheetRow *geo = sheet->row_geometry;
-
-  gint y = g_sheet_row_start_pixel (geo, row);
-
-  if ( sheet->column_titles_visible )
-    y += sheet->column_title_area.height;
-
-  return y;
-}
-
-
-/* Return the row containing pixel Y */
-static gint
-yyy_row_ypixel_to_row (const GtkSheet *sheet, gint y)
-{
-  GSheetRow *geo = sheet->row_geometry;
-
-  gint cy = sheet->voffset;
-
-  if (sheet->column_titles_visible)
-    cy += sheet->column_title_area.height;
-
-  if (y < cy) return 0;
-
-  return g_sheet_row_pixel_to_row (geo, y - cy);
-}
-
-
-/* gives the top pixel of the given row in context of
- * the sheet's voffset */
-static inline gint
-ROW_TOP_YPIXEL (const GtkSheet *sheet, gint row)
-{
-  return (sheet->voffset + yyy_row_top_ypixel (sheet, row));
-}
-
-
-/* returns the row index from a y pixel location in the
- * context of the sheet's voffset */
-static inline gint
-ROW_FROM_YPIXEL (const GtkSheet *sheet, gint y)
-{
-  return (yyy_row_ypixel_to_row (sheet, y));
-}
-
-static inline GtkSheetButton *
-xxx_column_button (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-  if ( col < 0 ) return NULL ;
-
-  return g_sheet_column_get_button (col_geo, col);
-}
-
-
-static inline gint
-xxx_column_left_xpixel (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *geo = sheet->column_geometry;
-
-  gint x = g_sheet_column_start_pixel (geo, col);
-
-  if ( sheet->row_titles_visible )
-    x += sheet->row_title_area.width;
-
-  return x;
-}
-
-static inline gint
-xxx_column_width (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_width (col_geo, col);
-}
-
-
-static inline void
-xxx_set_column_width (GtkSheet *sheet, gint col, gint width)
-{
-  if ( sheet->column_geometry )
-    g_sheet_column_set_width (sheet->column_geometry, col, width);
-}
-
-static inline void
-xxx_column_set_left_column (GtkSheet *sheet, gint col, gint i)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  g_sheet_column_set_left_text_column (col_geo, col, i);
-}
-
-static inline gint
-xxx_column_left_column (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_left_text_column (col_geo, col);
-}
-
-static inline void
-xxx_column_set_right_column (GtkSheet *sheet, gint col, gint i)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  g_sheet_column_set_right_text_column (col_geo, col, i);
-}
-
-static inline gint
-xxx_column_right_column (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_right_text_column (col_geo, col);
-}
-
-static inline GtkJustification
-xxx_column_justification (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_justification (col_geo, col);
-}
-
-static inline gint
-xxx_column_is_visible (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_visibility (col_geo, col);
-}
-
-
-static inline gint
-xxx_column_is_sensitive (const GtkSheet *sheet, gint col)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_sensitivity (col_geo, col);
-}
-
-
-/* gives the left pixel of the given column in context of
- * the sheet's hoffset */
-static inline gint
-COLUMN_LEFT_XPIXEL (const GtkSheet *sheet, gint ncol)
-{
-  return (sheet->hoffset + xxx_column_left_xpixel (sheet, ncol));
-}
-
-static inline gint
-xxx_column_count (const GtkSheet *sheet)
-{
-  GSheetColumn *col_geo = sheet->column_geometry;
-
-  return g_sheet_column_get_column_count (col_geo);
-}
-
-/* returns the column index from a x pixel location in the
- * context of the sheet's hoffset */
-static inline gint
-COLUMN_FROM_XPIXEL (const GtkSheet * sheet,
-                   gint x)
-{
-  gint i, cx;
-
-  cx = sheet->hoffset;
-  if ( sheet->row_titles_visible )
-    cx += sheet->row_title_area.width;
-
-  if (x < cx) return 0;
-  for (i = 0; i < xxx_column_count (sheet); i++)
-    {
-      if (x >= cx && x <= (cx + xxx_column_width (sheet, i)) &&
-         xxx_column_is_visible (sheet, i))
-       return i;
-      if ( xxx_column_is_visible (sheet, i))
-       cx += xxx_column_width (sheet, i);
-    }
-
-  /* no match */
-  return xxx_column_count (sheet) - 1;
-}
-
-/* returns the total height of the sheet */
-static inline gint SHEET_HEIGHT (GtkSheet *sheet)
-{
-  const gint n_rows = yyy_row_count (sheet);
-
-  return yyy_row_top_ypixel (sheet, n_rows - 1) +
-    yyy_row_height (sheet, n_rows - 1);
-}
-
-
-static inline GtkSheetButton *
-yyy_row_button (GtkSheet *sheet, gint row)
-{
-  GSheetRow *row_geo = sheet->row_geometry;
-
-  return g_sheet_row_get_button (row_geo, row);
-}
-
-
-
-
-static inline void
-yyy_set_row_height (GtkSheet *sheet, gint row, gint height)
-{
-  if ( sheet->row_geometry )
-    g_sheet_row_set_height (sheet->row_geometry, row, height);
-}
-
-
-
-/* returns the total width of the sheet */
-static inline gint SHEET_WIDTH (GtkSheet *sheet)
-{
-  gint i, cx;
-
-  cx = ( sheet->row_titles_visible ? sheet->row_title_area.width : 0);
-
-  for (i = 0; i < xxx_column_count (sheet); i++)
-    if (xxx_column_is_visible (sheet, i))
-      cx += xxx_column_width (sheet, i);
-
-  return cx;
-}
-
-#define MIN_VISIBLE_ROW(sheet) \
-    ROW_FROM_YPIXEL (sheet, sheet->column_title_area.height + 1)
-
-#define MAX_VISIBLE_ROW(sheet) \
-    ROW_FROM_YPIXEL (sheet, sheet->sheet_window_height - 1)
-
-#define MIN_VISIBLE_COLUMN(sheet) \
-    COLUMN_FROM_XPIXEL (sheet, sheet->row_title_area.width + 1)
-
-#define MAX_VISIBLE_COLUMN(sheet) \
-    COLUMN_FROM_XPIXEL (sheet, sheet->sheet_window_width)
-
-
-
-static inline gboolean
-POSSIBLE_XDRAG (const GtkSheet *sheet, gint x, gint *drag_column)
-{
-  gint column, xdrag;
-
-  column = COLUMN_FROM_XPIXEL (sheet, x);
-  *drag_column = column;
-
-  xdrag = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
-  if (x <= xdrag + DRAG_WIDTH / 2 && column != 0)
-    {
-      while (! xxx_column_is_visible (sheet, column - 1) && column > 0) column--;
-      *drag_column = column - 1;
-      return xxx_column_is_sensitive (sheet, column - 1);
-    }
-
-  xdrag += xxx_column_width (sheet, column);
-  if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
-    return xxx_column_is_sensitive (sheet, column);
-
-  return FALSE;
-}
-
-static inline gboolean
-POSSIBLE_YDRAG (const GtkSheet *sheet, gint y, gint *drag_row)
-{
-  gint row, ydrag;
-  row = ROW_FROM_YPIXEL (sheet, y);
-  *drag_row = row;
-
-  ydrag = ROW_TOP_YPIXEL (sheet, row)+CELL_SPACING;
-  if (y <= ydrag + DRAG_WIDTH / 2 && row != 0)
-    {
-      while (!yyy_row_is_visible (sheet, row - 1) && row > 0) row--;
-      *drag_row = row - 1;
-      return yyy_row_is_sensitive (sheet, row - 1);
-    }
-
-  ydrag +=yyy_row_height (sheet, row);
-
-  if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
-    return yyy_row_is_sensitive (sheet, row);
-
-
-  return FALSE;
-}
-
-static inline gboolean
-POSSIBLE_DRAG (const GtkSheet *sheet, gint x, gint y,
-              gint *drag_row, gint *drag_column)
-{
-  gint ydrag, xdrag;
-
-  /* Can't drag if nothing is selected */
-  if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
-       sheet->range.col0 < 0 || sheet->range.coli < 0 )
-    return FALSE;
-
-  *drag_column = COLUMN_FROM_XPIXEL (sheet, x);
-  *drag_row = ROW_FROM_YPIXEL (sheet, y);
-
-  if (x >= COLUMN_LEFT_XPIXEL (sheet, sheet->range.col0) - DRAG_WIDTH / 2 &&
-      x <= COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli) +
-      xxx_column_width (sheet, sheet->range.coli) + DRAG_WIDTH / 2)
-    {
-      ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.row0);
-      if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
-       {
-         *drag_row = sheet->range.row0;
-         return TRUE;
-       }
-      ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.rowi) +
-       yyy_row_height (sheet, sheet->range.rowi);
-      if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
-       {
-         *drag_row = sheet->range.rowi;
-         return TRUE;
-       }
-    }
-
-  if (y >= ROW_TOP_YPIXEL (sheet, sheet->range.row0) - DRAG_WIDTH / 2 &&
-      y <= ROW_TOP_YPIXEL (sheet, sheet->range.rowi) +
-      yyy_row_height (sheet, sheet->range.rowi) + DRAG_WIDTH / 2)
-    {
-      xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.col0);
-      if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
-       {
-         *drag_column = sheet->range.col0;
-         return TRUE;
-       }
-      xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli) +
-       xxx_column_width (sheet, sheet->range.coli);
-      if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
-       {
-         *drag_column = sheet->range.coli;
-         return TRUE;
-       }
-    }
-
-  return FALSE;
-}
-
-static inline gboolean
-POSSIBLE_RESIZE (const GtkSheet *sheet, gint x, gint y,
-                gint *drag_row, gint *drag_column)
-{
-  gint xdrag, ydrag;
-
-  /* Can't drag if nothing is selected */
-  if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
-       sheet->range.col0 < 0 || sheet->range.coli < 0 )
-    return FALSE;
-
-  xdrag = COLUMN_LEFT_XPIXEL (sheet, sheet->range.coli)+
-    xxx_column_width (sheet, sheet->range.coli);
-
-  ydrag = ROW_TOP_YPIXEL (sheet, sheet->range.rowi)+
-    yyy_row_height (sheet, sheet->range.rowi);
-
-  if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
-    ydrag = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet));
-
-  if (sheet->state == GTK_SHEET_ROW_SELECTED)
-    xdrag = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
-
-  *drag_column = COLUMN_FROM_XPIXEL (sheet, x);
-  *drag_row = ROW_FROM_YPIXEL (sheet, y);
-
-  if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
-      y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
-
-  return FALSE;
-}
-
-static void gtk_sheet_class_init                (GtkSheetClass * klass);
-static void gtk_sheet_init                      (GtkSheet * sheet);
-static void gtk_sheet_dispose                   (GObject * object);
-static void gtk_sheet_finalize                          (GObject * object);
-static void gtk_sheet_style_set                 (GtkWidget *widget,
-                                                 GtkStyle *previous_style);
-static void gtk_sheet_realize                   (GtkWidget * widget);
-static void gtk_sheet_unrealize                 (GtkWidget * widget);
-static void gtk_sheet_map                       (GtkWidget * widget);
-static void gtk_sheet_unmap                     (GtkWidget * widget);
-static gint gtk_sheet_expose                    (GtkWidget * widget,
-                                                 GdkEventExpose * event);
-static void gtk_sheet_forall                    (GtkContainer *container,
-                                                 gboolean include_internals,
-                                                 GtkCallback callback,
-                                                 gpointer callback_data);
-
-static void gtk_sheet_set_scroll_adjustments    (GtkSheet *sheet,
-                                                 GtkAdjustment *hadjustment,
-                                                 GtkAdjustment *vadjustment);
-
-static gint gtk_sheet_button_press              (GtkWidget * widget,
-                                                 GdkEventButton * event);
-static gint gtk_sheet_button_release            (GtkWidget * widget,
-                                                 GdkEventButton * event);
-static gint gtk_sheet_motion                    (GtkWidget * widget,
-                                                 GdkEventMotion * event);
-static gint gtk_sheet_entry_key_press           (GtkWidget *widget,
-                                                 GdkEventKey *key);
-static gint gtk_sheet_key_press                         (GtkWidget *widget,
-                                                 GdkEventKey *key);
-static void gtk_sheet_size_request              (GtkWidget * widget,
-                                                 GtkRequisition * requisition);
-static void gtk_sheet_size_allocate             (GtkWidget * widget,
-                                                 GtkAllocation * allocation);
-
-/* Sheet queries */
-
-static gboolean gtk_sheet_range_isvisible (const GtkSheet * sheet,
-                                          GtkSheetRange range);
-static gboolean gtk_sheet_cell_isvisible  (GtkSheet * sheet,
-                                          gint row, gint column);
-/* Drawing Routines */
-
-/* draw cell background and frame */
-static void gtk_sheet_cell_draw_default         (GtkSheet *sheet,
-                                                 gint row, gint column);
-
-/* draw cell contents */
-static void gtk_sheet_cell_draw_label           (GtkSheet *sheet,
-                                                 gint row, gint column);
-
-/* draw visible part of range. If range == NULL then draw the whole screen */
-static void gtk_sheet_range_draw                (GtkSheet *sheet,
-                                                 const GtkSheetRange *range);
-
-/* highlight the visible part of the selected range */
-static void gtk_sheet_range_draw_selection      (GtkSheet *sheet,
-                                                 GtkSheetRange range);
-
-/* Selection */
-
-static gboolean gtk_sheet_move_query                    (GtkSheet *sheet,
-                                                 gint row, gint column);
-static void gtk_sheet_real_select_range         (GtkSheet * sheet,
-                                                 const GtkSheetRange * range);
-static void gtk_sheet_real_unselect_range       (GtkSheet * sheet,
-                                                 const GtkSheetRange * range);
-static void gtk_sheet_extend_selection          (GtkSheet *sheet,
-                                                 gint row, gint column);
-static void gtk_sheet_new_selection             (GtkSheet *sheet,
-                                                 GtkSheetRange *range);
-static void gtk_sheet_draw_border               (GtkSheet *sheet,
-                                                 GtkSheetRange range);
-static void gtk_sheet_draw_corners              (GtkSheet *sheet,
-                                                 GtkSheetRange range);
-
-
-/* Active Cell handling */
-
-static void gtk_sheet_entry_changed             (GtkWidget *widget,
-                                                 gpointer data);
-static void gtk_sheet_deactivate_cell   (GtkSheet *sheet);
-static void gtk_sheet_hide_active_cell          (GtkSheet *sheet);
-static gboolean gtk_sheet_activate_cell                 (GtkSheet *sheet,
-                                                 gint row, gint col);
-static void gtk_sheet_draw_active_cell          (GtkSheet *sheet);
-static void gtk_sheet_show_active_cell          (GtkSheet *sheet);
-static void gtk_sheet_click_cell                (GtkSheet *sheet,
-                                                 gint row,
-                                                 gint column,
-                                                 gboolean *veto);
-
-/* Backing Pixmap */
-
-static void gtk_sheet_make_backing_pixmap       (GtkSheet *sheet,
-                                                 guint width, guint height);
-static void gtk_sheet_draw_backing_pixmap       (GtkSheet *sheet,
-                                                 GtkSheetRange range);
-/* Scrollbars */
-
-static void adjust_scrollbars                   (GtkSheet * sheet);
-static void vadjustment_value_changed           (GtkAdjustment * adjustment,
-                                                 gpointer data);
-static void hadjustment_value_changed           (GtkAdjustment * adjustment,
-                                                 gpointer data);
-
-
-static void draw_xor_vline                      (GtkSheet * sheet);
-static void draw_xor_hline                      (GtkSheet * sheet);
-static void draw_xor_rectangle                  (GtkSheet *sheet,
-                                                 GtkSheetRange range);
-
-static guint new_column_width                   (GtkSheet * sheet,
-                                                 gint column,
-                                                 gint * x);
-static guint new_row_height                     (GtkSheet * sheet,
-                                                 gint row,
-                                                 gint * y);
-/* Sheet Button */
-
-static void create_global_button                (GtkSheet *sheet);
-static void global_button_clicked               (GtkWidget *widget,
-                                                 gpointer data);
-/* Sheet Entry */
-
-static void create_sheet_entry                  (GtkSheet *sheet);
-static void gtk_sheet_size_allocate_entry       (GtkSheet *sheet);
-static void gtk_sheet_entry_set_max_size        (GtkSheet *sheet);
-
-/* Sheet button gadgets */
-
-static void size_allocate_column_title_buttons          (GtkSheet * sheet);
-static void size_allocate_row_title_buttons     (GtkSheet * sheet);
-
-
-static void size_allocate_global_button         (GtkSheet *sheet);
-static void gtk_sheet_button_size_request       (GtkSheet *sheet,
-                                                 const GtkSheetButton *button,
-                                                 GtkRequisition *requisition);
-
-/* Attributes routines */
-static void init_attributes                     (const GtkSheet *sheet, gint col,
-                                                 GtkSheetCellAttr *attributes);
-
-
-/* Memory allocation routines */
-static void gtk_sheet_real_range_clear                  (GtkSheet *sheet,
-                                                 const GtkSheetRange *range);
-
-static void gtk_sheet_real_cell_clear           (GtkSheet *sheet,
-                                                 gint row,
-                                                 gint column);
-
-
-/* Container Functions */
-static void gtk_sheet_remove                    (GtkContainer *container,
-                                                 GtkWidget *widget);
-static void gtk_sheet_realize_child             (GtkSheet *sheet,
-                                                 GtkSheetChild *child);
-static void gtk_sheet_position_child            (GtkSheet *sheet,
-                                                 GtkSheetChild *child);
-static void gtk_sheet_position_children                 (GtkSheet *sheet);
-static void gtk_sheet_child_show                (GtkSheetChild *child);
-static void gtk_sheet_child_hide                (GtkSheetChild *child);
-static void gtk_sheet_column_size_request (GtkSheet *sheet,
-                                          gint col,
-                                          guint *requisition);
-static void gtk_sheet_row_size_request (GtkSheet *sheet,
-                                       gint row,
-                                       guint *requisition);
-
-
-/* Signals */
-
-extern void
-_gtkextra_signal_emit (GtkObject *object, guint signal_id, ...);
-
-enum
-  {
-    SELECT_ROW,
-    SELECT_COLUMN,
-    DOUBLE_CLICK_ROW,
-    DOUBLE_CLICK_COLUMN,
-    BUTTON_EVENT_ROW,
-    BUTTON_EVENT_COLUMN,
-    SELECT_RANGE,
-    RESIZE_RANGE,
-    MOVE_RANGE,
-    TRAVERSE,
-    DEACTIVATE,
-    ACTIVATE,
-    CHANGED,
-    LAST_SIGNAL
-  };
-
-static GtkContainerClass *parent_class = NULL;
-static guint sheet_signals[LAST_SIGNAL] = { 0 };
-
-
-GType
-gtk_sheet_get_type ()
-{
-  static GType sheet_type = 0;
-
-  if (!sheet_type)
-    {
-      static const GTypeInfo sheet_info =
-       {
-         sizeof (GtkSheetClass),
-         NULL,
-         NULL,
-         (GClassInitFunc) gtk_sheet_class_init,
-         NULL,
-         NULL,
-         sizeof (GtkSheet),
-         0,
-         (GInstanceInitFunc) gtk_sheet_init,
-         NULL,
-       };
-      sheet_type =
-       g_type_register_static (GTK_TYPE_CONTAINER, "GtkSheet",
-                               &sheet_info, 0);
-    }
-  return sheet_type;
-}
-
-static GtkSheetRange*
-gtk_sheet_range_copy (const GtkSheetRange *range)
-{
-  GtkSheetRange *new_range;
-
-  g_return_val_if_fail (range != NULL, NULL);
-
-  new_range = g_new (GtkSheetRange, 1);
-
-  *new_range = *range;
-
-  return new_range;
-}
-
-static void
-gtk_sheet_range_free (GtkSheetRange *range)
-{
-  g_return_if_fail (range != NULL);
-
-  g_free (range);
-}
-
-GType
-gtk_sheet_range_get_type (void)
-{
-  static GType sheet_range_type = 0;
-
-  if (!sheet_range_type)
-    {
-      sheet_range_type =
-       g_boxed_type_register_static ("GtkSheetRange",
-                                     (GBoxedCopyFunc) gtk_sheet_range_copy,
-                                     (GBoxedFreeFunc) gtk_sheet_range_free);
-    }
-
-  return sheet_range_type;
-}
-
-
-static void column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data);
-
-/* Properties */
-enum
-  {
-    PROP_0,
-    PROP_ROW_GEO,
-    PROP_COL_GEO,
-    PROP_MODEL
-  };
-
-static void
-gtk_sheet_set_row_geometry (GtkSheet *sheet, GSheetRow *geo)
-{
-  if ( sheet->row_geometry ) g_object_unref (sheet->row_geometry);
-
-  sheet->row_geometry = geo;
-
-  if ( sheet->row_geometry ) g_object_ref (sheet->row_geometry);
-}
-
-static void
-gtk_sheet_set_column_geometry (GtkSheet *sheet, GSheetColumn *geo)
-{
-  if ( sheet->column_geometry ) g_object_unref (sheet->column_geometry);
-
-  sheet->column_geometry = geo;
-
-  if ( sheet->column_geometry ) g_object_ref (sheet->column_geometry);
-}
-
-
-static void
-gtk_sheet_set_property (GObject         *object,
-                       guint            prop_id,
-                       const GValue    *value,
-                       GParamSpec      *pspec)
-
-{
-  GtkSheet *sheet = GTK_SHEET (object);
-
-  switch (prop_id)
-    {
-    case PROP_ROW_GEO:
-      gtk_sheet_set_row_geometry (sheet, g_value_get_pointer (value));
-      break;
-    case PROP_COL_GEO:
-      gtk_sheet_set_column_geometry (sheet, g_value_get_pointer (value));
-      if ( sheet->column_geometry)
-       g_signal_connect (sheet->column_geometry, "columns_changed",
-                         G_CALLBACK (column_titles_changed), sheet);
-      break;
-    case PROP_MODEL:
-      gtk_sheet_set_model (sheet, g_value_get_pointer (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    };
-}
-
-static void
-gtk_sheet_get_property (GObject         *object,
-                       guint            prop_id,
-                       GValue          *value,
-                       GParamSpec      *pspec)
-{
-  GtkSheet *sheet = GTK_SHEET (object);
-
-  switch (prop_id)
-    {
-    case PROP_ROW_GEO:
-      g_value_set_pointer (value, sheet->row_geometry);
-      break;
-    case PROP_COL_GEO:
-      g_value_set_pointer (value, sheet->column_geometry);
-      break;
-    case PROP_MODEL:
-      g_value_set_pointer (value, sheet->model);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    };
-}
-
-
-static void
-gtk_sheet_class_init (GtkSheetClass * klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  GParamSpec *row_geo_spec ;
-  GParamSpec *col_geo_spec ;
-  GParamSpec *model_spec ;
-
-  GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
-  GtkContainerClass *container_class = (GtkContainerClass *) klass;
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  /**
-   * GtkSheet::select-row
-   * @sheet: the sheet widget that emitted the signal
-   * @row: the newly selected row index
-   *
-   * A row has been selected.
-   */
-  sheet_signals[SELECT_ROW] =
-    g_signal_new ("select-row",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, select_row),
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__INT,
-                 G_TYPE_NONE,
-                 1,
-                 G_TYPE_INT);
-
-
-  /**
-   * GtkSheet::select - column
-   * @sheet: the sheet widget that emitted the signal
-   * @column: the newly selected column index
-   *
-   * A column has been selected.
-   */
-  sheet_signals[SELECT_COLUMN] =
-    g_signal_new ("select-column",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, select_column),
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__INT,
-                 G_TYPE_NONE,
-                 1,
-                 G_TYPE_INT);
-
-
-  /**
-   * GtkSheet::double-click-row
-   * @sheet: the sheet widget that emitted the signal
-   * @row: the row that was double clicked.
-   *
-   * A row's title button has been double clicked
-   */
-  sheet_signals[DOUBLE_CLICK_ROW] =
-    g_signal_new ("double-click-row",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 0,
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__INT,
-                 G_TYPE_NONE,
-                 1,
-                 G_TYPE_INT);
-
-
-  /**
-   * GtkSheet::double-click-column
-   * @sheet: the sheet widget that emitted the signal
-   * @column: the column that was double clicked.
-   *
-   * A column's title button has been double clicked
-   */
-  sheet_signals[DOUBLE_CLICK_COLUMN] =
-    g_signal_new ("double-click-column",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 0,
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__INT,
-                 G_TYPE_NONE,
-                 1,
-                 G_TYPE_INT);
-
-
-  /**
-   * GtkSheet::button-event-column
-   * @sheet: the sheet widget that emitted the signal
-   * @column: the column on which the event occured.
-   *
-   * A button event occured on a column title button
-   */
-  sheet_signals[BUTTON_EVENT_COLUMN] =
-    g_signal_new ("button-event-column",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 0,
-                 NULL, NULL,
-                 gtkextra_VOID__INT_POINTER,
-                 G_TYPE_NONE,
-                 2,
-                 G_TYPE_INT,
-                 G_TYPE_POINTER
-                 );
-
-
-  /**
-   * GtkSheet::button-event-row
-   * @sheet: the sheet widget that emitted the signal
-   * @column: the column on which the event occured.
-   *
-   * A button event occured on a row title button
-   */
-  sheet_signals[BUTTON_EVENT_ROW] =
-    g_signal_new ("button-event-row",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 0,
-                 NULL, NULL,
-                 gtkextra_VOID__INT_POINTER,
-                 G_TYPE_NONE,
-                 2,
-                 G_TYPE_INT,
-                 G_TYPE_POINTER
-                 );
-
-
-  sheet_signals[SELECT_RANGE] =
-    g_signal_new ("select-range",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, select_range),
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__BOXED,
-                 G_TYPE_NONE,
-                 1,
-                 GTK_TYPE_SHEET_RANGE);
-
-
-  sheet_signals[RESIZE_RANGE] =
-    g_signal_new ("resize-range",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, resize_range),
-                 NULL, NULL,
-                 gtkextra_VOID__BOXED_BOXED,
-                 G_TYPE_NONE,
-                 2,
-                 GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
-                 );
-
-  sheet_signals[MOVE_RANGE] =
-    g_signal_new ("move-range",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, move_range),
-                 NULL, NULL,
-                 gtkextra_VOID__BOXED_BOXED,
-                 G_TYPE_NONE,
-                 2,
-                 GTK_TYPE_SHEET_RANGE, GTK_TYPE_SHEET_RANGE
-                 );
-
-  sheet_signals[TRAVERSE] =
-    g_signal_new ("traverse",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, traverse),
-                 NULL, NULL,
-                 gtkextra_BOOLEAN__INT_INT_POINTER_POINTER,
-                 G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT,
-                 G_TYPE_POINTER, G_TYPE_POINTER);
-
-
-  sheet_signals[DEACTIVATE] =
-    g_signal_new ("deactivate",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, deactivate),
-                 NULL, NULL,
-                 gtkextra_VOID__INT_INT,
-                 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-
-  sheet_signals[ACTIVATE] =
-    g_signal_new ("activate",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, activate),
-                 NULL, NULL,
-                 gtkextra_BOOLEAN__INT_INT,
-                 G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT);
-
-  sheet_signals[CHANGED] =
-    g_signal_new ("changed",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, changed),
-                 NULL, NULL,
-                 gtkextra_VOID__INT_INT,
-                 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-
-  widget_class->set_scroll_adjustments_signal =
-    g_signal_new ("set-scroll-adjustments",
-                 G_TYPE_FROM_CLASS (object_class),
-                 G_SIGNAL_RUN_LAST,
-                 offsetof (GtkSheetClass, set_scroll_adjustments),
-                 NULL, NULL,
-                 gtkextra_VOID__OBJECT_OBJECT,
-                 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
-
-
-  container_class->add = NULL;
-  container_class->remove = gtk_sheet_remove;
-  container_class->forall = gtk_sheet_forall;
-
-  object_class->dispose = gtk_sheet_dispose;
-  object_class->finalize = gtk_sheet_finalize;
-
-
-  row_geo_spec =
-    g_param_spec_pointer ("row-geometry",
-                         "Row Geometry",
-                         "A pointer to the model of the row geometry",
-                         G_PARAM_READABLE | G_PARAM_WRITABLE );
-
-  col_geo_spec =
-    g_param_spec_pointer ("column-geometry",
-                         "Column Geometry",
-                         "A pointer to the model of the column geometry",
-                         G_PARAM_READABLE | G_PARAM_WRITABLE );
-
-  model_spec =
-    g_param_spec_pointer ("model",
-                         "Model",
-                         "A pointer to the data model",
-                         G_PARAM_READABLE | G_PARAM_WRITABLE );
-
-
-  object_class->set_property = gtk_sheet_set_property;
-  object_class->get_property = gtk_sheet_get_property;
-
-  g_object_class_install_property (object_class,
-                                   PROP_ROW_GEO,
-                                   row_geo_spec);
-
-  g_object_class_install_property (object_class,
-                                   PROP_COL_GEO,
-                                   col_geo_spec);
-
-  g_object_class_install_property (object_class,
-                                   PROP_MODEL,
-                                   model_spec);
-
-
-  widget_class->realize = gtk_sheet_realize;
-  widget_class->unrealize = gtk_sheet_unrealize;
-  widget_class->map = gtk_sheet_map;
-  widget_class->unmap = gtk_sheet_unmap;
-  widget_class->style_set = gtk_sheet_style_set;
-  widget_class->button_press_event = gtk_sheet_button_press;
-  widget_class->button_release_event = gtk_sheet_button_release;
-  widget_class->motion_notify_event = gtk_sheet_motion;
-  widget_class->key_press_event = gtk_sheet_key_press;
-  widget_class->expose_event = gtk_sheet_expose;
-  widget_class->size_request = gtk_sheet_size_request;
-  widget_class->size_allocate = gtk_sheet_size_allocate;
-  widget_class->focus_in_event = NULL;
-  widget_class->focus_out_event = NULL;
-
-  klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments;
-  klass->select_row = NULL;
-  klass->select_column = NULL;
-  klass->select_range = NULL;
-  klass->resize_range = NULL;
-  klass->move_range = NULL;
-  klass->traverse = NULL;
-  klass->deactivate = NULL;
-  klass->activate = NULL;
-  klass->changed = NULL;
-}
-
-static void
-gtk_sheet_init (GtkSheet *sheet)
-{
-  sheet->model = NULL;
-  sheet->column_geometry = NULL;
-  sheet->row_geometry = NULL;
-
-  sheet->children = NULL;
-
-  sheet->flags = 0;
-  sheet->selection_mode = GTK_SELECTION_NONE;
-  sheet->freeze_count = 0;
-  sheet->state = GTK_SHEET_NORMAL;
-
-  GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
-  GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
-
-  sheet->column_title_window = NULL;
-  sheet->column_title_area.x = 0;
-  sheet->column_title_area.y = 0;
-  sheet->column_title_area.width = 0;
-  sheet->column_title_area.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
-
-  sheet->row_title_window = NULL;
-  sheet->row_title_area.x = 0;
-  sheet->row_title_area.y = 0;
-  sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
-  sheet->row_title_area.height = 0;
-
-
-  sheet->active_cell.row = 0;
-  sheet->active_cell.col = 0;
-  sheet->selection_cell.row = 0;
-  sheet->selection_cell.col = 0;
-
-  sheet->pixmap = NULL;
-
-  sheet->range.row0 = 0;
-  sheet->range.rowi = 0;
-  sheet->range.col0 = 0;
-  sheet->range.coli = 0;
-
-  sheet->state = GTK_SHEET_NORMAL;
-
-  sheet->sheet_window = NULL;
-  sheet->sheet_window_width = 0;
-  sheet->sheet_window_height = 0;
-  sheet->entry_widget = NULL;
-  sheet->entry_container = NULL;
-  sheet->button = NULL;
-
-  sheet->hoffset = 0;
-  sheet->voffset = 0;
-
-  sheet->hadjustment = NULL;
-  sheet->vadjustment = NULL;
-
-  sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
-  sheet->xor_gc = NULL;
-  sheet->fg_gc = NULL;
-  sheet->bg_gc = NULL;
-  sheet->x_drag = 0;
-  sheet->y_drag = 0;
-  gdk_color_parse ("white", &sheet->bg_color);
-  gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->bg_color, FALSE,
-                            TRUE);
-  gdk_color_parse ("gray", &sheet->grid_color);
-  gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->grid_color, FALSE,
-                            TRUE);
-
-  sheet->show_grid = TRUE;
-
-  sheet->motion_timer = 0;
-
-  sheet->columns_resizable = TRUE;
-  sheet->rows_resizable = TRUE;
-
-  sheet->row_titles_visible = TRUE;
-  sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
-
-  sheet->column_titles_visible = TRUE;
-  sheet->autoscroll = TRUE;
-  sheet->justify_entry = TRUE;
-
-
-  /* create sheet entry */
-  sheet->entry_type = 0;
-  create_sheet_entry (sheet);
-
-  /* create global selection button */
-  create_global_button (sheet);
-}
-
-
-/* Callback which occurs whenever columns are inserted / deleted in the model */
-static void
-columns_inserted_deleted_callback (GSheetModel *model, gint first_column,
-                                  gint n_columns,
-                                  gpointer data)
-{
-  gint i;
-  GtkSheet *sheet = GTK_SHEET (data);
-
-  GtkSheetRange range;
-  gint model_columns = g_sheet_model_get_column_count (model);
-
-
-  /* Need to update all the columns starting from the first column and onwards.
-   * Previous column are unchanged, so don't need to be updated.
-   */
-  range.col0 = first_column;
-  range.row0 = 0;
-  range.coli = xxx_column_count (sheet) - 1;
-  range.rowi = yyy_row_count (sheet) - 1;
-
-  adjust_scrollbars (sheet);
-
-  if (sheet->active_cell.col >= model_columns)
-    gtk_sheet_activate_cell (sheet, sheet->active_cell.row, model_columns - 1);
-
-  for (i = first_column; i <= MAX_VISIBLE_COLUMN (sheet); i++)
-    gtk_sheet_column_title_button_draw (sheet, i);
-
-  gtk_sheet_range_draw (sheet, &range);
-}
-
-
-/* Callback which occurs whenever rows are inserted / deleted in the model */
-static void
-rows_inserted_deleted_callback (GSheetModel *model, gint first_row,
-                               gint n_rows, gpointer data)
-{
-  gint i;
-  GtkSheet *sheet = GTK_SHEET (data);
-
-  GtkSheetRange range;
-
-  gint model_rows = g_sheet_model_get_row_count (model);
-
-  /* Need to update all the rows starting from the first row and onwards.
-   * Previous rows are unchanged, so don't need to be updated.
-   */
-  range.row0 = first_row;
-  range.col0 = 0;
-  range.rowi = yyy_row_count (sheet) - 1;
-  range.coli = xxx_column_count (sheet) - 1;
-
-  adjust_scrollbars (sheet);
-
-  if (sheet->active_cell.row >= model_rows)
-    gtk_sheet_activate_cell (sheet, model_rows - 1, sheet->active_cell.col);
-
-  for (i = first_row; i <= MAX_VISIBLE_ROW (sheet); i++)
-    gtk_sheet_row_title_button_draw (sheet, i);
-
-  gtk_sheet_range_draw (sheet, &range);
-}
-
-/*
-  If row0 or rowi are negative, then all rows will be updated.
-  If col0 or coli are negative, then all columns will be updated.
-*/
-static void
-range_update_callback (GSheetModel *m, gint row0, gint col0,
-                      gint rowi, gint coli, gpointer data)
-{
-  GtkSheet *sheet = GTK_SHEET (data);
-
-  GtkSheetRange range;
-
-  range.row0 = row0;
-  range.col0 = col0;
-  range.rowi = rowi;
-  range.coli = coli;
-
-  if ( MAX_VISIBLE_ROW (sheet) >
-       g_sheet_model_get_row_count (sheet->model)
-       ||
-       MAX_VISIBLE_COLUMN (sheet) >
-       g_sheet_model_get_column_count (sheet->model))
-    {
-      gtk_sheet_move_query (sheet, 0, 0);
-    }
-
-  if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
-    {
-      gint i;
-      gtk_sheet_range_draw (sheet, NULL);
-      adjust_scrollbars (sheet);
-
-      for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
-       gtk_sheet_row_title_button_draw (sheet, i);
-
-      for (i = MIN_VISIBLE_COLUMN (sheet);
-          i <= MAX_VISIBLE_COLUMN (sheet); i++)
-       gtk_sheet_column_title_button_draw (sheet, i);
-
-      return;
-    }
-  else if ( row0 < 0 || rowi < 0 )
-    {
-      range.row0 = MIN_VISIBLE_ROW (sheet);
-      range.rowi = MAX_VISIBLE_ROW (sheet);
-    }
-  else if ( col0 < 0 || coli < 0 )
-    {
-      range.col0 = MIN_VISIBLE_COLUMN (sheet);
-      range.coli = MAX_VISIBLE_COLUMN (sheet);
-    }
-
-  gtk_sheet_range_draw (sheet, &range);
-}
-
-
-/**
- * gtk_sheet_new:
- * @rows: initial number of rows
- * @columns: initial number of columns
- * @title: sheet title
- * @model: the model to use for the sheet data
- *
- * Creates a new sheet widget with the given number of rows and columns.
- *
- * Returns: the new sheet widget
- */
-GtkWidget *
-gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo, GSheetModel *model)
-{
-  GtkWidget *widget = g_object_new (GTK_TYPE_SHEET,
-                                   "row-geometry", vgeo,
-                                   "column-geometry", hgeo,
-                                   "model", model,
-                                   NULL);
-  return widget;
-}
-
-
-/**
- * gtk_sheet_set_model
- * @sheet: the sheet to set the model for
- * @model: the model to use for the sheet data
- *
- * Sets the model for a GtkSheet
- *
- */
-void
-gtk_sheet_set_model (GtkSheet *sheet, GSheetModel *model)
-{
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (sheet->model ) g_object_unref (sheet->model);
-
-  sheet->model = model;
-
-  if ( model)
-    {
-      g_object_ref (model);
-
-      g_signal_connect (model, "range_changed",
-                       G_CALLBACK (range_update_callback), sheet);
-
-      g_signal_connect (model, "rows_inserted",
-                       G_CALLBACK (rows_inserted_deleted_callback), sheet);
-
-      g_signal_connect (model, "rows_deleted",
-                       G_CALLBACK (rows_inserted_deleted_callback), sheet);
-
-      g_signal_connect (model, "columns_inserted",
-                       G_CALLBACK (columns_inserted_deleted_callback), sheet);
-
-      g_signal_connect (model, "columns_deleted",
-                       G_CALLBACK (columns_inserted_deleted_callback), sheet);
-    }
-}
-
-
-/* Call back for when the column titles have changed.
-   FIRST is the first column changed.
-   N_COLUMNS is the number of columns which have changed, or - 1, which
-   indicates that the column has changed to its right - most extremity
-*/
-static void
-column_titles_changed (GtkWidget *w, gint first, gint n_columns, gpointer data)
-{
-  GtkSheet *sheet = GTK_SHEET (data);
-  gboolean extremity = FALSE;
-
-  if ( n_columns == -1 )
-    {
-      extremity = TRUE;
-      n_columns = xxx_column_count (sheet) - 1 ;
-    }
-
-  if (!GTK_SHEET_IS_FROZEN (sheet))
-    {
-      gint i;
-      for ( i = first ; i <= first + n_columns ; ++i )
-       {
-         gtk_sheet_column_title_button_draw (sheet, i);
-         g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, i);
-       }
-    }
-
-  if ( extremity)
-    gtk_sheet_column_title_button_draw (sheet, -1);
-
-}
-
-void
-gtk_sheet_change_entry (GtkSheet *sheet, GtkType entry_type)
-{
-  gint state;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  state = sheet->state;
-
-  if (sheet->state == GTK_SHEET_NORMAL)
-    gtk_sheet_hide_active_cell (sheet);
-
-  sheet->entry_type = entry_type;
-
-  create_sheet_entry (sheet);
-
-  if (state == GTK_SHEET_NORMAL)
-    {
-      gtk_sheet_show_active_cell (sheet);
-      g_signal_connect (gtk_sheet_get_entry (sheet),
-                       "changed",
-                       G_CALLBACK (gtk_sheet_entry_changed),
-                       sheet);
-    }
-}
-
-void
-gtk_sheet_show_grid (GtkSheet *sheet, gboolean show)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (show == sheet->show_grid) return;
-
-  sheet->show_grid = show;
-
-  if (!GTK_SHEET_IS_FROZEN (sheet))
-    gtk_sheet_range_draw (sheet, NULL);
-}
-
-gboolean
-gtk_sheet_grid_visible (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  return sheet->show_grid;
-}
-
-void
-gtk_sheet_set_background (GtkSheet *sheet, GdkColor *color)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (!color)
-    {
-      gdk_color_parse ("white", &sheet->bg_color);
-      gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->bg_color, FALSE, TRUE);
-    }
-  else
-    sheet->bg_color = *color;
-
-  if (!GTK_SHEET_IS_FROZEN (sheet))
-    gtk_sheet_range_draw (sheet, NULL);
-}
-
-void
-gtk_sheet_set_grid (GtkSheet *sheet, GdkColor *color)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (!color)
-    {
-      gdk_color_parse ("black", &sheet->grid_color);
-      gdk_colormap_alloc_color (gdk_colormap_get_system (), &sheet->grid_color, FALSE, TRUE);
-    }
-  else
-    sheet->grid_color = *color;
-
-  if (!GTK_SHEET_IS_FROZEN (sheet))
-    gtk_sheet_range_draw (sheet, NULL);
-}
-
-guint
-gtk_sheet_get_columns_count (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  return xxx_column_count (sheet);
-}
-
-guint
-gtk_sheet_get_rows_count (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  return yyy_row_count (sheet);
-}
-
-gint
-gtk_sheet_get_state (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  return (sheet->state);
-}
-
-void
-gtk_sheet_set_selection_mode (GtkSheet *sheet, gint mode)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (GTK_WIDGET_REALIZED (sheet))
-    gtk_sheet_real_unselect_range (sheet, NULL);
-
-  sheet->selection_mode = mode;
-}
-
-void
-gtk_sheet_set_autoresize (GtkSheet *sheet, gboolean autoresize)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  sheet->autoresize = autoresize;
-}
-
-gboolean
-gtk_sheet_autoresize (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  return sheet->autoresize;
-}
-
-static void
-gtk_sheet_set_column_width (GtkSheet * sheet,
-                           gint column,
-                           guint width);
-
-
-static void
-gtk_sheet_autoresize_column (GtkSheet *sheet, gint column)
-{
-  gint text_width = 0;
-  gint row;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  if (column >= xxx_column_count (sheet) || column < 0) return;
-
-  for (row = 0; row < yyy_row_count (sheet); row++)
-    {
-      gchar *text = gtk_sheet_cell_get_text (sheet, row, column);
-      if (text && strlen (text) > 0)
-       {
-         GtkSheetCellAttr attributes;
-
-         gtk_sheet_get_attributes (sheet, row, column, &attributes);
-         if (attributes.is_visible)
-           {
-             gint width = STRING_WIDTH (GTK_WIDGET (sheet),
-                                        attributes.font_desc,
-                                        text)
-               + 2 * CELLOFFSET + attributes.border.width;
-             text_width = MAX (text_width, width);
-           }
-       }
-      dispose_string (sheet, text);
-    }
-
-  if (text_width > xxx_column_width (sheet, column) )
-    {
-      gtk_sheet_set_column_width (sheet, column, text_width);
-      GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
-    }
-}
-
-
-void
-gtk_sheet_set_autoscroll (GtkSheet *sheet, gboolean autoscroll)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  sheet->autoscroll = autoscroll;
-}
-
-gboolean
-gtk_sheet_autoscroll (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  return sheet->autoscroll;
-}
-
-
-void
-gtk_sheet_set_justify_entry (GtkSheet *sheet, gboolean justify)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  sheet->justify_entry = justify;
-}
-
-gboolean
-gtk_sheet_justify_entry (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  return sheet->justify_entry;
-}
-
-
-
-void
-gtk_sheet_freeze (GtkSheet *sheet)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  sheet->freeze_count++;
-  GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
-}
-
-void
-gtk_sheet_thaw (GtkSheet *sheet)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (sheet->freeze_count == 0) return;
-
-  sheet->freeze_count--;
-  if (sheet->freeze_count > 0) return;
-
-  adjust_scrollbars (sheet);
-
-  GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
-
-  sheet->old_vadjustment = -1.;
-  sheet->old_hadjustment = -1.;
-
-  if (sheet->hadjustment)
-    g_signal_emit_by_name (sheet->hadjustment,
-                          "value_changed");
-  if (sheet->vadjustment)
-    g_signal_emit_by_name (sheet->vadjustment,
-                          "value_changed");
-
-  if (sheet->state == GTK_STATE_NORMAL)
-    if (sheet->entry_widget && GTK_WIDGET_MAPPED (sheet->entry_widget))
-      {
-       gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
-                                sheet->active_cell.col);
-      }
-
-}
-
-void
-gtk_sheet_set_row_titles_width (GtkSheet *sheet, guint width)
-{
-  if (width < COLUMN_MIN_WIDTH) return;
-
-  sheet->row_title_area.width = width;
-
-  adjust_scrollbars (sheet);
-
-  sheet->old_hadjustment = -1.;
-  if (sheet->hadjustment)
-    g_signal_emit_by_name (sheet->hadjustment,
-                          "value_changed");
-  size_allocate_global_button (sheet);
-}
-
-void
-gtk_sheet_set_column_titles_height (GtkSheet *sheet, guint height)
-{
-  if (height < DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))) return;
-
-  sheet->column_title_area.height = height;
-
-  adjust_scrollbars (sheet);
-
-  sheet->old_vadjustment = -1.;
-  if (sheet->vadjustment)
-    g_signal_emit_by_name (sheet->vadjustment,
-                          "value_changed");
-  size_allocate_global_button (sheet);
-}
-
-void
-gtk_sheet_show_column_titles (GtkSheet *sheet)
-{
-  gint col;
-
-  if (sheet->column_titles_visible) return;
-
-  sheet->column_titles_visible = TRUE;
-
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      gdk_window_show (sheet->column_title_window);
-      gdk_window_move_resize (sheet->column_title_window,
-                             sheet->column_title_area.x,
-                             sheet->column_title_area.y,
-                             sheet->column_title_area.width,
-                             sheet->column_title_area.height);
-
-      for (col = MIN_VISIBLE_COLUMN (sheet);
-          col <= MAX_VISIBLE_COLUMN (sheet);
-          col++)
-       {
-         GtkSheetButton *button = xxx_column_button (sheet, col);
-         GtkSheetChild *child = button->child;
-         if (child)
-           gtk_sheet_child_show (child);
-         gtk_sheet_button_free (button);
-       }
-      adjust_scrollbars (sheet);
-    }
-
-  sheet->old_vadjustment = -1.;
-  if (sheet->vadjustment)
-    g_signal_emit_by_name (sheet->vadjustment,
-                          "value_changed");
-  size_allocate_global_button (sheet);
-}
-
-
-void
-gtk_sheet_show_row_titles (GtkSheet *sheet)
-{
-  gint row;
-
-  if (sheet->row_titles_visible) return;
-
-  sheet->row_titles_visible = TRUE;
-
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      gdk_window_show (sheet->row_title_window);
-      gdk_window_move_resize (sheet->row_title_window,
-                             sheet->row_title_area.x,
-                             sheet->row_title_area.y,
-                             sheet->row_title_area.width,
-                             sheet->row_title_area.height);
-
-      for (row = MIN_VISIBLE_ROW (sheet);
-          row <= MAX_VISIBLE_ROW (sheet);
-          row++)
-       {
-         const GtkSheetButton *button = yyy_row_button (sheet, row);
-         GtkSheetChild *child = button->child;
-
-         if (child)
-           {
-             gtk_sheet_child_show (child);
-           }
-       }
-      adjust_scrollbars (sheet);
-    }
-
-  sheet->old_hadjustment = -1.;
-  if (sheet->hadjustment)
-    g_signal_emit_by_name (sheet->hadjustment,
-                          "value_changed");
-  size_allocate_global_button (sheet);
-}
-
-void
-gtk_sheet_hide_column_titles (GtkSheet *sheet)
-{
-  gint col;
-
-  if (!sheet->column_titles_visible) return;
-
-  sheet->column_titles_visible = FALSE;
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      if (sheet->column_title_window)
-       gdk_window_hide (sheet->column_title_window);
-      if (GTK_WIDGET_VISIBLE (sheet->button))
-       gtk_widget_hide (sheet->button);
-
-      for (col = MIN_VISIBLE_COLUMN (sheet);
-          col <= MAX_VISIBLE_COLUMN (sheet);
-          col++)
-       {
-         GtkSheetButton *button = xxx_column_button (sheet, col);
-         GtkSheetChild *child = button->child;
-         if (child)
-           gtk_sheet_child_hide (child);
-         gtk_sheet_button_free (button);
-       }
-      adjust_scrollbars (sheet);
-    }
-
-  sheet->old_vadjustment = -1.;
-  if (sheet->vadjustment)
-    g_signal_emit_by_name (sheet->vadjustment,
-                          "value_changed");
-}
-
-void
-gtk_sheet_hide_row_titles (GtkSheet *sheet)
-{
-  gint row;
-
-  if (!sheet->row_titles_visible) return;
-
-  sheet->row_titles_visible = FALSE;
-
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      if (sheet->row_title_window)
-       gdk_window_hide (sheet->row_title_window);
-      if (GTK_WIDGET_VISIBLE (sheet->button))
-       gtk_widget_hide (sheet->button);
-      for (row = MIN_VISIBLE_ROW (sheet);
-          row <= MAX_VISIBLE_ROW (sheet);
-          row++)
-       {
-         const GtkSheetButton *button = yyy_row_button (sheet, row);
-         GtkSheetChild *child = button->child;
-
-         if (child)
-           gtk_sheet_child_hide (child);
-       }
-      adjust_scrollbars (sheet);
-    }
-
-  sheet->old_hadjustment = -1.;
-  if (sheet->hadjustment)
-    g_signal_emit_by_name (sheet->hadjustment,
-                          "value_changed");
-}
-
-gboolean
-gtk_sheet_column_titles_visible (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-  return sheet->column_titles_visible;
-}
-
-gboolean
-gtk_sheet_row_titles_visible (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-  return sheet->row_titles_visible;
-}
-
-void
-gtk_sheet_moveto (GtkSheet *sheet,
-                 gint row,
-                 gint column,
-                 gfloat row_align,
-                 gfloat col_align)
-{
-  gint x, y;
-  guint width, height;
-  gint adjust;
-  gint min_row, min_col;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  g_return_if_fail (sheet->hadjustment != NULL);
-  g_return_if_fail (sheet->vadjustment != NULL);
-
-  if (row < 0 || row >= yyy_row_count (sheet))
-    return;
-  if (column < 0 || column >= xxx_column_count (sheet))
-    return;
-
-  height = sheet->sheet_window_height;
-  width = sheet->sheet_window_width;
-
-  /* adjust vertical scrollbar */
-  if (row >= 0 && row_align >= 0.0)
-    {
-      y = ROW_TOP_YPIXEL (sheet, row) - sheet->voffset
-       - (gint) ( row_align * height + (1.0 - row_align)
-                  * yyy_row_height (sheet, row));
-
-      /* This forces the sheet to scroll when you don't see the entire cell */
-      min_row = row;
-      adjust = 0;
-      if (row_align >= 1.0)
-       {
-         while (min_row >= 0 && min_row > MIN_VISIBLE_ROW (sheet))
-           {
-             if (yyy_row_is_visible (sheet, min_row))
-               adjust += yyy_row_height (sheet, min_row);
-
-             if (adjust >= height)
-               {
-                 break;
-               }
-             min_row--;
-           }
-         min_row = MAX (min_row, 0);
-
-         min_row ++;
-
-         y = ROW_TOP_YPIXEL (sheet, min_row) - sheet->voffset +
-           yyy_row_height (sheet, min_row) - 1;
-       }
-
-      if (y < 0)
-       sheet->vadjustment->value = 0.0;
-      else
-       sheet->vadjustment->value = y;
-
-      sheet->old_vadjustment = -1.;
-      g_signal_emit_by_name (sheet->vadjustment,
-                            "value_changed");
-
-    }
-
-  /* adjust horizontal scrollbar */
-  if (column >= 0 && col_align >= 0.0)
-    {
-      x = COLUMN_LEFT_XPIXEL (sheet, column) - sheet->hoffset
-       - (gint) ( col_align*width + (1.0 - col_align)*
-                  xxx_column_width (sheet, column));
-
-      /* This forces the sheet to scroll when you don't see the entire cell */
-      min_col = column;
-      adjust = 0;
-      if (col_align == 1.0)
-       {
-         while (min_col >= 0 && min_col > MIN_VISIBLE_COLUMN (sheet))
-           {
-             if (xxx_column_is_visible (sheet, min_col))
-               adjust += xxx_column_width (sheet, min_col);
-
-             if (adjust >= width)
-               {
-                 break;
-               }
-             min_col--;
-           }
-         min_col = MAX (min_col, 0);
-         x = COLUMN_LEFT_XPIXEL (sheet, min_col) - sheet->hoffset +
-           xxx_column_width (sheet, min_col) - 1;
-       }
-
-      if (x < 0)
-       sheet->hadjustment->value = 0.0;
-      else
-       sheet->hadjustment->value = x;
-
-      sheet->old_vadjustment = -1.;
-      g_signal_emit_by_name (sheet->hadjustment,
-                            "value_changed");
-    }
-}
-
-
-void
-gtk_sheet_columns_set_resizable (GtkSheet *sheet, gboolean resizable)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  sheet->columns_resizable = resizable;
-}
-
-gboolean
-gtk_sheet_columns_resizable (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  return sheet->columns_resizable;
-}
-
-
-void
-gtk_sheet_rows_set_resizable (GtkSheet *sheet, gboolean resizable)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  sheet->rows_resizable = resizable;
-}
-
-gboolean
-gtk_sheet_rows_resizable (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  return sheet->rows_resizable;
-}
-
-
-void
-gtk_sheet_select_row (GtkSheet * sheet,
-                     gint row)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (row < 0 || row >= yyy_row_count (sheet))
-    return;
-
-  if (sheet->state != GTK_SHEET_NORMAL)
-    gtk_sheet_real_unselect_range (sheet, NULL);
-  else
-    gtk_sheet_deactivate_cell (sheet);
-
-  sheet->state = GTK_SHEET_ROW_SELECTED;
-  sheet->range.row0 = row;
-  sheet->range.col0 = 0;
-  sheet->range.rowi = row;
-  sheet->range.coli = xxx_column_count (sheet) - 1;
-  sheet->active_cell.row = row;
-  sheet->active_cell.col = 0;
-
-  g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
-  gtk_sheet_real_select_range (sheet, NULL);
-}
-
-
-void
-gtk_sheet_select_column (GtkSheet * sheet, gint column)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (column < 0 || column >= xxx_column_count (sheet))
-    return;
-
-  if (sheet->state != GTK_SHEET_NORMAL)
-    gtk_sheet_real_unselect_range (sheet, NULL);
-  else
-    gtk_sheet_deactivate_cell (sheet);
-
-
-  sheet->state = GTK_SHEET_COLUMN_SELECTED;
-  sheet->range.row0 = 0;
-  sheet->range.col0 = column;
-  sheet->range.rowi = yyy_row_count (sheet) - 1;
-  sheet->range.coli = column;
-  sheet->active_cell.row = 0;
-  sheet->active_cell.col = column;
-
-  g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
-  gtk_sheet_real_select_range (sheet, NULL);
-}
-
-
-
-
-static gboolean
-gtk_sheet_range_isvisible (const GtkSheet * sheet,
-                          GtkSheetRange range)
-{
-  g_return_val_if_fail (sheet != NULL, FALSE);
-
-  if (range.row0 < 0 || range.row0 >= yyy_row_count (sheet))
-    return FALSE;
-
-  if (range.rowi < 0 || range.rowi >= yyy_row_count (sheet))
-    return FALSE;
-
-  if (range.col0 < 0 || range.col0 >= xxx_column_count (sheet))
-    return FALSE;
-
-  if (range.coli < 0 || range.coli >= xxx_column_count (sheet))
-    return FALSE;
-
-  if (range.rowi < MIN_VISIBLE_ROW (sheet))
-    return FALSE;
-
-  if (range.row0 > MAX_VISIBLE_ROW (sheet))
-    return FALSE;
-
-  if (range.coli < MIN_VISIBLE_COLUMN (sheet))
-    return FALSE;
-
-  if (range.col0 > MAX_VISIBLE_COLUMN (sheet))
-    return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-gtk_sheet_cell_isvisible (GtkSheet * sheet,
-                         gint row, gint column)
-{
-  GtkSheetRange range;
-
-  range.row0 = row;
-  range.col0 = column;
-  range.rowi = row;
-  range.coli = column;
-
-  return gtk_sheet_range_isvisible (sheet, range);
-}
-
-void
-gtk_sheet_get_visible_range (GtkSheet *sheet, GtkSheetRange *range)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet)) ;
-  g_return_if_fail (range != NULL);
-
-  range->row0 = MIN_VISIBLE_ROW (sheet);
-  range->col0 = MIN_VISIBLE_COLUMN (sheet);
-  range->rowi = MAX_VISIBLE_ROW (sheet);
-  range->coli = MAX_VISIBLE_COLUMN (sheet);
-}
-
-GtkAdjustment *
-gtk_sheet_get_vadjustment (GtkSheet * sheet)
-{
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-
-  return sheet->vadjustment;
-}
-
-GtkAdjustment *
-gtk_sheet_get_hadjustment (GtkSheet * sheet)
-{
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-
-  return sheet->hadjustment;
-}
-
-void
-gtk_sheet_set_vadjustment (GtkSheet *sheet,
-                          GtkAdjustment *adjustment)
-{
-  GtkAdjustment *old_adjustment;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  if (adjustment)
-    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
-
-  if (sheet->vadjustment == adjustment)
-    return;
-
-  old_adjustment = sheet->vadjustment;
-
-  if (sheet->vadjustment)
-    {
-      g_signal_handlers_disconnect_matched (sheet->vadjustment,
-                                           G_SIGNAL_MATCH_DATA,
-                                           0, 0, 0, 0,
-                                           sheet);
-      g_object_unref (sheet->vadjustment);
-    }
-
-  sheet->vadjustment = adjustment;
-
-  if (sheet->vadjustment)
-    {
-      g_object_ref (sheet->vadjustment);
-      g_object_ref_sink (sheet->vadjustment);
-
-      g_signal_connect (sheet->vadjustment, "value_changed",
-                       G_CALLBACK (vadjustment_value_changed),
-                       sheet);
-    }
-
-  if (!sheet->vadjustment || !old_adjustment)
-    {
-      gtk_widget_queue_resize (GTK_WIDGET (sheet));
-      return;
-    }
-
-  sheet->old_vadjustment = sheet->vadjustment->value;
-}
-
-void
-gtk_sheet_set_hadjustment (GtkSheet *sheet,
-                          GtkAdjustment *adjustment)
-{
-  GtkAdjustment *old_adjustment;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  if (adjustment)
-    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
-
-  if (sheet->hadjustment == adjustment)
-    return;
-
-  old_adjustment = sheet->hadjustment;
-
-  if (sheet->hadjustment)
-    {
-      g_signal_handlers_disconnect_matched (sheet->hadjustment,
-                                           G_SIGNAL_MATCH_DATA,
-                                           0, 0, 0, 0,
-                                           sheet);
-      g_object_unref (sheet->hadjustment);
-    }
-
-  sheet->hadjustment = adjustment;
-
-  if (sheet->hadjustment)
-    {
-      g_object_ref (sheet->hadjustment);
-      g_object_ref_sink (sheet->hadjustment);
-
-      g_signal_connect (sheet->hadjustment, "value_changed",
-                       G_CALLBACK (hadjustment_value_changed),
-                       sheet);
-    }
-
-  if (!sheet->hadjustment || !old_adjustment)
-    {
-      gtk_widget_queue_resize (GTK_WIDGET (sheet));
-      return;
-    }
-
-  sheet->old_hadjustment = sheet->hadjustment->value;
-}
-
-static void
-gtk_sheet_set_scroll_adjustments (GtkSheet *sheet,
-                                 GtkAdjustment *hadjustment,
-                                 GtkAdjustment *vadjustment)
-{
-  if (sheet->hadjustment != hadjustment)
-    gtk_sheet_set_hadjustment (sheet, hadjustment);
-
-  if (sheet->vadjustment != vadjustment)
-    gtk_sheet_set_vadjustment (sheet, vadjustment);
-}
-
-static void
-gtk_sheet_finalize (GObject * object)
-{
-  GtkSheet *sheet;
-
-  g_return_if_fail (object != NULL);
-  g_return_if_fail (GTK_IS_SHEET (object));
-
-  sheet = GTK_SHEET (object);
-
-  if (G_OBJECT_CLASS (parent_class)->finalize)
-    (*G_OBJECT_CLASS (parent_class)->finalize) (object);
-}
-
-static void
-gtk_sheet_dispose  (GObject *object)
-{
-  GtkSheet *sheet = GTK_SHEET (object);
-  GList *children;
-
-  g_return_if_fail (object != NULL);
-  g_return_if_fail (GTK_IS_SHEET (object));
-
-  if ( sheet->dispose_has_run )
-    return ;
-
-  sheet->dispose_has_run = TRUE;
-
-  if (sheet->model) g_object_unref (sheet->model);
-  if (sheet->row_geometry) g_object_unref (sheet->row_geometry);
-  if (sheet->column_geometry) g_object_unref (sheet->column_geometry);
-
-  g_object_unref (sheet->entry_container);
-  sheet->entry_container = NULL;
-
-  g_object_unref (sheet->button);
-  sheet->button = NULL;
-
-  /* unref adjustments */
-  if (sheet->hadjustment)
-    {
-      g_signal_handlers_disconnect_matched (sheet->hadjustment,
-                                           G_SIGNAL_MATCH_DATA,
-                                           0, 0, 0, 0,
-                                           sheet);
-
-      g_object_unref (sheet->hadjustment);
-      sheet->hadjustment = NULL;
-    }
-
-  if (sheet->vadjustment)
-    {
-      g_signal_handlers_disconnect_matched (sheet->vadjustment,
-                                           G_SIGNAL_MATCH_DATA,
-                                           0, 0, 0, 0,
-                                           sheet);
-
-      g_object_unref (sheet->vadjustment);
-
-      sheet->vadjustment = NULL;
-    }
-
-  children = sheet->children;
-  while (children)
-    {
-      GtkSheetChild *child = (GtkSheetChild *)children->data;
-      if (child && child->widget)
-       gtk_sheet_remove (GTK_CONTAINER (sheet), child->widget);
-      children = sheet->children;
-    }
-  sheet->children = NULL;
-
-  if (G_OBJECT_CLASS (parent_class)->dispose)
-    (*G_OBJECT_CLASS (parent_class)->dispose) (object);
-}
-
-static void
-gtk_sheet_style_set (GtkWidget *widget,
-                    GtkStyle *previous_style)
-{
-  GtkSheet *sheet;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-
-  if (GTK_WIDGET_CLASS (parent_class)->style_set)
-    (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
-
-  sheet = GTK_SHEET (widget);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      gtk_style_set_background (widget->style, widget->window, widget->state);
-    }
-
-}
-
-static void
-gtk_sheet_realize (GtkWidget * widget)
-{
-  GtkSheet *sheet;
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-  GdkGCValues values, auxvalues;
-  GdkColormap *colormap;
-  GtkSheetChild *child;
-  GList *children;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-
-  sheet = GTK_SHEET (widget);
-
-  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
-
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.x = widget->allocation.x;
-  attributes.y = widget->allocation.y;
-  attributes.width = widget->allocation.width;
-  attributes.height = widget->allocation.height;
-  attributes.wclass = GDK_INPUT_OUTPUT;
-
-  attributes.visual = gtk_widget_get_visual (widget);
-  attributes.colormap = gtk_widget_get_colormap (widget);
-
-  attributes.event_mask = gtk_widget_get_events (widget);
-  attributes.event_mask |= (GDK_EXPOSURE_MASK |
-                           GDK_BUTTON_PRESS_MASK |
-                           GDK_BUTTON_RELEASE_MASK |
-                           GDK_KEY_PRESS_MASK |
-                           GDK_POINTER_MOTION_MASK |
-                           GDK_POINTER_MOTION_HINT_MASK);
-  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP |
-    GDK_WA_CURSOR;
-
-  attributes.cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
-
-  /* main window */
-  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
-
-  gdk_window_set_user_data (widget->window, sheet);
-
-  widget->style = gtk_style_attach (widget->style, widget->window);
-
-  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
-
-  attributes.x = 0;
-  if (sheet->row_titles_visible)
-    attributes.x = sheet->row_title_area.width;
-  attributes.y = 0;
-  attributes.width = sheet->column_title_area.width;
-  attributes.height = sheet->column_title_area.height;
-
-  /* column - title window */
-  sheet->column_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
-  gdk_window_set_user_data (sheet->column_title_window, sheet);
-  gtk_style_set_background (widget->style, sheet->column_title_window, GTK_STATE_NORMAL);
-
-  attributes.x = 0;
-  attributes.y = 0;
-  if (sheet->column_titles_visible)
-    attributes.y = sheet->column_title_area.height;
-  attributes.width = sheet->row_title_area.width;
-  attributes.height = sheet->row_title_area.height;
-
-  /* row - title window */
-  sheet->row_title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
-  gdk_window_set_user_data (sheet->row_title_window, sheet);
-  gtk_style_set_background (widget->style, sheet->row_title_window, GTK_STATE_NORMAL);
-
-  /* sheet - window */
-  attributes.cursor = gdk_cursor_new (GDK_PLUS);
-
-  attributes.x = 0;
-  attributes.y = 0;
-  attributes.width = sheet->sheet_window_width,
-    attributes.height = sheet->sheet_window_height;
-
-  sheet->sheet_window = gdk_window_new (widget->window, &attributes, attributes_mask);
-  gdk_window_set_user_data (sheet->sheet_window, sheet);
-
-  gdk_cursor_unref (attributes.cursor);
-
-  gdk_window_set_background (sheet->sheet_window, &widget->style->white);
-  gdk_window_show (sheet->sheet_window);
-
-  /* backing_pixmap */
-  gtk_sheet_make_backing_pixmap (sheet, 0, 0);
-
-  /* GCs */
-  if (sheet->fg_gc)
-    g_object_unref (sheet->fg_gc);
-  if (sheet->bg_gc)
-    g_object_unref (sheet->bg_gc);
-  sheet->fg_gc = gdk_gc_new (widget->window);
-  sheet->bg_gc = gdk_gc_new (widget->window);
-
-  colormap = gtk_widget_get_colormap (widget);
-
-  gdk_gc_get_values (sheet->fg_gc, &auxvalues);
-
-  values.foreground = widget->style->white;
-  values.function = GDK_INVERT;
-  values.subwindow_mode = GDK_INCLUDE_INFERIORS;
-  if (sheet->xor_gc)
-    g_object_unref (sheet->xor_gc);
-  sheet->xor_gc = gdk_gc_new_with_values (widget->window,
-                                         &values,
-                                         GDK_GC_FOREGROUND |
-                                         GDK_GC_FUNCTION |
-                                         GDK_GC_SUBWINDOW);
-
-
-  gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
-  gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
-
-  gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
-  gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
-
-
-  gdk_cursor_unref (sheet->cursor_drag);
-  sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
-
-  if (sheet->column_titles_visible)
-    gdk_window_show (sheet->column_title_window);
-  if (sheet->row_titles_visible)
-    gdk_window_show (sheet->row_title_window);
-
-  size_allocate_row_title_buttons (sheet);
-  size_allocate_column_title_buttons (sheet);
-
-  children = sheet->children;
-  while (children)
-    {
-      child = children->data;
-      children = children->next;
-
-      gtk_sheet_realize_child (sheet, child);
-    }
-
-  gtk_sheet_update_primary_selection (sheet);
-}
-
-static void
-create_global_button (GtkSheet *sheet)
-{
-  sheet->button = gtk_button_new_with_label (" ");
-
-  g_object_ref_sink (sheet->button);
-
-  g_signal_connect (sheet->button,
-                   "pressed",
-                   G_CALLBACK (global_button_clicked),
-                   sheet);
-}
-
-static void
-size_allocate_global_button (GtkSheet *sheet)
-{
-  GtkAllocation allocation;
-
-  if (!sheet->column_titles_visible) return;
-  if (!sheet->row_titles_visible) return;
-
-  gtk_widget_size_request (sheet->button, NULL);
-
-  allocation.x = 0;
-  allocation.y = 0;
-  allocation.width = sheet->row_title_area.width;
-  allocation.height = sheet->column_title_area.height;
-
-  gtk_widget_size_allocate (sheet->button, &allocation);
-  gtk_widget_show (sheet->button);
-}
-
-static void
-global_button_clicked (GtkWidget *widget, gpointer data)
-{
-  gboolean veto;
-
-  gtk_sheet_click_cell (GTK_SHEET (data), - 1, - 1, &veto);
-  gtk_widget_grab_focus (GTK_WIDGET (data));
-}
-
-
-static void
-gtk_sheet_unrealize (GtkWidget * widget)
-{
-  GtkSheet *sheet;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-
-  sheet = GTK_SHEET (widget);
-
-  gdk_cursor_unref (sheet->cursor_drag);
-
-  g_object_unref (sheet->xor_gc);
-  g_object_unref (sheet->fg_gc);
-  g_object_unref (sheet->bg_gc);
-
-  gdk_window_destroy (sheet->sheet_window);
-  gdk_window_destroy (sheet->column_title_window);
-  gdk_window_destroy (sheet->row_title_window);
-
-  if (sheet->pixmap)
-    {
-      g_object_unref (sheet->pixmap);
-      sheet->pixmap = NULL;
-    }
-
-  sheet->column_title_window = NULL;
-  sheet->sheet_window = NULL;
-  sheet->xor_gc = NULL;
-  sheet->fg_gc = NULL;
-  sheet->bg_gc = NULL;
-
-  gtk_widget_unparent (sheet->entry_widget);
-  if (sheet->button != NULL)
-    gtk_widget_unparent (sheet->button);
-
-  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
-    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
-}
-
-static void
-gtk_sheet_map (GtkWidget * widget)
-{
-  GtkSheet *sheet = GTK_SHEET (widget);
-  GtkSheetChild *child;
-  GList *children;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-
-  if (!GTK_WIDGET_MAPPED (widget))
-    {
-      GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
-
-      gdk_window_show (widget->window);
-      gdk_window_show (sheet->sheet_window);
-
-      if (sheet->column_titles_visible)
-       {
-         size_allocate_column_title_buttons (sheet);
-         gdk_window_show (sheet->column_title_window);
-       }
-      if (sheet->row_titles_visible)
-       {
-         size_allocate_row_title_buttons (sheet);
-         gdk_window_show (sheet->row_title_window);
-       }
-
-      if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
-         && sheet->active_cell.row >= 0
-         && sheet->active_cell.col >= 0 )
-       {
-         gtk_widget_show (sheet->entry_widget);
-         gtk_widget_map (sheet->entry_widget);
-       }
-
-      if (GTK_WIDGET_VISIBLE (sheet->button) &&
-         !GTK_WIDGET_MAPPED (sheet->button))
-       {
-         gtk_widget_show (sheet->button);
-         gtk_widget_map (sheet->button);
-       }
-
-      if (GTK_BIN (sheet->button)->child)
-       if (GTK_WIDGET_VISIBLE (GTK_BIN (sheet->button)->child) &&
-           !GTK_WIDGET_MAPPED (GTK_BIN (sheet->button)->child))
-         gtk_widget_map (GTK_BIN (sheet->button)->child);
-
-      gtk_sheet_range_draw (sheet, NULL);
-      gtk_sheet_activate_cell (sheet,
-                              sheet->active_cell.row,
-                              sheet->active_cell.col);
-
-      children = sheet->children;
-      while (children)
-       {
-         child = children->data;
-         children = children->next;
-
-         if (GTK_WIDGET_VISIBLE (child->widget) &&
-             !GTK_WIDGET_MAPPED (child->widget))
-           {
-             gtk_widget_map (child->widget);
-             gtk_sheet_position_child (sheet, child);
-           }
-       }
-
-    }
-}
-
-static void
-gtk_sheet_unmap (GtkWidget * widget)
-{
-  GtkSheet *sheet;
-  GtkSheetChild *child;
-  GList *children;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-
-  sheet = GTK_SHEET (widget);
-
-  if (GTK_WIDGET_MAPPED (widget))
-    {
-      GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
-
-      gdk_window_hide (sheet->sheet_window);
-      if (sheet->column_titles_visible)
-       gdk_window_hide (sheet->column_title_window);
-      if (sheet->row_titles_visible)
-       gdk_window_hide (sheet->row_title_window);
-      gdk_window_hide (widget->window);
-
-      if (GTK_WIDGET_MAPPED (sheet->entry_widget))
-       gtk_widget_unmap (sheet->entry_widget);
-
-      if (GTK_WIDGET_MAPPED (sheet->button))
-       gtk_widget_unmap (sheet->button);
-
-      children = sheet->children;
-      while (children)
-       {
-         child = children->data;
-         children = children->next;
-
-         if (GTK_WIDGET_VISIBLE (child->widget) &&
-             GTK_WIDGET_MAPPED (child->widget))
-           {
-             gtk_widget_unmap (child->widget);
-           }
-       }
-
-    }
-}
-
-
-static void
-gtk_sheet_cell_draw_default (GtkSheet *sheet, gint row, gint col)
-{
-  GtkWidget *widget;
-  GdkGC *fg_gc, *bg_gc;
-  GtkSheetCellAttr attributes;
-  GdkRectangle area;
-
-  g_return_if_fail (sheet != NULL);
-
-  /* bail now if we arn't drawable yet */
-  if (!GTK_WIDGET_DRAWABLE (sheet)) return;
-
-  if (row < 0 || row >= yyy_row_count (sheet)) return;
-  if (col < 0 || col >= xxx_column_count (sheet)) return;
-  if (! xxx_column_is_visible (sheet, col)) return;
-  if (! yyy_row_is_visible (sheet, row)) return;
-
-  widget = GTK_WIDGET (sheet);
-
-  gtk_sheet_get_attributes (sheet, row, col, &attributes);
-
-  /* select GC for background rectangle */
-  gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
-  gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
-
-  fg_gc = sheet->fg_gc;
-  bg_gc = sheet->bg_gc;
-
-  area.x = COLUMN_LEFT_XPIXEL (sheet, col);
-  area.y = ROW_TOP_YPIXEL (sheet, row);
-  area.width= xxx_column_width (sheet, col);
-  area.height = yyy_row_height (sheet, row);
-
-  gdk_draw_rectangle (sheet->pixmap,
-                     bg_gc,
-                     TRUE,
-                     area.x,
-                     area.y,
-                     area.width,
-                     area.height);
-
-  gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
-
-  if (sheet->show_grid)
-    {
-      gdk_gc_set_foreground (sheet->bg_gc, &sheet->grid_color);
-
-      gdk_draw_rectangle (sheet->pixmap,
-                         sheet->bg_gc,
-                         FALSE,
-                         area.x, area.y,
-                         area.width, area.height);
-    }
-}
-
-static void
-gtk_sheet_cell_draw_label (GtkSheet *sheet, gint row, gint col)
-{
-  GtkWidget *widget;
-  GdkRectangle area;
-  gint i;
-  gint text_width, text_height, y;
-  gint xoffset = 0;
-  gint size, sizel, sizer;
-  GdkGC *fg_gc, *bg_gc;
-  GtkSheetCellAttr attributes;
-  PangoLayout *layout;
-  PangoRectangle rect;
-  PangoRectangle logical_rect;
-  PangoLayoutLine *line;
-  PangoFontMetrics *metrics;
-  PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (sheet));
-  gint ascent, descent, y_pos;
-
-  gchar *label;
-
-  g_return_if_fail (sheet != NULL);
-
-  /* bail now if we aren't drawable yet */
-  if (!GTK_WIDGET_DRAWABLE (sheet))
-    return;
-
-  label = gtk_sheet_cell_get_text (sheet, row, col);
-  if (!label)
-    return;
-
-  if (row < 0 || row >= yyy_row_count (sheet)) return;
-  if (col < 0 || col >= xxx_column_count (sheet)) return;
-  if (! xxx_column_is_visible (sheet, col)) return;
-  if (!yyy_row_is_visible (sheet, row)) return;
-
-
-  widget = GTK_WIDGET (sheet);
-
-  gtk_sheet_get_attributes (sheet, row, col, &attributes);
-
-  /* select GC for background rectangle */
-  gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
-  gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
-
-  fg_gc = sheet->fg_gc;
-  bg_gc = sheet->bg_gc;
-
-  area.x = COLUMN_LEFT_XPIXEL (sheet, col);
-  area.y = ROW_TOP_YPIXEL (sheet, row);
-  area.width = xxx_column_width (sheet, col);
-  area.height = yyy_row_height (sheet, row);
-
-
-  layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
-  dispose_string (sheet, label);
-  pango_layout_set_font_description (layout, attributes.font_desc);
-
-  pango_layout_get_pixel_extents (layout, NULL, &rect);
-
-  line = pango_layout_get_lines (layout)->data;
-  pango_layout_line_get_extents (line, NULL, &logical_rect);
-
-  metrics = pango_context_get_metrics (context,
-                                      attributes.font_desc,
-                                      pango_context_get_language (context));
-
-  ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
-  descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
-
-  pango_font_metrics_unref (metrics);
-
-  /* Align primarily for locale's ascent / descent */
-
-  logical_rect.height /= PANGO_SCALE;
-  logical_rect.y /= PANGO_SCALE;
-  y_pos = area.height - logical_rect.height;
-
-  if (logical_rect.height > area.height)
-    y_pos = (logical_rect.height - area.height - 2 * CELLOFFSET) / 2;
-  else if (y_pos < 0)
-    y_pos = 0;
-  else if (y_pos + logical_rect.height > area.height)
-    y_pos = area.height - logical_rect.height;
-
-  text_width = rect.width;
-  text_height = rect.height;
-  y = area.y + y_pos - CELLOFFSET;
-
-  switch (attributes.justification)
-    {
-    case GTK_JUSTIFY_RIGHT:
-      size = area.width;
-      area.x +=area.width;
-      {
-       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
-         {
-           if ( !gtk_sheet_cell_empty (sheet, row, i)) break;
-           if (size >= text_width + CELLOFFSET) break;
-           size +=xxx_column_width (sheet, i);
-           xxx_column_set_right_column (sheet, i,
-                                        MAX (col,
-                                             xxx_column_right_column (sheet, i)));
-         }
-       area.width = size;
-      }
-      area.x -= size;
-      xoffset += area.width - text_width - 2 * CELLOFFSET -
-       attributes.border.width / 2;
-      break;
-    case GTK_JUSTIFY_CENTER:
-      sizel = area.width / 2;
-      sizer = area.width / 2;
-      area.x += area.width / 2;
-      {
-       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
-         {
-           if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
-           if (sizer >= text_width / 2) break;
-           sizer += xxx_column_width (sheet, i);
-           xxx_column_set_left_column (sheet, i,
-                                       MIN (
-                                            col,
-                                            xxx_column_left_column (sheet, i)));
-         }
-       for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
-         {
-           if ( ! gtk_sheet_cell_empty (sheet, row, i)) break;
-           if (sizel >= text_width / 2) break;
-           sizel +=xxx_column_width (sheet, i);
-           xxx_column_set_right_column (sheet, i,
-                                        MAX (col,
-                                             xxx_column_right_column (sheet, i)));
-         }
-       size = MIN (sizel, sizer);
-      }
-      area.x -= sizel;
-      xoffset += sizel - text_width / 2 - CELLOFFSET;
-      area.width = sizel + sizer;
-      break;
-    case GTK_JUSTIFY_LEFT:
-    default:
-      size = area.width;
-      {
-       for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
-         {
-           if (! gtk_sheet_cell_empty (sheet, row, i)) break;
-           if (size >= text_width + CELLOFFSET) break;
-           size +=xxx_column_width (sheet, i);
-           xxx_column_set_left_column (sheet, i,
-                                       MIN (
-                                            col,
-                                            xxx_column_left_column (sheet, i)));
-
-         }
-       area.width = size;
-      }
-      xoffset += attributes.border.width / 2;
-      break;
-    }
-
-  gdk_gc_set_clip_rectangle (fg_gc, &area);
-
-
-  gdk_draw_layout (sheet->pixmap, fg_gc,
-                  area.x + xoffset + CELLOFFSET,
-                  y,
-                  layout);
-
-  gdk_gc_set_clip_rectangle (fg_gc, NULL);
-  g_object_unref (layout);
-
-  gdk_draw_drawable (sheet->sheet_window,
-                  GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                  sheet->pixmap,
-                  area.x,
-                  area.y,
-                  area.x,
-                  area.y,
-                  area.width,
-                  area.height);
-
-}
-
-static void
-gtk_sheet_range_draw (GtkSheet *sheet, const GtkSheetRange *range)
-{
-  gint i, j;
-  GtkSheetRange drawing_range;
-  GdkRectangle area;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_SHEET (sheet));
-
-  if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-  if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
-
-  if (range == NULL)
-    {
-      drawing_range.row0 = MIN_VISIBLE_ROW (sheet);
-      drawing_range.col0 = MIN_VISIBLE_COLUMN (sheet);
-      drawing_range.rowi = MIN (MAX_VISIBLE_ROW (sheet),
-                               yyy_row_count (sheet) - 1);
-      drawing_range.coli = MAX_VISIBLE_COLUMN (sheet);
-
-
-      gdk_draw_rectangle (sheet->pixmap,
-                         GTK_WIDGET (sheet)->style->white_gc,
-                         TRUE,
-                         0, 0,
-                         sheet->sheet_window_width,
-                         sheet->sheet_window_height);
-    }
-  else
-    {
-      drawing_range.row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
-      drawing_range.col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
-      drawing_range.rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
-      drawing_range.coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
-    }
-
-  if (drawing_range.coli == xxx_column_count (sheet) - 1)
-    {
-      area.x = COLUMN_LEFT_XPIXEL (sheet,
-                                  xxx_column_count (sheet) - 1) +
-       xxx_column_width (sheet, xxx_column_count (sheet) - 1) + 1;
-
-      area.y = 0;
-
-      gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
-
-      gdk_draw_rectangle (sheet->pixmap,
-                         sheet->fg_gc,
-                         TRUE,
-                         area.x, area.y,
-                         sheet->sheet_window_width - area.x,
-                         sheet->sheet_window_height);
-
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      area.x,
-                      area.y,
-                      area.x,
-                      area.y,
-                      sheet->sheet_window_width - area.x,
-                      sheet->sheet_window_height);
-    }
-
-  if (drawing_range.rowi == yyy_row_count (sheet) - 1)
-    {
-      area.x = 0;
-      area.y = ROW_TOP_YPIXEL (sheet,
-                              yyy_row_count (sheet) - 1) +
-       yyy_row_height (sheet, yyy_row_count (sheet) - 1) + 1;
-
-      gdk_gc_set_foreground (sheet->fg_gc, &sheet->bg_color);
-
-      gdk_draw_rectangle (sheet->pixmap,
-                         sheet->fg_gc,
-                         TRUE,
-                         area.x, area.y,
-                         sheet->sheet_window_width,
-                         sheet->sheet_window_height - area.y);
-
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      area.x,
-                      area.y,
-                      area.x,
-                      area.y,
-                      sheet->sheet_window_width,
-                      sheet->sheet_window_height - area.y);
-    }
-
-  for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
-    for (j = drawing_range.col0; j <= drawing_range.coli; j++)
-      {
-       gtk_sheet_cell_draw_default (sheet, i, j);
-       gtk_sheet_cell_draw_label (sheet, i, j);
-      }
-
-  gtk_sheet_draw_backing_pixmap (sheet, drawing_range);
-
-  if (sheet->state != GTK_SHEET_NORMAL &&
-      gtk_sheet_range_isvisible (sheet, sheet->range))
-    gtk_sheet_range_draw_selection (sheet, drawing_range);
-
-  if (sheet->state == GTK_STATE_NORMAL &&
-      sheet->active_cell.row >= drawing_range.row0 &&
-      sheet->active_cell.row <= drawing_range.rowi &&
-      sheet->active_cell.col >= drawing_range.col0 &&
-      sheet->active_cell.col <= drawing_range.coli)
-    gtk_sheet_show_active_cell (sheet);
-}
-
-static void
-gtk_sheet_range_draw_selection (GtkSheet *sheet, GtkSheetRange range)
-{
-  GdkRectangle area;
-  gint i, j;
-  GtkSheetRange aux;
-
-  if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
-      range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
-    return;
-
-  if (!gtk_sheet_range_isvisible (sheet, range)) return;
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  aux = range;
-
-  range.col0 = MAX (sheet->range.col0, range.col0);
-  range.coli = MIN (sheet->range.coli, range.coli);
-  range.row0 = MAX (sheet->range.row0, range.row0);
-  range.rowi = MIN (sheet->range.rowi, range.rowi);
-
-  range.col0 = MAX (range.col0, MIN_VISIBLE_COLUMN (sheet));
-  range.coli = MIN (range.coli, MAX_VISIBLE_COLUMN (sheet));
-  range.row0 = MAX (range.row0, MIN_VISIBLE_ROW (sheet));
-  range.rowi = MIN (range.rowi, MAX_VISIBLE_ROW (sheet));
-
-  for (i = range.row0; i <= range.rowi; i++)
-    {
-      for (j = range.col0; j <= range.coli; j++)
-       {
-
-         if (gtk_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED &&
-             xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
-           {
-
-             area.x = COLUMN_LEFT_XPIXEL (sheet, j);
-             area.y = ROW_TOP_YPIXEL (sheet, i);
-             area.width= xxx_column_width (sheet, j);
-             area.height = yyy_row_height (sheet, i);
-
-             if (i == sheet->range.row0)
-               {
-                 area.y = area.y + 2;
-                 area.height = area.height - 2;
-               }
-             if (i == sheet->range.rowi) area.height = area.height - 3;
-             if (j == sheet->range.col0)
-               {
-                 area.x = area.x + 2;
-                 area.width = area.width - 2;
-               }
-             if (j == sheet->range.coli) area.width = area.width - 3;
-
-             if (i != sheet->active_cell.row || j != sheet->active_cell.col)
-               {
-                 gdk_draw_rectangle (sheet->sheet_window,
-                                     sheet->xor_gc,
-                                     TRUE,
-                                     area.x + 1, area.y + 1,
-                                     area.width, area.height);
-               }
-           }
-
-       }
-    }
-
-  gtk_sheet_draw_border (sheet, sheet->range);
-}
-
-static void
-gtk_sheet_draw_backing_pixmap (GtkSheet *sheet, GtkSheetRange range)
-{
-  gint x, y, width, height;
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
-  y = ROW_TOP_YPIXEL (sheet, range.row0);
-  width = COLUMN_LEFT_XPIXEL (sheet, range.coli) - x +
-    xxx_column_width (sheet, range.coli);
-
-  height = ROW_TOP_YPIXEL (sheet, range.rowi)- y + yyy_row_height (sheet, range.rowi);
-
-  if (range.row0 == sheet->range.row0)
-    {
-      y = y - 5;
-      height = height + 5;
-    }
-  if (range.rowi == sheet->range.rowi) height = height + 5;
-  if (range.col0 == sheet->range.col0)
-    {
-      x = x - 5;
-      width = width + 5;
-    }
-  if (range.coli == sheet->range.coli) width = width + 5;
-
-  width = MIN (width, sheet->sheet_window_width - x);
-  height = MIN (height, sheet->sheet_window_height - y);
-
-  x--;
-  y--;
-  width +=2;
-  height +=2;
-
-  x = (sheet->row_titles_visible)
-    ? MAX (x, sheet->row_title_area.width) : MAX (x, 0);
-  y = (sheet->column_titles_visible)
-    ? MAX (y, sheet->column_title_area.height) : MAX (y, 0);
-
-  if (range.coli == xxx_column_count (sheet) - 1)
-    width = sheet->sheet_window_width - x;
-  if (range.rowi == yyy_row_count (sheet) - 1)
-    height = sheet->sheet_window_height - y;
-
-  gdk_draw_drawable (sheet->sheet_window,
-                  GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                  sheet->pixmap,
-                  x,
-                  y,
-                  x,
-                  y,
-                  width + 1,
-                  height + 1);
-}
-
-
-void
-gtk_sheet_set_cell_text (GtkSheet *sheet, gint row, gint col, const gchar *text)
-{
-  GtkSheetCellAttr attributes;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
-  if (col < 0 || row < 0) return;
-
-  gtk_sheet_get_attributes (sheet, row, col, &attributes);
-  gtk_sheet_set_cell (sheet, row, col, attributes.justification, text);
-}
-
-static inline gint
-safe_strcmp (const gchar *s1, const gchar *s2)
-{
-  if ( !s1 && !s2) return 0;
-  if ( !s1) return - 1;
-  if ( !s2) return +1;
-  return strcmp (s1, s2);
-}
-
-void
-gtk_sheet_set_cell (GtkSheet *sheet, gint row, gint col,
-                   GtkJustification justification,
-                   const gchar *text)
-{
-  GSheetModel *model ;
-  gboolean changed ;
-  gchar *old_text ;
-
-  GtkSheetRange range;
-  gint text_width;
-  GtkSheetCellAttr attributes;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return;
-  if (col < 0 || row < 0) return;
-
-  gtk_sheet_get_attributes (sheet, row, col, &attributes);
-
-  attributes.justification = justification;
-
-  model = gtk_sheet_get_model (sheet);
-
-  old_text = g_sheet_model_get_string (model, row, col);
-
-  changed = FALSE;
-
-  if (0 != safe_strcmp (old_text, text))
-    changed = g_sheet_model_set_string (model, text, row, col);
-
-  if ( g_sheet_model_free_strings (model))
-    g_free (old_text);
-
-
-  if (changed && attributes.is_visible)
-    {
-      gchar *s = gtk_sheet_cell_get_text (sheet, row, col);
-      text_width = 0;
-      if (s && strlen (s) > 0)
-       {
-         text_width = STRING_WIDTH (GTK_WIDGET (sheet),
-                                    attributes.font_desc, text);
-       }
-      dispose_string (sheet, s);
-
-      range.row0 = row;
-      range.rowi = row;
-      range.col0 = MIN_VISIBLE_COLUMN (sheet);
-      range.coli = MAX_VISIBLE_COLUMN (sheet);
-
-      if (gtk_sheet_autoresize (sheet) &&
-         text_width > xxx_column_width (sheet, col) -
-         2 * CELLOFFSET- attributes.border.width)
-       {
-         gtk_sheet_set_column_width (sheet, col, text_width + 2 * CELLOFFSET
-                                     + attributes.border.width);
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
-       }
-      else
-       if (!GTK_SHEET_IS_FROZEN (sheet))
-         gtk_sheet_range_draw (sheet, &range);
-    }
-
-  if ( changed )
-    g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, col);
-
-}
-
-
-void
-gtk_sheet_cell_clear (GtkSheet *sheet, gint row, gint column)
-{
-  GtkSheetRange range;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-  if (column >= xxx_column_count (sheet) ||
-      row >= yyy_row_count (sheet)) return;
-
-  if (column < 0 || row < 0) return;
-
-  range.row0 = row;
-  range.rowi = row;
-  range.col0 = MIN_VISIBLE_COLUMN (sheet);
-  range.coli = MAX_VISIBLE_COLUMN (sheet);
-
-  gtk_sheet_real_cell_clear (sheet, row, column);
-
-  if (!GTK_SHEET_IS_FROZEN (sheet))
-    {
-      gtk_sheet_range_draw (sheet, &range);
-    }
-}
-
-static void
-gtk_sheet_real_cell_clear (GtkSheet *sheet, gint row, gint column)
-{
-  GSheetModel *model = gtk_sheet_get_model (sheet);
-
-  gchar *old_text = gtk_sheet_cell_get_text (sheet, row, column);
-
-  if (old_text && strlen (old_text) > 0 )
-    {
-      g_sheet_model_datum_clear (model, row, column);
-    }
-
-  dispose_string (sheet, old_text);
-}
-
-void
-gtk_sheet_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  gtk_sheet_real_range_clear (sheet, range);
-}
-
-static void
-gtk_sheet_real_range_clear (GtkSheet *sheet, const GtkSheetRange *range)
-{
-  gint i, j;
-  GtkSheetRange clear;
-
-  if (!range)
-    {
-      clear.row0 = 0;
-      clear.rowi = yyy_row_count (sheet) - 1;
-      clear.col0 = 0;
-      clear.coli = xxx_column_count (sheet) - 1;
-    }
-  else
-    clear=*range;
-
-  clear.row0 = MAX (clear.row0, 0);
-  clear.col0 = MAX (clear.col0, 0);
-  clear.rowi = MIN (clear.rowi, yyy_row_count (sheet) - 1 );
-  clear.coli = MIN (clear.coli, xxx_column_count (sheet) - 1 );
-
-  for (i = clear.row0; i <= clear.rowi; i++)
-    for (j = clear.col0; j <= clear.coli; j++)
-      {
-       gtk_sheet_real_cell_clear (sheet, i, j);
-      }
-
-  gtk_sheet_range_draw (sheet, NULL);
-}
-
-
-static gboolean
-gtk_sheet_cell_empty (const GtkSheet *sheet, gint row, gint col)
-{
-  gboolean empty;
-  char *text = gtk_sheet_cell_get_text (sheet, row, col);
-  empty = (text == NULL );
-
-  dispose_string (sheet, text);
-
-  return empty;
-}
-
-
-gchar *
-gtk_sheet_cell_get_text (const GtkSheet *sheet, gint row, gint col)
-{
-  GSheetModel *model;
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-
-  if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet))
-    return NULL;
-  if (col < 0 || row < 0) return NULL;
-
-  model = gtk_sheet_get_model (sheet);
-
-  if ( !model )
-    return NULL;
-
-  return g_sheet_model_get_string (model, row, col);
-}
-
-
-GtkStateType
-gtk_sheet_cell_get_state (GtkSheet *sheet, gint row, gint col)
-{
-  gint state;
-  GtkSheetRange *range;
-
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-  if (col >= xxx_column_count (sheet) || row >= yyy_row_count (sheet)) return 0;
-  if (col < 0 || row < 0) return 0;
-
-  state = sheet->state;
-  range = &sheet->range;
-
-  switch (state)
-    {
-    case GTK_SHEET_NORMAL:
-      return GTK_STATE_NORMAL;
-      break;
-    case GTK_SHEET_ROW_SELECTED:
-      if (row >= range->row0 && row <= range->rowi)
-       return GTK_STATE_SELECTED;
-      break;
-    case GTK_SHEET_COLUMN_SELECTED:
-      if (col >= range->col0 && col <= range->coli)
-       return GTK_STATE_SELECTED;
-      break;
-    case GTK_SHEET_RANGE_SELECTED:
-      if (row >= range->row0 && row <= range->rowi && \
-         col >= range->col0 && col <= range->coli)
-       return GTK_STATE_SELECTED;
-      break;
-    }
-  return GTK_STATE_NORMAL;
-}
-
-/* Convert X, Y (in pixels) to *ROW, *COLUMN (in cell coords)
-   -1 indicates the title buttons.
-   If the function returns FALSE, then the results will be unreliable.
-*/
-gboolean
-gtk_sheet_get_pixel_info (GtkSheet *sheet,
-                         gint x,
-                         gint y,
-                         gint *row,
-                         gint *column)
-{
-  gint trow, tcol;
-  *row = -G_MAXINT;
-  *column = -G_MAXINT;
-
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  /* bounds checking, return false if the user clicked
-     on a blank area */
-  if (y < 0)
-    return FALSE;
-
-  if (x < 0)
-    return FALSE;
-
-  if ( y < sheet->column_title_area.height + sheet->column_title_area.y)
-    *row = -1;
-
-  else
-    {
-      trow = ROW_FROM_YPIXEL (sheet, y);
-      if (trow > yyy_row_count (sheet))
-       return FALSE;
-
-      *row = trow;
-    }
-
-  if ( x < sheet->row_title_area.width + sheet->row_title_area.x)
-    *column = -1;
-  else
-    {
-      tcol = COLUMN_FROM_XPIXEL (sheet, x);
-      if (tcol > xxx_column_count (sheet))
-       return FALSE;
-
-      *column = tcol;
-    }
-
-  return TRUE;
-}
-
-gboolean
-gtk_sheet_get_cell_area (GtkSheet * sheet,
-                        gint row,
-                        gint column,
-                        GdkRectangle *area)
-{
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
-    return FALSE;
-
-  area->x = (column == -1) ? 0 : (COLUMN_LEFT_XPIXEL (sheet, column) -
-                                 (sheet->row_titles_visible
-                                  ? sheet->row_title_area.width
-                                  : 0));
-  area->y = (row == -1) ? 0 : (ROW_TOP_YPIXEL (sheet, row) -
-                              (sheet->column_titles_visible
-                               ? sheet->column_title_area.height
-                               : 0));
-  area->width= (column == -1) ? sheet->row_title_area.width
-    : xxx_column_width (sheet, column);
-
-  area->height= (row == -1) ? sheet->column_title_area.height
-    : yyy_row_height (sheet, row);
-
-  return TRUE;
-}
-
-gboolean
-gtk_sheet_set_active_cell (GtkSheet *sheet, gint row, gint column)
-{
-  g_return_val_if_fail (sheet != NULL, 0);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), 0);
-
-  if (row < - 1 || column < - 1) return FALSE;
-  if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
-    return FALSE;
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    gtk_sheet_deactivate_cell (sheet);
-
-  sheet->active_cell.row = row;
-  sheet->active_cell.col = column;
-
-  if ( row == -1 || column == -1)
-    {
-      gtk_sheet_hide_active_cell (sheet);
-      return TRUE;
-    }
-
-  if (!gtk_sheet_activate_cell (sheet, row, column)) return FALSE;
-
-  if (gtk_sheet_autoscroll (sheet))
-    gtk_sheet_move_query (sheet, row, column);
-
-  return TRUE;
-}
-
-void
-gtk_sheet_get_active_cell (GtkSheet *sheet, gint *row, gint *column)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if ( row ) *row = sheet->active_cell.row;
-  if (column) *column = sheet->active_cell.col;
-}
-
-static void
-gtk_sheet_entry_changed (GtkWidget *widget, gpointer data)
-{
-  GtkSheet *sheet;
-  gint row, col;
-  const char *text;
-  GtkJustification justification;
-  GtkSheetCellAttr attributes;
-
-  g_return_if_fail (data != NULL);
-  g_return_if_fail (GTK_IS_SHEET (data));
-
-  sheet = GTK_SHEET (data);
-
-  if (!GTK_WIDGET_VISIBLE (widget)) return;
-  if (sheet->state != GTK_STATE_NORMAL) return;
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  if (row < 0 || col < 0) return;
-
-  sheet->active_cell.row = -1;
-  sheet->active_cell.col = -1;
-
-  text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
-
-  GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
-
-  if (text && strlen (text) > 0)
-    {
-      gtk_sheet_get_attributes (sheet, row, col, &attributes);
-      justification = attributes.justification;
-      gtk_sheet_set_cell (sheet, row, col, justification, text);
-    }
-
-  if (sheet->freeze_count == 0)
-    GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
-
-  sheet->active_cell.row = row;;
-  sheet->active_cell.col = col;
-}
-
-
-static void
-gtk_sheet_deactivate_cell (GtkSheet *sheet)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return ;
-  if (sheet->state != GTK_SHEET_NORMAL) return ;
-
-  if ( sheet->active_cell.row == -1 || sheet->active_cell.col == -1 )
-    return ;
-
-  g_signal_emit (sheet, sheet_signals[DEACTIVATE], 0,
-                sheet->active_cell.row,
-                sheet->active_cell.col);
-
-
-  g_signal_handlers_disconnect_by_func (gtk_sheet_get_entry (sheet),
-                                       G_CALLBACK (gtk_sheet_entry_changed),
-                                       sheet);
-
-  gtk_sheet_hide_active_cell (sheet);
-  sheet->active_cell.row = -1;
-  sheet->active_cell.col = -1;
-
-  if (GTK_SHEET_REDRAW_PENDING (sheet))
-    {
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_REDRAW_PENDING);
-      gtk_sheet_range_draw (sheet, NULL);
-    }
-}
-
-static void
-gtk_sheet_hide_active_cell (GtkSheet *sheet)
-{
-  const char *text;
-  gint row, col;
-  GtkJustification justification;
-  GtkSheetCellAttr attributes;
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  if (row < 0 || col < 0) return;
-
-  if (sheet->freeze_count == 0)
-    GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IS_FROZEN);
-
-  text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
-
-  gtk_sheet_get_attributes (sheet, row, col, &attributes);
-  justification = attributes.justification;
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  gtk_widget_hide (sheet->entry_widget);
-  gtk_widget_unmap (sheet->entry_widget);
-
-  if (row != -1 && col != -1)
-    gdk_draw_drawable (sheet->sheet_window,
-                    GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                    sheet->pixmap,
-                    COLUMN_LEFT_XPIXEL (sheet, col)- 1,
-                    ROW_TOP_YPIXEL (sheet, row)- 1,
-                    COLUMN_LEFT_XPIXEL (sheet, col)- 1,
-                    ROW_TOP_YPIXEL (sheet, row)- 1,
-                    xxx_column_width (sheet, col) + 4,
-                    yyy_row_height (sheet, row)+4);
-
-  gtk_widget_grab_focus (GTK_WIDGET (sheet));
-
-  GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
-
-}
-
-static gboolean
-gtk_sheet_activate_cell (GtkSheet *sheet, gint row, gint col)
-{
-  gboolean veto = TRUE;
-
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  if (row < 0 || col < 0) return FALSE;
-
-  if ( row > yyy_row_count (sheet) || col > xxx_column_count (sheet))
-    return FALSE;
-
-  if (!veto) return FALSE;
-  if (sheet->state != GTK_SHEET_NORMAL)
-    {
-      sheet->state = GTK_SHEET_NORMAL;
-      gtk_sheet_real_unselect_range (sheet, NULL);
-    }
-
-  sheet->range.row0 = row;
-  sheet->range.col0 = col;
-  sheet->range.rowi = row;
-  sheet->range.coli = col;
-  sheet->active_cell.row = row;
-  sheet->active_cell.col = col;
-  sheet->selection_cell.row = row;
-  sheet->selection_cell.col = col;
-
-  GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-
-  gtk_sheet_show_active_cell (sheet);
-
-  g_signal_connect (gtk_sheet_get_entry (sheet),
-                   "changed",
-                   G_CALLBACK (gtk_sheet_entry_changed),
-                   sheet);
-
-  _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals [ACTIVATE], row, col, &veto);
-
-  return TRUE;
-}
-
-static void
-gtk_sheet_show_active_cell (GtkSheet *sheet)
-{
-  GtkEntry *sheet_entry;
-  GtkSheetCellAttr attributes;
-  gchar *text = NULL;
-  const gchar *old_text;
-  GtkJustification justification;
-  gint row, col;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  /* Don't show the active cell, if there is no active cell: */
-  if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
-    return;
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-  if (sheet->state != GTK_SHEET_NORMAL) return;
-  if (GTK_SHEET_IN_SELECTION (sheet)) return;
-
-  GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
-
-  sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
-
-  gtk_sheet_get_attributes (sheet, row, col, &attributes);
-
-  justification = GTK_JUSTIFY_LEFT;
-
-  if (gtk_sheet_justify_entry (sheet))
-    justification = attributes.justification;
-
-  text = gtk_sheet_cell_get_text (sheet, row, col);
-  if ( ! text )
-    text = g_strdup ("");
-
-  gtk_entry_set_visibility (GTK_ENTRY (sheet_entry), attributes.is_visible);
-
-
-  /*** Added by John Gotts. Mar 25, 2005 *********/
-  old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
-  if (strcmp (old_text, text) != 0)
-    {
-      if (!GTK_IS_ITEM_ENTRY (sheet_entry))
-       gtk_entry_set_text (GTK_ENTRY (sheet_entry), text);
-      else
-       gtk_item_entry_set_text (GTK_ITEM_ENTRY (sheet_entry), text, justification);
-    }
-
-  gtk_sheet_entry_set_max_size (sheet);
-  gtk_sheet_size_allocate_entry (sheet);
-
-  gtk_widget_map (sheet->entry_widget);
-
-  gtk_widget_grab_focus (GTK_WIDGET (sheet_entry));
-
-  dispose_string (sheet, text);
-}
-
-static void
-gtk_sheet_draw_active_cell (GtkSheet *sheet)
-{
-  gint row, col;
-  GtkSheetRange range;
-
-  if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  if (row < 0 || col < 0) return;
-
-  if (!gtk_sheet_cell_isvisible (sheet, row, col)) return;
-
-  range.col0 = range.coli = col;
-  range.row0 = range.rowi = row;
-
-  gtk_sheet_draw_border (sheet, range);
-}
-
-
-static void
-gtk_sheet_make_backing_pixmap (GtkSheet *sheet, guint width, guint height)
-{
-  gint pixmap_width, pixmap_height;
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  if (width == 0 && height == 0)
-    {
-      width = sheet->sheet_window_width + 80;
-      height = sheet->sheet_window_height + 80;
-    }
-
-  if (!sheet->pixmap)
-    {
-      /* allocate */
-      sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
-                                     width, height,
-                                     - 1);
-      if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
-    }
-  else
-    {
-      /* reallocate if sizes don't match */
-      gdk_drawable_get_size (sheet->pixmap,
-                            &pixmap_width, &pixmap_height);
-      if ( (pixmap_width != width) || (pixmap_height != height))
-       {
-         g_object_unref (sheet->pixmap);
-         sheet->pixmap = gdk_pixmap_new (sheet->sheet_window,
-                                         width, height,
-                                         - 1);
-         if (!GTK_SHEET_IS_FROZEN (sheet)) gtk_sheet_range_draw (sheet, NULL);
-       }
-    }
-}
-
-static void
-gtk_sheet_new_selection (GtkSheet *sheet, GtkSheetRange *range)
-{
-  gint i, j, mask1, mask2;
-  gint state, selected;
-  gint x, y, width, height;
-  GtkSheetRange new_range, aux_range;
-
-  g_return_if_fail (sheet != NULL);
-
-  if (range == NULL) range=&sheet->range;
-
-  new_range=*range;
-
-  range->row0 = MIN (range->row0, sheet->range.row0);
-  range->rowi = MAX (range->rowi, sheet->range.rowi);
-  range->col0 = MIN (range->col0, sheet->range.col0);
-  range->coli = MAX (range->coli, sheet->range.coli);
-
-  range->row0 = MAX (range->row0, MIN_VISIBLE_ROW (sheet));
-  range->rowi = MIN (range->rowi, MAX_VISIBLE_ROW (sheet));
-  range->col0 = MAX (range->col0, MIN_VISIBLE_COLUMN (sheet));
-  range->coli = MIN (range->coli, MAX_VISIBLE_COLUMN (sheet));
-
-  aux_range.row0 = MAX (new_range.row0, MIN_VISIBLE_ROW (sheet));
-  aux_range.rowi = MIN (new_range.rowi, MAX_VISIBLE_ROW (sheet));
-  aux_range.col0 = MAX (new_range.col0, MIN_VISIBLE_COLUMN (sheet));
-  aux_range.coli = MIN (new_range.coli, MAX_VISIBLE_COLUMN (sheet));
-
-  for (i = range->row0; i <= range->rowi; i++)
-    {
-      for (j = range->col0; j <= range->coli; j++)
-       {
-
-         state = gtk_sheet_cell_get_state (sheet, i, j);
-         selected= (i <= new_range.rowi && i >= new_range.row0 &&
-                    j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
-
-         if (state == GTK_STATE_SELECTED && selected &&
-             xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
-             (i == sheet->range.row0 || i == sheet->range.rowi ||
-              j == sheet->range.col0 || j == sheet->range.coli ||
-              i == new_range.row0 || i == new_range.rowi ||
-              j == new_range.col0 || j == new_range.coli))
-           {
-
-             mask1 = i == sheet->range.row0 ? 1 : 0;
-             mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
-             mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
-             mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
-
-             mask2 = i == new_range.row0 ? 1 : 0;
-             mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
-             mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
-             mask2 = j == new_range.coli ? mask2 + 8 : mask2;
-
-             if (mask1 != mask2)
-               {
-                 x = COLUMN_LEFT_XPIXEL (sheet, j);
-                 y = ROW_TOP_YPIXEL (sheet, i);
-                 width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
-                   xxx_column_width (sheet, j);
-                 height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
-
-                 if (i == sheet->range.row0)
-                   {
-                     y = y - 3;
-                     height = height + 3;
-                   }
-                 if (i == sheet->range.rowi) height = height + 3;
-                 if (j == sheet->range.col0)
-                   {
-                     x = x - 3;
-                     width = width + 3;
-                   }
-                 if (j == sheet->range.coli) width = width + 3;
-
-                 gdk_draw_drawable (sheet->sheet_window,
-                                  GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                                  sheet->pixmap,
-                                  x + 1,
-                                  y + 1,
-                                  x + 1,
-                                  y + 1,
-                                  width,
-                                  height);
-
-                 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
-                   {
-                     x = COLUMN_LEFT_XPIXEL (sheet, j);
-                     y = ROW_TOP_YPIXEL (sheet, i);
-                     width = COLUMN_LEFT_XPIXEL (sheet, j)- x+
-                       xxx_column_width (sheet, j);
-
-                     height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
-
-                     if (i == new_range.row0)
-                       {
-                         y = y+2;
-                         height = height - 2;
-                       }
-                     if (i == new_range.rowi) height = height - 3;
-                     if (j == new_range.col0)
-                       {
-                         x = x+2;
-                         width = width - 2;
-                       }
-                     if (j == new_range.coli) width = width - 3;
-
-                     gdk_draw_rectangle (sheet->sheet_window,
-                                         sheet->xor_gc,
-                                         TRUE,
-                                         x + 1, y + 1,
-                                         width, height);
-                   }
-               }
-           }
-       }
-    }
-
-  for (i = range->row0; i <= range->rowi; i++)
-    {
-      for (j = range->col0; j <= range->coli; j++)
-       {
-
-         state = gtk_sheet_cell_get_state (sheet, i, j);
-         selected= (i <= new_range.rowi && i >= new_range.row0 &&
-                    j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
-
-         if (state == GTK_STATE_SELECTED && !selected &&
-             xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
-           {
-
-             x = COLUMN_LEFT_XPIXEL (sheet, j);
-             y = ROW_TOP_YPIXEL (sheet, i);
-             width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
-             height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
-
-             if (i == sheet->range.row0)
-               {
-                 y = y - 3;
-                 height = height + 3;
-               }
-             if (i == sheet->range.rowi) height = height + 3;
-             if (j == sheet->range.col0)
-               {
-                 x = x - 3;
-                 width = width + 3;
-               }
-             if (j == sheet->range.coli) width = width + 3;
-
-             gdk_draw_drawable (sheet->sheet_window,
-                              GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                              sheet->pixmap,
-                              x + 1,
-                              y + 1,
-                              x + 1,
-                              y + 1,
-                              width,
-                              height);
-           }
-       }
-    }
-
-  for (i = range->row0; i <= range->rowi; i++)
-    {
-      for (j = range->col0; j <= range->coli; j++)
-       {
-
-         state = gtk_sheet_cell_get_state (sheet, i, j);
-         selected= (i <= new_range.rowi && i >= new_range.row0 &&
-                    j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
-
-         if (state != GTK_STATE_SELECTED && selected &&
-             xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i) &&
-             (i != sheet->active_cell.row || j != sheet->active_cell.col))
-           {
-
-             x = COLUMN_LEFT_XPIXEL (sheet, j);
-             y = ROW_TOP_YPIXEL (sheet, i);
-             width = COLUMN_LEFT_XPIXEL (sheet, j)- x+ xxx_column_width (sheet, j);
-             height = ROW_TOP_YPIXEL (sheet, i)- y + yyy_row_height (sheet, i);
-
-             if (i == new_range.row0)
-               {
-                 y = y+2;
-                 height = height - 2;
-               }
-             if (i == new_range.rowi) height = height - 3;
-             if (j == new_range.col0)
-               {
-                 x = x+2;
-                 width = width - 2;
-               }
-             if (j == new_range.coli) width = width - 3;
-
-             gdk_draw_rectangle (sheet->sheet_window,
-                                 sheet->xor_gc,
-                                 TRUE,
-                                 x + 1, y + 1,
-                                 width, height);
-
-           }
-
-       }
-    }
-
-  for (i = aux_range.row0; i <= aux_range.rowi; i++)
-    {
-      for (j = aux_range.col0; j <= aux_range.coli; j++)
-       {
-
-         if (xxx_column_is_visible (sheet, j) && yyy_row_is_visible (sheet, i))
-           {
-
-             state = gtk_sheet_cell_get_state (sheet, i, j);
-
-             mask1 = i == sheet->range.row0 ? 1 : 0;
-             mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
-             mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
-             mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
-
-             mask2 = i == new_range.row0 ? 1 : 0;
-             mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
-             mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
-             mask2 = j == new_range.coli ? mask2 + 8 : mask2;
-             if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
-               {
-                 x = COLUMN_LEFT_XPIXEL (sheet, j);
-                 y = ROW_TOP_YPIXEL (sheet, i);
-                 width = xxx_column_width (sheet, j);
-                 height = yyy_row_height (sheet, i);
-                 if (mask2 & 1)
-                   gdk_draw_rectangle (sheet->sheet_window,
-                                       sheet->xor_gc,
-                                       TRUE,
-                                       x + 1, y - 1,
-                                       width, 3);
-
-
-                 if (mask2 & 2)
-                   gdk_draw_rectangle (sheet->sheet_window,
-                                       sheet->xor_gc,
-                                       TRUE,
-                                       x + 1, y + height - 1,
-                                       width, 3);
-
-                 if (mask2 & 4)
-                   gdk_draw_rectangle (sheet->sheet_window,
-                                       sheet->xor_gc,
-                                       TRUE,
-                                       x - 1, y + 1,
-                                       3, height);
-
-
-                 if (mask2 & 8)
-                   gdk_draw_rectangle (sheet->sheet_window,
-                                       sheet->xor_gc,
-                                       TRUE,
-                                       x + width - 1, y + 1,
-                                       3, height);
-
-
-
-               }
-
-           }
-
-       }
-    }
-
-
-  *range = new_range;
-  gtk_sheet_draw_corners (sheet, new_range);
-
-}
-
-static void
-gtk_sheet_draw_border (GtkSheet *sheet, GtkSheetRange new_range)
-{
-  GtkWidget *widget;
-  GdkRectangle area;
-  gint i;
-  gint x, y, width, height;
-
-  widget = GTK_WIDGET (sheet);
-
-  x = COLUMN_LEFT_XPIXEL (sheet, new_range.col0);
-  y = ROW_TOP_YPIXEL (sheet, new_range.row0);
-  width = COLUMN_LEFT_XPIXEL (sheet, new_range.coli) - x +
-    xxx_column_width (sheet, new_range.coli);
-
-  height = ROW_TOP_YPIXEL (sheet, new_range.rowi) - y +
-    yyy_row_height (sheet, new_range.rowi);
-
-  area.x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet));
-  area.y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet));
-  area.width = sheet->sheet_window_width;
-  area.height = sheet->sheet_window_height;
-
-  if (x < 0)
-    {
-      width = width + x;
-      x = 0;
-    }
-  if (width > area.width) width = area.width + 10;
-  if (y < 0)
-    {
-      height = height + y;
-      y = 0;
-    }
-  if (height > area.height) height = area.height + 10;
-
-  gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
-
-  for (i = -1; i <= 1; ++i)
-    gdk_draw_rectangle (sheet->sheet_window,
-                       sheet->xor_gc,
-                       FALSE,
-                       x + i,
-                       y + i,
-                       width - 2 * i,
-                       height - 2 * i);
-
-  gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
-
-
-  gtk_sheet_draw_corners (sheet, new_range);
-}
-
-static void
-gtk_sheet_draw_corners (GtkSheet *sheet, GtkSheetRange range)
-{
-  gint x, y;
-  guint width = 1;
-
-  if (gtk_sheet_cell_isvisible (sheet, range.row0, range.col0))
-    {
-      x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
-      y = ROW_TOP_YPIXEL (sheet, range.row0);
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      x - 1,
-                      y - 1,
-                      x - 1,
-                      y - 1,
-                      3,
-                      3);
-      gdk_draw_rectangle (sheet->sheet_window,
-                         sheet->xor_gc,
-                         TRUE,
-                         x - 1, y - 1,
-                         3, 3);
-    }
-
-  if (gtk_sheet_cell_isvisible (sheet, range.row0, range.coli) ||
-      sheet->state == GTK_SHEET_COLUMN_SELECTED)
-    {
-      x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
-       xxx_column_width (sheet, range.coli);
-      y = ROW_TOP_YPIXEL (sheet, range.row0);
-      width = 1;
-      if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
-       {
-         y = ROW_TOP_YPIXEL (sheet, MIN_VISIBLE_ROW (sheet))+3;
-         width = 3;
-       }
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      x - width,
-                      y - width,
-                      x - width,
-                      y - width,
-                      2 * width + 1,
-                      2 * width + 1);
-      gdk_draw_rectangle (sheet->sheet_window,
-                         sheet->xor_gc,
-                         TRUE,
-                         x - width + width / 2, y - width + width / 2,
-                         2 + width, 2 + width);
-    }
-
-  if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.col0) ||
-      sheet->state == GTK_SHEET_ROW_SELECTED)
-    {
-      x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
-      y = ROW_TOP_YPIXEL (sheet, range.rowi)+
-       yyy_row_height (sheet, range.rowi);
-      width = 1;
-      if (sheet->state == GTK_SHEET_ROW_SELECTED)
-       {
-         x = COLUMN_LEFT_XPIXEL (sheet, MIN_VISIBLE_COLUMN (sheet))+3;
-         width = 3;
-       }
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      x - width,
-                      y - width,
-                      x - width,
-                      y - width,
-                      2 * width + 1,
-                      2 * width + 1);
-      gdk_draw_rectangle (sheet->sheet_window,
-                         sheet->xor_gc,
-                         TRUE,
-                         x - width + width / 2, y - width + width / 2,
-                         2 + width, 2 + width);
-    }
-
-  if (gtk_sheet_cell_isvisible (sheet, range.rowi, range.coli))
-    {
-      x = COLUMN_LEFT_XPIXEL (sheet, range.coli)+
-       xxx_column_width (sheet, range.coli);
-      y = ROW_TOP_YPIXEL (sheet, range.rowi)+
-       yyy_row_height (sheet, range.rowi);
-      width = 1;
-      if (sheet->state == GTK_SHEET_RANGE_SELECTED) width = 3;
-      if (sheet->state == GTK_SHEET_NORMAL) width = 3;
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      x - width,
-                      y - width,
-                      x - width,
-                      y - width,
-                      2 * width + 1,
-                      2 * width + 1);
-      gdk_draw_rectangle (sheet->sheet_window,
-                         sheet->xor_gc,
-                         TRUE,
-                         x - width + width / 2, y - width + width / 2,
-                         2 + width, 2 + width);
-
-    }
-
-}
-
-
-static void
-gtk_sheet_real_select_range (GtkSheet * sheet,
-                            const GtkSheetRange * range)
-{
-  gint state;
-
-  g_return_if_fail (sheet != NULL);
-
-  if (range == NULL) range = &sheet->range;
-
-  memcpy (&sheet->range, range, sizeof (*range));
-
-  if (range->row0 < 0 || range->rowi < 0) return;
-  if (range->col0 < 0 || range->coli < 0) return;
-
-  state = sheet->state;
-
-  if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
-      range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
-    {
-      gtk_sheet_new_selection (sheet, &sheet->range);
-    }
-  else
-    {
-      gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
-      gtk_sheet_range_draw_selection (sheet, sheet->range);
-    }
-
-  gtk_sheet_update_primary_selection (sheet);
-
-  g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
-}
-
-
-void
-gtk_sheet_get_selected_range           (GtkSheet *sheet,
-                                        GtkSheetRange *range)
-{
-  g_return_if_fail (sheet != NULL);
-  *range = sheet->range;
-}
-
-
-void
-gtk_sheet_select_range (GtkSheet * sheet, const GtkSheetRange *range)
-{
-  g_return_if_fail (sheet != NULL);
-
-  if (range == NULL) range=&sheet->range;
-
-  if (range->row0 < 0 || range->rowi < 0) return;
-  if (range->col0 < 0 || range->coli < 0) return;
-
-
-  if (sheet->state != GTK_SHEET_NORMAL)
-    gtk_sheet_real_unselect_range (sheet, NULL);
-  else
-    gtk_sheet_deactivate_cell (sheet);
-
-  sheet->range.row0 = range->row0;
-  sheet->range.rowi = range->rowi;
-  sheet->range.col0 = range->col0;
-  sheet->range.coli = range->coli;
-  sheet->active_cell.row = range->row0;
-  sheet->active_cell.col = range->col0;
-  sheet->selection_cell.row = range->rowi;
-  sheet->selection_cell.col = range->coli;
-
-  sheet->state = GTK_SHEET_RANGE_SELECTED;
-  gtk_sheet_real_select_range (sheet, NULL);
-
-}
-
-void
-gtk_sheet_unselect_range (GtkSheet * sheet)
-{
-  if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    return;
-
-  gtk_sheet_real_unselect_range (sheet, NULL);
-  sheet->state = GTK_STATE_NORMAL;
-
-  gtk_sheet_activate_cell (sheet,
-                          sheet->active_cell.row, sheet->active_cell.col);
-}
-
-
-static void
-gtk_sheet_real_unselect_range (GtkSheet * sheet,
-                              const GtkSheetRange *range)
-{
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
-
-  if ( range == NULL)
-    range = &sheet->range;
-
-  if (range->row0 < 0 || range->rowi < 0) return;
-  if (range->col0 < 0 || range->coli < 0) return;
-
-  g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
-  g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
-
-  if (gtk_sheet_range_isvisible (sheet, *range))
-    gtk_sheet_draw_backing_pixmap (sheet, *range);
-
-  sheet->range.row0 = -1;
-  sheet->range.rowi = -1;
-  sheet->range.col0 = -1;
-  sheet->range.coli = -1;
-
-  gtk_sheet_position_children (sheet);
-}
-
-
-static gint
-gtk_sheet_expose (GtkWidget * widget,
-                 GdkEventExpose * event)
-{
-  GtkSheet *sheet;
-  GtkSheetRange range;
-
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
-
-  sheet = GTK_SHEET (widget);
-
-  if (GTK_WIDGET_DRAWABLE (widget))
-    {
-      range.row0 = ROW_FROM_YPIXEL (sheet, event->area.y);
-      range.col0 = COLUMN_FROM_XPIXEL (sheet, event->area.x);
-      range.rowi = ROW_FROM_YPIXEL (sheet, event->area.y + event->area.height);
-      range.coli = COLUMN_FROM_XPIXEL (sheet, event->area.x + event->area.width);
-
-      /* exposure events on the sheet */
-      if (event->window == sheet->row_title_window &&
-         sheet->row_titles_visible)
-       {
-         gint i;
-         for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
-           gtk_sheet_row_title_button_draw (sheet, i);
-       }
-
-      if (event->window == sheet->column_title_window &&
-         sheet->column_titles_visible)
-       {
-         gint i;
-         for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
-           gtk_sheet_column_title_button_draw (sheet, i);
-       }
-
-      if (event->window == sheet->sheet_window)
-       {
-         gtk_sheet_draw_backing_pixmap (sheet, range);
-
-         if (sheet->state != GTK_SHEET_NORMAL)
-           {
-             if (gtk_sheet_range_isvisible (sheet, sheet->range))
-               gtk_sheet_draw_backing_pixmap (sheet, sheet->range);
-             if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
-               gtk_sheet_draw_backing_pixmap (sheet, sheet->drag_range);
-
-             if (gtk_sheet_range_isvisible (sheet, sheet->range))
-               gtk_sheet_range_draw_selection (sheet, sheet->range);
-             if (GTK_SHEET_IN_RESIZE (sheet) || GTK_SHEET_IN_DRAG (sheet))
-               draw_xor_rectangle (sheet, sheet->drag_range);
-           }
-
-         if ((!GTK_SHEET_IN_XDRAG (sheet)) && (!GTK_SHEET_IN_YDRAG (sheet)))
-           {
-             if (sheet->state == GTK_SHEET_NORMAL)
-               gtk_sheet_draw_active_cell (sheet);
-           }
-       }
-    }
-
-  if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
-    gtk_widget_grab_focus (GTK_WIDGET (sheet));
-
-  (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
-
-  return FALSE;
-}
-
-
-static gboolean
-gtk_sheet_button_press (GtkWidget * widget,
-                       GdkEventButton * event)
-{
-  GtkSheet *sheet;
-  GdkModifierType mods;
-  gint x, y, row, column;
-  gboolean veto;
-
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
-  sheet = GTK_SHEET (widget);
-
-  /* Cancel any pending tooltips */
-  if (sheet->motion_timer)
-    {
-      g_source_remove (sheet->motion_timer);
-      sheet->motion_timer = 0;
-    }
-
-  gtk_widget_get_pointer (widget, &x, &y);
-  gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
-
-
-  if (event->window == sheet->column_title_window)
-    {
-      g_signal_emit (sheet,
-                    sheet_signals[BUTTON_EVENT_COLUMN], 0,
-                    column, event);
-
-      if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
-       g_signal_emit (sheet,
-                      sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
-
-    }
-  else if (event->window == sheet->row_title_window)
-    {
-      g_signal_emit (sheet,
-                    sheet_signals[BUTTON_EVENT_ROW], 0,
-                    row, event);
-
-      if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
-       g_signal_emit (sheet,
-                      sheet_signals[DOUBLE_CLICK_ROW], 0, row);
-    }
-
-
-  gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
-
-  if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
-
-
-  /* press on resize windows */
-  if (event->window == sheet->column_title_window &&
-      gtk_sheet_columns_resizable (sheet))
-    {
-      gtk_widget_get_pointer (widget, &sheet->x_drag, NULL);
-      if (POSSIBLE_XDRAG (sheet, sheet->x_drag, &sheet->drag_cell.col))
-       {
-         guint req;
-         if (event->type == GDK_2BUTTON_PRESS)
-           {
-             gtk_sheet_autoresize_column (sheet, sheet->drag_cell.col);
-             GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
-             return TRUE;
-           }
-         gtk_sheet_column_size_request (sheet, sheet->drag_cell.col, &req);
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
-         gdk_pointer_grab (sheet->column_title_window, FALSE,
-                           GDK_POINTER_MOTION_HINT_MASK |
-                           GDK_BUTTON1_MOTION_MASK |
-                           GDK_BUTTON_RELEASE_MASK,
-                           NULL, NULL, event->time);
-
-         draw_xor_vline (sheet);
-         return TRUE;
-       }
-    }
-
-  if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable (sheet))
-    {
-      gtk_widget_get_pointer (widget, NULL, &sheet->y_drag);
-
-      if (POSSIBLE_YDRAG (sheet, sheet->y_drag, &sheet->drag_cell.row))
-       {
-         guint req;
-         gtk_sheet_row_size_request (sheet, sheet->drag_cell.row, &req);
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
-         gdk_pointer_grab (sheet->row_title_window, FALSE,
-                           GDK_POINTER_MOTION_HINT_MASK |
-                           GDK_BUTTON1_MOTION_MASK |
-                           GDK_BUTTON_RELEASE_MASK,
-                           NULL, NULL, event->time);
-
-         draw_xor_hline (sheet);
-         return TRUE;
-       }
-    }
-
-  /* the sheet itself does not handle other than single click events */
-  if (event->type != GDK_BUTTON_PRESS) return FALSE;
-
-  /* selections on the sheet */
-  if (event->window == sheet->sheet_window)
-    {
-      gtk_widget_get_pointer (widget, &x, &y);
-      gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
-      gdk_pointer_grab (sheet->sheet_window, FALSE,
-                       GDK_POINTER_MOTION_HINT_MASK |
-                       GDK_BUTTON1_MOTION_MASK |
-                       GDK_BUTTON_RELEASE_MASK,
-                       NULL, NULL, event->time);
-      gtk_grab_add (GTK_WIDGET (sheet));
-
-      /* This seems to be a kludge to work around a problem where the sheet
-        scrolls to another position.  The timeout scrolls it back to its
-        original posn.          JMD 3 July 2007
-      */
-      gtk_widget_grab_focus (GTK_WIDGET (sheet));
-
-      if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
-         sheet->selection_mode != GTK_SELECTION_NONE &&
-         sheet->cursor_drag->type == GDK_SIZING &&
-         !GTK_SHEET_IN_SELECTION (sheet) && !GTK_SHEET_IN_RESIZE (sheet))
-       {
-         if (sheet->state == GTK_STATE_NORMAL)
-           {
-             row = sheet->active_cell.row;
-             column = sheet->active_cell.col;
-             gtk_sheet_deactivate_cell (sheet);
-             sheet->active_cell.row = row;
-             sheet->active_cell.col = column;
-             sheet->drag_range = sheet->range;
-             sheet->state = GTK_SHEET_RANGE_SELECTED;
-             gtk_sheet_select_range (sheet, &sheet->drag_range);
-           }
-         sheet->x_drag = x;
-         sheet->y_drag = y;
-         if (row > sheet->range.rowi) row--;
-         if (column > sheet->range.coli) column--;
-         sheet->drag_cell.row = row;
-         sheet->drag_cell.col = column;
-         sheet->drag_range = sheet->range;
-         draw_xor_rectangle (sheet, sheet->drag_range);
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
-       }
-      else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
-              !GTK_SHEET_IN_SELECTION (sheet)
-              && ! GTK_SHEET_IN_DRAG (sheet)
-              && sheet->active_cell.row >= 0
-              && sheet->active_cell.col >= 0
-              )
-       {
-         if (sheet->state == GTK_STATE_NORMAL)
-           {
-             row = sheet->active_cell.row;
-             column = sheet->active_cell.col;
-             gtk_sheet_deactivate_cell (sheet);
-             sheet->active_cell.row = row;
-             sheet->active_cell.col = column;
-             sheet->drag_range = sheet->range;
-             sheet->state = GTK_SHEET_RANGE_SELECTED;
-             gtk_sheet_select_range (sheet, &sheet->drag_range);
-           }
-         sheet->x_drag = x;
-         sheet->y_drag = y;
-         if (row < sheet->range.row0) row++;
-         if (row > sheet->range.rowi) row--;
-         if (column < sheet->range.col0) column++;
-         if (column > sheet->range.coli) column--;
-         sheet->drag_cell.row = row;
-         sheet->drag_cell.col = column;
-         sheet->drag_range = sheet->range;
-         draw_xor_rectangle (sheet, sheet->drag_range);
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
-       }
-      else
-       {
-         gtk_sheet_click_cell (sheet, row, column, &veto);
-         if (veto) GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-       }
-    }
-
-  if (event->window == sheet->column_title_window)
-    {
-      gtk_widget_get_pointer (widget, &x, &y);
-      column = COLUMN_FROM_XPIXEL (sheet, x);
-
-      if (xxx_column_is_sensitive (sheet, column))
-       {
-         gtk_sheet_click_cell (sheet, - 1, column, &veto);
-         gtk_grab_add (GTK_WIDGET (sheet));
-         gtk_widget_grab_focus (GTK_WIDGET (sheet));
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-       }
-    }
-
-  if (event->window == sheet->row_title_window)
-    {
-      gtk_widget_get_pointer (widget, &x, &y);
-      row = ROW_FROM_YPIXEL (sheet, y);
-      if (yyy_row_is_sensitive (sheet, row))
-       {
-         gtk_sheet_click_cell (sheet, row, - 1, &veto);
-         gtk_grab_add (GTK_WIDGET (sheet));
-         gtk_widget_grab_focus (GTK_WIDGET (sheet));
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-       }
-    }
-
-  return TRUE;
-}
-
-static void
-gtk_sheet_click_cell (GtkSheet *sheet, gint row, gint column, gboolean *veto)
-{
-  *veto = TRUE;
-
-  if (row >= yyy_row_count (sheet) || column >= xxx_column_count (sheet))
-    {
-      *veto = FALSE;
-      return;
-    }
-
-  if (column >= 0 && row >= 0)
-    if (! xxx_column_is_visible (sheet, column) || !yyy_row_is_visible (sheet, row))
-      {
-       *veto = FALSE;
-       return;
-      }
-
-  _gtkextra_signal_emit (GTK_OBJECT (sheet), sheet_signals[TRAVERSE],
-                        sheet->active_cell.row, sheet->active_cell.col,
-                        &row, &column, veto);
-
-  if (!*veto)
-    {
-      if (sheet->state == GTK_STATE_NORMAL) return;
-
-      row = sheet->active_cell.row;
-      column = sheet->active_cell.col;
-
-      gtk_sheet_activate_cell (sheet, row, column);
-      return;
-    }
-
-  if (row == -1 && column >= 0)
-    {
-      if (gtk_sheet_autoscroll (sheet))
-       gtk_sheet_move_query (sheet, row, column);
-      gtk_sheet_select_column (sheet, column);
-      return;
-    }
-  if (column == -1 && row >= 0)
-    {
-      if (gtk_sheet_autoscroll (sheet))
-       gtk_sheet_move_query (sheet, row, column);
-      gtk_sheet_select_row (sheet, row);
-      return;
-    }
-
-  if (row == - 1 && column == - 1)
-    {
-      sheet->range.row0 = 0;
-      sheet->range.col0 = 0;
-      sheet->range.rowi = yyy_row_count (sheet) - 1;
-      sheet->range.coli = xxx_column_count (sheet) - 1;
-      sheet->active_cell.row = 0;
-      sheet->active_cell.col = 0;
-      gtk_sheet_select_range (sheet, NULL);
-      return;
-    }
-
-  if (row != -1 && column != -1)
-    {
-      if (sheet->state != GTK_SHEET_NORMAL)
-       {
-         sheet->state = GTK_SHEET_NORMAL;
-         gtk_sheet_real_unselect_range (sheet, NULL);
-       }
-      else
-       {
-         gtk_sheet_deactivate_cell (sheet);
-         gtk_sheet_activate_cell (sheet, row, column);
-       }
-
-      if (gtk_sheet_autoscroll (sheet))
-       gtk_sheet_move_query (sheet, row, column);
-      sheet->active_cell.row = row;
-      sheet->active_cell.col = column;
-      sheet->selection_cell.row = row;
-      sheet->selection_cell.col = column;
-      sheet->range.row0 = row;
-      sheet->range.col0 = column;
-      sheet->range.rowi = row;
-      sheet->range.coli = column;
-      sheet->state = GTK_SHEET_NORMAL;
-      GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-      gtk_sheet_draw_active_cell (sheet);
-      return;
-    }
-
-  g_assert_not_reached ();
-  gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
-                          sheet->active_cell.col);
-}
-
-static gint
-gtk_sheet_button_release (GtkWidget * widget,
-                         GdkEventButton * event)
-{
-  GtkSheet *sheet;
-  gint x, y;
-
-  sheet = GTK_SHEET (widget);
-
-  /* release on resize windows */
-  if (GTK_SHEET_IN_XDRAG (sheet))
-    {
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_XDRAG);
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-      gtk_widget_get_pointer (widget, &x, NULL);
-      gdk_pointer_ungrab (event->time);
-      draw_xor_vline (sheet);
-
-      gtk_sheet_set_column_width (sheet, sheet->drag_cell.col,
-                                 new_column_width (sheet, sheet->drag_cell.col, &x));
-      sheet->old_hadjustment = -1.;
-      g_signal_emit_by_name (sheet->hadjustment, "value_changed");
-      return TRUE;
-    }
-
-  if (GTK_SHEET_IN_YDRAG (sheet))
-    {
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_YDRAG);
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-      gtk_widget_get_pointer (widget, NULL, &y);
-      gdk_pointer_ungrab (event->time);
-      draw_xor_hline (sheet);
-
-      gtk_sheet_set_row_height (sheet, sheet->drag_cell.row, new_row_height (sheet, sheet->drag_cell.row, &y));
-      sheet->old_vadjustment = -1.;
-      g_signal_emit_by_name (sheet->vadjustment, "value_changed");
-      return TRUE;
-    }
-
-
-  if (GTK_SHEET_IN_DRAG (sheet))
-    {
-      GtkSheetRange old_range;
-      draw_xor_rectangle (sheet, sheet->drag_range);
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_DRAG);
-      gdk_pointer_ungrab (event->time);
-
-      gtk_sheet_real_unselect_range (sheet, NULL);
-
-      sheet->active_cell.row = sheet->active_cell.row +
-       (sheet->drag_range.row0 - sheet->range.row0);
-      sheet->active_cell.col = sheet->active_cell.col +
-       (sheet->drag_range.col0 - sheet->range.col0);
-      sheet->selection_cell.row = sheet->selection_cell.row +
-       (sheet->drag_range.row0 - sheet->range.row0);
-      sheet->selection_cell.col = sheet->selection_cell.col +
-       (sheet->drag_range.col0 - sheet->range.col0);
-      old_range = sheet->range;
-      sheet->range = sheet->drag_range;
-      sheet->drag_range = old_range;
-      g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
-                    &sheet->drag_range, &sheet->range);
-      gtk_sheet_select_range (sheet, &sheet->range);
-    }
-
-  if (GTK_SHEET_IN_RESIZE (sheet))
-    {
-      GtkSheetRange old_range;
-      draw_xor_rectangle (sheet, sheet->drag_range);
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_RESIZE);
-      gdk_pointer_ungrab (event->time);
-
-      gtk_sheet_real_unselect_range (sheet, NULL);
-
-      sheet->active_cell.row = sheet->active_cell.row +
-       (sheet->drag_range.row0 - sheet->range.row0);
-      sheet->active_cell.col = sheet->active_cell.col +
-       (sheet->drag_range.col0 - sheet->range.col0);
-      if (sheet->drag_range.row0 < sheet->range.row0)
-       sheet->selection_cell.row = sheet->drag_range.row0;
-      if (sheet->drag_range.rowi >= sheet->range.rowi)
-       sheet->selection_cell.row = sheet->drag_range.rowi;
-      if (sheet->drag_range.col0 < sheet->range.col0)
-       sheet->selection_cell.col = sheet->drag_range.col0;
-      if (sheet->drag_range.coli >= sheet->range.coli)
-       sheet->selection_cell.col = sheet->drag_range.coli;
-      old_range = sheet->range;
-      sheet->range = sheet->drag_range;
-      sheet->drag_range = old_range;
-
-      if (sheet->state == GTK_STATE_NORMAL) sheet->state = GTK_SHEET_RANGE_SELECTED;
-      g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
-                    &sheet->drag_range, &sheet->range);
-      gtk_sheet_select_range (sheet, &sheet->range);
-    }
-
-  if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION (sheet))
-    {
-      GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-      gdk_pointer_ungrab (event->time);
-      gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
-                              sheet->active_cell.col);
-    }
-
-  if (GTK_SHEET_IN_SELECTION)
-    gdk_pointer_ungrab (event->time);
-  gtk_grab_remove (GTK_WIDGET (sheet));
-
-  GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-
-  return TRUE;
-}
-
-/* Shamelessly lifted from gtktooltips */
-static gboolean
-gtk_sheet_subtitle_paint_window (GtkWidget *tip_window)
-{
-  GtkRequisition req;
-
-  gtk_widget_size_request (tip_window, &req);
-  gtk_paint_flat_box (tip_window->style, tip_window->window,
-                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-                     NULL, GTK_WIDGET(tip_window), "tooltip",
-                     0, 0, req.width, req.height);
-
-  return FALSE;
-}
-
-static GtkSheetHoverTitle *
-create_hover_window (void)
-{
-  GtkSheetHoverTitle *hw = malloc (sizeof (*hw));
-
-  hw->window = gtk_window_new (GTK_WINDOW_POPUP);
-
-#if GTK_CHECK_VERSION (2, 9, 0)
-  gtk_window_set_type_hint (GTK_WINDOW (hw->window),
-                           GDK_WINDOW_TYPE_HINT_TOOLTIP);
-#endif
-
-  gtk_widget_set_app_paintable (hw->window, TRUE);
-  gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
-  gtk_widget_set_name (hw->window, "gtk-tooltips");
-  gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
-
-  g_signal_connect (hw->window,
-                   "expose_event",
-                   G_CALLBACK (gtk_sheet_subtitle_paint_window),
-                   NULL);
-
-  hw->label = gtk_label_new (NULL);
-
-
-  gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
-  gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
-
-  gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
-
-  gtk_widget_show (hw->label);
-
-  g_signal_connect (hw->window,
-                   "destroy",
-                   G_CALLBACK (gtk_widget_destroyed),
-                   &hw->window);
-
-  return hw;
-}
-
-#define HOVER_WINDOW_Y_OFFSET 2
-
-static void
-show_subtitle (GtkSheet *sheet, gint row, gint column, const gchar *subtitle)
-{
-  gint x, y;
-  gint px, py;
-  gint width;
-
-  if ( ! subtitle )
-    return;
-
-  if ( ! sheet->hover_window)
-    {
-      sheet->hover_window = create_hover_window ();
-      gtk_widget_add_events (GTK_WIDGET (sheet), GDK_LEAVE_NOTIFY_MASK);
-
-      g_signal_connect_swapped (sheet, "leave-notify-event",
-                               G_CALLBACK (gtk_widget_hide),
-                               sheet->hover_window->window);
-    }
-
-  gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
-                     subtitle);
-
-
-  sheet->hover_window->row = row;
-  sheet->hover_window->column = column;
-
-  gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
-
-  gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
-
-  gtk_widget_show (sheet->hover_window->window);
-
-  width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
-
-  if (row == -1 )
-    {
-      x += px;
-      x -= width / 2;
-      y += sheet->column_title_area.y;
-      y += sheet->column_title_area.height;
-      y += HOVER_WINDOW_Y_OFFSET;
-    }
-
-  if ( column == -1 )
-    {
-      y += py;
-      x += sheet->row_title_area.x;
-      x += sheet->row_title_area.width * 2 / 3.0;
-    }
-
-  gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
-                  x, y);
-}
-
-static gboolean
-motion_timeout_callback (gpointer data)
-{
-  GtkSheet *sheet = GTK_SHEET (data);
-  gint x, y;
-  gint row, column;
-  gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
-
-  if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) )
-    {
-      if ( column == -1 && row == -1 )
-       return FALSE;
-
-      if ( column == -1)
-       {
-         GSheetRow *row_geo = sheet->row_geometry;
-         gchar *text;
-
-         text = g_sheet_row_get_subtitle (row_geo, row);
-
-         show_subtitle (sheet, row, column, text);
-         g_free (text);
-       }
-
-      if ( row == -1)
-       {
-         GSheetColumn *col_geo = sheet->column_geometry;
-         gchar *text;
-
-         text = g_sheet_column_get_subtitle (col_geo, column);
-
-         show_subtitle (sheet, row, column, text );
-
-         g_free (text);
-       }
-    }
-
-  return FALSE;
-}
-
-static gint
-gtk_sheet_motion (GtkWidget * widget,
-                 GdkEventMotion * event)
-{
-  GtkSheet *sheet;
-  GdkModifierType mods;
-  GdkCursorType new_cursor;
-  gint x, y;
-  gint row, column;
-
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
-  sheet = GTK_SHEET (widget);
-
-  /* selections on the sheet */
-  x = event->x;
-  y = event->y;
-
-  if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window))
-    {
-      if ( sheet->motion_timer > 0 )
-       g_source_remove (sheet->motion_timer);
-      sheet->motion_timer = g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
-    }
-  else
-    {
-      gint row, column;
-      gint wx, wy;
-      gtk_widget_get_pointer (widget, &wx, &wy);
-
-      if ( gtk_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
-       {
-         if ( row != sheet->hover_window->row || column != sheet->hover_window->column)
-           {
-             gtk_widget_hide (sheet->hover_window->window);
-           }
-       }
-    }
-
-  if (event->window == sheet->column_title_window &&
-      gtk_sheet_columns_resizable (sheet))
-    {
-      gtk_widget_get_pointer (widget, &x, &y);
-      if (!GTK_SHEET_IN_SELECTION (sheet) &&
-         POSSIBLE_XDRAG (sheet, x, &column))
-       {
-         new_cursor = GDK_SB_H_DOUBLE_ARROW;
-         if (new_cursor != sheet->cursor_drag->type)
-           {
-             gdk_cursor_unref (sheet->cursor_drag);
-             sheet->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
-             gdk_window_set_cursor (sheet->column_title_window,
-                                    sheet->cursor_drag);
-           }
-       }
-      else
-       {
-         new_cursor = GDK_TOP_LEFT_ARROW;
-         if (!GTK_SHEET_IN_XDRAG (sheet) &&
-             new_cursor != sheet->cursor_drag->type)
-           {
-             gdk_cursor_unref (sheet->cursor_drag);
-             sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
-             gdk_window_set_cursor (sheet->column_title_window,
-                                    sheet->cursor_drag);
-           }
-       }
-    }
-
-  if (event->window == sheet->row_title_window &&
-      gtk_sheet_rows_resizable (sheet))
-    {
-      gtk_widget_get_pointer (widget, &x, &y);
-      if (!GTK_SHEET_IN_SELECTION (sheet) && POSSIBLE_YDRAG (sheet, y, &column))
-       {
-         new_cursor = GDK_SB_V_DOUBLE_ARROW;
-         if (new_cursor != sheet->cursor_drag->type)
-           {
-             gdk_cursor_unref (sheet->cursor_drag);
-             sheet->cursor_drag = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
-             gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
-           }
-       }
-      else
-       {
-         new_cursor = GDK_TOP_LEFT_ARROW;
-         if (!GTK_SHEET_IN_YDRAG (sheet) &&
-             new_cursor != sheet->cursor_drag->type)
-           {
-             gdk_cursor_unref (sheet->cursor_drag);
-             sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
-             gdk_window_set_cursor (sheet->row_title_window, sheet->cursor_drag);
-           }
-       }
-    }
-
-  new_cursor = GDK_PLUS;
-  if ( event->window == sheet->sheet_window &&
-       !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
-       !GTK_SHEET_IN_DRAG (sheet) &&
-       !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
-       !GTK_SHEET_IN_RESIZE (sheet) &&
-       new_cursor != sheet->cursor_drag->type)
-    {
-      gdk_cursor_unref (sheet->cursor_drag);
-      sheet->cursor_drag = gdk_cursor_new (GDK_PLUS);
-      gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
-    }
-
-  new_cursor = GDK_TOP_LEFT_ARROW;
-  if ( event->window == sheet->sheet_window &&
-       ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE (sheet)) && (POSSIBLE_DRAG (sheet, x, y, &row, &column) || GTK_SHEET_IN_DRAG (sheet)) &&
-
-       new_cursor != sheet->cursor_drag->type)
-    {
-      gdk_cursor_unref (sheet->cursor_drag);
-      sheet->cursor_drag = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
-      gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
-    }
-
-  new_cursor = GDK_SIZING;
-  if ( event->window == sheet->sheet_window &&
-       sheet->selection_mode != GTK_SELECTION_NONE &&
-       !GTK_SHEET_IN_DRAG (sheet) &&
-       (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
-       GTK_SHEET_IN_RESIZE (sheet)) &&
-       new_cursor != sheet->cursor_drag->type)
-    {
-      gdk_cursor_unref (sheet->cursor_drag);
-      sheet->cursor_drag = gdk_cursor_new (GDK_SIZING);
-      gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
-    }
-
-
-  gdk_window_get_pointer (widget->window, &x, &y, &mods);
-  if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
-
-  if (GTK_SHEET_IN_XDRAG (sheet))
-    {
-      if (event->is_hint || event->window != widget->window)
-       gtk_widget_get_pointer (widget, &x, NULL);
-      else
-       x = event->x;
-
-      new_column_width (sheet, sheet->drag_cell.col, &x);
-      if (x != sheet->x_drag)
-       {
-         draw_xor_vline (sheet);
-         sheet->x_drag = x;
-         draw_xor_vline (sheet);
-       }
-      return TRUE;
-    }
-
-  if (GTK_SHEET_IN_YDRAG (sheet))
-    {
-      if (event->is_hint || event->window != widget->window)
-       gtk_widget_get_pointer (widget, NULL, &y);
-      else
-       y = event->y;
-
-      new_row_height (sheet, sheet->drag_cell.row, &y);
-      if (y != sheet->y_drag)
-       {
-         draw_xor_hline (sheet);
-         sheet->y_drag = y;
-         draw_xor_hline (sheet);
-       }
-      return TRUE;
-    }
-
-  if (GTK_SHEET_IN_DRAG (sheet))
-    {
-      GtkSheetRange aux;
-      column = COLUMN_FROM_XPIXEL (sheet, x)- sheet->drag_cell.col;
-      row = ROW_FROM_YPIXEL (sheet, y)- sheet->drag_cell.row;
-      if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
-      if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
-      sheet->x_drag = x;
-      sheet->y_drag = y;
-      aux = sheet->range;
-      if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
-         aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
-       {
-         aux = sheet->drag_range;
-         sheet->drag_range.row0 = sheet->range.row0 + row;
-         sheet->drag_range.col0 = sheet->range.col0 + column;
-         sheet->drag_range.rowi = sheet->range.rowi + row;
-         sheet->drag_range.coli = sheet->range.coli + column;
-         if (aux.row0 != sheet->drag_range.row0 ||
-             aux.col0 != sheet->drag_range.col0)
-           {
-             draw_xor_rectangle (sheet, aux);
-             draw_xor_rectangle (sheet, sheet->drag_range);
-           }
-       }
-      return TRUE;
-    }
-
-  if (GTK_SHEET_IN_RESIZE (sheet))
-    {
-      GtkSheetRange aux;
-      gint v_h, current_col, current_row, col_threshold, row_threshold;
-      v_h = 1;
-
-      if (abs (x - COLUMN_LEFT_XPIXEL (sheet, sheet->drag_cell.col)) >
-         abs (y - ROW_TOP_YPIXEL (sheet, sheet->drag_cell.row))) v_h = 2;
-
-      current_col = COLUMN_FROM_XPIXEL (sheet, x);
-      current_row = ROW_FROM_YPIXEL (sheet, y);
-      column = current_col - sheet->drag_cell.col;
-      row = current_row - sheet->drag_cell.row;
-
-      /*use half of column width resp. row height as threshold to
-       expand selection*/
-      col_threshold = COLUMN_LEFT_XPIXEL (sheet, current_col) +
-       xxx_column_width (sheet, current_col) / 2;
-      if (column > 0)
-       {
-         if (x < col_threshold)
-           column -= 1;
-       }
-      else if (column < 0)
-       {
-         if (x > col_threshold)
-           column +=1;
-       }
-      row_threshold = ROW_TOP_YPIXEL (sheet, current_row) +
-       yyy_row_height (sheet, current_row)/2;
-      if (row > 0)
-       {
-         if (y < row_threshold)
-           row -= 1;
-       }
-      else if (row < 0)
-       {
-         if (y > row_threshold)
-           row +=1;
-       }
-
-      if (sheet->state == GTK_SHEET_COLUMN_SELECTED) row = 0;
-      if (sheet->state == GTK_SHEET_ROW_SELECTED) column = 0;
-      sheet->x_drag = x;
-      sheet->y_drag = y;
-      aux = sheet->range;
-
-      if (v_h == 1)
-       column = 0;
-      else
-       row = 0;
-
-      if (aux.row0 + row >= 0 && aux.rowi + row < yyy_row_count (sheet) &&
-         aux.col0 + column >= 0 && aux.coli + column < xxx_column_count (sheet))
-       {
-         aux = sheet->drag_range;
-         sheet->drag_range = sheet->range;
-
-         if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
-         if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
-         if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
-         if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
-
-         if (aux.row0 != sheet->drag_range.row0 ||
-             aux.rowi != sheet->drag_range.rowi ||
-             aux.col0 != sheet->drag_range.col0 ||
-             aux.coli != sheet->drag_range.coli)
-           {
-             draw_xor_rectangle (sheet, aux);
-             draw_xor_rectangle (sheet, sheet->drag_range);
-           }
-       }
-      return TRUE;
-    }
-
-  gtk_sheet_get_pixel_info (sheet, x, y, &row, &column);
-
-  if (sheet->state == GTK_SHEET_NORMAL && row == sheet->active_cell.row &&
-      column == sheet->active_cell.col) return TRUE;
-
-  if (GTK_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
-    gtk_sheet_extend_selection (sheet, row, column);
-
-  return TRUE;
-}
-
-static gboolean
-gtk_sheet_move_query (GtkSheet *sheet, gint row, gint column)
-{
-  gint row_move, column_move;
-  gfloat row_align, col_align;
-  guint height, width;
-  gint new_row = row;
-  gint new_col = column;
-
-  row_move = FALSE;
-  column_move = FALSE;
-  row_align = -1.;
-  col_align = -1.;
-
-  height = sheet->sheet_window_height;
-  width = sheet->sheet_window_width;
-
-  if (row >= MAX_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
-    {
-      row_align = 1.;
-      new_row = MIN (yyy_row_count (sheet) - 1, row + 1);
-      row_move = TRUE;
-      if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet) - 1 &&
-         ROW_TOP_YPIXEL (sheet, yyy_row_count (sheet)- 1) +
-         yyy_row_height (sheet, yyy_row_count (sheet)- 1) < height)
-       {
-         row_move = FALSE;
-         row_align = -1.;
-       }
-    }
-  if (row < MIN_VISIBLE_ROW (sheet) && sheet->state != GTK_SHEET_COLUMN_SELECTED)
-    {
-      row_align= 0.;
-      row_move = TRUE;
-    }
-  if (column >= MAX_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
-    {
-      col_align = 1.;
-      new_col = MIN (xxx_column_count (sheet) - 1, column + 1);
-      column_move = TRUE;
-      if (MAX_VISIBLE_COLUMN (sheet) == (xxx_column_count (sheet) - 1) &&
-         COLUMN_LEFT_XPIXEL (sheet, xxx_column_count (sheet) - 1) +
-         xxx_column_width (sheet, xxx_column_count (sheet) - 1) < width)
-       {
-         column_move = FALSE;
-         col_align = -1.;
-       }
-    }
-  if (column < MIN_VISIBLE_COLUMN (sheet) && sheet->state != GTK_SHEET_ROW_SELECTED)
-    {
-      col_align = 0.;
-      column_move = TRUE;
-    }
-
-  if (row_move || column_move)
-    {
-      gtk_sheet_moveto (sheet, new_row, new_col, row_align, col_align);
-    }
-
-  return (row_move || column_move);
-}
-
-static void
-gtk_sheet_extend_selection (GtkSheet *sheet, gint row, gint column)
-{
-  GtkSheetRange range;
-  gint state;
-  gint r, c;
-
-  if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
-    return;
-
-  if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
-
-  gtk_sheet_move_query (sheet, row, column);
-  gtk_widget_grab_focus (GTK_WIDGET (sheet));
-
-  if (GTK_SHEET_IN_DRAG (sheet)) return;
-
-  state = sheet->state;
-
-  switch (sheet->state)
-    {
-    case GTK_SHEET_ROW_SELECTED:
-      column = xxx_column_count (sheet) - 1;
-      break;
-    case GTK_SHEET_COLUMN_SELECTED:
-      row = yyy_row_count (sheet) - 1;
-      break;
-    case GTK_SHEET_NORMAL:
-      sheet->state = GTK_SHEET_RANGE_SELECTED;
-      r = sheet->active_cell.row;
-      c = sheet->active_cell.col;
-      sheet->range.col0 = c;
-      sheet->range.row0 = r;
-      sheet->range.coli = c;
-      sheet->range.rowi = r;
-      gdk_draw_drawable (sheet->sheet_window,
-                      GTK_WIDGET (sheet)->style->fg_gc[GTK_STATE_NORMAL],
-                      sheet->pixmap,
-                      COLUMN_LEFT_XPIXEL (sheet, c)- 1,
-                      ROW_TOP_YPIXEL (sheet, r)- 1,
-                      COLUMN_LEFT_XPIXEL (sheet, c)- 1,
-                      ROW_TOP_YPIXEL (sheet, r)- 1,
-                      xxx_column_width (sheet, c)+4,
-                      yyy_row_height (sheet, r)+4);
-      gtk_sheet_range_draw_selection (sheet, sheet->range);
-    case GTK_SHEET_RANGE_SELECTED:
-      sheet->state = GTK_SHEET_RANGE_SELECTED;
-    }
-
-  sheet->selection_cell.row = row;
-  sheet->selection_cell.col = column;
-
-  range.col0 = MIN (column, sheet->active_cell.col);
-  range.coli = MAX (column, sheet->active_cell.col);
-  range.row0 = MIN (row, sheet->active_cell.row);
-  range.rowi = MAX (row, sheet->active_cell.row);
-
-  if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
-      range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
-      state == GTK_SHEET_NORMAL)
-    gtk_sheet_real_select_range (sheet, &range);
-
-}
-
-static gint
-gtk_sheet_entry_key_press (GtkWidget *widget,
-                          GdkEventKey *key)
-{
-  gboolean focus;
-  g_signal_emit_by_name (widget, "key_press_event", key, &focus);
-  return focus;
-}
-
-static gint
-gtk_sheet_key_press (GtkWidget *widget,
-                    GdkEventKey *key)
-{
-  GtkSheet *sheet;
-  gint row, col;
-  gint state;
-  gboolean extend_selection = FALSE;
-  gboolean force_move = FALSE;
-  gboolean in_selection = FALSE;
-  gboolean veto = TRUE;
-  gint scroll = 1;
-
-  sheet = GTK_SHEET (widget);
-
-  if (key->state & GDK_CONTROL_MASK || key->keyval == GDK_Control_L ||
-      key->keyval == GDK_Control_R) return FALSE;
-
-  extend_selection = (key->state & GDK_SHIFT_MASK) || key->keyval == GDK_Shift_L
-    || key->keyval == GDK_Shift_R;
-
-  state = sheet->state;
-  in_selection = GTK_SHEET_IN_SELECTION (sheet);
-  GTK_SHEET_UNSET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-
-  switch (key->keyval)
-    {
-    case GDK_Return: case GDK_KP_Enter:
-      if (sheet->state == GTK_SHEET_NORMAL &&
-         !GTK_SHEET_IN_SELECTION (sheet))
-       g_signal_stop_emission_by_name (gtk_sheet_get_entry (sheet),
-                                       "key-press-event");
-      row = sheet->active_cell.row;
-      col = sheet->active_cell.col;
-      if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
-       row = MIN_VISIBLE_ROW (sheet)- 1;
-      if (sheet->state == GTK_SHEET_ROW_SELECTED)
-       col = MIN_VISIBLE_COLUMN (sheet);
-      if (row < yyy_row_count (sheet) - 1)
-       {
-         row = row + scroll;
-         while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1)
-           row++;
-       }
-      gtk_sheet_click_cell (sheet, row, col, &veto);
-      extend_selection = FALSE;
-      break;
-    case GDK_ISO_Left_Tab:
-      row = sheet->active_cell.row;
-      col = sheet->active_cell.col;
-      if (sheet->state == GTK_SHEET_ROW_SELECTED)
-       col = MIN_VISIBLE_COLUMN (sheet)- 1;
-      if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
-       row = MIN_VISIBLE_ROW (sheet);
-      if (col > 0)
-       {
-         col = col - scroll;
-         while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
-         col = MAX (0, col);
-       }
-      gtk_sheet_click_cell (sheet, row, col, &veto);
-      extend_selection = FALSE;
-      break;
-    case GDK_Tab:
-      row = sheet->active_cell.row;
-      col = sheet->active_cell.col;
-      if (sheet->state == GTK_SHEET_ROW_SELECTED)
-       col = MIN_VISIBLE_COLUMN (sheet)- 1;
-      if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
-       row = MIN_VISIBLE_ROW (sheet);
-      if (col < xxx_column_count (sheet) - 1)
-       {
-         col = col + scroll;
-         while (! xxx_column_is_visible (sheet, col) &&
-                col < xxx_column_count (sheet) - 1)
-           col++;
-       }
-      gtk_sheet_click_cell (sheet, row, col, &veto);
-      extend_selection = FALSE;
-      break;
-    case GDK_Page_Up:
-      scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
-    case GDK_Up:
-      if (extend_selection)
-       {
-         if (state == GTK_STATE_NORMAL)
-           {
-             row = sheet->active_cell.row;
-             col = sheet->active_cell.col;
-             gtk_sheet_click_cell (sheet, row, col, &veto);
-             if (!veto) break;
-           }
-         if (sheet->selection_cell.row > 0)
-           {
-             row = sheet->selection_cell.row - scroll;
-             while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
-             row = MAX (0, row);
-             gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
-           }
-         return TRUE;
-       }
-      col = sheet->active_cell.col;
-      row = sheet->active_cell.row;
-      if (state == GTK_SHEET_COLUMN_SELECTED)
-       row = MIN_VISIBLE_ROW (sheet);
-      if (state == GTK_SHEET_ROW_SELECTED)
-       col = MIN_VISIBLE_COLUMN (sheet);
-      row = row - scroll;
-      while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
-      row = MAX (0, row);
-      gtk_sheet_click_cell (sheet, row, col, &veto);
-      extend_selection = FALSE;
-      break;
-    case GDK_Page_Down:
-      scroll = MAX_VISIBLE_ROW (sheet)- MIN_VISIBLE_ROW (sheet)+1;
-    case GDK_Down:
-      if (extend_selection)
-       {
-         if (state == GTK_STATE_NORMAL)
-           {
-             row = sheet->active_cell.row;
-             col = sheet->active_cell.col;
-             gtk_sheet_click_cell (sheet, row, col, &veto);
-             if (!veto) break;
-           }
-         if (sheet->selection_cell.row < yyy_row_count (sheet)- 1)
-           {
-             row = sheet->selection_cell.row + scroll;
-             while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
-             row = MIN (yyy_row_count (sheet)- 1, row);
-             gtk_sheet_extend_selection (sheet, row, sheet->selection_cell.col);
-           }
-         return TRUE;
-       }
-      col = sheet->active_cell.col;
-      row = sheet->active_cell.row;
-      if (sheet->active_cell.row < yyy_row_count (sheet)- 1)
-       {
-         if (state == GTK_SHEET_COLUMN_SELECTED)
-           row = MIN_VISIBLE_ROW (sheet)- 1;
-         if (state == GTK_SHEET_ROW_SELECTED)
-           col = MIN_VISIBLE_COLUMN (sheet);
-         row = row + scroll;
-         while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet)- 1) row++;
-         row = MIN (yyy_row_count (sheet)- 1, row);
-       }
-      gtk_sheet_click_cell (sheet, row, col, &veto);
-      extend_selection = FALSE;
-      break;
-    case GDK_Right:
-      if (extend_selection)
-       {
-         if (state == GTK_STATE_NORMAL)
-           {
-             row = sheet->active_cell.row;
-             col = sheet->active_cell.col;
-             gtk_sheet_click_cell (sheet, row, col, &veto);
-             if (!veto) break;
-           }
-         if (sheet->selection_cell.col < xxx_column_count (sheet) - 1)
-           {
-             col = sheet->selection_cell.col + 1;
-             while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1)
-               col++;
-             gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
-           }
-         return TRUE;
-       }
-      col = sheet->active_cell.col;
-      row = sheet->active_cell.row;
-      if (sheet->active_cell.col < xxx_column_count (sheet) - 1)
-       {
-         col ++;
-         if (state == GTK_SHEET_ROW_SELECTED)
-           col = MIN_VISIBLE_COLUMN (sheet)- 1;
-         if (state == GTK_SHEET_COLUMN_SELECTED)
-           row = MIN_VISIBLE_ROW (sheet);
-         while (! xxx_column_is_visible (sheet, col) && col < xxx_column_count (sheet) - 1) col++;
-         if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
-             || force_move)
-           {
-             gtk_sheet_click_cell (sheet, row, col, &veto);
-           }
-         else
-           return FALSE;
-       }
-      extend_selection = FALSE;
-      break;
-    case GDK_Left:
-      if (extend_selection)
-       {
-         if (state == GTK_STATE_NORMAL)
-           {
-             row = sheet->active_cell.row;
-             col = sheet->active_cell.col;
-             gtk_sheet_click_cell (sheet, row, col, &veto);
-             if (!veto) break;
-           }
-         if (sheet->selection_cell.col > 0)
-           {
-             col = sheet->selection_cell.col - 1;
-             while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
-             gtk_sheet_extend_selection (sheet, sheet->selection_cell.row, col);
-           }
-         return TRUE;
-       }
-      col = sheet->active_cell.col - 1;
-      row = sheet->active_cell.row;
-      if (state == GTK_SHEET_ROW_SELECTED)
-       col = MIN_VISIBLE_COLUMN (sheet)- 1;
-      if (state == GTK_SHEET_COLUMN_SELECTED)
-       row = MIN_VISIBLE_ROW (sheet);
-      while (! xxx_column_is_visible (sheet, col) && col > 0) col--;
-      col = MAX (0, col);
-
-      if (strlen (gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)))) == 0
-         || force_move)
-       {
-         gtk_sheet_click_cell (sheet, row, col, &veto);
-       }
-      else
-       return FALSE;
-      extend_selection = FALSE;
-      break;
-    case GDK_Home:
-      row = 0;
-      while (!yyy_row_is_visible (sheet, row) && row < yyy_row_count (sheet) - 1) row++;
-      gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
-      extend_selection = FALSE;
-      break;
-    case GDK_End:
-      row = yyy_row_count (sheet) - 1;
-      while (!yyy_row_is_visible (sheet, row) && row > 0) row--;
-      gtk_sheet_click_cell (sheet, row, sheet->active_cell.col, &veto);
-      extend_selection = FALSE;
-      break;
-    default:
-      if (in_selection)
-       {
-         GTK_SHEET_SET_FLAGS (sheet, GTK_SHEET_IN_SELECTION);
-         if (extend_selection) return TRUE;
-       }
-      if (state == GTK_SHEET_ROW_SELECTED)
-       sheet->active_cell.col = MIN_VISIBLE_COLUMN (sheet);
-      if (state == GTK_SHEET_COLUMN_SELECTED)
-       sheet->active_cell.row = MIN_VISIBLE_ROW (sheet);
-      return FALSE;
-    }
-
-  if (extend_selection) return TRUE;
-
-  gtk_sheet_activate_cell (sheet, sheet->active_cell.row,
-                          sheet->active_cell.col);
-
-  return TRUE;
-}
-
-static void
-gtk_sheet_size_request (GtkWidget * widget,
-                       GtkRequisition * requisition)
-{
-  GtkSheet *sheet;
-  GList *children;
-  GtkSheetChild *child;
-  GtkRequisition child_requisition;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-  g_return_if_fail (requisition != NULL);
-
-  sheet = GTK_SHEET (widget);
-
-  requisition->width = 3*DEFAULT_COLUMN_WIDTH;
-  requisition->height = 3*DEFAULT_ROW_HEIGHT (widget);
-
-  /* compute the size of the column title area */
-  if (sheet->column_titles_visible)
-    requisition->height += sheet->column_title_area.height;
-
-  /* compute the size of the row title area */
-  if (sheet->row_titles_visible)
-    requisition->width += sheet->row_title_area.width;
-
-  children = sheet->children;
-  while (children)
-    {
-      child = children->data;
-      children = children->next;
-
-      gtk_widget_size_request (child->widget, &child_requisition);
-    }
-}
-
-
-static void
-gtk_sheet_size_allocate (GtkWidget * widget,
-                        GtkAllocation * allocation)
-{
-  GtkSheet *sheet;
-  GtkAllocation sheet_allocation;
-  gint border_width;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_SHEET (widget));
-  g_return_if_fail (allocation != NULL);
-
-  sheet = GTK_SHEET (widget);
-  widget->allocation = *allocation;
-  border_width = GTK_CONTAINER (widget)->border_width;
-
-  if (GTK_WIDGET_REALIZED (widget))
-    gdk_window_move_resize (widget->window,
-                           allocation->x + border_width,
-                           allocation->y + border_width,
-                           allocation->width - 2 * border_width,
-                           allocation->height - 2 * border_width);
-
-  /* use internal allocation structure for all the math
-   * because it's easier than always subtracting the container
-   * border width */
-  sheet->internal_allocation.x = 0;
-  sheet->internal_allocation.y = 0;
-  sheet->internal_allocation.width = allocation->width - 2 * border_width;
-  sheet->internal_allocation.height = allocation->height - 2 * border_width;
-
-  sheet_allocation.x = 0;
-  sheet_allocation.y = 0;
-  sheet_allocation.width = allocation->width - 2 * border_width;
-  sheet_allocation.height = allocation->height - 2 * border_width;
-
-  sheet->sheet_window_width = sheet_allocation.width;
-  sheet->sheet_window_height = sheet_allocation.height;
-
-  if (GTK_WIDGET_REALIZED (widget))
-    gdk_window_move_resize (sheet->sheet_window,
-                           sheet_allocation.x,
-                           sheet_allocation.y,
-                           sheet_allocation.width,
-                           sheet_allocation.height);
-
-  /* position the window which holds the column title buttons */
-  sheet->column_title_area.x = 0;
-  sheet->column_title_area.y = 0;
-  if (sheet->row_titles_visible)
-    sheet->column_title_area.x = sheet->row_title_area.width;
-  sheet->column_title_area.width = sheet_allocation.width -
-    sheet->column_title_area.x;
-  if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
-    gdk_window_move_resize (sheet->column_title_window,
-                           sheet->column_title_area.x,
-                           sheet->column_title_area.y,
-                           sheet->column_title_area.width,
-                           sheet->column_title_area.height);
-
-  sheet->sheet_window_width = sheet_allocation.width;
-  sheet->sheet_window_height = sheet_allocation.height;
-
-  /* column button allocation */
-  size_allocate_column_title_buttons (sheet);
-
-  /* position the window which holds the row title buttons */
-  sheet->row_title_area.x = 0;
-  sheet->row_title_area.y = 0;
-  if (sheet->column_titles_visible)
-    sheet->row_title_area.y = sheet->column_title_area.height;
-  sheet->row_title_area.height = sheet_allocation.height -
-    sheet->row_title_area.y;
-
-  if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
-    gdk_window_move_resize (sheet->row_title_window,
-                           sheet->row_title_area.x,
-                           sheet->row_title_area.y,
-                           sheet->row_title_area.width,
-                           sheet->row_title_area.height);
-
-
-  /* row button allocation */
-  size_allocate_row_title_buttons (sheet);
-  size_allocate_column_title_buttons (sheet);
-
-  /* re - scale backing pixmap */
-  gtk_sheet_make_backing_pixmap (sheet, 0, 0);
-  gtk_sheet_position_children (sheet);
-
-  /* set the scrollbars adjustments */
-  adjust_scrollbars (sheet);
-}
-
-static void
-size_allocate_column_title_buttons (GtkSheet * sheet)
-{
-  gint i;
-  gint x, width;
-
-  if (!sheet->column_titles_visible) return;
-  if (!GTK_WIDGET_REALIZED (sheet))
-    return;
-
-  width = sheet->sheet_window_width;
-  x = 0;
-
-  if (sheet->row_titles_visible)
-    {
-      width -= sheet->row_title_area.width;
-      x = sheet->row_title_area.width;
-    }
-
-  if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
-    {
-      sheet->column_title_area.width = width;
-      sheet->column_title_area.x = x;
-      gdk_window_move_resize (sheet->column_title_window,
-                             sheet->column_title_area.x,
-                             sheet->column_title_area.y,
-                             sheet->column_title_area.width,
-                             sheet->column_title_area.height);
-    }
-
-
-  if (MAX_VISIBLE_COLUMN (sheet) == xxx_column_count (sheet) - 1)
-    gdk_window_clear_area (sheet->column_title_window,
-                          0, 0,
-                          sheet->column_title_area.width,
-                          sheet->column_title_area.height);
-
-  if (!GTK_WIDGET_DRAWABLE (sheet)) return;
-
-  size_allocate_global_button (sheet);
-
-  for (i = MIN_VISIBLE_COLUMN (sheet); i <= MAX_VISIBLE_COLUMN (sheet); i++)
-    gtk_sheet_column_title_button_draw (sheet, i);
-}
-
-static void
-size_allocate_row_title_buttons (GtkSheet * sheet)
-{
-  gint i;
-  gint y, height;
-
-  if (!sheet->row_titles_visible) return;
-  if (!GTK_WIDGET_REALIZED (sheet))
-    return;
-
-  height = sheet->sheet_window_height;
-  y = 0;
-
-  if (sheet->column_titles_visible)
-    {
-      height -= sheet->column_title_area.height;
-      y = sheet->column_title_area.height;
-    }
-
-  if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
-    {
-      sheet->row_title_area.y = y;
-      sheet->row_title_area.height = height;
-      gdk_window_move_resize (sheet->row_title_window,
-                             sheet->row_title_area.x,
-                             sheet->row_title_area.y,
-                             sheet->row_title_area.width,
-                             sheet->row_title_area.height);
-    }
-  if (MAX_VISIBLE_ROW (sheet) == yyy_row_count (sheet)- 1)
-    gdk_window_clear_area (sheet->row_title_window,
-                          0, 0,
-                          sheet->row_title_area.width,
-                          sheet->row_title_area.height);
-
-  if (!GTK_WIDGET_DRAWABLE (sheet)) return;
-
-  size_allocate_global_button (sheet);
-
-  for (i = MIN_VISIBLE_ROW (sheet); i <= MAX_VISIBLE_ROW (sheet); i++)
-    {
-      if ( i >= yyy_row_count (sheet))
-       break;
-      gtk_sheet_row_title_button_draw (sheet, i);
-    }
-}
-
-
-static void
-gtk_sheet_size_allocate_entry (GtkSheet *sheet)
-{
-  GtkAllocation shentry_allocation;
-  GtkSheetCellAttr attributes = { 0 };
-  GtkEntry *sheet_entry;
-  GtkStyle *style = NULL, *previous_style = NULL;
-  gint row, col;
-  gint size, max_size, text_size, column_width;
-  const gchar *text;
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-  if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
-
-  sheet_entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
-
-  if ( ! gtk_sheet_get_attributes (sheet, sheet->active_cell.row,
-                                  sheet->active_cell.col,
-                                  &attributes) )
-    return ;
-
-  if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
-    {
-      if (!GTK_WIDGET (sheet_entry)->style)
-       gtk_widget_ensure_style (GTK_WIDGET (sheet_entry));
-
-      previous_style = GTK_WIDGET (sheet_entry)->style;
-
-      style = gtk_style_copy (previous_style);
-      style->bg[GTK_STATE_NORMAL] = attributes.background;
-      style->fg[GTK_STATE_NORMAL] = attributes.foreground;
-      style->text[GTK_STATE_NORMAL] = attributes.foreground;
-      style->bg[GTK_STATE_ACTIVE] = attributes.background;
-      style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
-      style->text[GTK_STATE_ACTIVE] = attributes.foreground;
-
-      pango_font_description_free (style->font_desc);
-      g_assert (attributes.font_desc);
-      style->font_desc = pango_font_description_copy (attributes.font_desc);
-
-      GTK_WIDGET (sheet_entry)->style = style;
-      gtk_widget_size_request (sheet->entry_widget, NULL);
-      GTK_WIDGET (sheet_entry)->style = previous_style;
-
-      if (style != previous_style)
-       {
-         if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
-           {
-             style->bg[GTK_STATE_NORMAL] = previous_style->bg[GTK_STATE_NORMAL];
-             style->fg[GTK_STATE_NORMAL] = previous_style->fg[GTK_STATE_NORMAL];
-             style->bg[GTK_STATE_ACTIVE] = previous_style->bg[GTK_STATE_ACTIVE];
-             style->fg[GTK_STATE_ACTIVE] = previous_style->fg[GTK_STATE_ACTIVE];
-           }
-         gtk_widget_set_style (GTK_WIDGET (sheet_entry), style);
-          g_object_unref (style);
-       }
-    }
-
-  if (GTK_IS_ITEM_ENTRY (sheet_entry))
-    max_size = GTK_ITEM_ENTRY (sheet_entry)->text_max_size;
-  else
-    max_size = 0;
-
-  text_size = 0;
-  text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
-  if (text && strlen (text) > 0)
-    text_size = STRING_WIDTH (GTK_WIDGET (sheet), attributes.font_desc, text);
-
-  column_width = xxx_column_width (sheet, sheet->active_cell.col);
-
-  size = MIN (text_size, max_size);
-  size = MAX (size, column_width - 2 * CELLOFFSET);
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  shentry_allocation.x = COLUMN_LEFT_XPIXEL (sheet, sheet->active_cell.col);
-  shentry_allocation.y = ROW_TOP_YPIXEL (sheet, sheet->active_cell.row);
-  shentry_allocation.width = column_width;
-  shentry_allocation.height = yyy_row_height (sheet, sheet->active_cell.row);
-
-  if (GTK_IS_ITEM_ENTRY (sheet->entry_widget))
-    {
-      shentry_allocation.height -= 2 * CELLOFFSET;
-      shentry_allocation.y += CELLOFFSET;
-      shentry_allocation.width = size;
-
-      switch (GTK_ITEM_ENTRY (sheet_entry)->justification)
-       {
-       case GTK_JUSTIFY_CENTER:
-         shentry_allocation.x += column_width / 2 - size / 2;
-         break;
-       case GTK_JUSTIFY_RIGHT:
-         shentry_allocation.x += column_width - size - CELLOFFSET;
-         break;
-       case GTK_JUSTIFY_LEFT:
-       case GTK_JUSTIFY_FILL:
-         shentry_allocation.x += CELLOFFSET;
-         break;
-       }
-    }
-
-  if (!GTK_IS_ITEM_ENTRY (sheet->entry_widget))
-    {
-      shentry_allocation.x += 2;
-      shentry_allocation.y += 2;
-      shentry_allocation.width -= MIN (shentry_allocation.width, 3);
-      shentry_allocation.height -= MIN (shentry_allocation.height, 3);
-    }
-
-  gtk_widget_size_allocate (sheet->entry_widget, &shentry_allocation);
-
-  if (previous_style == style) g_object_unref (previous_style);
-}
-
-static void
-gtk_sheet_entry_set_max_size (GtkSheet *sheet)
-{
-  gint i;
-  gint size = 0;
-  gint sizel = 0, sizer = 0;
-  gint row, col;
-  GtkJustification justification;
-  gchar *s = NULL;
-
-  row = sheet->active_cell.row;
-  col = sheet->active_cell.col;
-
-  if ( ! GTK_IS_ITEM_ENTRY (sheet->entry_widget) )
-    return;
-
-  justification = GTK_ITEM_ENTRY (sheet->entry_widget)->justification;
-
-  switch (justification)
-    {
-    case GTK_JUSTIFY_FILL:
-    case GTK_JUSTIFY_LEFT:
-      for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
-       {
-         if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
-           {
-             g_free (s);
-             break;
-           }
-         size +=xxx_column_width (sheet, i);
-       }
-      size = MIN (size, sheet->sheet_window_width - COLUMN_LEFT_XPIXEL (sheet, col));
-      break;
-    case GTK_JUSTIFY_RIGHT:
-      for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
-       {
-         if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
-           {
-             g_free (s);
-             break;
-           }
-         size +=xxx_column_width (sheet, i);
-       }
-      break;
-    case GTK_JUSTIFY_CENTER:
-      for (i = col + 1; i <= MAX_VISIBLE_COLUMN (sheet); i++)
-       {
-         sizer += xxx_column_width (sheet, i);
-       }
-      for (i = col - 1; i >= MIN_VISIBLE_COLUMN (sheet); i--)
-       {
-         if ((s = gtk_sheet_cell_get_text (sheet, row, i)))
-           {
-             g_free (s);
-             break;
-           }
-         sizel +=xxx_column_width (sheet, i);
-       }
-      size = 2 * MIN (sizel, sizer);
-      break;
-    }
-
-  if (size != 0)
-    size += xxx_column_width (sheet, col);
-  GTK_ITEM_ENTRY (sheet->entry_widget)->text_max_size = size;
-}
-
-
-static void
-create_sheet_entry (GtkSheet *sheet)
-{
-  if (sheet->entry_widget)
-    {
-      gtk_widget_unparent (sheet->entry_widget);
-    }
-
-  if (sheet->entry_type)
-    {
-      sheet->entry_container = g_object_new (sheet->entry_type, NULL);
-      g_object_ref_sink (sheet->entry_container);
-      sheet->entry_widget = gtk_sheet_get_entry (sheet);
-
-      if  ( NULL == sheet->entry_widget)
-       {
-         g_warning ("Entry type is %s. It must be GtkEntry subclass, or a widget containing one. "
-                    "Using default", g_type_name (sheet->entry_type));
-         g_object_unref (sheet->entry_container);
-         sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
-       }
-      else
-       {
-         sheet->entry_widget = sheet->entry_container ;
-       }
-    }
-  else
-    {
-      sheet->entry_widget = sheet->entry_container = gtk_item_entry_new ();
-      g_object_ref_sink (sheet->entry_container);
-    }
-
-  gtk_widget_size_request (sheet->entry_widget, NULL);
-
-  if (GTK_WIDGET_REALIZED (sheet))
-    {
-      gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
-      gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
-      gtk_widget_realize (sheet->entry_widget);
-    }
-
-  g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
-                           G_CALLBACK (gtk_sheet_entry_key_press),
-                           sheet);
-
-  gtk_widget_show (sheet->entry_widget);
-}
-
-
-/* Finds the last child widget that happens to be of type GtkEntry */
-static void
-find_entry (GtkWidget *w, gpointer user_data)
-{
-  GtkWidget **entry = user_data;
-  if ( GTK_IS_ENTRY (w))
-    {
-      *entry = w;
-    }
-}
-
-GtkWidget *
-gtk_sheet_get_entry (GtkSheet *sheet)
-{
-  GtkWidget *parent;
-  GtkWidget *entry = NULL;
-  GtkTableChild *table_child;
-  GtkBoxChild *box_child;
-  GList *children = NULL;
-
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-  g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
-
-  if (GTK_IS_ENTRY (sheet->entry_container))
-    return (sheet->entry_container);
-
-  parent = sheet->entry_container;
-
-  if (GTK_IS_TABLE (parent)) children = GTK_TABLE (parent)->children;
-  if (GTK_IS_BOX (parent)) children = GTK_BOX (parent)->children;
-
-  if (GTK_IS_CONTAINER (parent))
-    {
-      gtk_container_forall (GTK_CONTAINER (parent), find_entry, &entry);
-
-      if (GTK_IS_ENTRY (entry))
-       return entry;
-    }
-
-  if (!children) return NULL;
-
-  while (children)
-    {
-      if (GTK_IS_TABLE (parent))
-       {
-         table_child = children->data;
-         entry = table_child->widget;
-       }
-      if (GTK_IS_BOX (parent))
-       {
-         box_child = children->data;
-         entry = box_child->widget;
-       }
-
-      if (GTK_IS_ENTRY (entry))
-       break;
-      children = children->next;
-    }
-
-
-  if (!GTK_IS_ENTRY (entry)) return NULL;
-
-  return (entry);
-
-}
-
-GtkWidget *
-gtk_sheet_get_entry_widget (GtkSheet *sheet)
-{
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-  g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
-
-  return (sheet->entry_widget);
-}
-
-
-static void
-gtk_sheet_button_draw (GtkSheet *sheet, GdkWindow *window,
-                      GtkSheetButton *button, gboolean is_sensitive,
-                      GdkRectangle allocation)
-{
-  GtkShadowType shadow_type;
-  gint text_width = 0, text_height = 0;
-  GtkSheetChild *child = NULL;
-  PangoAlignment align = PANGO_ALIGN_LEFT;
-
-  gboolean rtl ;
-
-  gint state = 0;
-  gint len = 0;
-  gchar *line = 0;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (button != NULL);
-
-  rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
-
-  gdk_window_clear_area (window,
-                        allocation.x, allocation.y,
-                        allocation.width, allocation.height);
-
-  gtk_paint_box (sheet->button->style, window,
-                GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-                &allocation, GTK_WIDGET (sheet->button),
-                "buttondefault",
-                allocation.x, allocation.y,
-                allocation.width, allocation.height);
-
-  state = button->state;
-  if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
-
-  if (state == GTK_STATE_ACTIVE)
-    shadow_type = GTK_SHADOW_IN;
-  else
-    shadow_type = GTK_SHADOW_OUT;
-
-  if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
-    gtk_paint_box (sheet->button->style, window,
-                  button->state, shadow_type,
-                  &allocation, GTK_WIDGET (sheet->button),
-                  "button",
-                  allocation.x, allocation.y,
-                  allocation.width, allocation.height);
-
-  if (button->label_visible)
-    {
-
-      text_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet))- 2 * CELLOFFSET;
-
-      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
-                                &allocation);
-      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, &allocation);
-
-      allocation.y += 2 * sheet->button->style->ythickness;
-
-
-      if (button->label && strlen (button->label)>0)
-       {
-         gchar *words = 0;
-         PangoLayout *layout = NULL;
-         gint real_x = allocation.x, real_y = allocation.y;
-
-         words = button->label;
-         line = g_new (gchar, 1);
-         line[0]='\0';
-
-         while (words && *words != '\0')
-           {
-             if (*words != '\n')
-               {
-                 len = strlen (line);
-                 line = g_realloc (line, len + 2);
-                 line[len]=*words;
-                 line[len + 1]='\0';
-               }
-             if (*words == '\n' || * (words + 1) == '\0')
-               {
-                 text_width = STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, line);
-
-                 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
-                 switch (button->justification)
-                   {
-                   case GTK_JUSTIFY_LEFT:
-                     real_x = allocation.x + CELLOFFSET;
-                     align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
-                     break;
-                   case GTK_JUSTIFY_RIGHT:
-                     real_x = allocation.x + allocation.width - text_width - CELLOFFSET;
-                     align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
-                     break;
-                   case GTK_JUSTIFY_CENTER:
-                   default:
-                     real_x = allocation.x + (allocation.width - text_width)/2;
-                     align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
-                     pango_layout_set_justify (layout, TRUE);
-                   }
-                 pango_layout_set_alignment (layout, align);
-                 gtk_paint_layout (GTK_WIDGET (sheet)->style,
-                                   window,
-                                   state,
-                                   FALSE,
-                                   &allocation,
-                                   GTK_WIDGET (sheet),
-                                   "label",
-                                   real_x, real_y,
-                                   layout);
-                 g_object_unref (layout);
-
-                 real_y += text_height + 2;
-
-                 g_free (line);
-                 line = g_new (gchar, 1);
-                 line[0]='\0';
-               }
-             words++;
-           }
-         g_free (line);
-       }
-
-      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
-                                NULL);
-      gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
-
-    }
-
-  if ((child = button->child) && (child->widget))
-    {
-      child->x = allocation.x;
-      child->y = allocation.y;
-
-      child->x += (allocation.width - child->widget->requisition.width) / 2;
-      child->y += (allocation.height - child->widget->requisition.height) / 2;
-      allocation.x = child->x;
-      allocation.y = child->y;
-      allocation.width = child->widget->requisition.width;
-      allocation.height = child->widget->requisition.height;
-
-      allocation.x = child->x;
-      allocation.y = child->y;
-
-      gtk_widget_set_state (child->widget, button->state);
-
-      if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
-         GTK_WIDGET_MAPPED (child->widget))
-       {
-         gtk_widget_size_allocate (child->widget,
-                                   &allocation);
-         gtk_widget_queue_draw (child->widget);
-       }
-    }
-
-  gtk_sheet_button_free (button);
-}
-
-
-/* COLUMN value of - 1 indicates that the area to the right of the rightmost
-   button should be redrawn */
-static void
-gtk_sheet_column_title_button_draw (GtkSheet *sheet, gint column)
-{
-  GdkWindow *window = NULL;
-  GdkRectangle allocation;
-
-  gboolean is_sensitive = FALSE;
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  if (column >= 0 && ! xxx_column_is_visible (sheet, column)) return;
-  if (column >= 0 && !sheet->column_titles_visible) return;
-  if (column >= 0 && column < MIN_VISIBLE_COLUMN (sheet)) return;
-  if (column >= 0 && column > MAX_VISIBLE_COLUMN (sheet)) return;
-
-  window = sheet->column_title_window;
-  allocation.y = 0;
-  allocation.height = sheet->column_title_area.height;
-
-  if ( column == -1 )
-    {
-      const gint cols = xxx_column_count (sheet) ;
-      allocation.x = COLUMN_LEFT_XPIXEL (sheet, cols - 1)
-       ;
-      allocation.width = sheet->column_title_area.width
-       + sheet->column_title_area.x
-       - allocation.x;
-
-      gdk_window_clear_area (window,
-                            allocation.x, allocation.y,
-                            allocation.width, allocation.height);
-    }
-  else
-    {
-      GtkSheetButton *button = xxx_column_button (sheet, column);
-      allocation.x = COLUMN_LEFT_XPIXEL (sheet, column) + CELL_SPACING;
-      if (sheet->row_titles_visible)
-       allocation.x -= sheet->row_title_area.width;
-
-      allocation.width = xxx_column_width (sheet, column);
-
-      is_sensitive = xxx_column_is_sensitive (sheet, column);
-      gtk_sheet_button_draw (sheet, window, button,
-                            is_sensitive, allocation);
-
-      /* FIXME: Not freeing this button is correct (sort of),
-        because in PSPP the model always returns a static copy */
-
-      /* gtk_sheet_button_free (button); */
-
-    }
-}
-
-static void
-gtk_sheet_row_title_button_draw (GtkSheet *sheet, gint row)
-{
-  GdkWindow *window = NULL;
-  GdkRectangle allocation;
-  GtkSheetButton *button = NULL;
-  gboolean is_sensitive = FALSE;
-
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
-
-  if (row >= 0 && !yyy_row_is_visible (sheet, row)) return;
-  if (row >= 0 && !sheet->row_titles_visible) return;
-  if (row >= 0 && row < MIN_VISIBLE_ROW (sheet)) return;
-  if (row >= 0 && row > MAX_VISIBLE_ROW (sheet)) return;
-
-
-  window = sheet->row_title_window;
-  button = yyy_row_button (sheet, row);
-  allocation.x = 0;
-  allocation.y = ROW_TOP_YPIXEL (sheet, row) + CELL_SPACING;
-  if (sheet->column_titles_visible)
-    allocation.y -= sheet->column_title_area.height;
-  allocation.width = sheet->row_title_area.width;
-  allocation.height = yyy_row_height (sheet, row);
-  is_sensitive = yyy_row_is_sensitive (sheet, row);
-
-  gtk_sheet_button_draw (sheet, window, button, is_sensitive, allocation);
-}
-
-/* SCROLLBARS
- *
- * functions:
- * adjust_scrollbars
- * vadjustment_value_changed
- * hadjustment_value_changed */
-
-static void
-adjust_scrollbars (GtkSheet * sheet)
-{
-  if (sheet->vadjustment)
-    {
-      sheet->vadjustment->page_size = sheet->sheet_window_height;
-      sheet->vadjustment->page_increment = sheet->sheet_window_height / 2;
-      sheet->vadjustment->step_increment = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
-      sheet->vadjustment->lower = 0;
-      sheet->vadjustment->upper = SHEET_HEIGHT (sheet) + 80;
-      g_signal_emit_by_name (sheet->vadjustment, "changed");
-
-    }
-
-  if (sheet->hadjustment)
-    {
-      sheet->hadjustment->page_size = sheet->sheet_window_width;
-      sheet->hadjustment->page_increment = sheet->sheet_window_width / 2;
-      sheet->hadjustment->step_increment = DEFAULT_COLUMN_WIDTH;
-      sheet->hadjustment->lower = 0;
-      sheet->hadjustment->upper = SHEET_WIDTH (sheet)+ 80;
-      g_signal_emit_by_name (sheet->hadjustment, "changed");
-
-    }
-}
-
-static void
-vadjustment_value_changed (GtkAdjustment * adjustment,
-                          gpointer data)
-{
-  GtkSheet *sheet;
-  gint diff, value, old_value;
-  gint row, new_row;
-  gint y = 0;
-
-  g_return_if_fail (adjustment != NULL);
-  g_return_if_fail (data != NULL);
-  g_return_if_fail (GTK_IS_SHEET (data));
-
-  sheet = GTK_SHEET (data);
-
-  if (GTK_SHEET_IS_FROZEN (sheet)) return;
-
-  row = ROW_FROM_YPIXEL (sheet, CELL_SPACING);
-
-  old_value = - sheet->voffset;
-
-  new_row = g_sheet_row_pixel_to_row (sheet->row_geometry,
-                                     adjustment->value);
-
-  y = g_sheet_row_start_pixel (sheet->row_geometry, new_row);
-
-  if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. &&
-      yyy_row_height (sheet, row) > sheet->vadjustment->step_increment)
-    {
-      /* This avoids embarrassing twitching */
-      if (row == new_row && row != yyy_row_count (sheet) - 1 &&
-         adjustment->value - sheet->old_vadjustment >=
-         sheet->vadjustment->step_increment &&
-         new_row + 1 != MIN_VISIBLE_ROW (sheet))
-       {
-         new_row +=1;
-         y = y+yyy_row_height (sheet, row);
-       }
-    }
-
-  /* Negative old_adjustment enforces the redraw, otherwise avoid
-     spureous redraw */
-  if (sheet->old_vadjustment >= 0. && row == new_row)
-    {
-      sheet->old_vadjustment = sheet->vadjustment->value;
-      return;
-    }
-
-  sheet->old_vadjustment = sheet->vadjustment->value;
-  adjustment->value = y;
-
-
-  if (new_row == 0)
-    {
-      sheet->vadjustment->step_increment = yyy_row_height (sheet, 0);
-    }
-  else
-    {
-      sheet->vadjustment->step_increment =
-       MIN (yyy_row_height (sheet, new_row), yyy_row_height (sheet, new_row - 1));
-    }
-
-  sheet->vadjustment->value = adjustment->value;
-
-  value = adjustment->value;
-
-  if (value >= - sheet->voffset)
-    {
-      /* scroll down */
-      diff = value + sheet->voffset;
-    }
-  else
-    {
-      /* scroll up */
-      diff = - sheet->voffset - value;
-    }
-
-  sheet->voffset = - value;
-
-  if (GTK_WIDGET_REALIZED (sheet->entry_widget) &&
-      sheet->state == GTK_SHEET_NORMAL &&
-      sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
-      !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
-                                sheet->active_cell.col))
-    {
-      const gchar *text;
-
-      text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
-
-      if (!text || strlen (text) == 0)
-       gtk_sheet_cell_clear (sheet,
-                             sheet->active_cell.row,
-                             sheet->active_cell.col);
-      gtk_widget_unmap (sheet->entry_widget);
-    }
-
-  gtk_sheet_position_children (sheet);
-
-  gtk_sheet_range_draw (sheet, NULL);
-  size_allocate_row_title_buttons (sheet);
-  size_allocate_global_button (sheet);
-}
-
-static void
-hadjustment_value_changed (GtkAdjustment * adjustment,
-                          gpointer data)
-{
-  GtkSheet *sheet;
-  gint i, diff, value, old_value;
-  gint column, new_column;
-  gint x = 0;
-
-  g_return_if_fail (adjustment != NULL);
-  g_return_if_fail (data != NULL);
-  g_return_if_fail (GTK_IS_SHEET (data));
-
-  sheet = GTK_SHEET (data);
-
-  if (GTK_SHEET_IS_FROZEN (sheet)) return;
-
-  column = COLUMN_FROM_XPIXEL (sheet, CELL_SPACING);
-
-  old_value = - sheet->hoffset;
-
-  for (i = 0; i < xxx_column_count (sheet); i++)
-    {
-      if (xxx_column_is_visible (sheet, i)) x += xxx_column_width (sheet, i);
-      if (x > adjustment->value) break;
-    }
-  x -= xxx_column_width (sheet, i);
-  new_column = i;
-
-  if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 &&
-      xxx_column_width (sheet, i) > sheet->hadjustment->step_increment)
-    {
-      /* This avoids embarrassing twitching */
-      if (column == new_column && column != xxx_column_count (sheet) - 1 &&
-         adjustment->value - sheet->old_hadjustment >=
-         sheet->hadjustment->step_increment &&
-         new_column + 1 != MIN_VISIBLE_COLUMN (sheet))
-       {
-         new_column += 1;
-         x += xxx_column_width (sheet, column);
-       }
-    }
-
-  /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
-  if (sheet->old_hadjustment >= 0. && new_column == column)
-    {
-      sheet->old_hadjustment = sheet->hadjustment->value;
-      return;
-    }
-
-  sheet->old_hadjustment = sheet->hadjustment->value;
-  adjustment->value = x;
-
-  if (new_column == 0)
-    {
-      sheet->hadjustment->step_increment = xxx_column_width (sheet, 0);
-    }
-  else
-    {
-      sheet->hadjustment->step_increment =
-       MIN (xxx_column_width (sheet, new_column), xxx_column_width (sheet, new_column - 1));
-    }
-
-
-  sheet->hadjustment->value = adjustment->value;
-
-  value = adjustment->value;
-
-  if (value >= - sheet->hoffset)
-    {
-      /* scroll right */
-      diff = value + sheet->hoffset;
-    }
-  else
-    {
-      /* scroll left */
-      diff = - sheet->hoffset - value;
-    }
-
-  sheet->hoffset = - value;
-  if (GTK_WIDGET_REALIZED (sheet->entry_widget) &&
-      sheet->state == GTK_SHEET_NORMAL &&
-      sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
-      !gtk_sheet_cell_isvisible (sheet, sheet->active_cell.row,
-                                sheet->active_cell.col))
-    {
-      const gchar *text;
-
-      text = gtk_entry_get_text (GTK_ENTRY (gtk_sheet_get_entry (sheet)));
-      if (!text || strlen (text) == 0)
-       gtk_sheet_cell_clear (sheet,
-                             sheet->active_cell.row,
-                             sheet->active_cell.col);
-
-      gtk_widget_unmap (sheet->entry_widget);
-    }
-
-  gtk_sheet_position_children (sheet);
-
-  gtk_sheet_range_draw (sheet, NULL);
-  size_allocate_column_title_buttons (sheet);
-}
-
-
-/* COLUMN RESIZING */
-static void
-draw_xor_vline (GtkSheet * sheet)
-{
-  GtkWidget *widget;
-
-  g_return_if_fail (sheet != NULL);
-
-  widget = GTK_WIDGET (sheet);
-
-  gdk_draw_line (widget->window, sheet->xor_gc,
-                sheet->x_drag,
-                sheet->column_title_area.height,
-                sheet->x_drag,
-                sheet->sheet_window_height + 1);
-}
-
-/* ROW RESIZING */
-static void
-draw_xor_hline (GtkSheet * sheet)
-{
-  GtkWidget *widget;
-
-  g_return_if_fail (sheet != NULL);
-
-  widget = GTK_WIDGET (sheet);
-
-  gdk_draw_line (widget->window, sheet->xor_gc,
-                sheet->row_title_area.width,
-                sheet->y_drag,
-
-                sheet->sheet_window_width + 1,
-                sheet->y_drag);
-}
-
-/* SELECTED RANGE */
-static void
-draw_xor_rectangle (GtkSheet *sheet, GtkSheetRange range)
-{
-  gint i;
-  GdkRectangle clip_area, area;
-  GdkGCValues values;
-
-  area.x = COLUMN_LEFT_XPIXEL (sheet, range.col0);
-  area.y = ROW_TOP_YPIXEL (sheet, range.row0);
-  area.width = COLUMN_LEFT_XPIXEL (sheet, range.coli)- area.x+
-    xxx_column_width (sheet, range.coli);
-  area.height = ROW_TOP_YPIXEL (sheet, range.rowi)- area.y+
-    yyy_row_height (sheet, range.rowi);
-
-  clip_area.x = sheet->row_title_area.width;
-  clip_area.y = sheet->column_title_area.height;
-  clip_area.width = sheet->sheet_window_width;
-  clip_area.height = sheet->sheet_window_height;
-
-  if (!sheet->row_titles_visible) clip_area.x = 0;
-  if (!sheet->column_titles_visible) clip_area.y = 0;
-
-  if (area.x < 0)
-    {
-      area.width = area.width + area.x;
-      area.x = 0;
-    }
-  if (area.width > clip_area.width) area.width = clip_area.width + 10;
-  if (area.y < 0)
-    {
-      area.height = area.height + area.y;
-      area.y = 0;
-    }
-  if (area.height > clip_area.height) area.height = clip_area.height + 10;
-
-  clip_area.x--;
-  clip_area.y--;
-  clip_area.width += 3;
-  clip_area.height += 3;
-
-  gdk_gc_get_values (sheet->xor_gc, &values);
-
-  gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
-
-  for (i = -1; i <= 1; ++i)
-    gdk_draw_rectangle (sheet->sheet_window,
-                       sheet->xor_gc,
-                       FALSE,
-                       area.x + i, area.y + i,
-                       area.width - 2 * i, area.height - 2 * i);
-
-
-  gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
-
-  gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
-
-}
-
-
-/* this function returns the new width of the column being resized given
- * the column and x position of the cursor; the x cursor position is passed
- * in as a pointer and automaticaly corrected if it's beyond min / max limits */
-static guint
-new_column_width (GtkSheet * sheet,
-                 gint column,
-                 gint * x)
-{
-  gint cx, width;
-  guint min_width;
-
-  cx = *x;
-
-  min_width = sheet->column_requisition;
-
-  /* you can't shrink a column to less than its minimum width */
-  if (cx < COLUMN_LEFT_XPIXEL (sheet, column) + min_width)
-    {
-      *x = cx = COLUMN_LEFT_XPIXEL (sheet, column) + min_width;
-    }
-
-  /* calculate new column width making sure it doesn't end up
-   * less than the minimum width */
-  width = cx - COLUMN_LEFT_XPIXEL (sheet, column);
-  if (width < min_width)
-    width = min_width;
-
-  xxx_set_column_width (sheet, column, width);
-  size_allocate_column_title_buttons (sheet);
-
-  return width;
-}
-
-/* this function returns the new height of the row being resized given
- * the row and y position of the cursor; the y cursor position is passed
- * in as a pointer and automaticaly corrected if it's beyond min / max limits */
-static guint
-new_row_height (GtkSheet * sheet,
-               gint row,
-               gint * y)
-{
-  gint cy, height;
-  guint min_height;
-
-  cy = *y;
-  min_height = sheet->row_requisition;
-
-  /* you can't shrink a row to less than its minimum height */
-  if (cy < ROW_TOP_YPIXEL (sheet, row) + min_height)
-
-    {
-      *y = cy = ROW_TOP_YPIXEL (sheet, row) + min_height;
-    }
-
-  /* calculate new row height making sure it doesn't end up
-   * less than the minimum height */
-  height = (cy - ROW_TOP_YPIXEL (sheet, row));
-  if (height < min_height)
-    height = min_height;
-
-  yyy_set_row_height (sheet, row, height);
-  size_allocate_row_title_buttons (sheet);
-
-  return height;
-}
-
-static void
-gtk_sheet_set_column_width (GtkSheet * sheet,
-                           gint column,
-                           guint width)
-{
-  guint min_width;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (column < 0 || column >= xxx_column_count (sheet))
-    return;
-
-  gtk_sheet_column_size_request (sheet, column, &min_width);
-  if (width < min_width) return;
-
-  xxx_set_column_width (sheet, column, width);
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
-    {
-      size_allocate_column_title_buttons (sheet);
-      adjust_scrollbars (sheet);
-      gtk_sheet_size_allocate_entry (sheet);
-      gtk_sheet_range_draw (sheet, NULL);
-    }
-
-  g_signal_emit (sheet, sheet_signals[CHANGED], 0, -1, column);
-}
-
-
-
-void
-gtk_sheet_set_row_height (GtkSheet * sheet,
-                         gint row,
-                         guint height)
-{
-  guint min_height;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  if (row < 0 || row >= yyy_row_count (sheet))
-    return;
-
-  gtk_sheet_row_size_request (sheet, row, &min_height);
-  if (height < min_height) return;
-
-  yyy_set_row_height (sheet, row, height);
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) && !GTK_SHEET_IS_FROZEN (sheet))
-    {
-      size_allocate_row_title_buttons (sheet);
-      adjust_scrollbars (sheet);
-      gtk_sheet_size_allocate_entry (sheet);
-      gtk_sheet_range_draw (sheet, NULL);
-    }
-
-  g_signal_emit (sheet, sheet_signals[CHANGED], 0, row, - 1);
-}
-gboolean
-gtk_sheet_get_attributes (const GtkSheet *sheet, gint row, gint col,
-                         GtkSheetCellAttr *attributes)
-{
-  const GdkColor *fg, *bg;
-  const GtkJustification *j ;
-  const PangoFontDescription *font_desc ;
-  const GtkSheetCellBorder *border ;
-
-  g_return_val_if_fail (sheet != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), FALSE);
-
-  if (row < 0 || col < 0) return FALSE;
-
-  init_attributes (sheet, col, attributes);
-
-  if ( !sheet->model)
-    return FALSE;
-
-  attributes->is_editable = g_sheet_model_is_editable (sheet->model, row, col);
-  attributes->is_visible = g_sheet_model_is_visible (sheet->model, row, col);
-
-  fg = g_sheet_model_get_foreground (sheet->model, row, col);
-  if ( fg )
-    attributes->foreground = *fg;
-
-  bg = g_sheet_model_get_background (sheet->model, row, col);
-  if ( bg )
-    attributes->background = *bg;
-
-  j = g_sheet_model_get_justification (sheet->model, row, col);
-  if (j) attributes->justification = *j;
-
-  font_desc = g_sheet_model_get_font_desc (sheet->model, row, col);
-  if ( font_desc ) attributes->font_desc = font_desc;
-
-  border = g_sheet_model_get_cell_border (sheet->model, row, col);
-
-  if ( border ) attributes->border = *border;
-
-  return TRUE;
-}
-
-static void
-init_attributes (const GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes)
-{
-  /* DEFAULT VALUES */
-  attributes->foreground = GTK_WIDGET (sheet)->style->black;
-  attributes->background = sheet->bg_color;
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      GdkColormap *colormap;
-      colormap = gdk_colormap_get_system ();
-      attributes->background = sheet->bg_color;
-    }
-  attributes->justification = xxx_column_justification (sheet, col);
-  attributes->border.width = 0;
-  attributes->border.line_style = GDK_LINE_SOLID;
-  attributes->border.cap_style = GDK_CAP_NOT_LAST;
-  attributes->border.join_style = GDK_JOIN_MITER;
-  attributes->border.mask = 0;
-  attributes->border.color = GTK_WIDGET (sheet)->style->black;
-  attributes->is_editable = TRUE;
-  attributes->is_visible = TRUE;
-  attributes->font_desc = GTK_WIDGET (sheet)->style->font_desc;
-}
-
-
-/********************************************************************
- * Container Functions:
- * gtk_sheet_add
- * gtk_sheet_put
- * gtk_sheet_attach
- * gtk_sheet_remove
- * gtk_sheet_move_child
- * gtk_sheet_position_child
- * gtk_sheet_position_children
- * gtk_sheet_realize_child
- * gtk_sheet_get_child_at
- ********************************************************************/
-
-GtkSheetChild *
-gtk_sheet_put (GtkSheet *sheet, GtkWidget *child, gint x, gint y)
-{
-  GtkRequisition child_requisition;
-  GtkSheetChild *child_info;
-
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-  g_return_val_if_fail (child != NULL, NULL);
-  g_return_val_if_fail (child->parent == NULL, NULL);
-
-  child_info = g_new (GtkSheetChild, 1);
-  child_info->widget = child;
-  child_info->x = x;
-  child_info->y = y;
-  child_info->attached_to_cell = FALSE;
-  child_info->floating = TRUE;
-  child_info->xpadding = child_info->ypadding = 0;
-  child_info->xexpand = child_info->yexpand = FALSE;
-  child_info->xshrink = child_info->yshrink = FALSE;
-  child_info->xfill = child_info->yfill = FALSE;
-
-  sheet->children = g_list_append (sheet->children, child_info);
-
-  gtk_widget_set_parent (child, GTK_WIDGET (sheet));
-
-  gtk_widget_size_request (child, &child_requisition);
-
-  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
-    {
-      if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
-         (!GTK_WIDGET_REALIZED (child) || GTK_WIDGET_NO_WINDOW (child)))
-       gtk_sheet_realize_child (sheet, child_info);
-
-      if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
-         !GTK_WIDGET_MAPPED (child))
-       gtk_widget_map (child);
-    }
-
-  gtk_sheet_position_child (sheet, child_info);
-
-  /* This will avoid drawing on the titles */
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      if (sheet->row_titles_visible)
-       gdk_window_show (sheet->row_title_window);
-      if (sheet->column_titles_visible)
-       gdk_window_show (sheet->column_title_window);
-    }
-
-  return (child_info);
-}
-
-void
-gtk_sheet_attach_floating (GtkSheet *sheet,
-                          GtkWidget *widget,
-                          gint row, gint col)
-{
-  GdkRectangle area;
-  GtkSheetChild *child;
-
-  if (row < 0 || col < 0)
-    {
-      gtk_sheet_button_attach (sheet, widget, row, col);
-      return;
-    }
-
-  gtk_sheet_get_cell_area (sheet, row, col, &area);
-  child = gtk_sheet_put (sheet, widget, area.x, area.y);
-  child->attached_to_cell = TRUE;
-  child->row = row;
-  child->col = col;
-}
-
-void
-gtk_sheet_attach_default (GtkSheet *sheet,
-                         GtkWidget *widget,
-                         gint row, gint col)
-{
-  if (row < 0 || col < 0)
-    {
-      gtk_sheet_button_attach (sheet, widget, row, col);
-      return;
-    }
-
-  gtk_sheet_attach (sheet, widget, row, col,
-                   GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
-}
-
-void
-gtk_sheet_attach (GtkSheet *sheet,
-                 GtkWidget *widget,
-                 gint row, gint col,
-                 gint xoptions,
-                 gint yoptions,
-                 gint xpadding,
-                 gint ypadding)
-{
-  GdkRectangle area;
-  GtkSheetChild *child = NULL;
-
-  if (row < 0 || col < 0)
-    {
-      gtk_sheet_button_attach (sheet, widget, row, col);
-      return;
-    }
-
-  child = g_new0 (GtkSheetChild, 1);
-  child->attached_to_cell = TRUE;
-  child->floating = FALSE;
-  child->widget = widget;
-  child->row = row;
-  child->col = col;
-  child->xpadding = xpadding;
-  child->ypadding = ypadding;
-  child->xexpand = (xoptions & GTK_EXPAND) != 0;
-  child->yexpand = (yoptions & GTK_EXPAND) != 0;
-  child->xshrink = (xoptions & GTK_SHRINK) != 0;
-  child->yshrink = (yoptions & GTK_SHRINK) != 0;
-  child->xfill = (xoptions & GTK_FILL) != 0;
-  child->yfill = (yoptions & GTK_FILL) != 0;
-
-  sheet->children = g_list_append (sheet->children, child);
-
-  gtk_sheet_get_cell_area (sheet, row, col, &area);
-
-  child->x = area.x + child->xpadding;
-  child->y = area.y + child->ypadding;
-
-  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
-    {
-      if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
-         (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
-       gtk_sheet_realize_child (sheet, child);
-
-      if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
-         !GTK_WIDGET_MAPPED (widget))
-       gtk_widget_map (widget);
-    }
-
-  gtk_sheet_position_child (sheet, child);
-
-  /* This will avoid drawing on the titles */
-
-  if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    {
-      if (GTK_SHEET_ROW_TITLES_VISIBLE (sheet))
-       gdk_window_show (sheet->row_title_window);
-      if (GTK_SHEET_COL_TITLES_VISIBLE (sheet))
-       gdk_window_show (sheet->column_title_window);
-    }
-
-}
-
-void
-gtk_sheet_button_attach                 (GtkSheet *sheet,
-                                 GtkWidget *widget,
-                                 gint row, gint col)
-{
-  GtkSheetButton *button = 0;
-  GtkSheetChild *child;
-  GtkRequisition button_requisition;
-
-  if (row >= 0 && col >= 0) return;
-  if (row < 0 && col < 0) return;
-
-  child = g_new (GtkSheetChild, 1);
-  child->widget = widget;
-  child->x = 0;
-  child->y = 0;
-  child->attached_to_cell = TRUE;
-  child->floating = FALSE;
-  child->row = row;
-  child->col = col;
-  child->xpadding = child->ypadding = 0;
-  child->xshrink = child->yshrink = FALSE;
-  child->xfill = child->yfill = FALSE;
-
-
-  sheet->children = g_list_append (sheet->children, child);
-
-  gtk_sheet_button_size_request (sheet, button, &button_requisition);
-
-
-  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (sheet)))
-    {
-      if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) &&
-         (!GTK_WIDGET_REALIZED (widget) || GTK_WIDGET_NO_WINDOW (widget)))
-       gtk_sheet_realize_child (sheet, child);
-
-      if (GTK_WIDGET_MAPPED (GTK_WIDGET (sheet)) &&
-         !GTK_WIDGET_MAPPED (widget))
-       gtk_widget_map (widget);
-    }
-
-  if (row == -1) size_allocate_column_title_buttons (sheet);
-  if (col == -1) size_allocate_row_title_buttons (sheet);
-
-}
-
-static void
-label_size_request (GtkSheet *sheet, gchar *label, GtkRequisition *req)
-{
-  gchar *words;
-  gchar word[1000];
-  gint n = 0;
-  gint row_height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet)) - 2 * CELLOFFSET + 2;
-
-  req->height = 0;
-  req->width = 0;
-  words = label;
-
-  while (words && *words != '\0')
-    {
-      if (*words == '\n' || * (words + 1) == '\0')
-       {
-         req->height += row_height;
-
-         word[n] = '\0';
-         req->width = MAX (req->width, STRING_WIDTH (GTK_WIDGET (sheet), GTK_WIDGET (sheet)->style->font_desc, word));
-         n = 0;
-       }
-      else
-       {
-         word[n++] = *words;
-       }
-      words++;
-    }
-
-  if (n > 0) req->height -= 2;
-}
-
-static void
-gtk_sheet_button_size_request   (GtkSheet *sheet,
-                                 const GtkSheetButton *button,
-                                 GtkRequisition *button_requisition)
-{
-  GtkRequisition requisition;
-  GtkRequisition label_requisition;
-
-  if (gtk_sheet_autoresize (sheet) && button->label && strlen (button->label) > 0)
-    {
-      label_size_request (sheet, button->label, &label_requisition);
-      label_requisition.width += 2 * CELLOFFSET;
-      label_requisition.height += 2 * CELLOFFSET;
-    }
-  else
-    {
-      label_requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
-      label_requisition.width = COLUMN_MIN_WIDTH;
-    }
-
-  if (button->child)
-    {
-      gtk_widget_size_request (button->child->widget, &requisition);
-      requisition.width += 2 * button->child->xpadding;
-      requisition.height += 2 * button->child->ypadding;
-      requisition.width += 2 * sheet->button->style->xthickness;
-      requisition.height += 2 * sheet->button->style->ythickness;
-    }
-  else
-    {
-      requisition.height = DEFAULT_ROW_HEIGHT (GTK_WIDGET (sheet));
-      requisition.width = COLUMN_MIN_WIDTH;
-    }
-
-  *button_requisition = requisition;
-  button_requisition->width = MAX (requisition.width, label_requisition.width);
-  button_requisition->height = MAX (requisition.height, label_requisition.height);
-
-}
-
-static void
-gtk_sheet_row_size_request (GtkSheet *sheet,
-                           gint row,
-                           guint *requisition)
-{
-  GtkRequisition button_requisition;
-  GList *children;
-
-  gtk_sheet_button_size_request (sheet,
-                                yyy_row_button (sheet, row),
-                                &button_requisition);
-
-  *requisition = button_requisition.height;
-
-  children = sheet->children;
-  while (children)
-    {
-      GtkSheetChild *child = (GtkSheetChild *)children->data;
-      GtkRequisition child_requisition;
-
-      if (child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink)
-       {
-         gtk_widget_get_child_requisition (child->widget, &child_requisition);
-
-         if (child_requisition.height + 2 * child->ypadding > *requisition)
-           *requisition = child_requisition.height + 2 * child->ypadding;
-       }
-      children = children->next;
-    }
-
-  sheet->row_requisition = * requisition;
-}
-
-static void
-gtk_sheet_column_size_request (GtkSheet *sheet,
-                              gint col,
-                              guint *requisition)
-{
-  GtkRequisition button_requisition;
-  GList *children;
-  GtkSheetButton *button = xxx_column_button (sheet, col);
-
-  gtk_sheet_button_size_request (sheet,
-                                button,
-                                &button_requisition);
-
-  gtk_sheet_button_free (button);
-
-  *requisition = button_requisition.width;
-
-  children = sheet->children;
-  while (children)
-    {
-      GtkSheetChild *child = (GtkSheetChild *)children->data;
-      GtkRequisition child_requisition;
-
-      if (child->attached_to_cell && child->col == col && child->row != -1 && !child->floating && !child->xshrink)
-       {
-         gtk_widget_get_child_requisition (child->widget, &child_requisition);
-
-         if (child_requisition.width + 2 * child->xpadding > *requisition)
-           *requisition = child_requisition.width + 2 * child->xpadding;
-       }
-      children = children->next;
-    }
-
-  sheet->column_requisition = *requisition;
-}
-
-void
-gtk_sheet_move_child (GtkSheet *sheet, GtkWidget *widget, gint x, gint y)
-{
-  GtkSheetChild *child;
-  GList *children;
-
-  g_return_if_fail (sheet != NULL);
-  g_return_if_fail (GTK_IS_SHEET (sheet));
-
-  children = sheet->children;
-  while (children)
-    {
-      child = children->data;
-
-      if (child->widget == widget)
-       {
-         child->x = x;
-         child->y = y;
-         child->row = ROW_FROM_YPIXEL (sheet, y);
-         child->col = COLUMN_FROM_XPIXEL (sheet, x);
-         gtk_sheet_position_child (sheet, child);
-         return;
-       }
-
-      children = children->next;
-    }
-
-  g_warning ("Widget must be a GtkSheet child");
-
-}
-
-static void
-gtk_sheet_position_child (GtkSheet *sheet, GtkSheetChild *child)
-{
-  GtkRequisition child_requisition;
-  GtkAllocation child_allocation;
-  gint xoffset = 0;
-  gint yoffset = 0;
-  gint x = 0, y = 0;
-  GdkRectangle area;
-
-  gtk_widget_get_child_requisition (child->widget, &child_requisition);
-
-  if (sheet->column_titles_visible)
-    yoffset = sheet->column_title_area.height;
-
-  if (sheet->row_titles_visible)
-    xoffset = sheet->row_title_area.width;
-
-  if (child->attached_to_cell)
-    {
-      gtk_sheet_get_cell_area (sheet, child->row, child->col, &area);
-      child->x = area.x + child->xpadding;
-      child->y = area.y + child->ypadding;
-
-      if (!child->floating)
-       {
-         if (child_requisition.width + 2 * child->xpadding <= xxx_column_width (sheet, child->col))
-           {
-             if (child->xfill)
-               {
-                 child_requisition.width = child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
-               }
-             else
-               {
-                 if (child->xexpand)
-                   {
-                     child->x = area.x + xxx_column_width (sheet, child->col) / 2 -
-                       child_requisition.width / 2;
-                   }
-                 child_allocation.width = child_requisition.width;
-               }
-           }
-         else
-           {
-             if (!child->xshrink)
-               {
-                 gtk_sheet_set_column_width (sheet, child->col, child_requisition.width + 2 * child->xpadding);
-               }
-             child_allocation.width = xxx_column_width (sheet, child->col) - 2 * child->xpadding;
-           }
-
-         if (child_requisition.height +
-             2 * child->ypadding <= yyy_row_height (sheet, child->row))
-           {
-             if (child->yfill)
-               {
-                 child_requisition.height = child_allocation.height =
-                   yyy_row_height (sheet, child->row) - 2 * child->ypadding;
-               }
-             else
-               {
-                 if (child->yexpand)
-                   {
-                     child->y = area.y + yyy_row_height (sheet, child->row) / 2
-                       - child_requisition.height / 2;
-                   }
-                 child_allocation.height = child_requisition.height;
-               }
-           }
-         else
-           {
-             if (!child->yshrink)
-               {
-                 gtk_sheet_set_row_height (sheet, child->row, child_requisition.height + 2 * child->ypadding);
-               }
-             child_allocation.height = yyy_row_height (sheet, child->row) -
-               2 * child->ypadding;
-           }
-       }
-      else
-       {
-         child_allocation.width = child_requisition.width;
-         child_allocation.height = child_requisition.height;
-       }
-
-      x = child_allocation.x = child->x + xoffset;
-      y = child_allocation.y = child->y + yoffset;
-    }
-  else
-    {
-      x = child_allocation.x = child->x + sheet->hoffset + xoffset;
-      x = child_allocation.x = child->x + xoffset;
-      y = child_allocation.y = child->y + sheet->voffset + yoffset;
-      y = child_allocation.y = child->y + yoffset;
-      child_allocation.width = child_requisition.width;
-      child_allocation.height = child_requisition.height;
-    }
-
-  gtk_widget_size_allocate (child->widget, &child_allocation);
-  gtk_widget_queue_draw (child->widget);
-}
-
-static void
-gtk_sheet_forall (GtkContainer *container,
-                 gboolean include_internals,
-                 GtkCallback callback,
-                 gpointer callback_data)
-{
-  GtkSheet *sheet;
-  GtkSheetChild *child;
-  GList *children;
-
-  g_return_if_fail (GTK_IS_SHEET (container));
-  g_return_if_fail (callback != NULL);
-
-  sheet = GTK_SHEET (container);
-  children = sheet->children;
-  while (children)
-    {
-      child = children->data;
-      children = children->next;
-
-      (* callback) (child->widget, callback_data);
-    }
-
-  if (sheet->button && sheet->button->parent)
-    (* callback) (sheet->button, callback_data);
-
-  if (sheet->entry_container && GTK_IS_CONTAINER (sheet->entry_container))
-    (* callback) (sheet->entry_container, callback_data);
-}
-
-
-static void
-gtk_sheet_position_children (GtkSheet *sheet)
-{
-  GList *children;
-  GtkSheetChild *child;
-
-  children = sheet->children;
-
-  while (children)
-    {
-      child = (GtkSheetChild *)children->data;
-
-      if (child->col != -1 && child->row != -1)
-       gtk_sheet_position_child (sheet, child);
-
-      if (child->row == -1)
-       {
-         if (child->col < MIN_VISIBLE_COLUMN (sheet) ||
-             child->col > MAX_VISIBLE_COLUMN (sheet))
-           gtk_sheet_child_hide (child);
-         else
-           gtk_sheet_child_show (child);
-       }
-      if (child->col == -1)
-       {
-         if (child->row < MIN_VISIBLE_ROW (sheet) ||
-             child->row > MAX_VISIBLE_ROW (sheet))
-           gtk_sheet_child_hide (child);
-         else
-           gtk_sheet_child_show (child);
-       }
-
-      children = children->next;
-    }
-}
-
-static void
-gtk_sheet_remove (GtkContainer *container, GtkWidget *widget)
-{
-  GtkSheet *sheet;
-  GList *children;
-  GtkSheetChild *child = 0;
-
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_SHEET (container));
-
-  sheet = GTK_SHEET (container);
-
-  children = sheet->children;
-
-  while (children)
-    {
-      child = (GtkSheetChild *)children->data;
-
-      if (child->widget == widget) break;
-
-      children = children->next;
-    }
-
-  if (children)
-    {
-      gtk_widget_unparent (widget);
-      child->widget = NULL;
-
-      sheet->children = g_list_remove_link (sheet->children, children);
-      g_list_free_1 (children);
-      g_free (child);
-    }
-
-  gtk_widget_unparent (sheet->button);
-}
-
-static void
-gtk_sheet_realize_child (GtkSheet *sheet, GtkSheetChild *child)
-{
-  GtkWidget *widget;
-
-  widget = GTK_WIDGET (sheet);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      if (child->row == -1)
-       gtk_widget_set_parent_window (child->widget, sheet->column_title_window);
-      else if (child->col == -1)
-       gtk_widget_set_parent_window (child->widget, sheet->row_title_window);
-      else
-       gtk_widget_set_parent_window (child->widget, sheet->sheet_window);
-    }
-
-  gtk_widget_set_parent (child->widget, widget);
-}
-
-
-
-GtkSheetChild *
-gtk_sheet_get_child_at (GtkSheet *sheet, gint row, gint col)
-{
-  GList *children;
-  GtkSheetChild *child = 0;
-
-  g_return_val_if_fail (sheet != NULL, NULL);
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-
-  children = sheet->children;
-
-  while (children)
-    {
-      child = (GtkSheetChild *)children->data;
-
-      if (child->attached_to_cell)
-       if (child->row == row && child->col == col) break;
-
-      children = children->next;
-    }
-
-  if (children) return child;
-
-  return NULL;
-}
-
-static void
-gtk_sheet_child_hide (GtkSheetChild *child)
-{
-  g_return_if_fail (child != NULL);
-  gtk_widget_hide (child->widget);
-}
-
-static void
-gtk_sheet_child_show (GtkSheetChild *child)
-{
-  g_return_if_fail (child != NULL);
-
-  gtk_widget_show (child->widget);
-}
-
-GSheetModel *
-gtk_sheet_get_model (const GtkSheet *sheet)
-{
-  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);
-
-  return sheet->model;
-}
-
-
-GtkSheetButton *
-gtk_sheet_button_new (void)
-{
-  GtkSheetButton *button = g_malloc (sizeof (GtkSheetButton));
-
-  button->state = GTK_STATE_NORMAL;
-  button->label = NULL;
-  button->label_visible = TRUE;
-  button->child = NULL;
-  button->justification = GTK_JUSTIFY_FILL;
-
-  return button;
-}
-
-
-void
-gtk_sheet_button_free (GtkSheetButton *button)
-{
-  if (!button) return ;
-
-  g_free (button->label);
-  g_free (button);
-}
-
-
-static void
-append_cell_text (GString *string, const GtkSheet *sheet, gint r, gint c)
-{
-  gchar *celltext = gtk_sheet_cell_get_text (sheet, r, c);
-
-  if ( NULL == celltext)
-    return;
-
-  g_string_append (string, celltext);
-  g_free (celltext);
-}
-
-static GString *
-range_to_text (const GtkSheet *sheet)
-{
-  gint r, c;
-  GString *string;
-
-  if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
-    return NULL;
-
-  string = g_string_sized_new (80);
-
-  for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
-    {
-      for (c = sheet->range.col0; c < sheet->range.coli; ++c)
-       {
-         append_cell_text (string, sheet, r, c);
-         g_string_append (string, "\t");
-       }
-      append_cell_text (string, sheet, r, c);
-      if ( r < sheet->range.rowi)
-       g_string_append (string, "\n");
-    }
-
-  return string;
-}
-
-static GString *
-range_to_html (const GtkSheet *sheet)
-{
-  gint r, c;
-  GString *string;
-
-  if ( !gtk_sheet_range_isvisible (sheet, sheet->range))
-    return NULL;
-
-  string = g_string_sized_new (480);
-
-  g_string_append (string, "<html>\n");
-  g_string_append (string, "<body>\n");
-  g_string_append (string, "<table>\n");
-  for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
-    {
-      g_string_append (string, "<tr>\n");
-      for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
-       {
-         g_string_append (string, "<td>");
-         append_cell_text (string, sheet, r, c);
-         g_string_append (string, "</td>\n");
-       }
-      g_string_append (string, "</tr>\n");
-    }
-  g_string_append (string, "</table>\n");
-  g_string_append (string, "</body>\n");
-  g_string_append (string, "</html>\n");
-
-  return string;
-}
-
-enum {
-  SELECT_FMT_NULL,
-  SELECT_FMT_TEXT,
-  SELECT_FMT_HTML
-};
-
-static void
-primary_get_cb (GtkClipboard     *clipboard,
-               GtkSelectionData *selection_data,
-               guint             info,
-               gpointer          data)
-{
-  GtkSheet *sheet = GTK_SHEET (data);
-  GString *string = NULL;
-
-  switch (info)
-    {
-    case SELECT_FMT_TEXT:
-      string = range_to_text (sheet);
-      break;
-    case SELECT_FMT_HTML:
-      string = range_to_html (sheet);
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-
-  gtk_selection_data_set (selection_data, selection_data->target,
-                         8,
-                         (const guchar *) string->str, string->len);
-  g_string_free (string, TRUE);
-}
-
-static void
-primary_clear_cb (GtkClipboard *clipboard,
-                 gpointer      data)
-{
-  GtkSheet *sheet = GTK_SHEET (data);
-  if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
-    return;
-
-  gtk_sheet_real_unselect_range (sheet, NULL);
-}
-
-static void
-gtk_sheet_update_primary_selection (GtkSheet *sheet)
-{
-  static const GtkTargetEntry targets[] = {
-    { "UTF8_STRING",   0, SELECT_FMT_TEXT },
-    { "STRING",        0, SELECT_FMT_TEXT },
-    { "TEXT",          0, SELECT_FMT_TEXT },
-    { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
-    { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
-    { "text/plain",    0, SELECT_FMT_TEXT },
-    { "text/html",     0, SELECT_FMT_HTML }
-  };
-
-  GtkClipboard *clipboard;
-
-  if (!GTK_WIDGET_REALIZED (sheet))
-    return;
-
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
-                                       GDK_SELECTION_PRIMARY);
-
-  if (gtk_sheet_range_isvisible (sheet, sheet->range))
-    {
-      if (!gtk_clipboard_set_with_owner (clipboard, targets,
-                                        G_N_ELEMENTS (targets),
-                                        primary_get_cb, primary_clear_cb,
-                                        G_OBJECT (sheet)))
-       primary_clear_cb (clipboard, sheet);
-    }
-  else
-    {
-      if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
-       gtk_clipboard_clear (clipboard);
-    }
-}
-
diff --git a/lib/gtksheet/gtksheet.h b/lib/gtksheet/gtksheet.h
deleted file mode 100644 (file)
index 4f40225..0000000
+++ /dev/null
@@ -1,694 +0,0 @@
-/* This version of GtkSheet has been heavily modified, for the specific
-   requirements of PSPPIRE. */
-
-
-/* GtkSheet widget for Gtk+.
- * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
- *
- * Based on GtkClist widget by Jay Painter, but major changes.
- * Memory allocation routines inspired on SC (Spreadsheet Calculator)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef __GTK_SHEET_H__
-#define __GTK_SHEET_H__
-
-#include <gtk/gtk.h>
-
-#include "gtkextra-sheet.h"
-#include "gsheetmodel.h"
-#include "gsheet-column-iface.h"
-#include "gsheet-row-iface.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-typedef enum
-{
-  GTK_SHEET_FOREGROUND,
-  GTK_SHEET_BACKGROUND,
-  GTK_SHEET_FONT,
-  GTK_SHEET_JUSTIFICATION,
-  GTK_SHEET_BORDER,
-  GTK_SHEET_BORDER_COLOR,
-  GTK_SHEET_IS_EDITABLE,
-  GTK_SHEET_IS_VISIBLE
-} GtkSheetAttrType;
-
-/* sheet->state */
-
-enum
-{
-  GTK_SHEET_NORMAL,
-  GTK_SHEET_ROW_SELECTED,
-  GTK_SHEET_COLUMN_SELECTED,
-  GTK_SHEET_RANGE_SELECTED
-};
-
-
-#define GTK_TYPE_SHEET_RANGE (gtk_sheet_range_get_type ())
-#define GTK_TYPE_SHEET (gtk_sheet_get_type ())
-
-#define GTK_SHEET(obj)          GTK_CHECK_CAST (obj, gtk_sheet_get_type (), GtkSheet)
-#define GTK_SHEET_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_sheet_get_type (), GtkSheetClass)
-#define GTK_IS_SHEET(obj)       GTK_CHECK_TYPE (obj, gtk_sheet_get_type ())
-
-/* Public flags, for compatibility */
-
-#define GTK_SHEET_ROW_FROZEN(sheet)      !gtk_sheet_rows_resizable (sheet)
-#define GTK_SHEET_COLUMN_FROZEN(sheet)   !gtk_sheet_columns_resizable (sheet)
-#define GTK_SHEET_AUTORESIZE(sheet)      gtk_sheet_autoresize (sheet)
-#define GTK_SHEET_ROW_TITLES_VISIBLE(sheet)   gtk_sheet_row_titles_visible (sheet)
-#define GTK_SHEET_COL_TITLES_VISIBLE(sheet)   gtk_sheet_column_titles_visible (sheet)
-#define GTK_SHEET_AUTO_SCROLL(sheet)     gtk_sheet_autoscroll (sheet)
-#define GTK_SHEET_JUSTIFY_ENTRY(sheet)   gtk_sheet_justify_entry (sheet)
-
-
-typedef struct _GtkSheetClass GtkSheetClass;
-typedef struct _GtkSheetCellAttr     GtkSheetCellAttr;
-typedef struct _GtkSheetCell GtkSheetCell;
-typedef struct _GtkSheetHoverTitle GtkSheetHoverTitle;
-
-
-struct _GtkSheetCellAttr
-{
-  GtkJustification justification;
-  const PangoFontDescription *font_desc;
-  GdkColor foreground;
-  GdkColor background;
-  GtkSheetCellBorder border;
-  gboolean is_editable;
-  gboolean is_visible;
-};
-
-struct _GtkSheetCell
-{
-  gint row;
-  gint col;
-};
-
-struct _GtkSheetHoverTitle
-{
-  GtkWidget *window;
-  GtkWidget *label;
-  gint row, column;
-};
-
-struct _GtkSheet
-{
-  GtkContainer container;
-
-
-  gboolean dispose_has_run;
-  GSheetColumn *column_geometry;
-  GSheetRow *row_geometry;
-
-  guint16 flags;
-
-  GSheetModel *model;
-
-  GtkSelectionMode selection_mode;
-  gboolean autoresize;
-  gboolean autoscroll;
-  gboolean justify_entry;
-
-  /* Background colors */
-  GdkColor bg_color;
-  GdkColor grid_color;
-  gboolean show_grid;
-
-  /* sheet children */
-  GList *children;
-
-  /* allocation rectangle after the container_border_width
-     and the width of the shadow border */
-  GdkRectangle internal_allocation;
-
-  gint16 column_requisition;
-  gint16 row_requisition;
-
-  gboolean rows_resizable;
-  gboolean columns_resizable;
-
-  /* active cell */
-  GtkSheetCell active_cell;
-
-  /* The GtkEntry used for editing the cells */
-  GtkWidget *entry_widget;
-
-  /* The widget containing entry_widget, or
-     entry_widget itself if no container */
-  GtkWidget *entry_container;
-
-  /* The type of entry_widget */
-  GtkType entry_type;
-
-  /* expanding selection */
-  GtkSheetCell selection_cell;
-
-  /* global selection button */
-  GtkWidget *button;
-
-  /* sheet state */
-  gint state;
-
-  /* selected range */
-  GtkSheetRange range;
-
-  /*the scrolling window and it's height and width to
-   * make things a little speedier */
-  GdkWindow *sheet_window;
-  guint sheet_window_width;
-  guint sheet_window_height;
-
-  /* sheet backing pixmap */
-  GdkPixmap *pixmap;
-
-  /* offsets for scrolling */
-  gint hoffset;
-  gint voffset;
-  gfloat old_hadjustment;
-  gfloat old_vadjustment;
-
-  /* border shadow style */
-  GtkShadowType shadow_type;
-
-  /* Column Titles */
-  GdkRectangle column_title_area;
-  GdkWindow *column_title_window;
-  gboolean column_titles_visible;
-
-  /* Row Titles */
-  GdkRectangle row_title_area;
-  GdkWindow *row_title_window;
-  gboolean row_titles_visible;
-
-  /*scrollbars*/
-  GtkAdjustment *hadjustment;
-  GtkAdjustment *vadjustment;
-
-  gint freeze_count;
-
-  /* xor GC for the verticle drag line */
-  GdkGC *xor_gc;
-
-  /* gc for drawing unselected cells */
-  GdkGC *fg_gc;
-  GdkGC *bg_gc;
-
-  /* cursor used to indicate dragging */
-  GdkCursor *cursor_drag;
-
-  /* the current x-pixel location of the xor-drag vline */
-  gint x_drag;
-
-  /* the current y-pixel location of the xor-drag hline */
-  gint y_drag;
-
-  /* current cell being dragged */
-  GtkSheetCell drag_cell;
-  /* current range being dragged */
-  GtkSheetRange drag_range;
-
-  /* Used for the subtitle (popups) */
-  gint motion_timer;
-  GtkSheetHoverTitle *hover_window;
-};
-
-struct _GtkSheetClass
-{
- GtkContainerClass parent_class;
-
- void (*set_scroll_adjustments) (GtkSheet *sheet,
-                                GtkAdjustment *hadjustment,
-                                GtkAdjustment *vadjustment);
-
- void (*select_row)            (GtkSheet *sheet, gint row);
-
- void (*select_column)                 (GtkSheet *sheet, gint column);
-
- void (*select_range)          (GtkSheet *sheet, GtkSheetRange *range);
-
- void (*resize_range)          (GtkSheet *sheet,
-                               GtkSheetRange *old_range,
-                               GtkSheetRange *new_range);
-
- void (*move_range)                    (GtkSheet *sheet,
-                               GtkSheetRange *old_range,
-                               GtkSheetRange *new_range);
-
- gboolean (*traverse)          (GtkSheet *sheet,
-                               gint row, gint column,
-                               gint *new_row, gint *new_column);
-
- gboolean (*deactivate)                (GtkSheet *sheet,
-                               gint row, gint column);
-
- gboolean (*activate)          (GtkSheet *sheet,
-                               gint row, gint column);
-
- void (*changed)               (GtkSheet *sheet,
-                               gint row, gint column);
-};
-
-GType gtk_sheet_get_type (void);
-GtkType gtk_sheet_range_get_type (void);
-
-
-/* create a new sheet */
-GtkWidget * gtk_sheet_new (GSheetRow *vgeo, GSheetColumn *hgeo,
-                          GSheetModel *model);
-
-
-
-
-/* create a new browser sheet. It cells can not be edited */
-GtkWidget *
-gtk_sheet_new_browser                  (guint rows, guint columns, const gchar *title);
-
-void
-gtk_sheet_construct_browser            (GtkSheet *sheet,
-                                                guint rows, guint columns, const gchar *title);
-
-/* create a new sheet with custom entry */
-GtkWidget *
-gtk_sheet_new_with_custom_entry        (GSheetRow *vgeo,
-                                        GSheetColumn *hgeo,
-                                        GtkType entry_type);
-void
-gtk_sheet_construct_with_custom_entry  (GtkSheet *sheet,
-                                        GSheetRow *vgeo,
-                                        GSheetColumn *hgeo,
-                                        GtkType entry_type);
-/* change scroll adjustments */
-void
-gtk_sheet_set_hadjustment              (GtkSheet *sheet,
-                                        GtkAdjustment *adjustment);
-void
-gtk_sheet_set_vadjustment              (GtkSheet *sheet,
-                                        GtkAdjustment *adjustment);
-/* Change entry */
-void
-gtk_sheet_change_entry                 (GtkSheet *sheet, GtkType entry_type);
-
-/* Returns sheet's entry widget */
-GtkWidget *
-gtk_sheet_get_entry                    (GtkSheet *sheet);
-GtkWidget *
-gtk_sheet_get_entry_widget             (GtkSheet *sheet);
-
-/* Returns sheet->state
- * Added by Steven Rostedt <steven.rostedt@lmco.com> */
-gint
-gtk_sheet_get_state                    (GtkSheet *sheet);
-
-/* Returns sheet's ranges
- * Added by Murray Cumming */
-guint
-gtk_sheet_get_columns_count            (GtkSheet *sheet);
-
-guint
-gtk_sheet_get_rows_count               (GtkSheet *sheet);
-
-void
-gtk_sheet_get_visible_range            (GtkSheet *sheet,
-                                        GtkSheetRange *range);
-
-void
-gtk_sheet_get_selected_range           (GtkSheet *sheet,
-                                        GtkSheetRange *range);
-
-void
-gtk_sheet_set_selection_mode           (GtkSheet *sheet, gint mode);
-
-void
-gtk_sheet_set_autoresize               (GtkSheet *sheet, gboolean autoresize);
-
-gboolean
-gtk_sheet_autoresize                   (GtkSheet *sheet);
-
-void
-gtk_sheet_set_autoscroll               (GtkSheet *sheet, gboolean autoscroll);
-
-gboolean
-gtk_sheet_autoscroll                   (GtkSheet *sheet);
-
-void
-gtk_sheet_set_justify_entry            (GtkSheet *sheet, gboolean justify);
-
-gboolean
-gtk_sheet_justify_entry                        (GtkSheet *sheet);
-
-void
-gtk_sheet_set_locked                   (GtkSheet *sheet, gboolean lock);
-
-gboolean
-gtk_sheet_locked                       (const GtkSheet *sheet);
-
-/* set sheet title */
-void
-gtk_sheet_set_title                    (GtkSheet *sheet, const gchar *title);
-
-/* freeze all visual updates of the sheet.
- * Then thaw the sheet after you have made a number of changes.
- * The updates will occure in a more efficent way than if
- * you made them on a unfrozen sheet */
-void
-gtk_sheet_freeze                       (GtkSheet *sheet);
-void
-gtk_sheet_thaw                         (GtkSheet *sheet);
-/* Background colors */
-void
-gtk_sheet_set_background               (GtkSheet *sheet,
-                                        GdkColor *bg_color);
-void
-gtk_sheet_set_grid                     (GtkSheet *sheet,
-                                        GdkColor *grid_color);
-void
-gtk_sheet_show_grid                    (GtkSheet *sheet,
-                                        gboolean show);
-gboolean
-gtk_sheet_grid_visible                 (GtkSheet *sheet);
-
-/* set/get column title */
-void
-gtk_sheet_set_column_title             (GtkSheet * sheet,
-                                       gint column,
-                                       const gchar * title);
-
-const gchar *
-gtk_sheet_get_column_title             (GtkSheet * sheet,
-                                       gint column);
-
-/* set/get row title */
-void
-gtk_sheet_set_row_title                (GtkSheet * sheet,
-                                       gint row,
-                                       const gchar * title);
-const gchar *
-gtk_sheet_get_row_title                (GtkSheet * sheet,
-                                       gint row);
-
-
-/* set/get button label */
-void
-gtk_sheet_row_button_add_label         (GtkSheet *sheet,
-                                       gint row, const gchar *label);
-const gchar *
-gtk_sheet_row_button_get_label         (GtkSheet *sheet,
-                                       gint row);
-void
-gtk_sheet_row_button_justify           (GtkSheet *sheet,
-                                       gint row, GtkJustification justification);
-
-
-
-/* scroll the viewing area of the sheet to the given column
- * and row; row_align and col_align are between 0-1 representing the
- * location the row should appear on the screen, 0.0 being top or left,
- * 1.0 being bottom or right; if row or column is negative then there
- * is no change */
-void
-gtk_sheet_moveto (GtkSheet *sheet,
-                 gint row,
-                 gint column,
-                 gfloat row_align,
-                  gfloat col_align);
-
-
-void
-gtk_sheet_show_row_titles              (GtkSheet *sheet);
-void
-gtk_sheet_hide_row_titles              (GtkSheet *sheet);
-void
-gtk_sheet_show_column_titles           (GtkSheet *sheet);
-void
-gtk_sheet_hide_column_titles           (GtkSheet *sheet);
-
-gboolean
-gtk_sheet_row_titles_visible           (GtkSheet *sheet);
-
-
-/* set row button sensitivity. If sensitivity is TRUE can be toggled,
- * otherwise it acts as a title */
-void
-gtk_sheet_row_set_sensitivity          (GtkSheet *sheet,
-                                       gint row,  gboolean sensitive);
-
-/* set sensitivity for all row buttons */
-void
-gtk_sheet_rows_set_sensitivity         (GtkSheet *sheet, gboolean sensitive);
-void
-gtk_sheet_rows_set_resizable           (GtkSheet *sheet, gboolean resizable);
-gboolean
-gtk_sheet_rows_resizable               (GtkSheet *sheet);
-
-/* set row visibility. The default value is TRUE. If FALSE, the
- * row is hidden */
-void
-gtk_sheet_row_set_visibility           (GtkSheet *sheet,
-                                        gint row, gboolean visible);
-void
-gtk_sheet_row_label_set_visibility     (GtkSheet *sheet,
-                                        gint row, gboolean visible);
-void
-gtk_sheet_rows_labels_set_visibility   (GtkSheet *sheet, gboolean visible);
-
-
-/* select the row. The range is then highlighted, and the bounds are stored
- * in sheet->range  */
-void
-gtk_sheet_select_row                   (GtkSheet * sheet,
-                                       gint row);
-
-/* select the column. The range is then highlighted, and the bounds are stored
- * in sheet->range  */
-void
-gtk_sheet_select_column                (GtkSheet * sheet,
-                                       gint column);
-
-/* get scrollbars adjustment */
-GtkAdjustment *
-gtk_sheet_get_vadjustment              (GtkSheet * sheet);
-GtkAdjustment *
-gtk_sheet_get_hadjustment              (GtkSheet * sheet);
-
-/* highlight the selected range and store bounds in sheet->range */
-void gtk_sheet_select_range            (GtkSheet *sheet,
-                                        const GtkSheetRange *range);
-
-/* obvious */
-void gtk_sheet_unselect_range          (GtkSheet *sheet);
-
-/* set active cell where the entry will be displayed
- * returns FALSE if current cell can't be deactivated or
- * requested cell can't be activated */
-gboolean
-gtk_sheet_set_active_cell              (GtkSheet *sheet,
-                                       gint row, gint column);
-
-/* Sets *ROW and *COLUMN to be the coordinates of the active cell.
-   ROW and/or COLUMN may be null if the caller is not interested in their
-   values */
-void
-gtk_sheet_get_active_cell              (GtkSheet *sheet,
-                                       gint *row, gint *column);
-
-/* set cell contents and allocate memory if needed */
-void
-gtk_sheet_set_cell                     (GtkSheet *sheet,
-                                       gint row, gint col,
-                                        GtkJustification justification,
-                                       const gchar *text);
-void
-gtk_sheet_set_cell_text                        (GtkSheet *sheet,
-                                       gint row, gint col,
-                                       const gchar *text);
-/* get cell contents */
-gchar *
-gtk_sheet_cell_get_text                (const GtkSheet *sheet, gint row, gint col);
-
-/* clear cell contents */
-void
-gtk_sheet_cell_clear                   (GtkSheet *sheet, gint row, gint col);
-
-/* clear range contents. If range==NULL the whole sheet will be cleared */
-void
-gtk_sheet_range_clear                  (GtkSheet *sheet,
-                                        const GtkSheetRange *range);
-
-/* get cell state: GTK_STATE_NORMAL, GTK_STATE_SELECTED */
-GtkStateType
-gtk_sheet_cell_get_state               (GtkSheet *sheet, gint row, gint col);
-
-/* get row and column correspondig to the given position in the screen */
-gboolean
-gtk_sheet_get_pixel_info (GtkSheet * sheet,
-                         gint x,
-                         gint y,
-                         gint * row,
-                         gint * column);
-
-/* get area of a given cell */
-gboolean
-gtk_sheet_get_cell_area (GtkSheet *sheet,
-                         gint row,
-                         gint column,
-                         GdkRectangle *area);
-
-/* set row height */
-void
-gtk_sheet_set_row_height (GtkSheet * sheet,
-                         gint row,
-                         guint height);
-
-
-/* delete nrows rows starting in row */
-void
-gtk_sheet_delete_rows                  (GtkSheet *sheet, guint row, guint nrows);
-
-/* append nrows row to the end of the sheet */
-void
-gtk_sheet_add_row                      (GtkSheet *sheet, guint nrows);
-
-/* insert nrows rows before the given row and pull right */
-void
-gtk_sheet_insert_rows                  (GtkSheet *sheet, guint row, guint nrows);
-
-/* set abckground color of the given range */
-void
-gtk_sheet_range_set_background         (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       const GdkColor *color);
-
-/* set foreground color (text color) of the given range */
-void
-gtk_sheet_range_set_foreground         (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       const GdkColor *color);
-
-/* set text justification (GTK_JUSTIFY_LEFT, RIGHT, CENTER) of the given range.
- * The default value is GTK_JUSTIFY_LEFT. If autoformat is on, the
- * default justification for numbers is GTK_JUSTIFY_RIGHT */
-void
-gtk_sheet_range_set_justification      (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       GtkJustification justification);
-void
-gtk_sheet_column_set_justification      (GtkSheet *sheet,
-                                        gint column,
-                                        GtkJustification justification);
-/* set if cell contents can be edited or not in the given range:
- * accepted values are TRUE or FALSE. */
-void
-gtk_sheet_range_set_editable           (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       gint editable);
-
-/* set if cell contents are visible or not in the given range:
- * accepted values are TRUE or FALSE.*/
-void
-gtk_sheet_range_set_visible            (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       gboolean visible);
-
-/* set cell border style in the given range.
- * mask values are CELL_LEFT_BORDER, CELL_RIGHT_BORDER, CELL_TOP_BORDER,
- * CELL_BOTTOM_BORDER
- * width is the width of the border line in pixels
- * line_style is the line_style for the border line */
-void
-gtk_sheet_range_set_border             (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       gint mask,
-                                       guint width,
-                                       gint line_style);
-
-/* set border color for the given range */
-void
-gtk_sheet_range_set_border_color       (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       const GdkColor *color);
-
-/* set font for the given range */
-void
-gtk_sheet_range_set_font               (GtkSheet *sheet,
-                                       const GtkSheetRange *range,
-                                       PangoFontDescription *font);
-
-/* get cell attributes of the given cell */
-/* TRUE means that the cell is currently allocated */
-gboolean
-gtk_sheet_get_attributes               (const GtkSheet *sheet,
-                                       gint row, gint col,
-                                       GtkSheetCellAttr *attributes);
-
-
-GtkSheetChild *
-gtk_sheet_put                          (GtkSheet *sheet,
-                                        GtkWidget *widget,
-                                        gint x, gint y);
-void
-gtk_sheet_attach_floating               (GtkSheet *sheet,
-                                         GtkWidget *widget,
-                                         gint row, gint col);
-void
-gtk_sheet_attach_default                (GtkSheet *sheet,
-                                         GtkWidget *widget,
-                                         gint row, gint col);
-void
-gtk_sheet_attach                        (GtkSheet *sheet,
-                                         GtkWidget *widget,
-                                         gint row, gint col,
-                                         gint xoptions,
-                                         gint yoptions,
-                                         gint xpadding,
-                                         gint ypadding);
-
-
-void
-gtk_sheet_move_child                   (GtkSheet *sheet,
-                                        GtkWidget *widget,
-                                        gint x, gint y);
-
-GtkSheetChild *
-gtk_sheet_get_child_at                 (GtkSheet *sheet,
-                                        gint row, gint col);
-
-void
-gtk_sheet_button_attach                        (GtkSheet *sheet,
-                                        GtkWidget *widget,
-                                        gint row, gint col);
-
-
-
-void           gtk_sheet_set_model (GtkSheet *sheet,
-                                  GSheetModel *model);
-
-GSheetModel * gtk_sheet_get_model (const GtkSheet *sheet);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-#endif /* __GTK_SHEET_H__ */
-
-
index 650646c6ee83a8b739bb4e1695ff24775e106fd5..30fd2e592d4c48fd674a27dbcadaa60f10275e97 100644 (file)
@@ -1,8 +1,8 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-noinst_LIBRARIES += lib/linreg/liblinreg.a
+noinst_LTLIBRARIES += lib/linreg/liblinreg.la
 
-lib_linreg_liblinreg_a_SOURCES = \
+lib_linreg_liblinreg_la_SOURCES = \
        lib/linreg/sweep.c  lib/linreg/sweep.h 
 
 EXTRA_DIST += lib/linreg/OChangeLog
diff --git a/perl-module/COPYING b/perl-module/COPYING
new file mode 100644 (file)
index 0000000..4432540
--- /dev/null
@@ -0,0 +1,676 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                      TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/perl-module/Changes b/perl-module/Changes
new file mode 100644 (file)
index 0000000..5919ad6
--- /dev/null
@@ -0,0 +1,13 @@
+Revision history for Perl extension Pspp.
+
+0.7.0 Sun Jan 11 10:35:56 2009
+      Integrated the module into Pspp's build environment. Added support for 
+      reading from an existing system file.
+
+0.4.3 Sat May 19 14:24:05 2007
+       - first release
+
+0.01  Fri Apr  6 14:13:45 2007
+       - original version; created by h2xs 1.23 with options
+               -A -n Pspp pspp/src/libpspp/version.h
+
diff --git a/perl-module/Examples.pod b/perl-module/Examples.pod
new file mode 100644 (file)
index 0000000..cb286ca
--- /dev/null
@@ -0,0 +1,146 @@
+=pod
+
+=head1 PSPP::Examples
+
+This page shows some simple examples of using the PSPP module.
+See L<PSPP> for details on each of the subroutines.
+
+=head2 A Simple example
+
+This example creates a system file called F<foo.sav>, containing one 
+variable called "id".  It contains no data.
+
+       use PSPP;
+
+       my $dict = PSPP::Dict->new ();
+       my $var = PSPP::Var->new ($dict, "id");
+
+       my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict);
+       $sysfile->close();
+
+
+=head2 A slightly more complex example
+
+In this example there are three variables, called "id", "name" and "dob".
+Their formats are F2.0, A80 and DATETIME17 respectively.
+
+       use PSPP;
+
+       my $dict = PSPP::Dict->new ();
+       PSPP::Var->new ($dict, "id",
+                  (fmt=>PSPP::Fmt::F, width=>2, decimals=>0) );
+       
+       PSPP::Var->new ($dict, "name", (fmt=>PSPP::Fmt::A, width=>80) );
+       PSPP::Var->new ($dict, "dob",  (fmt=>PSPP::Fmt::DATETIME) );
+
+       my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict);
+       $sysfile->close();
+
+=head2 Changing the properties of variables
+
+After a variable has been created, parameters may be set for it.
+
+       use PSPP;
+
+       my $dict = PSPP::Dict->new ();
+       my $var1 = PSPP::Var->new ($dict, "id");
+
+       $var1->set_label ("A unique identifier");
+       $var1->add_value_label (0, "Zero");
+       $var1->add_value_label (1, "One");
+
+
+=head2 Appending data to the file
+
+When a file is created, it contains no data.  Data is added by
+appending cases to the file.
+
+This example creates a file with 3 cases.
+
+       use PSPP;
+
+       my $dict = PSPP::Dict->new ();
+       PSPP::Var->new ($dict, "id", 
+          (fmt=>PSPP::Fmt::F, width=>2, decimals=>0) );
+
+       PSPP::Var->new ($dict, "name", (fmt=>PSPP::Fmt::A, width=>8) );
+
+       my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict);
+
+       $sysfile->append_case ( [1, "Alf"] );
+       $sysfile->append_case ( [2, "Bert"] );
+       $sysfile->append_case ( [3, "Charlie"] );
+
+       $sysfile->close();
+
+=head2  Variables with differing input and output formats
+
+By default,  a variable's output format corresponds to the input format.
+However, the output format may be changed after the variable has 
+been created.
+
+This example shows how  to create a DATETIME variable using the current time
+as its value.  Since pspp uses a different epoch to perl, the constant 
+PSPP::PERL_EPOCH needs to be added to the value returned from time(), in order 
+that it be correctly represented by pspp.
+
+       use PSPP;
+
+       my $dict = PSPP::Dict->new ();
+
+       my $var1 = PSPP::Var->new ($dict, "entrytime", 
+               (fmt=>PSPP::Fmt::F) );
+
+       $var1->set_output_format ( (fmt=>PSPP::Fmt::DATETIME, width=>20) );
+
+       my $sysfile = PSPP::Sysfile->new ("foo.sav", $dict);
+
+       my $now = time ();
+
+       $sysfile->append_case ( [ $now  + PSPP::PERL_EPOCH]  ) 
+               || die "Cant write case";
+       
+       $sysfile->close();
+
+=head2  Reading data
+
+Data can be read from a system file or other source:
+
+       use PSPP;
+
+       my $sf = PSPP::Reader->open ("foo.sav");
+
+       my $dict = $sf->get_dict ();
+
+
+Once opened, the dictionary can be used like any other.
+
+       for ($v = 0 ; $v < $dict->get_var_cnt() ; $v++)
+       {
+           my $var = $dict->get_var ($v);
+
+           # Print the variables
+           my $name = $var->get_name ();
+           my $label = $var->get_label ();
+           print "Var: $name, Label: $label\n";
+
+           # Retrieve and print the value labels
+           my $vl = $var->get_value_labels ();
+           print "$_: $vl->{$_}\n" for keys %$vl;
+       }
+
+
+Reading of data must be done sequentially using the C<get_next_case> method.
+
+       while (my $c = $sf->get_next_case () )
+       {
+           my $v;
+           for ($v = 0; $v < $dict->get_var_cnt(); $v++)
+           {
+               print "val$v: @$c[$v] ";
+           }
+           print "\n";
+       }
+
+
+=cut
\ No newline at end of file
diff --git a/perl-module/MANIFEST b/perl-module/MANIFEST
new file mode 100644 (file)
index 0000000..2546905
--- /dev/null
@@ -0,0 +1,13 @@
+Changes
+const-c.inc
+const-xs.inc
+COPYING
+Examples.pod
+lib/PSPP.pm
+Makefile.PL
+MANIFEST
+ppport.h
+PSPP.xs
+README
+t/Pspp.t
+typemap
diff --git a/perl-module/Makefile.PL b/perl-module/Makefile.PL
new file mode 100644 (file)
index 0000000..045b4e7
--- /dev/null
@@ -0,0 +1,52 @@
+use 5.008008;
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+
+do 'pspp-module-config' || do {
+    my $build = prompt ("Enter the location of the build directory of the configured pspp source:", "" );
+    my $src = $top_srcdir;
+
+    %Locations = (SourceDir => "$src", BuildDir => "$build");
+};
+
+WriteMakefile(
+    FULLPERL          => "PSPP_TEST_CMD=\"$Locations{BuildDir}/src/ui/terminal/pspp -B $Locations{SourceDir}/config\" \$(PERL)",
+    NAME              => 'PSPP',
+    DISTNAME          => 'PSPP-Perl',
+    VERSION_FROM      => "$Locations{BuildDir}/src/libpspp/version.c", 
+    PREREQ_PM         => {POSIX=>0}, 
+    PM                => {"lib/PSPP.pm", "\$(INST_LIBDIR)/PSPP.pm"},
+    ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
+      (ABSTRACT_FROM  => 'lib/PSPP.pm', # retrieve abstract from module
+       AUTHOR         => 'John Darrington <john@darrington.wattle.id.au>') : ()),
+    INC       => "-I $Locations{SourceDir}  -I $Locations{SourceDir}/src -I $Locations{SourceDir}/gl -I $Locations{BuildDir}/gl -I $Locations{BuildDir}", 
+    MYEXTLIB  => "$Locations{BuildDir}/src/.libs/libpspp-core.\$(DLEXT)",
+    MAN3PODS  => {"lib/PSPP.pm", "\$(INST_MAN3DIR)/PSPP.3pm",
+           "Examples.pod", "\$(INST_MAN3DIR)/PSPP::Examples.3pm"}
+);
+
+if  (eval {require ExtUtils::Constant; 1}) {
+  # If you edit these definitions to change the constants used by this module,
+  # you will need to use the generated const-c.inc and const-xs.inc
+  # files to replace their "fallback" counterparts before distributing your
+  # changes.
+  my @names = (qw());
+  ExtUtils::Constant::WriteConstants(
+                                     NAME         => 'PSPP',
+                                     NAMES        => \@names,
+                                     DEFAULT_TYPE => 'IV',
+                                     C_FILE       => 'const-c.inc',
+                                     XS_FILE      => 'const-xs.inc',
+                                  );
+
+}
+else {
+  use File::Copy;
+  use File::Spec;
+  foreach my $file ('const-c.inc', 'const-xs.inc') {
+    my $fallback = File::Spec->catfile('fallback', $file);
+    copy ($fallback, $file) or die "Can't copy $fallback to $file: $!";
+  }
+}
diff --git a/perl-module/PSPP.bs b/perl-module/PSPP.bs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/perl-module/PSPP.xs b/perl-module/PSPP.xs
new file mode 100644 (file)
index 0000000..237ae95
--- /dev/null
@@ -0,0 +1,694 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007, 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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include <config.h>
+
+#include "ppport.h"
+
+#include "minmax.h"
+#include <libpspp/message.h>
+#include <libpspp/version.h>
+#include <gl/xalloc.h>
+#include <data/dictionary.h>
+#include <data/case.h>
+#include <data/casereader.h>
+#include <data/variable.h>
+#include <data/attributes.h>
+#include <data/file-handle-def.h>
+#include <data/sys-file-writer.h>
+#include <data/sys-file-reader.h>
+#include <data/value.h>
+#include <data/value-labels.h>
+#include <data/format.h>
+#include <data/data-in.h>
+#include <string.h>
+
+typedef struct fmt_spec input_format ;
+typedef struct fmt_spec output_format ;
+
+
+/*  A thin wrapper around sfm_writer */
+struct sysfile_info
+{
+  bool opened;
+
+  /* A pointer to the writer. The writer is owned by the struct */
+  struct casewriter *writer;
+
+  /* A pointer to the dictionary. Owned externally */
+  const struct dictionary *dict;
+
+  /* The scalar containing the dictionary */
+  SV *dict_sv;
+};
+
+
+/*  A thin wrapper around sfm_reader */
+struct sysreader_info
+{
+  struct sfm_read_info opts;
+
+  /* A pointer to the reader. The reader is owned by the struct */
+  struct casereader *reader;
+
+  /* A pointer to the dictionary. */
+  struct dictionary *dict;
+};
+
+
+
+/*  A message handler which writes messages to PSPP::errstr */
+static void
+message_handler (const struct msg *m)
+{
+ SV *errstr = get_sv("PSPP::errstr", TRUE);
+ sv_setpv (errstr, m->text);
+}
+
+static int
+sysfile_close (struct sysfile_info *sfi)
+{
+  int retval ;
+  if ( ! sfi->opened )
+    return 0;
+
+  retval = casewriter_destroy (sfi->writer);
+  if (retval > 0 )
+    sfi->opened = false;
+
+  return retval;
+}
+
+static void
+scalar_to_value (union value *val, SV *scalar, const struct variable *var)
+{
+  if ( var_is_numeric (var))
+    {
+       if ( SvNOK (scalar) || SvIOK (scalar) )
+          val->f = SvNV (scalar);
+       else
+          val->f = SYSMIS;
+    }
+  else
+    {
+       STRLEN len;
+       const char *p = SvPV (scalar, len);
+       memset (val->s, ' ', var_get_width (var));
+       memcpy (val->s, p, len);
+    }
+}
+
+
+static SV *
+value_to_scalar (const union value *val, const struct variable *var)
+{
+  if ( var_is_numeric (var))
+    {
+      if ( var_is_value_missing (var, val, MV_SYSTEM))
+       return newSVpvn ("", 0);
+
+      return newSVnv (val->f);
+    }
+  else
+    return newSVpvn (val->s, var_get_width (var));
+}
+
+
+static void
+var_set_input_format (struct variable *v, input_format ip_fmt)
+{
+  struct fmt_spec *if_copy = malloc (sizeof (*if_copy));
+  memcpy (if_copy, &ip_fmt, sizeof (ip_fmt));
+  var_attach_aux (v, if_copy, var_dtor_free);
+}
+
+static union value *
+make_value_from_scalar (SV *val, const struct variable *var)
+{
+ union value *uv = value_create (var_get_width (var));
+ scalar_to_value (uv, val, var);
+ return uv;
+}
+
+
+MODULE = PSPP
+
+MODULE = PSPP          PACKAGE = PSPP
+
+void
+onBoot (ver)
+ const char *ver
+CODE:
+ assert (0 == strcmp (ver, bare_version));
+ msg_init (NULL, message_handler);
+ settings_init (0, 0);
+ fh_init ();
+
+SV *
+format_value (val, var)
+ SV *val
+ struct variable *var
+CODE:
+ SV *ret;
+ const struct fmt_spec *fmt = var_get_print_format (var);
+ union value *uv = make_value_from_scalar (val, var);
+ char *s;
+ s = malloc (fmt->w);
+ memset (s, '\0', fmt->w);
+ data_out (uv, fmt, s);
+ free (uv);
+ ret = newSVpv (s, fmt->w);
+ free (s);
+ RETVAL = ret;
+ OUTPUT:
+RETVAL
+
+
+int
+value_is_missing (val, var)
+ SV *val
+ struct variable *var
+CODE:
+ union value *uv = make_value_from_scalar (val, var);
+ int ret = var_is_value_missing (var, uv, MV_ANY);
+ free (uv);
+ RETVAL = ret;
+ OUTPUT:
+RETVAL
+
+
+
+MODULE = PSPP          PACKAGE = PSPP::Dict
+
+struct dictionary *
+pxs_dict_new()
+CODE:
+ RETVAL = dict_create ();
+OUTPUT:
+ RETVAL
+
+
+void
+DESTROY (dict)
+ struct dictionary *dict
+CODE:
+ dict_destroy (dict);
+
+
+int
+get_var_cnt (dict)
+ struct dictionary *dict
+CODE:
+ RETVAL = dict_get_var_cnt (dict);
+OUTPUT:
+RETVAL
+
+void
+set_label (dict, label)
+ struct dictionary *dict
+ char *label
+CODE:
+ dict_set_label (dict, label);
+
+void
+set_documents (dict, docs)
+ struct dictionary *dict
+ char *docs
+CODE:
+ dict_set_documents (dict, docs);
+
+
+void
+add_document (dict, doc)
+ struct dictionary *dict
+ char *doc
+CODE:
+ dict_add_document_line (dict, doc);
+
+
+void
+clear_documents (dict)
+ struct dictionary *dict
+CODE:
+ dict_clear_documents (dict);
+
+
+void
+set_weight (dict, var)
+ struct dictionary *dict
+ struct variable *var
+CODE:
+ dict_set_weight (dict, var);
+
+
+struct variable *
+pxs_get_variable (dict, idx)
+ struct dictionary *dict
+ SV *idx
+INIT:
+ SV *errstr = get_sv("PSPP::errstr", TRUE);
+ sv_setpv (errstr, "");
+ if ( SvIV (idx) >= dict_get_var_cnt (dict))
+  {
+    sv_setpv (errstr, "The dictionary doesn't have that many variables.");
+    XSRETURN_UNDEF;
+  }
+CODE:
+ RETVAL = dict_get_var (dict, SvIV (idx));
+ OUTPUT:
+RETVAL
+
+
+struct variable *
+pxs_get_var_by_name (dict, name)
+ struct dictionary *dict
+ const char *name
+INIT:
+ SV *errstr = get_sv("PSPP::errstr", TRUE);
+ sv_setpv (errstr, "");
+CODE:
+ struct variable *var = dict_lookup_var (dict, name);
+ if ( ! var )
+      sv_setpv (errstr, "No such variable.");
+ RETVAL = var;
+ OUTPUT:
+RETVAL
+
+
+MODULE = PSPP          PACKAGE = PSPP::Var
+
+
+struct variable *
+pxs_dict_create_var (dict, name, ip_fmt)
+ struct dictionary * dict
+ char *name
+ input_format ip_fmt
+INIT:
+ SV *errstr = get_sv("PSPP::errstr", TRUE);
+ sv_setpv (errstr, "");
+ if ( ! var_is_plausible_name (name, false))
+  {
+    sv_setpv (errstr, "The variable name is not valid.");
+    XSRETURN_UNDEF;
+  }
+CODE:
+ struct fmt_spec op_fmt;
+
+ struct variable *v;
+ op_fmt = fmt_for_output_from_input (&ip_fmt);
+ v = dict_create_var (dict, name,
+       fmt_is_string (op_fmt.type) ? op_fmt.w : 0);
+ if ( NULL == v )
+  {
+    sv_setpv (errstr, "The variable could not be created (probably already exists).");
+    XSRETURN_UNDEF;
+  }
+ var_set_both_formats (v, &op_fmt);
+ var_set_input_format (v, ip_fmt);
+ RETVAL = v;
+OUTPUT:
+ RETVAL
+
+
+int
+set_missing_values (var, v1, ...)
+ struct variable *var;
+ SV *v1;
+INIT:
+ int i;
+ union value val[3];
+
+ if ( items > 4 )
+  croak ("No more than 3 missing values are permitted");
+
+ for (i = 0; i < items - 1; ++i)
+   scalar_to_value (&val[i], ST(i+1), var);
+CODE:
+ struct missing_values mv;
+ mv_init (&mv, var_get_width (var));
+ for (i = 0 ; i < items - 1; ++i )
+   mv_add_value (&mv, &val[i]);
+ var_set_missing_values (var, &mv);
+
+
+void
+set_label (var, label)
+ struct variable *var;
+ char *label
+CODE:
+  var_set_label (var, label);
+
+
+void
+clear_value_labels (var)
+ struct variable *var;
+CODE:
+ var_clear_value_labels (var);
+
+void
+pxs_set_write_format (var, fmt)
+ struct variable *var
+ output_format fmt
+CODE:
+ var_set_write_format (var, &fmt);
+
+
+void
+pxs_set_print_format (var, fmt)
+ struct variable *var
+ output_format fmt
+CODE:
+ var_set_print_format (var, &fmt);
+
+void
+pxs_set_output_format (var, fmt)
+ struct variable *var
+ output_format fmt
+CODE:
+ var_set_both_formats (var, &fmt);
+
+
+int
+add_value_label (var, key, label)
+ struct variable *var
+ SV *key
+ char *label
+INIT:
+ SV *errstr = get_sv("PSPP::errstr", TRUE);
+ sv_setpv (errstr, "");
+CODE:
+ union value the_value;
+
+ if ( var_is_numeric (var))
+ {
+  if ( ! looks_like_number (key))
+    {
+      sv_setpv (errstr, "Cannot add label with string key to a numeric variable");
+      XSRETURN_IV (0);
+    }
+  the_value.f = SvNV (key);
+ }
+ else
+ {
+   if ( var_is_long_string (var) )
+     {
+      sv_setpv (errstr, "Cannot add label to a long string variable");
+      XSRETURN_IV (0);
+     }
+  strncpy (the_value.s, SvPV_nolen(key), MAX_SHORT_STRING);
+ }
+ if (! var_add_value_label (var, &the_value, label) )
+ {
+   sv_setpv (errstr, "Something went wrong");
+   XSRETURN_IV (0);
+ }
+ XSRETURN_IV (1);
+
+
+SV *
+get_attributes (var)
+ struct variable *var
+CODE:
+ HV *attrhash = (HV *) sv_2mortal ((SV *) newHV());
+
+ struct attrset *as = var_get_attributes (var);
+
+ if ( as )
+   {
+     struct attrset_iterator iter;
+     struct attribute *attr;
+
+     for (attr = attrset_first (as, &iter);
+         attr;
+         attr = attrset_next (as, &iter))
+       {
+        int i;
+        const char *name = attribute_get_name (attr);
+
+        AV *values = newAV ();
+
+        for (i = 0 ; i < attribute_get_n_values (attr); ++i )
+          {
+            const char *value = attribute_get_value (attr, i);
+            av_push (values, newSVpv (value, 0));
+          }
+
+        hv_store (attrhash, name, strlen (name),
+                  newRV_noinc ((SV*) values), 0);
+       }
+   }
+
+ RETVAL = newRV ((SV *) attrhash);
+ OUTPUT:
+RETVAL
+
+
+const char *
+get_name (var)
+ struct variable * var
+CODE:
+ RETVAL = var_get_name (var);
+ OUTPUT:
+RETVAL
+
+
+const char *
+get_label (var)
+ struct variable * var
+CODE:
+ RETVAL = var_get_label (var);
+ OUTPUT:
+RETVAL
+
+
+SV *
+get_value_labels (var)
+ struct variable *var
+CODE:
+ HV *labelhash = (HV *) sv_2mortal ((SV *) newHV());
+ struct val_lab *vl;
+ struct val_labs_iterator *viter = NULL;
+ const struct val_labs *labels = var_get_value_labels (var);
+
+ if ( labels )
+   {
+     for (vl = val_labs_first (labels, &viter);
+         vl;
+         vl = val_labs_next (labels, &viter))
+       {
+        SV *sv = value_to_scalar (&vl->value, var);
+        STRLEN len;
+        const char *s = SvPV (sv, len);
+        hv_store (labelhash, s, len, newSVpv (vl->label, 0), 0);
+       }
+   }
+
+ RETVAL = newRV ((SV *) labelhash);
+ OUTPUT:
+RETVAL
+
+
+
+MODULE = PSPP          PACKAGE = PSPP::Sysfile
+
+
+struct sysfile_info *
+pxs_create_sysfile (name, dict_ref, opts_hr)
+ char *name
+ SV *dict_ref
+ SV *opts_hr
+INIT:
+ SV *dict_sv = SvRV (dict_ref);
+ struct dictionary *dict = (void *) SvIV (dict_sv);
+ struct sfm_write_options opts;
+ if (!SvROK (opts_hr))
+  {
+    opts = sfm_writer_default_options ();
+  }
+ else
+  {
+    HV *opt_h = (HV *) SvRV (opts_hr);
+    SV** readonly = hv_fetch(opt_h, "readonly", 8, 0);
+    SV** compress = hv_fetch(opt_h, "compress", 8, 0);
+    SV** version = hv_fetch(opt_h, "version", 7, 0);
+
+    opts.create_writeable = readonly ? ! SvIV (*readonly) : true;
+    opts.compress = compress ? SvIV (*compress) : false;
+    opts.version = version ? SvIV (*version) : 3 ;
+  }
+CODE:
+ struct file_handle *fh =
+  fh_create_file (NULL, name, fh_default_properties () );
+ struct sysfile_info *sfi = xmalloc (sizeof (*sfi));
+ sfi->writer = sfm_open_writer (fh, dict, opts);
+ sfi->dict = dict;
+ sfi->opened = true;
+ sfi->dict_sv = dict_sv;
+ SvREFCNT_inc (sfi->dict_sv);
+ RETVAL = sfi;
+ OUTPUT:
+RETVAL
+
+int
+close (sfi)
+ struct sysfile_info *sfi
+CODE:
+ RETVAL = sysfile_close (sfi);
+OUTPUT:
+ RETVAL
+
+void
+DESTROY (sfi)
+ struct sysfile_info *sfi
+CODE:
+ sysfile_close (sfi);
+ SvREFCNT_dec (sfi->dict_sv);
+ free (sfi);
+
+int
+append_case (sfi, ccase)
+ struct sysfile_info *sfi
+ SV *ccase
+INIT:
+ SV *errstr = get_sv("PSPP::errstr", TRUE);
+ sv_setpv (errstr, "");
+ if ( (!SvROK(ccase)))
+  {
+    XSRETURN_UNDEF;
+  }
+CODE:
+ int i = 0;
+ AV *av_case = (AV*) SvRV (ccase);
+
+ const struct variable **vv;
+ size_t nv;
+ struct ccase *c;
+ SV *sv;
+
+ if ( av_len (av_case) >= dict_get_var_cnt (sfi->dict))
+   XSRETURN_UNDEF;
+
+ c =  case_create (dict_get_next_value_idx (sfi->dict));
+
+ dict_get_vars (sfi->dict, &vv, &nv, 1u << DC_ORDINARY | 1u << DC_SYSTEM);
+
+ for (sv = av_shift (av_case); SvOK (sv);  sv = av_shift (av_case))
+ {
+    const struct variable *v = vv[i++];
+    const struct fmt_spec *ifmt = var_get_aux (v);
+
+    /* If an input format has been set, then use it.
+       Otherwise just convert the raw value.
+    */
+    if ( ifmt )
+      {
+       struct substring ss = ss_cstr (SvPV_nolen (sv));
+       if ( ! data_in (ss, LEGACY_NATIVE, ifmt->type, 0, 0, 0,
+                       case_data_rw (c, v),
+                       var_get_width (v)) )
+         {
+           RETVAL = 0;
+           goto finish;
+         }
+      }
+    else
+      {
+       scalar_to_value (case_data_rw (c, v), sv, v);
+      }
+ }
+
+ /* The remaining variables must be sysmis or blank string */
+ while (i < dict_get_var_cnt (sfi->dict))
+ {
+   const struct variable *v = vv[i++];
+   union value *val = case_data_rw (c, v);
+   if ( var_is_numeric (v))
+       val->f = SYSMIS;
+   else
+       memset (val->s, ' ', var_get_width (v));
+ }
+ RETVAL = casewriter_write (sfi->writer, c);
+ finish:
+ free (vv);
+OUTPUT:
+ RETVAL
+
+
+\f
+
+MODULE = PSPP          PACKAGE = PSPP::Reader
+
+struct sysreader_info *
+pxs_open_sysfile (name)
+ char * name
+CODE:
+ struct casereader *reader;
+ struct sysreader_info *sri = NULL;
+ struct file_handle *fh =
+        fh_create_file (NULL, name, fh_default_properties () );
+
+ sri = xmalloc (sizeof (*sri));
+ sri->reader = sfm_open_reader (fh, &sri->dict, &sri->opts);
+
+ if ( NULL == sri->reader)
+ {
+   free (sri);
+   sri = NULL;
+ }
+
+ RETVAL = sri;
+ OUTPUT:
+RETVAL
+
+
+struct dictionary *
+pxs_get_dict (reader)
+ struct sysreader_info *reader;
+CODE:
+ RETVAL = reader->dict;
+ OUTPUT:
+RETVAL
+
+
+void
+get_next_case (sfr)
+ struct sysreader_info *sfr;
+PPCODE:
+ struct ccase *c;
+
+ if (c = casereader_read (sfr->reader))
+ {
+  int v;
+
+  EXTEND (SP, dict_get_var_cnt (sfr->dict));
+  for (v = 0; v < dict_get_var_cnt (sfr->dict); ++v )
+    {
+      const struct variable *var = dict_get_var (sfr->dict, v);
+      const union value *val = case_data (c, var);
+
+      PUSHs (sv_2mortal (value_to_scalar (val, var)));
+    }
+
+  case_unref (c);
+ }
diff --git a/perl-module/README b/perl-module/README
new file mode 100644 (file)
index 0000000..74d6220
--- /dev/null
@@ -0,0 +1,48 @@
+PSPP version 0.7
+================
+
+This module provides an interface allowing perl programs to create pspp 
+system files.
+
+INSTALLATION
+
+To install you must have first installed and built pspp 0.7.2 or
+later.  Pspp is not required to use this module, only to install
+it. 
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+
+
+DEPENDENCIES
+
+This module requires the POSIX module.
+
+The modules Test::More, Text::Diff, File::Temp and the pspp source are
+required during installation, but are not needed to run the module.
+
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2007, 2009 by 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 the Free Software Foundation; either version 2 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, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
diff --git a/perl-module/automake.mk b/perl-module/automake.mk
new file mode 100644 (file)
index 0000000..9527dbb
--- /dev/null
@@ -0,0 +1,70 @@
+## Process this file with automake to produce Makefile.in  -*- makefile -*-
+
+# PSPP
+
+module_sources = \
+ perl-module/Changes \
+ perl-module/COPYING \
+ perl-module/Examples.pod \
+ perl-module/Makefile.PL \
+ perl-module/MANIFEST \
+ perl-module/ppport.h \
+ perl-module/PSPP.xs \
+ perl-module/README \
+ perl-module/typemap \
+ perl-module/lib/PSPP.pm \
+ perl-module/t/Pspp.t
+
+perl-module/pspp-module-config: Makefile
+       target=`mktemp`;\
+       echo '%Locations = (' > $$target ;\
+       printf "  SourceDir => '" >> $$target ;\
+       (cd $(top_srcdir) && echo `pwd`\', ) >> $$target ;\
+       printf "  BuildDir => '" >> $$target ;\
+       (cd $(top_builddir) && echo `pwd`\' ) >> $$target ;\
+       echo ');' >> $$target ;\
+       cp $$target $(top_builddir)/perl-module/pspp-module-config
+
+perl-module/Makefile: perl-module/Makefile.PL perl-module/pspp-module-config
+       cd perl-module && $(PERL) Makefile.PL PREFIX=$(prefix)
+
+perl-module/PSPP-Perl-$(VERSION).tar.gz: $(module_sources)
+       $(RM) $@
+       cd perl-module && $(MAKE) $(AM_MAKEFLAGS) tardist
+
+PHONY += module-make
+module-make: perl-module/Makefile src/libpspp-core.la
+       cd perl-module && $(MAKE) $(AM_MAKEFLAGS)
+
+all-local: 
+       if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \
+        for f in $(module_sources); do \
+         destdir=`dirname $$f` ;\
+         mkdir -p $$destdir ;\
+         if test "$(top_srcdir)/$$f" -nt "$(top_builddir)/$$f" ; then \
+                cp $(top_srcdir)/$$f $$destdir ; \
+                echo cp $(top_srcdir)/$$f $$destdir ; \
+         fi ; \
+        done \
+       fi
+       $(MAKE) $(AM_MAKEFLAGS) module-make perl-module/PSPP-Perl-$(VERSION).tar.gz
+
+check-local:
+       loc=`pwd` ; cd $(top_builddir)/src/.libs ; llp=`pwd` ; cd $$loc ;  \
+       LANG=C LD_LIBRARY_PATH=$$llp sh -c "cd perl-module && $(MAKE) $(AM_MAKEFLAGS) test"
+
+
+clean-local:
+       cd perl-module && $(MAKE) $(AM_MAKEFLAGS) clean || true
+       if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \
+         $(RM) $(module_sources) ; \
+       fi
+       $(RM) perl-module/Makefile.old
+
+CLEANFILES += \
+        perl-module/PSPP-Perl-$(VERSION).tar.gz \
+       perl-module/pspp-module-config \
+       perl-module/const-c.inc \
+       perl-module/const-xs.inc 
+
+EXTRA_DIST +=  $(module_sources)
diff --git a/perl-module/lib/PSPP.pm b/perl-module/lib/PSPP.pm
new file mode 100644 (file)
index 0000000..3cb9f3b
--- /dev/null
@@ -0,0 +1,550 @@
+use 5.008008;
+use strict;
+use warnings;
+
+=head1 NAME
+
+PSPP-Perl - Perl extension to PSPP
+
+=head1 SYNOPSIS
+
+  use PSPP;
+
+=head1 DESCRIPTION
+
+PSPP-Perl provides an interface to the libraries used by pspp to read and
+write system files.  
+
+=head1 EXPORT
+
+None by default.
+
+=cut
+BEGIN {
+       $PSPP::VERSION='0.7.2';
+       require XSLoader;
+       XSLoader::load('PSPP', $PSPP::VERSION);
+}
+
+PSPP::onBoot($PSPP::VERSION);
+
+=pod
+
+=head1 PROGRAMMER'S INTERFACE
+
+The subroutines in this package return zero or unref on error.
+When errors occur, a string describing the error is written 
+to C<$PSPP::errstr>. 
+
+=cut
+
+package PSPP;
+use POSIX ;
+
+use constant { SYSMIS => -(POSIX::DBL_MAX), 
+              PERL_EPOCH => 12219379200 # Number of seconds between 
+                   # 14th October 1582
+                  # and 
+                  # 1st January 1970 
+              };
+
+
+
+package PSPP::Dict;
+
+=pod
+
+=head2 PSPP::Dict::new
+
+Creates a new dictionary.  This returned dictionary will be empty.
+Returns undef on failure.
+
+=head3 set_documents ($string)
+
+Sets the documents (comments) to C<string>.
+
+=head3 add_document ($string)
+
+Appends C<string> to the documents.
+
+=head3 clear_documents ()
+
+Removes all documents.
+
+=head3 set_weight ($var)
+
+Sets the weighting variable to C<var>.
+
+=cut
+
+sub new
+{
+    my $class = shift;
+    my $self = pxs_dict_new ();
+    bless ($self, $class);
+    return $self;
+}
+
+=pod
+
+=head3 get_var_cnt ()
+
+Returns the number of variables in the dictionary.
+
+=head3 get_var ($idx)
+
+Returns the C<idx>th variable from the dictionary.
+Returns undef if C<idx> is greater than or equal to the number
+of variables in the dictionary.
+
+=cut
+
+sub get_var
+{
+    my $dict = shift;
+    my $idx = shift;
+    my $var = pxs_get_variable ($dict, $idx);
+
+    if ( ref $var ) 
+    {
+       bless ($var, "PSPP::Var");
+    }
+    return $var;
+}
+
+=pod
+
+=head3 get_var_by_name ($name)
+
+Returns the variable from the dictionary whose name is C<name>.
+If there is no such variable, a null reference will be returned.
+
+=cut
+
+sub get_var_by_name
+{
+    my $dict = shift;
+    my $name = shift;
+    my $var = pxs_get_var_by_name ($dict, $name);
+
+    if ( ref $var ) 
+    {
+       bless ($var, "PSPP::Var");
+    }
+    return $var;
+}
+
+
+package PSPP::Fmt;
+
+=pod
+
+=head2 PSPP::Fmt
+
+Contains constants used to denote variable format types.  
+The identifiers are the same as  those used in pspp to denote formats.
+For  example C<PSPP::Fmt::F> defines floating point format, and
+C<PSPP::Fmt::A> denotes string format.
+
+=cut
+
+# These must correspond to the values in src/data/format.h
+use constant {
+    F =>        0,
+    COMMA =>    1,
+    DOT =>      2, 
+    DOLLAR =>   3, 
+    PCT =>      4, 
+    E =>        5, 
+    CCA =>      6, 
+    CCB =>      7, 
+    CCC =>      8, 
+    CCD =>      9, 
+    CCE =>      10, 
+    N =>        11, 
+    Z =>        12, 
+    P =>        13, 
+    PK =>       14, 
+    IB =>       15, 
+    PIB =>      16, 
+    PIBHEX =>   17, 
+    RB =>       18, 
+    RBHEX =>    19, 
+    DATE =>     20, 
+    ADATE =>    21, 
+    EDATE =>    22, 
+    JDATE =>    23, 
+    SDATE =>    24, 
+    QYR =>      25, 
+    MOYR =>     26, 
+    WKYR =>     27, 
+    DATETIME => 28, 
+    TIME =>     29, 
+    DTIME =>    30, 
+    WKDAY =>    31, 
+    MONTH =>    32, 
+    A =>        33, 
+    AHEX =>     34
+};
+
+
+=head2 PSPP::Var
+
+=cut
+
+package PSPP::Var;
+
+=head3 new ($dict, $name, %input_fmt)
+
+Creates and returns a new variable in the dictionary C<dict>.  The 
+new variable will have the name C<name>.
+The input format is set by the C<input_fmt> parameter 
+(See L</PSPP::Fmt>).
+By default, the write and print formats are the same as the input format.
+The write and print formats may be changed (See L</set_write_format>), 
+L</set_print_format>).  The input format may not be changed after
+the variable has been created.
+If the variable cannot be created, undef is returned.
+
+=cut
+
+sub new
+{
+    my $class = shift;
+    my $dict = shift;
+    my $name = shift;
+    my %format = @_;
+    my $self = pxs_dict_create_var ($dict, $name, \%format);
+    if ( ref $self ) 
+    {
+       bless ($self, $class);
+    }
+    return $self;
+}
+
+=pod
+
+=head3 set_label ($label)
+
+Sets the variable label to C<label>.
+
+
+=cut
+
+=pod
+
+=head3 set_write_format (%fmt)
+
+Sets the write format to C<fmt>. <fmt> is a hash containing the keys:
+
+=over 2
+
+=item FMT
+
+A constant denoting the format type.  See L</PSPP::Fmt>.
+
+=item decimals
+
+An integer denoting the number of decimal places for the format.
+
+=item width
+
+An integer denoting the number of width of the format.
+
+=back
+
+On error the subroutine returns zero.
+
+=cut
+
+sub set_write_format
+{
+    my $var = shift;
+    my %format = @_;
+    pxs_set_write_format ($var, \%format);
+}
+
+=pod
+
+=head3 set_print_format (%fmt)
+
+Sets the print format to C<fmt>.
+On error the subroutine returns zero.
+
+=cut
+
+sub set_print_format
+{
+    my $var = shift;
+    my %format = @_;
+    pxs_set_print_format ($var, \%format);
+}
+
+=pod
+
+=head3 set_output_format (%fmt)
+
+Sets the write and print formats to C<fmt>.  This is the same as
+calling set_write_format followed by set_print_format.
+On error the subroutine returns zero.
+
+=cut
+
+
+sub set_output_format
+{
+    my $var = shift;
+    my %format = @_;
+    pxs_set_output_format ($var, \%format);
+}
+
+=pod
+
+=head3 clear_value_labels ()
+
+Removes all value labels from the variable.
+
+=cut
+
+
+=pod
+
+=head3 add_value_label ($key, $label)
+
+Adds the value label C<label> to the variable for the value C<key>.
+On error the subroutine returns zero.
+
+=head3 add_value_labels (@array)
+
+=cut
+
+sub add_value_labels
+{
+    my $var = shift;
+    my %values = @_;
+    my @li;
+
+    my $n = 0;
+    while ( @li = each %values ) 
+    {
+       if ( $var->add_value_label ($li[0], "$li[1]") ) 
+       {
+           $n++;
+       }
+    }
+
+    return $n;
+}
+
+=pod
+
+=head3 set_value_labels ($key, $value)
+
+C<Set_value_labels> is identical to calling L</clear_value_labels>
+followed by L</add_value_labels>.
+On error the subroutine returns zero.
+
+=cut
+
+sub set_value_labels
+{
+    my $self = shift;
+    my %labels = @_;
+    $self->clear_value_labels () ;
+    $self->add_value_labels (%labels);
+}
+
+=pod
+
+=head3 set_missing_values ($val1 [, $val2[, $val3] ])
+
+Sets the missing values for the variable.  
+No more than three missing values may be specified.
+
+=head3 get_attributes()
+
+Returns a reference to a hash of the custom variable attributes.
+Each value of the hash is a reference to an array containing the 
+attribute values.
+
+=head3 get_name ()
+
+Returns the name of the variable.
+
+=head3 get_label ()
+
+Returns the label of the variable or undef if there is no label.
+
+=head3 get_value_labels ()
+
+Returns a reference to a hash containing the value labels for the variable.
+The hash is keyed by data values which correpond to the labels.
+
+=cut
+
+package PSPP::Sysfile;
+
+=pod
+
+=head2 PSPP::Sysfile
+
+=head3 new ($filename, $dict [,%opts])
+
+Creates a new system file from the dictionary C<dict>.  The file will
+be written to the file called C<filename>.
+C<opt>, if specified, is a hash containing optional parameters for the
+system file.  Currently, the only supported parameter is
+C<compress>. If C<compress> is non zero, then the system file written
+will be in the compressed format.
+On error, undef is returned.
+
+
+=head3 append_case (@case)
+
+Appends a case to the system file.
+C<Case> is an array of scalars, each of which are the values of 
+the variables in the dictionary corresponding to the system file.
+The special value C<PSPP::SYSMIS> may be used to indicate that a value
+is system missing.
+If the array contains less elements than variables in the dictionary,
+remaining values will be set to system missing.
+
+=cut
+
+sub new
+{
+    my $class = shift;
+    my $filename = shift;
+    my $dict = shift;
+    my $opts = shift;
+
+    my $self  = pxs_create_sysfile ($filename, $dict, $opts);
+
+    if ( ref $self ) 
+    {
+       bless ($self, $class);
+    }
+    return $self;
+}
+
+=pod
+
+=head3 close ()
+
+Closes the system file.
+
+This subroutine closes the system file and flushes it to disk.  No
+further cases may be written once the file has been closed.
+The system file will be automatically closed when it goes out of scope.
+
+=cut
+
+package PSPP::Reader;
+
+=pod
+
+=head2 PSPP::Reader
+
+=cut
+
+sub open
+{
+    my $class = shift;
+    my $filename = shift;
+
+    my $self  = pxs_open_sysfile ($filename);
+
+    if ( ref $self ) 
+    {
+       bless ($self, $class);
+    }
+    return $self;
+}
+
+=pod
+
+=head3 open ($filename)
+
+Opens a system file for reading.
+
+Open is used to read data from an existing system file. 
+It creates and returns a PSPP::Reader object which can be used to read 
+data and dictionary information from C<filename>.
+
+=cut
+
+sub get_dict
+{
+    my $reader = shift;
+
+    my $dict = pxs_get_dict ($reader);
+
+    bless ($dict, "PSPP::Dict");
+
+    return $dict;
+}
+
+=pod
+
+=head3 get_dict ()
+
+Returns the dictionary associated with the reader.
+
+=head3 get_next_case ()
+
+Retrieves the next case from the reader.
+This method returns an array of scalars, each of which are the values of 
+the data in the system file.
+The first call to C<get_next_case> after C<open> has been called retrieves
+the first case in the system file.  Each subsequent call retrieves the next
+case.  If there are no more cases to be read, the function returns an empty
+list.
+
+If the case contains system missing values, these values are set to the 
+empty string.
+
+=head2 Miscellaneous subroutines
+
+The following subroutines provide (hopefully) useful information about the 
+values retrieved from a reader.
+
+=head3 PSPP::format_value ($value, $variable)
+
+Returns a scalar containing a string representing C<value> formatted accoring 
+to the print format of C<variable>.
+In the most common ussage,  C<value> should be a value of C<variable>.
+
+
+=head3 PSPP::value_is_missing ($value, $variable)
+
+Returns non-zero if C<value> is either system missing, or if it matches the 
+user missing criteria for C<variable>.
+
+=cut
+
+1;
+__END__
+
+
+=head1 AUTHOR
+
+John Darrington, E<lt>john@darrington.wattle.id.auE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2007, 2008, 2009 by 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
+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/>.
+
+=cut
diff --git a/perl-module/ppport.h b/perl-module/ppport.h
new file mode 100644 (file)
index 0000000..181fb2b
--- /dev/null
@@ -0,0 +1,4954 @@
+#if 0
+<<'SKIP';
+#endif
+/*
+----------------------------------------------------------------------
+
+    ppport.h -- Perl/Pollution/Portability Version 3.06_01
+
+    Automatically created by Devel::PPPort running under
+    perl 5.008008 on Fri Apr  6 14:13:45 2007.
+
+    Do NOT edit this file directly! -- Edit PPPort_pm.PL and the
+    includes in parts/inc/ instead.
+
+    Use 'perldoc ppport.h' to view the documentation below.
+
+----------------------------------------------------------------------
+
+SKIP
+
+=pod
+
+=head1 NAME
+
+ppport.h - Perl/Pollution/Portability version 3.06_01
+
+=head1 SYNOPSIS
+
+  perl ppport.h [options] [source files]
+
+  Searches current directory for files if no [source files] are given
+
+  --help                      show short help
+
+  --patch=file                write one patch file with changes
+  --copy=suffix               write changed copies with suffix
+  --diff=program              use diff program and options
+
+  --compat-version=version    provide compatibility with Perl version
+  --cplusplus                 accept C++ comments
+
+  --quiet                     don't output anything except fatal errors
+  --nodiag                    don't show diagnostics
+  --nohints                   don't show hints
+  --nochanges                 don't suggest changes
+  --nofilter                  don't filter input files
+
+  --list-provided             list provided API
+  --list-unsupported          list unsupported API
+  --api-info=name             show Perl API portability information
+
+=head1 COMPATIBILITY
+
+This version of F<ppport.h> is designed to support operation with Perl
+installations back to 5.003, and has been tested up to 5.9.3.
+
+=head1 OPTIONS
+
+=head2 --help
+
+Display a brief usage summary.
+
+=head2 --patch=I<file>
+
+If this option is given, a single patch file will be created if
+any changes are suggested. This requires a working diff program
+to be installed on your system.
+
+=head2 --copy=I<suffix>
+
+If this option is given, a copy of each file will be saved with
+the given suffix that contains the suggested changes. This does
+not require any external programs.
+
+If neither C<--patch> or C<--copy> are given, the default is to
+simply print the diffs for each file. This requires either
+C<Text::Diff> or a C<diff> program to be installed.
+
+=head2 --diff=I<program>
+
+Manually set the diff program and options to use. The default
+is to use C<Text::Diff>, when installed, and output unified
+context diffs.
+
+=head2 --compat-version=I<version>
+
+Tell F<ppport.h> to check for compatibility with the given
+Perl version. The default is to check for compatibility with Perl
+version 5.003. You can use this option to reduce the output
+of F<ppport.h> if you intend to be backward compatible only
+up to a certain Perl version.
+
+=head2 --cplusplus
+
+Usually, F<ppport.h> will detect C++ style comments and
+replace them with C style comments for portability reasons.
+Using this option instructs F<ppport.h> to leave C++
+comments untouched.
+
+=head2 --quiet
+
+Be quiet. Don't print anything except fatal errors.
+
+=head2 --nodiag
+
+Don't output any diagnostic messages. Only portability
+alerts will be printed.
+
+=head2 --nohints
+
+Don't output any hints. Hints often contain useful portability
+notes.
+
+=head2 --nochanges
+
+Don't suggest any changes. Only give diagnostic output and hints
+unless these are also deactivated.
+
+=head2 --nofilter
+
+Don't filter the list of input files. By default, files not looking
+like source code (i.e. not *.xs, *.c, *.cc, *.cpp or *.h) are skipped.
+
+=head2 --list-provided
+
+Lists the API elements for which compatibility is provided by
+F<ppport.h>. Also lists if it must be explicitly requested,
+if it has dependencies, and if there are hints for it.
+
+=head2 --list-unsupported
+
+Lists the API elements that are known not to be supported by
+F<ppport.h> and below which version of Perl they probably
+won't be available or work.
+
+=head2 --api-info=I<name>
+
+Show portability information for API elements matching I<name>.
+If I<name> is surrounded by slashes, it is interpreted as a regular
+expression.
+
+=head1 DESCRIPTION
+
+In order for a Perl extension (XS) module to be as portable as possible
+across differing versions of Perl itself, certain steps need to be taken.
+
+=over 4
+
+=item *
+
+Including this header is the first major one. This alone will give you
+access to a large part of the Perl API that hasn't been available in
+earlier Perl releases. Use
+
+    perl ppport.h --list-provided
+
+to see which API elements are provided by ppport.h.
+
+=item *
+
+You should avoid using deprecated parts of the API. For example, using
+global Perl variables without the C<PL_> prefix is deprecated. Also,
+some API functions used to have a C<perl_> prefix. Using this form is
+also deprecated. You can safely use the supported API, as F<ppport.h>
+will provide wrappers for older Perl versions.
+
+=item *
+
+If you use one of a few functions that were not present in earlier
+versions of Perl, and that can't be provided using a macro, you have
+to explicitly request support for these functions by adding one or
+more C<#define>s in your source code before the inclusion of F<ppport.h>.
+
+These functions will be marked C<explicit> in the list shown by
+C<--list-provided>.
+
+Depending on whether you module has a single or multiple files that
+use such functions, you want either C<static> or global variants.
+
+For a C<static> function, use:
+
+    #define NEED_function
+
+For a global function, use:
+
+    #define NEED_function_GLOBAL
+
+Note that you mustn't have more than one global request for one
+function in your project.
+
+    Function                  Static Request               Global Request
+    -----------------------------------------------------------------------------------------
+    eval_pv()                 NEED_eval_pv                 NEED_eval_pv_GLOBAL
+    grok_bin()                NEED_grok_bin                NEED_grok_bin_GLOBAL
+    grok_hex()                NEED_grok_hex                NEED_grok_hex_GLOBAL
+    grok_number()             NEED_grok_number             NEED_grok_number_GLOBAL
+    grok_numeric_radix()      NEED_grok_numeric_radix      NEED_grok_numeric_radix_GLOBAL
+    grok_oct()                NEED_grok_oct                NEED_grok_oct_GLOBAL
+    newCONSTSUB()             NEED_newCONSTSUB             NEED_newCONSTSUB_GLOBAL
+    newRV_noinc()             NEED_newRV_noinc             NEED_newRV_noinc_GLOBAL
+    sv_2pv_nolen()            NEED_sv_2pv_nolen            NEED_sv_2pv_nolen_GLOBAL
+    sv_2pvbyte()              NEED_sv_2pvbyte              NEED_sv_2pvbyte_GLOBAL
+    sv_catpvf_mg()            NEED_sv_catpvf_mg            NEED_sv_catpvf_mg_GLOBAL
+    sv_catpvf_mg_nocontext()  NEED_sv_catpvf_mg_nocontext  NEED_sv_catpvf_mg_nocontext_GLOBAL
+    sv_setpvf_mg()            NEED_sv_setpvf_mg            NEED_sv_setpvf_mg_GLOBAL
+    sv_setpvf_mg_nocontext()  NEED_sv_setpvf_mg_nocontext  NEED_sv_setpvf_mg_nocontext_GLOBAL
+    vnewSVpvf()               NEED_vnewSVpvf               NEED_vnewSVpvf_GLOBAL
+
+To avoid namespace conflicts, you can change the namespace of the
+explicitly exported functions using the C<DPPP_NAMESPACE> macro.
+Just C<#define> the macro before including C<ppport.h>:
+
+    #define DPPP_NAMESPACE MyOwnNamespace_
+    #include "ppport.h"
+
+The default namespace is C<DPPP_>.
+
+=back
+
+The good thing is that most of the above can be checked by running
+F<ppport.h> on your source code. See the next section for
+details.
+
+=head1 EXAMPLES
+
+To verify whether F<ppport.h> is needed for your module, whether you
+should make any changes to your code, and whether any special defines
+should be used, F<ppport.h> can be run as a Perl script to check your
+source code. Simply say:
+
+    perl ppport.h
+
+The result will usually be a list of patches suggesting changes
+that should at least be acceptable, if not necessarily the most
+efficient solution, or a fix for all possible problems.
+
+If you know that your XS module uses features only available in
+newer Perl releases, if you're aware that it uses C++ comments,
+and if you want all suggestions as a single patch file, you could
+use something like this:
+
+    perl ppport.h --compat-version=5.6.0 --cplusplus --patch=test.diff
+
+If you only want your code to be scanned without any suggestions
+for changes, use:
+
+    perl ppport.h --nochanges
+
+You can specify a different C<diff> program or options, using
+the C<--diff> option:
+
+    perl ppport.h --diff='diff -C 10'
+
+This would output context diffs with 10 lines of context.
+
+To display portability information for the C<newSVpvn> function,
+use:
+
+    perl ppport.h --api-info=newSVpvn
+
+Since the argument to C<--api-info> can be a regular expression,
+you can use
+
+    perl ppport.h --api-info=/_nomg$/
+
+to display portability information for all C<_nomg> functions or
+
+    perl ppport.h --api-info=/./
+
+to display information for all known API elements.
+
+=head1 BUGS
+
+If this version of F<ppport.h> is causing failure during
+the compilation of this module, please check if newer versions
+of either this module or C<Devel::PPPort> are available on CPAN
+before sending a bug report.
+
+If F<ppport.h> was generated using the latest version of
+C<Devel::PPPort> and is causing failure of this module, please
+file a bug report using the CPAN Request Tracker at L<http://rt.cpan.org/>.
+
+Please include the following information:
+
+=over 4
+
+=item 1.
+
+The complete output from running "perl -V"
+
+=item 2.
+
+This file.
+
+=item 3.
+
+The name and version of the module you were trying to build.
+
+=item 4.
+
+A full log of the build that failed.
+
+=item 5.
+
+Any other information that you think could be relevant.
+
+=back
+
+For the latest version of this code, please get the C<Devel::PPPort>
+module from CPAN.
+
+=head1 COPYRIGHT
+
+Version 3.x, Copyright (c) 2004-2005, Marcus Holland-Moritz.
+
+Version 2.x, Copyright (C) 2001, Paul Marquess.
+
+Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+See L<Devel::PPPort>.
+
+=cut
+
+use strict;
+
+my %opt = (
+  quiet     => 0,
+  diag      => 1,
+  hints     => 1,
+  changes   => 1,
+  cplusplus => 0,
+  filter    => 1,
+);
+
+my($ppport) = $0 =~ /([\w.]+)$/;
+my $LF = '(?:\r\n|[\r\n])';   # line feed
+my $HS = "[ \t]";             # horizontal whitespace
+
+eval {
+  require Getopt::Long;
+  Getopt::Long::GetOptions(\%opt, qw(
+    help quiet diag! filter! hints! changes! cplusplus
+    patch=s copy=s diff=s compat-version=s
+    list-provided list-unsupported api-info=s
+  )) or usage();
+};
+
+if ($@ and grep /^-/, @ARGV) {
+  usage() if "@ARGV" =~ /^--?h(?:elp)?$/;
+  die "Getopt::Long not found. Please don't use any options.\n";
+}
+
+usage() if $opt{help};
+
+if (exists $opt{'compat-version'}) {
+  my($r,$v,$s) = eval { parse_version($opt{'compat-version'}) };
+  if ($@) {
+    die "Invalid version number format: '$opt{'compat-version'}'\n";
+  }
+  die "Only Perl 5 is supported\n" if $r != 5;
+  die "Invalid version number: $opt{'compat-version'}\n" if $v >= 1000 || $s >= 1000;
+  $opt{'compat-version'} = sprintf "%d.%03d%03d", $r, $v, $s;
+}
+else {
+  $opt{'compat-version'} = 5;
+}
+
+# Never use C comments in this file!!!!!
+my $ccs  = '/'.'*';
+my $cce  = '*'.'/';
+my $rccs = quotemeta $ccs;
+my $rcce = quotemeta $cce;
+
+my %API = map { /^(\w+)\|([^|]*)\|([^|]*)\|(\w*)$/
+                ? ( $1 => {
+                      ($2                  ? ( base     => $2 ) : ()),
+                      ($3                  ? ( todo     => $3 ) : ()),
+                      (index($4, 'v') >= 0 ? ( varargs  => 1  ) : ()),
+                      (index($4, 'p') >= 0 ? ( provided => 1  ) : ()),
+                      (index($4, 'n') >= 0 ? ( nothxarg => 1  ) : ()),
+                    } )
+                : die "invalid spec: $_" } qw(
+AvFILLp|5.004050||p
+AvFILL|||
+CLASS|||n
+CX_CURPAD_SAVE|||
+CX_CURPAD_SV|||
+CopFILEAV|5.006000||p
+CopFILEGV_set|5.006000||p
+CopFILEGV|5.006000||p
+CopFILESV|5.006000||p
+CopFILE_set|5.006000||p
+CopFILE|5.006000||p
+CopSTASHPV_set|5.006000||p
+CopSTASHPV|5.006000||p
+CopSTASH_eq|5.006000||p
+CopSTASH_set|5.006000||p
+CopSTASH|5.006000||p
+CopyD|5.009002||p
+Copy|||
+CvPADLIST|||
+CvSTASH|||
+CvWEAKOUTSIDE|||
+DEFSV|5.004050||p
+END_EXTERN_C|5.005000||p
+ENTER|||
+ERRSV|5.004050||p
+EXTEND|||
+EXTERN_C|5.005000||p
+FREETMPS|||
+GIMME_V||5.004000|n
+GIMME|||n
+GROK_NUMERIC_RADIX|5.007002||p
+G_ARRAY|||
+G_DISCARD|||
+G_EVAL|||
+G_NOARGS|||
+G_SCALAR|||
+G_VOID||5.004000|
+GetVars|||
+GvSV|||
+Gv_AMupdate|||
+HEf_SVKEY||5.004000|
+HeHASH||5.004000|
+HeKEY||5.004000|
+HeKLEN||5.004000|
+HePV||5.004000|
+HeSVKEY_force||5.004000|
+HeSVKEY_set||5.004000|
+HeSVKEY||5.004000|
+HeVAL||5.004000|
+HvNAME|||
+INT2PTR|5.006000||p
+IN_LOCALE_COMPILETIME|5.007002||p
+IN_LOCALE_RUNTIME|5.007002||p
+IN_LOCALE|5.007002||p
+IN_PERL_COMPILETIME|5.008001||p
+IS_NUMBER_GREATER_THAN_UV_MAX|5.007002||p
+IS_NUMBER_INFINITY|5.007002||p
+IS_NUMBER_IN_UV|5.007002||p
+IS_NUMBER_NAN|5.007003||p
+IS_NUMBER_NEG|5.007002||p
+IS_NUMBER_NOT_INT|5.007002||p
+IVSIZE|5.006000||p
+IVTYPE|5.006000||p
+IVdf|5.006000||p
+LEAVE|||
+LVRET|||
+MARK|||
+MY_CXT_CLONE|5.009002||p
+MY_CXT_INIT|5.007003||p
+MY_CXT|5.007003||p
+MoveD|5.009002||p
+Move|||
+NEWSV|||
+NOOP|5.005000||p
+NUM2PTR|5.006000||p
+NVTYPE|5.006000||p
+NVef|5.006001||p
+NVff|5.006001||p
+NVgf|5.006001||p
+Newc|||
+Newz|||
+New|||
+Nullav|||
+Nullch|||
+Nullcv|||
+Nullhv|||
+Nullsv|||
+ORIGMARK|||
+PAD_BASE_SV|||
+PAD_CLONE_VARS|||
+PAD_COMPNAME_FLAGS|||
+PAD_COMPNAME_GEN_set|||
+PAD_COMPNAME_GEN|||
+PAD_COMPNAME_OURSTASH|||
+PAD_COMPNAME_PV|||
+PAD_COMPNAME_TYPE|||
+PAD_RESTORE_LOCAL|||
+PAD_SAVE_LOCAL|||
+PAD_SAVE_SETNULLPAD|||
+PAD_SETSV|||
+PAD_SET_CUR_NOSAVE|||
+PAD_SET_CUR|||
+PAD_SVl|||
+PAD_SV|||
+PERL_BCDVERSION|5.009003||p
+PERL_GCC_BRACE_GROUPS_FORBIDDEN|5.008001||p
+PERL_INT_MAX|5.004000||p
+PERL_INT_MIN|5.004000||p
+PERL_LONG_MAX|5.004000||p
+PERL_LONG_MIN|5.004000||p
+PERL_MAGIC_arylen|5.007002||p
+PERL_MAGIC_backref|5.007002||p
+PERL_MAGIC_bm|5.007002||p
+PERL_MAGIC_collxfrm|5.007002||p
+PERL_MAGIC_dbfile|5.007002||p
+PERL_MAGIC_dbline|5.007002||p
+PERL_MAGIC_defelem|5.007002||p
+PERL_MAGIC_envelem|5.007002||p
+PERL_MAGIC_env|5.007002||p
+PERL_MAGIC_ext|5.007002||p
+PERL_MAGIC_fm|5.007002||p
+PERL_MAGIC_glob|5.007002||p
+PERL_MAGIC_isaelem|5.007002||p
+PERL_MAGIC_isa|5.007002||p
+PERL_MAGIC_mutex|5.007002||p
+PERL_MAGIC_nkeys|5.007002||p
+PERL_MAGIC_overload_elem|5.007002||p
+PERL_MAGIC_overload_table|5.007002||p
+PERL_MAGIC_overload|5.007002||p
+PERL_MAGIC_pos|5.007002||p
+PERL_MAGIC_qr|5.007002||p
+PERL_MAGIC_regdata|5.007002||p
+PERL_MAGIC_regdatum|5.007002||p
+PERL_MAGIC_regex_global|5.007002||p
+PERL_MAGIC_shared_scalar|5.007003||p
+PERL_MAGIC_shared|5.007003||p
+PERL_MAGIC_sigelem|5.007002||p
+PERL_MAGIC_sig|5.007002||p
+PERL_MAGIC_substr|5.007002||p
+PERL_MAGIC_sv|5.007002||p
+PERL_MAGIC_taint|5.007002||p
+PERL_MAGIC_tiedelem|5.007002||p
+PERL_MAGIC_tiedscalar|5.007002||p
+PERL_MAGIC_tied|5.007002||p
+PERL_MAGIC_utf8|5.008001||p
+PERL_MAGIC_uvar_elem|5.007003||p
+PERL_MAGIC_uvar|5.007002||p
+PERL_MAGIC_vec|5.007002||p
+PERL_MAGIC_vstring|5.008001||p
+PERL_QUAD_MAX|5.004000||p
+PERL_QUAD_MIN|5.004000||p
+PERL_REVISION|5.006000||p
+PERL_SCAN_ALLOW_UNDERSCORES|5.007003||p
+PERL_SCAN_DISALLOW_PREFIX|5.007003||p
+PERL_SCAN_GREATER_THAN_UV_MAX|5.007003||p
+PERL_SCAN_SILENT_ILLDIGIT|5.008001||p
+PERL_SHORT_MAX|5.004000||p
+PERL_SHORT_MIN|5.004000||p
+PERL_SUBVERSION|5.006000||p
+PERL_UCHAR_MAX|5.004000||p
+PERL_UCHAR_MIN|5.004000||p
+PERL_UINT_MAX|5.004000||p
+PERL_UINT_MIN|5.004000||p
+PERL_ULONG_MAX|5.004000||p
+PERL_ULONG_MIN|5.004000||p
+PERL_UNUSED_DECL|5.007002||p
+PERL_UQUAD_MAX|5.004000||p
+PERL_UQUAD_MIN|5.004000||p
+PERL_USHORT_MAX|5.004000||p
+PERL_USHORT_MIN|5.004000||p
+PERL_VERSION|5.006000||p
+PL_DBsingle|||pn
+PL_DBsub|||pn
+PL_DBtrace|||n
+PL_Sv|5.005000||p
+PL_compiling|5.004050||p
+PL_copline|5.005000||p
+PL_curcop|5.004050||p
+PL_curstash|5.004050||p
+PL_debstash|5.004050||p
+PL_defgv|5.004050||p
+PL_diehook|5.004050||p
+PL_dirty|5.004050||p
+PL_dowarn|||pn
+PL_errgv|5.004050||p
+PL_hexdigit|5.005000||p
+PL_hints|5.005000||p
+PL_last_in_gv|||n
+PL_modglobal||5.005000|n
+PL_na|5.004050||pn
+PL_no_modify|5.006000||p
+PL_ofs_sv|||n
+PL_perl_destruct_level|5.004050||p
+PL_perldb|5.004050||p
+PL_ppaddr|5.006000||p
+PL_rsfp_filters|5.004050||p
+PL_rsfp|5.004050||p
+PL_rs|||n
+PL_stack_base|5.004050||p
+PL_stack_sp|5.004050||p
+PL_stdingv|5.004050||p
+PL_sv_arenaroot|5.004050||p
+PL_sv_no|5.004050||pn
+PL_sv_undef|5.004050||pn
+PL_sv_yes|5.004050||pn
+PL_tainted|5.004050||p
+PL_tainting|5.004050||p
+POPi|||n
+POPl|||n
+POPn|||n
+POPpbytex||5.007001|n
+POPpx||5.005030|n
+POPp|||n
+POPs|||n
+PTR2IV|5.006000||p
+PTR2NV|5.006000||p
+PTR2UV|5.006000||p
+PTR2ul|5.007001||p
+PTRV|5.006000||p
+PUSHMARK|||
+PUSHi|||
+PUSHmortal|5.009002||p
+PUSHn|||
+PUSHp|||
+PUSHs|||
+PUSHu|5.004000||p
+PUTBACK|||
+PerlIO_clearerr||5.007003|
+PerlIO_close||5.007003|
+PerlIO_eof||5.007003|
+PerlIO_error||5.007003|
+PerlIO_fileno||5.007003|
+PerlIO_fill||5.007003|
+PerlIO_flush||5.007003|
+PerlIO_get_base||5.007003|
+PerlIO_get_bufsiz||5.007003|
+PerlIO_get_cnt||5.007003|
+PerlIO_get_ptr||5.007003|
+PerlIO_read||5.007003|
+PerlIO_seek||5.007003|
+PerlIO_set_cnt||5.007003|
+PerlIO_set_ptrcnt||5.007003|
+PerlIO_setlinebuf||5.007003|
+PerlIO_stderr||5.007003|
+PerlIO_stdin||5.007003|
+PerlIO_stdout||5.007003|
+PerlIO_tell||5.007003|
+PerlIO_unread||5.007003|
+PerlIO_write||5.007003|
+Poison|5.008000||p
+RETVAL|||n
+Renewc|||
+Renew|||
+SAVECLEARSV|||
+SAVECOMPPAD|||
+SAVEPADSV|||
+SAVETMPS|||
+SAVE_DEFSV|5.004050||p
+SPAGAIN|||
+SP|||
+START_EXTERN_C|5.005000||p
+START_MY_CXT|5.007003||p
+STMT_END|||p
+STMT_START|||p
+ST|||
+SVt_IV|||
+SVt_NV|||
+SVt_PVAV|||
+SVt_PVCV|||
+SVt_PVHV|||
+SVt_PVMG|||
+SVt_PV|||
+Safefree|||
+Slab_Alloc|||
+Slab_Free|||
+StructCopy|||
+SvCUR_set|||
+SvCUR|||
+SvEND|||
+SvGETMAGIC|5.004050||p
+SvGROW|||
+SvIOK_UV||5.006000|
+SvIOK_notUV||5.006000|
+SvIOK_off|||
+SvIOK_only_UV||5.006000|
+SvIOK_only|||
+SvIOK_on|||
+SvIOKp|||
+SvIOK|||
+SvIVX|||
+SvIV_nomg|5.009001||p
+SvIV_set|||
+SvIVx|||
+SvIV|||
+SvIsCOW_shared_hash||5.008003|
+SvIsCOW||5.008003|
+SvLEN_set|||
+SvLEN|||
+SvLOCK||5.007003|
+SvMAGIC_set||5.009003|
+SvNIOK_off|||
+SvNIOKp|||
+SvNIOK|||
+SvNOK_off|||
+SvNOK_only|||
+SvNOK_on|||
+SvNOKp|||
+SvNOK|||
+SvNVX|||
+SvNV_set|||
+SvNVx|||
+SvNV|||
+SvOK|||
+SvOOK|||
+SvPOK_off|||
+SvPOK_only_UTF8||5.006000|
+SvPOK_only|||
+SvPOK_on|||
+SvPOKp|||
+SvPOK|||
+SvPVX|||
+SvPV_force_nomg|5.007002||p
+SvPV_force|||
+SvPV_nolen|5.006000||p
+SvPV_nomg|5.007002||p
+SvPV_set|||
+SvPVbyte_force||5.009002|
+SvPVbyte_nolen||5.006000|
+SvPVbytex_force||5.006000|
+SvPVbytex||5.006000|
+SvPVbyte|5.006000||p
+SvPVutf8_force||5.006000|
+SvPVutf8_nolen||5.006000|
+SvPVutf8x_force||5.006000|
+SvPVutf8x||5.006000|
+SvPVutf8||5.006000|
+SvPVx|||
+SvPV|||
+SvREFCNT_dec|||
+SvREFCNT_inc|||
+SvREFCNT|||
+SvROK_off|||
+SvROK_on|||
+SvROK|||
+SvRV_set||5.009003|
+SvRV|||
+SvSETMAGIC|||
+SvSHARE||5.007003|
+SvSTASH_set||5.009003|
+SvSTASH|||
+SvSetMagicSV_nosteal||5.004000|
+SvSetMagicSV||5.004000|
+SvSetSV_nosteal||5.004000|
+SvSetSV|||
+SvTAINTED_off||5.004000|
+SvTAINTED_on||5.004000|
+SvTAINTED||5.004000|
+SvTAINT|||
+SvTRUE|||
+SvTYPE|||
+SvUNLOCK||5.007003|
+SvUOK||5.007001|
+SvUPGRADE|||
+SvUTF8_off||5.006000|
+SvUTF8_on||5.006000|
+SvUTF8||5.006000|
+SvUVXx|5.004000||p
+SvUVX|5.004000||p
+SvUV_nomg|5.009001||p
+SvUV_set||5.009003|
+SvUVx|5.004000||p
+SvUV|5.004000||p
+SvVOK||5.008001|
+THIS|||n
+UNDERBAR|5.009002||p
+UVSIZE|5.006000||p
+UVTYPE|5.006000||p
+UVXf|5.007001||p
+UVof|5.006000||p
+UVuf|5.006000||p
+UVxf|5.006000||p
+XCPT_CATCH|5.009002||p
+XCPT_RETHROW|5.009002||p
+XCPT_TRY_END|5.009002||p
+XCPT_TRY_START|5.009002||p
+XPUSHi|||
+XPUSHmortal|5.009002||p
+XPUSHn|||
+XPUSHp|||
+XPUSHs|||
+XPUSHu|5.004000||p
+XSRETURN_EMPTY|||
+XSRETURN_IV|||
+XSRETURN_NO|||
+XSRETURN_NV|||
+XSRETURN_PV|||
+XSRETURN_UNDEF|||
+XSRETURN_UV|5.008001||p
+XSRETURN_YES|||
+XSRETURN|||
+XST_mIV|||
+XST_mNO|||
+XST_mNV|||
+XST_mPV|||
+XST_mUNDEF|||
+XST_mUV|5.008001||p
+XST_mYES|||
+XS_VERSION_BOOTCHECK|||
+XS_VERSION|||
+XS|||
+ZeroD|5.009002||p
+Zero|||
+_aMY_CXT|5.007003||p
+_pMY_CXT|5.007003||p
+aMY_CXT_|5.007003||p
+aMY_CXT|5.007003||p
+aTHX_|5.006000||p
+aTHX|5.006000||p
+add_data|||
+allocmy|||
+amagic_call|||
+any_dup|||
+ao|||
+append_elem|||
+append_list|||
+apply_attrs_my|||
+apply_attrs_string||5.006001|
+apply_attrs|||
+apply|||
+asIV|||
+asUV|||
+atfork_lock||5.007003|n
+atfork_unlock||5.007003|n
+av_arylen_p||5.009003|
+av_clear|||
+av_delete||5.006000|
+av_exists||5.006000|
+av_extend|||
+av_fake|||
+av_fetch|||
+av_fill|||
+av_len|||
+av_make|||
+av_pop|||
+av_push|||
+av_reify|||
+av_shift|||
+av_store|||
+av_undef|||
+av_unshift|||
+ax|||n
+bad_type|||
+bind_match|||
+block_end|||
+block_gimme||5.004000|
+block_start|||
+boolSV|5.004000||p
+boot_core_PerlIO|||
+boot_core_UNIVERSAL|||
+boot_core_xsutils|||
+bytes_from_utf8||5.007001|
+bytes_to_utf8||5.006001|
+cache_re|||
+call_argv|5.006000||p
+call_atexit||5.006000|
+call_body|||
+call_list_body|||
+call_list||5.004000|
+call_method|5.006000||p
+call_pv|5.006000||p
+call_sv|5.006000||p
+calloc||5.007002|n
+cando|||
+cast_i32||5.006000|
+cast_iv||5.006000|
+cast_ulong||5.006000|
+cast_uv||5.006000|
+check_uni|||
+checkcomma|||
+checkposixcc|||
+ck_anoncode|||
+ck_bitop|||
+ck_concat|||
+ck_defined|||
+ck_delete|||
+ck_die|||
+ck_eof|||
+ck_eval|||
+ck_exec|||
+ck_exists|||
+ck_exit|||
+ck_ftst|||
+ck_fun|||
+ck_glob|||
+ck_grep|||
+ck_index|||
+ck_join|||
+ck_lengthconst|||
+ck_lfun|||
+ck_listiob|||
+ck_match|||
+ck_method|||
+ck_null|||
+ck_open|||
+ck_repeat|||
+ck_require|||
+ck_retarget|||
+ck_return|||
+ck_rfun|||
+ck_rvconst|||
+ck_sassign|||
+ck_select|||
+ck_shift|||
+ck_sort|||
+ck_spair|||
+ck_split|||
+ck_subr|||
+ck_substr|||
+ck_svconst|||
+ck_trunc|||
+ck_unpack|||
+cl_and|||
+cl_anything|||
+cl_init_zero|||
+cl_init|||
+cl_is_anything|||
+cl_or|||
+closest_cop|||
+convert|||
+cop_free|||
+cr_textfilter|||
+croak_nocontext|||vn
+croak|||v
+csighandler||5.007001|n
+custom_op_desc||5.007003|
+custom_op_name||5.007003|
+cv_ckproto|||
+cv_clone|||
+cv_const_sv||5.004000|
+cv_dump|||
+cv_undef|||
+cx_dump||5.005000|
+cx_dup|||
+cxinc|||
+dAXMARK||5.009003|
+dAX|5.007002||p
+dITEMS|5.007002||p
+dMARK|||
+dMY_CXT_SV|5.007003||p
+dMY_CXT|5.007003||p
+dNOOP|5.006000||p
+dORIGMARK|||
+dSP|||
+dTHR|5.004050||p
+dTHXa|5.006000||p
+dTHXoa|5.006000||p
+dTHX|5.006000||p
+dUNDERBAR|5.009002||p
+dXCPT|5.009002||p
+dXSARGS|||
+dXSI32|||
+dXSTARG|5.006000||p
+deb_curcv|||
+deb_nocontext|||vn
+deb_stack_all|||
+deb_stack_n|||
+debop||5.005000|
+debprofdump||5.005000|
+debprof|||
+debstackptrs||5.007003|
+debstack||5.007003|
+deb||5.007003|v
+del_he|||
+del_sv|||
+delimcpy||5.004000|
+depcom|||
+deprecate_old|||
+deprecate|||
+despatch_signals||5.007001|
+die_nocontext|||vn
+die_where|||
+die|||v
+dirp_dup|||
+div128|||
+djSP|||
+do_aexec5|||
+do_aexec|||
+do_aspawn|||
+do_binmode||5.004050|
+do_chomp|||
+do_chop|||
+do_close|||
+do_dump_pad|||
+do_eof|||
+do_exec3|||
+do_execfree|||
+do_exec|||
+do_gv_dump||5.006000|
+do_gvgv_dump||5.006000|
+do_hv_dump||5.006000|
+do_ipcctl|||
+do_ipcget|||
+do_join|||
+do_kv|||
+do_magic_dump||5.006000|
+do_msgrcv|||
+do_msgsnd|||
+do_oddball|||
+do_op_dump||5.006000|
+do_open9||5.006000|
+do_openn||5.007001|
+do_open||5.004000|
+do_pipe|||
+do_pmop_dump||5.006000|
+do_print|||
+do_readline|||
+do_seek|||
+do_semop|||
+do_shmio|||
+do_spawn_nowait|||
+do_spawn|||
+do_sprintf|||
+do_sv_dump||5.006000|
+do_sysseek|||
+do_tell|||
+do_trans_complex_utf8|||
+do_trans_complex|||
+do_trans_count_utf8|||
+do_trans_count|||
+do_trans_simple_utf8|||
+do_trans_simple|||
+do_trans|||
+do_vecget|||
+do_vecset|||
+do_vop|||
+docatch_body|||
+docatch|||
+doeval|||
+dofile|||
+dofindlabel|||
+doform|||
+doing_taint||5.008001|n
+dooneliner|||
+doopen_pm|||
+doparseform|||
+dopoptoeval|||
+dopoptolabel|||
+dopoptoloop|||
+dopoptosub_at|||
+dopoptosub|||
+dounwind|||
+dowantarray|||
+dump_all||5.006000|
+dump_eval||5.006000|
+dump_fds|||
+dump_form||5.006000|
+dump_indent||5.006000|v
+dump_mstats|||
+dump_packsubs||5.006000|
+dump_sub||5.006000|
+dump_vindent||5.006000|
+dumpuntil|||
+dup_attrlist|||
+emulate_eaccess|||
+eval_pv|5.006000||p
+eval_sv|5.006000||p
+expect_number|||
+fbm_compile||5.005000|
+fbm_instr||5.005000|
+fd_on_nosuid_fs|||
+filter_add|||
+filter_del|||
+filter_gets|||
+filter_read|||
+find_beginning|||
+find_byclass|||
+find_in_my_stash|||
+find_runcv|||
+find_rundefsvoffset||5.009002|
+find_script|||
+find_uninit_var|||
+fold_constants|||
+forbid_setid|||
+force_ident|||
+force_list|||
+force_next|||
+force_version|||
+force_word|||
+form_nocontext|||vn
+form||5.004000|v
+fp_dup|||
+fprintf_nocontext|||vn
+free_global_struct|||
+free_tied_hv_pool|||
+free_tmps|||
+gen_constant_list|||
+get_av|5.006000||p
+get_context||5.006000|n
+get_cv|5.006000||p
+get_db_sub|||
+get_debug_opts|||
+get_hash_seed|||
+get_hv|5.006000||p
+get_mstats|||
+get_no_modify|||
+get_num|||
+get_op_descs||5.005000|
+get_op_names||5.005000|
+get_opargs|||
+get_ppaddr||5.006000|
+get_sv|5.006000||p
+get_vtbl||5.005030|
+getcwd_sv||5.007002|
+getenv_len|||
+gp_dup|||
+gp_free|||
+gp_ref|||
+grok_bin|5.007003||p
+grok_hex|5.007003||p
+grok_number|5.007002||p
+grok_numeric_radix|5.007002||p
+grok_oct|5.007003||p
+group_end|||
+gv_AVadd|||
+gv_HVadd|||
+gv_IOadd|||
+gv_autoload4||5.004000|
+gv_check|||
+gv_dump||5.006000|
+gv_efullname3||5.004000|
+gv_efullname4||5.006001|
+gv_efullname|||
+gv_ename|||
+gv_fetchfile|||
+gv_fetchmeth_autoload||5.007003|
+gv_fetchmethod_autoload||5.004000|
+gv_fetchmethod|||
+gv_fetchmeth|||
+gv_fetchpvn_flags||5.009002|
+gv_fetchpv|||
+gv_fetchsv||5.009002|
+gv_fullname3||5.004000|
+gv_fullname4||5.006001|
+gv_fullname|||
+gv_handler||5.007001|
+gv_init_sv|||
+gv_init|||
+gv_share|||
+gv_stashpvn|5.006000||p
+gv_stashpv|||
+gv_stashsv|||
+he_dup|||
+hek_dup|||
+hfreeentries|||
+hsplit|||
+hv_assert||5.009001|
+hv_auxinit|||
+hv_clear_placeholders||5.009001|
+hv_clear|||
+hv_delayfree_ent||5.004000|
+hv_delete_common|||
+hv_delete_ent||5.004000|
+hv_delete|||
+hv_eiter_p||5.009003|
+hv_eiter_set||5.009003|
+hv_exists_ent||5.004000|
+hv_exists|||
+hv_fetch_common|||
+hv_fetch_ent||5.004000|
+hv_fetch|||
+hv_free_ent||5.004000|
+hv_iterinit|||
+hv_iterkeysv||5.004000|
+hv_iterkey|||
+hv_iternext_flags||5.008000|
+hv_iternextsv|||
+hv_iternext|||
+hv_iterval|||
+hv_ksplit||5.004000|
+hv_magic_check|||
+hv_magic|||
+hv_name_set||5.009003|
+hv_notallowed|||
+hv_placeholders_get||5.009003|
+hv_placeholders_p||5.009003|
+hv_placeholders_set||5.009003|
+hv_riter_p||5.009003|
+hv_riter_set||5.009003|
+hv_scalar||5.009001|
+hv_store_ent||5.004000|
+hv_store_flags||5.008000|
+hv_store|||
+hv_undef|||
+ibcmp_locale||5.004000|
+ibcmp_utf8||5.007003|
+ibcmp|||
+incl_perldb|||
+incline|||
+incpush|||
+ingroup|||
+init_argv_symbols|||
+init_debugger|||
+init_global_struct|||
+init_i18nl10n||5.006000|
+init_i18nl14n||5.006000|
+init_ids|||
+init_interp|||
+init_lexer|||
+init_main_stash|||
+init_perllib|||
+init_postdump_symbols|||
+init_predump_symbols|||
+init_stacks||5.005000|
+init_tm||5.007002|
+instr|||
+intro_my|||
+intuit_method|||
+intuit_more|||
+invert|||
+io_close|||
+isALNUM|||
+isALPHA|||
+isDIGIT|||
+isLOWER|||
+isSPACE|||
+isUPPER|||
+is_an_int|||
+is_gv_magical_sv|||
+is_gv_magical|||
+is_handle_constructor|||
+is_list_assignment|||
+is_lvalue_sub||5.007001|
+is_uni_alnum_lc||5.006000|
+is_uni_alnumc_lc||5.006000|
+is_uni_alnumc||5.006000|
+is_uni_alnum||5.006000|
+is_uni_alpha_lc||5.006000|
+is_uni_alpha||5.006000|
+is_uni_ascii_lc||5.006000|
+is_uni_ascii||5.006000|
+is_uni_cntrl_lc||5.006000|
+is_uni_cntrl||5.006000|
+is_uni_digit_lc||5.006000|
+is_uni_digit||5.006000|
+is_uni_graph_lc||5.006000|
+is_uni_graph||5.006000|
+is_uni_idfirst_lc||5.006000|
+is_uni_idfirst||5.006000|
+is_uni_lower_lc||5.006000|
+is_uni_lower||5.006000|
+is_uni_print_lc||5.006000|
+is_uni_print||5.006000|
+is_uni_punct_lc||5.006000|
+is_uni_punct||5.006000|
+is_uni_space_lc||5.006000|
+is_uni_space||5.006000|
+is_uni_upper_lc||5.006000|
+is_uni_upper||5.006000|
+is_uni_xdigit_lc||5.006000|
+is_uni_xdigit||5.006000|
+is_utf8_alnumc||5.006000|
+is_utf8_alnum||5.006000|
+is_utf8_alpha||5.006000|
+is_utf8_ascii||5.006000|
+is_utf8_char_slow|||
+is_utf8_char||5.006000|
+is_utf8_cntrl||5.006000|
+is_utf8_digit||5.006000|
+is_utf8_graph||5.006000|
+is_utf8_idcont||5.008000|
+is_utf8_idfirst||5.006000|
+is_utf8_lower||5.006000|
+is_utf8_mark||5.006000|
+is_utf8_print||5.006000|
+is_utf8_punct||5.006000|
+is_utf8_space||5.006000|
+is_utf8_string_loclen||5.009003|
+is_utf8_string_loc||5.008001|
+is_utf8_string||5.006001|
+is_utf8_upper||5.006000|
+is_utf8_xdigit||5.006000|
+isa_lookup|||
+items|||n
+ix|||n
+jmaybe|||
+keyword|||
+leave_scope|||
+lex_end|||
+lex_start|||
+linklist|||
+listkids|||
+list|||
+load_module_nocontext|||vn
+load_module||5.006000|v
+localize|||
+looks_like_number|||
+lop|||
+mPUSHi|5.009002||p
+mPUSHn|5.009002||p
+mPUSHp|5.009002||p
+mPUSHu|5.009002||p
+mXPUSHi|5.009002||p
+mXPUSHn|5.009002||p
+mXPUSHp|5.009002||p
+mXPUSHu|5.009002||p
+magic_clear_all_env|||
+magic_clearenv|||
+magic_clearpack|||
+magic_clearsig|||
+magic_dump||5.006000|
+magic_existspack|||
+magic_freearylen_p|||
+magic_freeovrld|||
+magic_freeregexp|||
+magic_getarylen|||
+magic_getdefelem|||
+magic_getglob|||
+magic_getnkeys|||
+magic_getpack|||
+magic_getpos|||
+magic_getsig|||
+magic_getsubstr|||
+magic_gettaint|||
+magic_getuvar|||
+magic_getvec|||
+magic_get|||
+magic_killbackrefs|||
+magic_len|||
+magic_methcall|||
+magic_methpack|||
+magic_nextpack|||
+magic_regdata_cnt|||
+magic_regdatum_get|||
+magic_regdatum_set|||
+magic_scalarpack|||
+magic_set_all_env|||
+magic_setamagic|||
+magic_setarylen|||
+magic_setbm|||
+magic_setcollxfrm|||
+magic_setdbline|||
+magic_setdefelem|||
+magic_setenv|||
+magic_setfm|||
+magic_setglob|||
+magic_setisa|||
+magic_setmglob|||
+magic_setnkeys|||
+magic_setpack|||
+magic_setpos|||
+magic_setregexp|||
+magic_setsig|||
+magic_setsubstr|||
+magic_settaint|||
+magic_setutf8|||
+magic_setuvar|||
+magic_setvec|||
+magic_set|||
+magic_sizepack|||
+magic_wipepack|||
+magicname|||
+make_trie|||
+malloced_size|||n
+malloc||5.007002|n
+markstack_grow|||
+measure_struct|||
+memEQ|5.004000||p
+memNE|5.004000||p
+mem_collxfrm|||
+mess_alloc|||
+mess_nocontext|||vn
+mess||5.006000|v
+method_common|||
+mfree||5.007002|n
+mg_clear|||
+mg_copy|||
+mg_dup|||
+mg_find|||
+mg_free|||
+mg_get|||
+mg_length||5.005000|
+mg_localize|||
+mg_magical|||
+mg_set|||
+mg_size||5.005000|
+mini_mktime||5.007002|
+missingterm|||
+mode_from_discipline|||
+modkids|||
+mod|||
+moreswitches|||
+mul128|||
+mulexp10|||n
+my_atof2||5.007002|
+my_atof||5.006000|
+my_attrs|||
+my_bcopy|||n
+my_betoh16|||n
+my_betoh32|||n
+my_betoh64|||n
+my_betohi|||n
+my_betohl|||n
+my_betohs|||n
+my_bzero|||n
+my_chsize|||
+my_exit_jump|||
+my_exit|||
+my_failure_exit||5.004000|
+my_fflush_all||5.006000|
+my_fork||5.007003|n
+my_htobe16|||n
+my_htobe32|||n
+my_htobe64|||n
+my_htobei|||n
+my_htobel|||n
+my_htobes|||n
+my_htole16|||n
+my_htole32|||n
+my_htole64|||n
+my_htolei|||n
+my_htolel|||n
+my_htoles|||n
+my_htonl|||
+my_kid|||
+my_letoh16|||n
+my_letoh32|||n
+my_letoh64|||n
+my_letohi|||n
+my_letohl|||n
+my_letohs|||n
+my_lstat|||
+my_memcmp||5.004000|n
+my_memset|||n
+my_ntohl|||
+my_pclose||5.004000|
+my_popen_list||5.007001|
+my_popen||5.004000|
+my_setenv|||
+my_socketpair||5.007003|n
+my_stat|||
+my_strftime||5.007002|
+my_swabn|||n
+my_swap|||
+my_unexec|||
+my|||
+newANONATTRSUB||5.006000|
+newANONHASH|||
+newANONLIST|||
+newANONSUB|||
+newASSIGNOP|||
+newATTRSUB||5.006000|
+newAVREF|||
+newAV|||
+newBINOP|||
+newCONDOP|||
+newCONSTSUB|5.006000||p
+newCVREF|||
+newDEFSVOP|||
+newFORM|||
+newFOROP|||
+newGVOP|||
+newGVREF|||
+newGVgen|||
+newHVREF|||
+newHVhv||5.005000|
+newHV|||
+newIO|||
+newLISTOP|||
+newLOGOP|||
+newLOOPEX|||
+newLOOPOP|||
+newMYSUB||5.006000|
+newNULLLIST|||
+newOP|||
+newPADOP||5.006000|
+newPMOP|||
+newPROG|||
+newPVOP|||
+newRANGE|||
+newRV_inc|5.004000||p
+newRV_noinc|5.006000||p
+newRV|||
+newSLICEOP|||
+newSTATEOP|||
+newSUB|||
+newSVOP|||
+newSVREF|||
+newSVhek||5.009003|
+newSViv|||
+newSVnv|||
+newSVpvf_nocontext|||vn
+newSVpvf||5.004000|v
+newSVpvn_share||5.007001|
+newSVpvn|5.006000||p
+newSVpv|||
+newSVrv|||
+newSVsv|||
+newSVuv|5.006000||p
+newSV|||
+newUNOP|||
+newWHILEOP||5.009003|
+newXSproto||5.006000|
+newXS||5.006000|
+new_collate||5.006000|
+new_constant|||
+new_ctype||5.006000|
+new_he|||
+new_logop|||
+new_numeric||5.006000|
+new_stackinfo||5.005000|
+new_version||5.009000|
+next_symbol|||
+nextargv|||
+nextchar|||
+ninstr|||
+no_bareword_allowed|||
+no_fh_allowed|||
+no_op|||
+not_a_number|||
+nothreadhook||5.008000|
+nuke_stacks|||
+num_overflow|||n
+oopsAV|||
+oopsCV|||
+oopsHV|||
+op_clear|||
+op_const_sv|||
+op_dump||5.006000|
+op_free|||
+op_null||5.007002|
+op_refcnt_lock||5.009002|
+op_refcnt_unlock||5.009002|
+open_script|||
+pMY_CXT_|5.007003||p
+pMY_CXT|5.007003||p
+pTHX_|5.006000||p
+pTHX|5.006000||p
+pack_cat||5.007003|
+pack_rec|||
+package|||
+packlist||5.008001|
+pad_add_anon|||
+pad_add_name|||
+pad_alloc|||
+pad_block_start|||
+pad_check_dup|||
+pad_compname_type|||
+pad_findlex|||
+pad_findmy|||
+pad_fixup_inner_anons|||
+pad_free|||
+pad_leavemy|||
+pad_new|||
+pad_push|||
+pad_reset|||
+pad_setsv|||
+pad_sv|||
+pad_swipe|||
+pad_tidy|||
+pad_undef|||
+parse_body|||
+parse_unicode_opts|||
+path_is_absolute|||
+peep|||
+pending_ident|||
+perl_alloc_using|||n
+perl_alloc|||n
+perl_clone_using|||n
+perl_clone|||n
+perl_construct|||n
+perl_destruct||5.007003|n
+perl_free|||n
+perl_parse||5.006000|n
+perl_run|||n
+pidgone|||
+pmflag|||
+pmop_dump||5.006000|
+pmruntime|||
+pmtrans|||
+pop_scope|||
+pregcomp|||
+pregexec|||
+pregfree|||
+prepend_elem|||
+printf_nocontext|||vn
+ptr_table_clear|||
+ptr_table_fetch|||
+ptr_table_free|||
+ptr_table_new|||
+ptr_table_split|||
+ptr_table_store|||
+push_scope|||
+put_byte|||
+pv_display||5.006000|
+pv_uni_display||5.007003|
+qerror|||
+re_croak2|||
+re_dup|||
+re_intuit_start||5.006000|
+re_intuit_string||5.006000|
+realloc||5.007002|n
+reentrant_free|||
+reentrant_init|||
+reentrant_retry|||vn
+reentrant_size|||
+refkids|||
+refto|||
+ref|||
+reg_node|||
+reganode|||
+regatom|||
+regbranch|||
+regclass_swash||5.007003|
+regclass|||
+regcp_set_to|||
+regcppop|||
+regcppush|||
+regcurly|||
+regdump||5.005000|
+regexec_flags||5.005000|
+reghop3|||
+reghopmaybe3|||
+reghopmaybe|||
+reghop|||
+reginclass|||
+reginitcolors||5.006000|
+reginsert|||
+regmatch|||
+regnext||5.005000|
+regoptail|||
+regpiece|||
+regpposixcc|||
+regprop|||
+regrepeat_hard|||
+regrepeat|||
+regtail|||
+regtry|||
+reguni|||
+regwhite|||
+reg|||
+repeatcpy|||
+report_evil_fh|||
+report_uninit|||
+require_errno|||
+require_pv||5.006000|
+rninstr|||
+rsignal_restore|||
+rsignal_save|||
+rsignal_state||5.004000|
+rsignal||5.004000|
+run_body|||
+runops_debug||5.005000|
+runops_standard||5.005000|
+rvpv_dup|||
+rxres_free|||
+rxres_restore|||
+rxres_save|||
+safesyscalloc||5.006000|n
+safesysfree||5.006000|n
+safesysmalloc||5.006000|n
+safesysrealloc||5.006000|n
+same_dirent|||
+save_I16||5.004000|
+save_I32|||
+save_I8||5.006000|
+save_aelem||5.004050|
+save_alloc||5.006000|
+save_aptr|||
+save_ary|||
+save_bool||5.008001|
+save_clearsv|||
+save_delete|||
+save_destructor_x||5.006000|
+save_destructor||5.006000|
+save_freeop|||
+save_freepv|||
+save_freesv|||
+save_generic_pvref||5.006001|
+save_generic_svref||5.005030|
+save_gp||5.004000|
+save_hash|||
+save_hek_flags|||
+save_helem||5.004050|
+save_hints||5.005000|
+save_hptr|||
+save_int|||
+save_item|||
+save_iv||5.005000|
+save_lines|||
+save_list|||
+save_long|||
+save_magic|||
+save_mortalizesv||5.007001|
+save_nogv|||
+save_op|||
+save_padsv||5.007001|
+save_pptr|||
+save_re_context||5.006000|
+save_scalar_at|||
+save_scalar|||
+save_set_svflags||5.009000|
+save_shared_pvref||5.007003|
+save_sptr|||
+save_svref|||
+save_threadsv||5.005000|
+save_vptr||5.006000|
+savepvn|||
+savepv|||
+savesharedpv||5.007003|
+savestack_grow_cnt||5.008001|
+savestack_grow|||
+savesvpv||5.009002|
+sawparens|||
+scalar_mod_type|||
+scalarboolean|||
+scalarkids|||
+scalarseq|||
+scalarvoid|||
+scalar|||
+scan_bin||5.006000|
+scan_commit|||
+scan_const|||
+scan_formline|||
+scan_heredoc|||
+scan_hex|||
+scan_ident|||
+scan_inputsymbol|||
+scan_num||5.007001|
+scan_oct|||
+scan_pat|||
+scan_str|||
+scan_subst|||
+scan_trans|||
+scan_version||5.009001|
+scan_vstring||5.008001|
+scan_word|||
+scope|||
+screaminstr||5.005000|
+seed|||
+set_context||5.006000|n
+set_csh|||
+set_numeric_local||5.006000|
+set_numeric_radix||5.006000|
+set_numeric_standard||5.006000|
+setdefout|||
+setenv_getix|||
+share_hek_flags|||
+share_hek|||
+si_dup|||
+sighandler|||n
+simplify_sort|||
+skipspace|||
+sortsv||5.007003|
+ss_dup|||
+stack_grow|||
+start_glob|||
+start_subparse||5.004000|
+stashpv_hvname_match||5.009003|
+stdize_locale|||
+strEQ|||
+strGE|||
+strGT|||
+strLE|||
+strLT|||
+strNE|||
+str_to_version||5.006000|
+strnEQ|||
+strnNE|||
+study_chunk|||
+sub_crush_depth|||
+sublex_done|||
+sublex_push|||
+sublex_start|||
+sv_2bool|||
+sv_2cv|||
+sv_2io|||
+sv_2iuv_non_preserve|||
+sv_2iv_flags||5.009001|
+sv_2iv|||
+sv_2mortal|||
+sv_2nv|||
+sv_2pv_flags||5.007002|
+sv_2pv_nolen|5.006000||p
+sv_2pvbyte_nolen|||
+sv_2pvbyte|5.006000||p
+sv_2pvutf8_nolen||5.006000|
+sv_2pvutf8||5.006000|
+sv_2pv|||
+sv_2uv_flags||5.009001|
+sv_2uv|5.004000||p
+sv_add_arena|||
+sv_add_backref|||
+sv_backoff|||
+sv_bless|||
+sv_cat_decode||5.008001|
+sv_catpv_mg|5.006000||p
+sv_catpvf_mg_nocontext|||pvn
+sv_catpvf_mg|5.006000|5.004000|pv
+sv_catpvf_nocontext|||vn
+sv_catpvf||5.004000|v
+sv_catpvn_flags||5.007002|
+sv_catpvn_mg|5.006000||p
+sv_catpvn_nomg|5.007002||p
+sv_catpvn|||
+sv_catpv|||
+sv_catsv_flags||5.007002|
+sv_catsv_mg|5.006000||p
+sv_catsv_nomg|5.007002||p
+sv_catsv|||
+sv_chop|||
+sv_clean_all|||
+sv_clean_objs|||
+sv_clear|||
+sv_cmp_locale||5.004000|
+sv_cmp|||
+sv_collxfrm|||
+sv_compile_2op||5.008001|
+sv_copypv||5.007003|
+sv_dec|||
+sv_del_backref|||
+sv_derived_from||5.004000|
+sv_dump|||
+sv_dup|||
+sv_eq|||
+sv_force_normal_flags||5.007001|
+sv_force_normal||5.006000|
+sv_free2|||
+sv_free_arenas|||
+sv_free|||
+sv_gets||5.004000|
+sv_grow|||
+sv_inc|||
+sv_insert|||
+sv_isa|||
+sv_isobject|||
+sv_iv||5.005000|
+sv_len_utf8||5.006000|
+sv_len|||
+sv_magicext||5.007003|
+sv_magic|||
+sv_mortalcopy|||
+sv_newmortal|||
+sv_newref|||
+sv_nolocking||5.007003|
+sv_nosharing||5.007003|
+sv_nounlocking||5.007003|
+sv_nv||5.005000|
+sv_peek||5.005000|
+sv_pos_b2u||5.006000|
+sv_pos_u2b||5.006000|
+sv_pvbyten_force||5.006000|
+sv_pvbyten||5.006000|
+sv_pvbyte||5.006000|
+sv_pvn_force_flags||5.007002|
+sv_pvn_force|||p
+sv_pvn_nomg|5.007003||p
+sv_pvn|5.006000||p
+sv_pvutf8n_force||5.006000|
+sv_pvutf8n||5.006000|
+sv_pvutf8||5.006000|
+sv_pv||5.006000|
+sv_recode_to_utf8||5.007003|
+sv_reftype|||
+sv_release_COW|||
+sv_release_IVX|||
+sv_replace|||
+sv_report_used|||
+sv_reset|||
+sv_rvweaken||5.006000|
+sv_setiv_mg|5.006000||p
+sv_setiv|||
+sv_setnv_mg|5.006000||p
+sv_setnv|||
+sv_setpv_mg|5.006000||p
+sv_setpvf_mg_nocontext|||pvn
+sv_setpvf_mg|5.006000|5.004000|pv
+sv_setpvf_nocontext|||vn
+sv_setpvf||5.004000|v
+sv_setpviv_mg||5.008001|
+sv_setpviv||5.008001|
+sv_setpvn_mg|5.006000||p
+sv_setpvn|||
+sv_setpv|||
+sv_setref_iv|||
+sv_setref_nv|||
+sv_setref_pvn|||
+sv_setref_pv|||
+sv_setref_uv||5.007001|
+sv_setsv_cow|||
+sv_setsv_flags||5.007002|
+sv_setsv_mg|5.006000||p
+sv_setsv_nomg|5.007002||p
+sv_setsv|||
+sv_setuv_mg|5.006000||p
+sv_setuv|5.006000||p
+sv_tainted||5.004000|
+sv_taint||5.004000|
+sv_true||5.005000|
+sv_unglob|||
+sv_uni_display||5.007003|
+sv_unmagic|||
+sv_unref_flags||5.007001|
+sv_unref|||
+sv_untaint||5.004000|
+sv_upgrade|||
+sv_usepvn_mg|5.006000||p
+sv_usepvn|||
+sv_utf8_decode||5.006000|
+sv_utf8_downgrade||5.006000|
+sv_utf8_encode||5.006000|
+sv_utf8_upgrade_flags||5.007002|
+sv_utf8_upgrade||5.007001|
+sv_uv|5.006000||p
+sv_vcatpvf_mg|5.006000|5.004000|p
+sv_vcatpvfn||5.004000|
+sv_vcatpvf|5.006000|5.004000|p
+sv_vsetpvf_mg|5.006000|5.004000|p
+sv_vsetpvfn||5.004000|
+sv_vsetpvf|5.006000|5.004000|p
+svtype|||
+swallow_bom|||
+swash_fetch||5.007002|
+swash_init||5.006000|
+sys_intern_clear|||
+sys_intern_dup|||
+sys_intern_init|||
+taint_env|||
+taint_proper|||
+tmps_grow||5.006000|
+toLOWER|||
+toUPPER|||
+to_byte_substr|||
+to_uni_fold||5.007003|
+to_uni_lower_lc||5.006000|
+to_uni_lower||5.007003|
+to_uni_title_lc||5.006000|
+to_uni_title||5.007003|
+to_uni_upper_lc||5.006000|
+to_uni_upper||5.007003|
+to_utf8_case||5.007003|
+to_utf8_fold||5.007003|
+to_utf8_lower||5.007003|
+to_utf8_substr|||
+to_utf8_title||5.007003|
+to_utf8_upper||5.007003|
+tokeq|||
+tokereport|||
+too_few_arguments|||
+too_many_arguments|||
+unlnk|||
+unpack_rec|||
+unpack_str||5.007003|
+unpackstring||5.008001|
+unshare_hek_or_pvn|||
+unshare_hek|||
+unsharepvn||5.004000|
+upg_version||5.009000|
+usage|||
+utf16_textfilter|||
+utf16_to_utf8_reversed||5.006001|
+utf16_to_utf8||5.006001|
+utf16rev_textfilter|||
+utf8_distance||5.006000|
+utf8_hop||5.006000|
+utf8_length||5.007001|
+utf8_mg_pos_init|||
+utf8_mg_pos|||
+utf8_to_bytes||5.006001|
+utf8_to_uvchr||5.007001|
+utf8_to_uvuni||5.007001|
+utf8n_to_uvchr||5.007001|
+utf8n_to_uvuni||5.007001|
+utilize|||
+uvchr_to_utf8_flags||5.007003|
+uvchr_to_utf8||5.007001|
+uvuni_to_utf8_flags||5.007003|
+uvuni_to_utf8||5.007001|
+validate_suid|||
+varname|||
+vcmp||5.009000|
+vcroak||5.006000|
+vdeb||5.007003|
+vdie|||
+vform||5.006000|
+visit|||
+vivify_defelem|||
+vivify_ref|||
+vload_module||5.006000|
+vmess||5.006000|
+vnewSVpvf|5.006000|5.004000|p
+vnormal||5.009002|
+vnumify||5.009000|
+vstringify||5.009000|
+vwarner||5.006000|
+vwarn||5.006000|
+wait4pid|||
+warn_nocontext|||vn
+warner_nocontext|||vn
+warner||5.006000|v
+warn|||v
+watch|||
+whichsig|||
+write_to_stderr|||
+yyerror|||
+yylex|||
+yyparse|||
+yywarn|||
+);
+
+if (exists $opt{'list-unsupported'}) {
+  my $f;
+  for $f (sort { lc $a cmp lc $b } keys %API) {
+    next unless $API{$f}{todo};
+    print "$f ", '.'x(40-length($f)), " ", format_version($API{$f}{todo}), "\n";
+  }
+  exit 0;
+}
+
+# Scan for possible replacement candidates
+
+my(%replace, %need, %hints, %depends);
+my $replace = 0;
+my $hint = '';
+
+while (<DATA>) {
+  if ($hint) {
+    if (m{^\s*\*\s(.*?)\s*$}) {
+      $hints{$hint} ||= '';  # suppress warning with older perls
+      $hints{$hint} .= "$1\n";
+    }
+    else {
+      $hint = '';
+    }
+  }
+  $hint = $1 if m{^\s*$rccs\sHint:\s+(\w+)\s*$};
+
+  $replace     = $1 if m{^\s*$rccs\s+Replace:\s+(\d+)\s+$rcce\s*$};
+  $replace{$2} = $1 if $replace and m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+)};
+  $replace{$2} = $1 if m{^\s*#\s*define\s+(\w+)(?:\([^)]*\))?\s+(\w+).*$rccs\s+Replace\s+$rcce};
+  $replace{$1} = $2 if m{^\s*$rccs\s+Replace (\w+) with (\w+)\s+$rcce\s*$};
+
+  if (m{^\s*$rccs\s+(\w+)\s+depends\s+on\s+(\w+(\s*,\s*\w+)*)\s+$rcce\s*$}) {
+    push @{$depends{$1}}, map { s/\s+//g; $_ } split /,/, $2;
+  }
+
+  $need{$1} = 1 if m{^#if\s+defined\(NEED_(\w+)(?:_GLOBAL)?\)};
+}
+
+if (exists $opt{'api-info'}) {
+  my $f;
+  my $count = 0;
+  my $match = $opt{'api-info'} =~ m!^/(.*)/$! ? $1 : "^\Q$opt{'api-info'}\E\$";
+  for $f (sort { lc $a cmp lc $b } keys %API) {
+    next unless $f =~ /$match/;
+    print "\n=== $f ===\n\n";
+    my $info = 0;
+    if ($API{$f}{base} || $API{$f}{todo}) {
+      my $base = format_version($API{$f}{base} || $API{$f}{todo});
+      print "Supported at least starting from perl-$base.\n";
+      $info++;
+    }
+    if ($API{$f}{provided}) {
+      my $todo = $API{$f}{todo} ? format_version($API{$f}{todo}) : "5.003";
+      print "Support by $ppport provided back to perl-$todo.\n";
+      print "Support needs to be explicitly requested by NEED_$f.\n" if exists $need{$f};
+      print "Depends on: ", join(', ', @{$depends{$f}}), ".\n" if exists $depends{$f};
+      print "$hints{$f}" if exists $hints{$f};
+      $info++;
+    }
+    unless ($info) {
+      print "No portability information available.\n";
+    }
+    $count++;
+  }
+  if ($count > 0) {
+    print "\n";
+  }
+  else {
+    print "Found no API matching '$opt{'api-info'}'.\n";
+  }
+  exit 0;
+}
+
+if (exists $opt{'list-provided'}) {
+  my $f;
+  for $f (sort { lc $a cmp lc $b } keys %API) {
+    next unless $API{$f}{provided};
+    my @flags;
+    push @flags, 'explicit' if exists $need{$f};
+    push @flags, 'depend'   if exists $depends{$f};
+    push @flags, 'hint'     if exists $hints{$f};
+    my $flags = @flags ? '  ['.join(', ', @flags).']' : '';
+    print "$f$flags\n";
+  }
+  exit 0;
+}
+
+my @files;
+my @srcext = qw( xs c h cc cpp );
+my $srcext = join '|', @srcext;
+
+if (@ARGV) {
+  my %seen;
+  @files = grep { -f && !exists $seen{$_} } map { glob $_ } @ARGV;
+}
+else {
+  eval {
+    require File::Find;
+    File::Find::find(sub {
+      $File::Find::name =~ /\.($srcext)$/i
+          and push @files, $File::Find::name;
+    }, '.');
+  };
+  if ($@) {
+    @files = map { glob "*.$_" } @srcext;
+  }
+}
+
+if (!@ARGV || $opt{filter}) {
+  my(@in, @out);
+  my %xsc = map { /(.*)\.xs$/ ? ("$1.c" => 1, "$1.cc" => 1) : () } @files;
+  for (@files) {
+    my $out = exists $xsc{$_} || /\b\Q$ppport\E$/i || !/\.($srcext)$/i;
+    push @{ $out ? \@out : \@in }, $_;
+  }
+  if (@ARGV && @out) {
+    warning("Skipping the following files (use --nofilter to avoid this):\n| ", join "\n| ", @out);
+  }
+  @files = @in;
+}
+
+unless (@files) {
+  die "No input files given!\n";
+}
+
+my(%files, %global, %revreplace);
+%revreplace = reverse %replace;
+my $filename;
+my $patch_opened = 0;
+
+for $filename (@files) {
+  unless (open IN, "<$filename") {
+    warn "Unable to read from $filename: $!\n";
+    next;
+  }
+
+  info("Scanning $filename ...");
+
+  my $c = do { local $/; <IN> };
+  close IN;
+
+  my %file = (orig => $c, changes => 0);
+
+  # temporarily remove C comments from the code
+  my @ccom;
+  $c =~ s{
+    (
+        [^"'/]+
+      |
+        (?:"[^"\\]*(?:\\.[^"\\]*)*" [^"'/]*)+
+      |
+        (?:'[^'\\]*(?:\\.[^'\\]*)*' [^"'/]*)+
+    )
+  |
+    (/ (?:
+        \*[^*]*\*+(?:[^$ccs][^*]*\*+)* /
+        |
+        /[^\r\n]*
+      ))
+  }{
+    defined $2 and push @ccom, $2;
+    defined $1 ? $1 : "$ccs$#ccom$cce";
+  }egsx;
+
+  $file{ccom} = \@ccom;
+  $file{code} = $c;
+  $file{has_inc_ppport} = ($c =~ /#.*include.*\Q$ppport\E/);
+
+  my $func;
+
+  for $func (keys %API) {
+    my $match = $func;
+    $match .= "|$revreplace{$func}" if exists $revreplace{$func};
+    if ($c =~ /\b(?:Perl_)?($match)\b/) {
+      $file{uses_replace}{$1}++ if exists $revreplace{$func} && $1 eq $revreplace{$func};
+      $file{uses_Perl}{$func}++ if $c =~ /\bPerl_$func\b/;
+      if (exists $API{$func}{provided}) {
+        if (!exists $API{$func}{base} || $API{$func}{base} > $opt{'compat-version'}) {
+          $file{uses}{$func}++;
+          my @deps = rec_depend($func);
+          if (@deps) {
+            $file{uses_deps}{$func} = \@deps;
+            for (@deps) {
+              $file{uses}{$_} = 0 unless exists $file{uses}{$_};
+            }
+          }
+          for ($func, @deps) {
+            if (exists $need{$_}) {
+              $file{needs}{$_} = 'static';
+            }
+          }
+        }
+      }
+      if (exists $API{$func}{todo} && $API{$func}{todo} > $opt{'compat-version'}) {
+        if ($c =~ /\b$func\b/) {
+          $file{uses_todo}{$func}++;
+        }
+      }
+    }
+  }
+
+  while ($c =~ /^$HS*#$HS*define$HS+(NEED_(\w+?)(_GLOBAL)?)\b/mg) {
+    if (exists $need{$2}) {
+      $file{defined $3 ? 'needed_global' : 'needed_static'}{$2}++;
+    }
+    else {
+      warning("Possibly wrong #define $1 in $filename");
+    }
+  }
+
+  for (qw(uses needs uses_todo needed_global needed_static)) {
+    for $func (keys %{$file{$_}}) {
+      push @{$global{$_}{$func}}, $filename;
+    }
+  }
+
+  $files{$filename} = \%file;
+}
+
+# Globally resolve NEED_'s
+my $need;
+for $need (keys %{$global{needs}}) {
+  if (@{$global{needs}{$need}} > 1) {
+    my @targets = @{$global{needs}{$need}};
+    my @t = grep $files{$_}{needed_global}{$need}, @targets;
+    @targets = @t if @t;
+    @t = grep /\.xs$/i, @targets;
+    @targets = @t if @t;
+    my $target = shift @targets;
+    $files{$target}{needs}{$need} = 'global';
+    for (@{$global{needs}{$need}}) {
+      $files{$_}{needs}{$need} = 'extern' if $_ ne $target;
+    }
+  }
+}
+
+for $filename (@files) {
+  exists $files{$filename} or next;
+
+  info("=== Analyzing $filename ===");
+
+  my %file = %{$files{$filename}};
+  my $func;
+  my $c = $file{code};
+
+  for $func (sort keys %{$file{uses_Perl}}) {
+    if ($API{$func}{varargs}) {
+      my $changes = ($c =~ s{\b(Perl_$func\s*\(\s*)(?!aTHX_?)(\)|[^\s)]*\))}
+                            { $1 . ($2 eq ')' ? 'aTHX' : 'aTHX_ ') . $2 }ge);
+      if ($changes) {
+        warning("Doesn't pass interpreter argument aTHX to Perl_$func");
+        $file{changes} += $changes;
+      }
+    }
+    else {
+      warning("Uses Perl_$func instead of $func");
+      $file{changes} += ($c =~ s{\bPerl_$func(\s*)\((\s*aTHX_?)?\s*}
+                                {$func$1(}g);
+    }
+  }
+
+  for $func (sort keys %{$file{uses_replace}}) {
+    warning("Uses $func instead of $replace{$func}");
+    $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g);
+  }
+
+  for $func (sort keys %{$file{uses}}) {
+    next unless $file{uses}{$func};   # if it's only a dependency
+    if (exists $file{uses_deps}{$func}) {
+      diag("Uses $func, which depends on ", join(', ', @{$file{uses_deps}{$func}}));
+    }
+    elsif (exists $replace{$func}) {
+      warning("Uses $func instead of $replace{$func}");
+      $file{changes} += ($c =~ s/\b$func\b/$replace{$func}/g);
+    }
+    else {
+      diag("Uses $func");
+    }
+    hint($func);
+  }
+
+  for $func (sort keys %{$file{uses_todo}}) {
+    warning("Uses $func, which may not be portable below perl ",
+            format_version($API{$func}{todo}));
+  }
+
+  for $func (sort keys %{$file{needed_static}}) {
+    my $message = '';
+    if (not exists $file{uses}{$func}) {
+      $message = "No need to define NEED_$func if $func is never used";
+    }
+    elsif (exists $file{needs}{$func} && $file{needs}{$func} ne 'static') {
+      $message = "No need to define NEED_$func when already needed globally";
+    }
+    if ($message) {
+      diag($message);
+      $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_$func\b.*$LF//mg);
+    }
+  }
+
+  for $func (sort keys %{$file{needed_global}}) {
+    my $message = '';
+    if (not exists $global{uses}{$func}) {
+      $message = "No need to define NEED_${func}_GLOBAL if $func is never used";
+    }
+    elsif (exists $file{needs}{$func}) {
+      if ($file{needs}{$func} eq 'extern') {
+        $message = "No need to define NEED_${func}_GLOBAL when already needed globally";
+      }
+      elsif ($file{needs}{$func} eq 'static') {
+        $message = "No need to define NEED_${func}_GLOBAL when only used in this file";
+      }
+    }
+    if ($message) {
+      diag($message);
+      $file{changes} += ($c =~ s/^$HS*#$HS*define$HS+NEED_${func}_GLOBAL\b.*$LF//mg);
+    }
+  }
+
+  $file{needs_inc_ppport} = keys %{$file{uses}};
+
+  if ($file{needs_inc_ppport}) {
+    my $pp = '';
+
+    for $func (sort keys %{$file{needs}}) {
+      my $type = $file{needs}{$func};
+      next if $type eq 'extern';
+      my $suffix = $type eq 'global' ? '_GLOBAL' : '';
+      unless (exists $file{"needed_$type"}{$func}) {
+        if ($type eq 'global') {
+          diag("Files [@{$global{needs}{$func}}] need $func, adding global request");
+        }
+        else {
+          diag("File needs $func, adding static request");
+        }
+        $pp .= "#define NEED_$func$suffix\n";
+      }
+    }
+
+    if ($pp && ($c =~ s/^(?=$HS*#$HS*define$HS+NEED_\w+)/$pp/m)) {
+      $pp = '';
+      $file{changes}++;
+    }
+
+    unless ($file{has_inc_ppport}) {
+      diag("Needs to include '$ppport'");
+      $pp .= qq(#include "$ppport"\n)
+    }
+
+    if ($pp) {
+      $file{changes} += ($c =~ s/^($HS*#$HS*define$HS+NEED_\w+.*?)^/$1$pp/ms)
+                     || ($c =~ s/^(?=$HS*#$HS*include.*\Q$ppport\E)/$pp/m)
+                     || ($c =~ s/^($HS*#$HS*include.*XSUB.*\s*?)^/$1$pp/m)
+                     || ($c =~ s/^/$pp/);
+    }
+  }
+  else {
+    if ($file{has_inc_ppport}) {
+      diag("No need to include '$ppport'");
+      $file{changes} += ($c =~ s/^$HS*?#$HS*include.*\Q$ppport\E.*?$LF//m);
+    }
+  }
+
+  # put back in our C comments
+  my $ix;
+  my $cppc = 0;
+  my @ccom = @{$file{ccom}};
+  for $ix (0 .. $#ccom) {
+    if (!$opt{cplusplus} && $ccom[$ix] =~ s!^//!!) {
+      $cppc++;
+      $file{changes} += $c =~ s/$rccs$ix$rcce/$ccs$ccom[$ix] $cce/;
+    }
+    else {
+      $c =~ s/$rccs$ix$rcce/$ccom[$ix]/;
+    }
+  }
+
+  if ($cppc) {
+    my $s = $cppc != 1 ? 's' : '';
+    warning("Uses $cppc C++ style comment$s, which is not portable");
+  }
+
+  if ($file{changes}) {
+    if (exists $opt{copy}) {
+      my $newfile = "$filename$opt{copy}";
+      if (-e $newfile) {
+        error("'$newfile' already exists, refusing to write copy of '$filename'");
+      }
+      else {
+        local *F;
+        if (open F, ">$newfile") {
+          info("Writing copy of '$filename' with changes to '$newfile'");
+          print F $c;
+          close F;
+        }
+        else {
+          error("Cannot open '$newfile' for writing: $!");
+        }
+      }
+    }
+    elsif (exists $opt{patch} || $opt{changes}) {
+      if (exists $opt{patch}) {
+        unless ($patch_opened) {
+          if (open PATCH, ">$opt{patch}") {
+            $patch_opened = 1;
+          }
+          else {
+            error("Cannot open '$opt{patch}' for writing: $!");
+            delete $opt{patch};
+            $opt{changes} = 1;
+            goto fallback;
+          }
+        }
+        mydiff(\*PATCH, $filename, $c);
+      }
+      else {
+fallback:
+        info("Suggested changes:");
+        mydiff(\*STDOUT, $filename, $c);
+      }
+    }
+    else {
+      my $s = $file{changes} == 1 ? '' : 's';
+      info("$file{changes} potentially required change$s detected");
+    }
+  }
+  else {
+    info("Looks good");
+  }
+}
+
+close PATCH if $patch_opened;
+
+exit 0;
+
+
+sub mydiff
+{
+  local *F = shift;
+  my($file, $str) = @_;
+  my $diff;
+
+  if (exists $opt{diff}) {
+    $diff = run_diff($opt{diff}, $file, $str);
+  }
+
+  if (!defined $diff and can_use('Text::Diff')) {
+    $diff = Text::Diff::diff($file, \$str, { STYLE => 'Unified' });
+    $diff = <<HEADER . $diff;
+--- $file
++++ $file.patched
+HEADER
+  }
+
+  if (!defined $diff) {
+    $diff = run_diff('diff -u', $file, $str);
+  }
+
+  if (!defined $diff) {
+    $diff = run_diff('diff', $file, $str);
+  }
+
+  if (!defined $diff) {
+    error("Cannot generate a diff. Please install Text::Diff or use --copy.");
+    return;
+  }
+
+  print F $diff;
+
+}
+
+sub run_diff
+{
+  my($prog, $file, $str) = @_;
+  my $tmp = 'dppptemp';
+  my $suf = 'aaa';
+  my $diff = '';
+  local *F;
+
+  while (-e "$tmp.$suf") { $suf++ }
+  $tmp = "$tmp.$suf";
+
+  if (open F, ">$tmp") {
+    print F $str;
+    close F;
+
+    if (open F, "$prog $file $tmp |") {
+      while (<F>) {
+        s/\Q$tmp\E/$file.patched/;
+        $diff .= $_;
+      }
+      close F;
+      unlink $tmp;
+      return $diff;
+    }
+
+    unlink $tmp;
+  }
+  else {
+    error("Cannot open '$tmp' for writing: $!");
+  }
+
+  return undef;
+}
+
+sub can_use
+{
+  eval "use @_;";
+  return $@ eq '';
+}
+
+sub rec_depend
+{
+  my $func = shift;
+  my %seen;
+  return () unless exists $depends{$func};
+  grep !$seen{$_}++, map { ($_, rec_depend($_)) } @{$depends{$func}};
+}
+
+sub parse_version
+{
+  my $ver = shift;
+
+  if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) {
+    return ($1, $2, $3);
+  }
+  elsif ($ver !~ /^\d+\.[\d_]+$/) {
+    die "cannot parse version '$ver'\n";
+  }
+
+  $ver =~ s/_//g;
+  $ver =~ s/$/000000/;
+
+  my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
+
+  $v = int $v;
+  $s = int $s;
+
+  if ($r < 5 || ($r == 5 && $v < 6)) {
+    if ($s % 10) {
+      die "cannot parse version '$ver'\n";
+    }
+  }
+
+  return ($r, $v, $s);
+}
+
+sub format_version
+{
+  my $ver = shift;
+
+  $ver =~ s/$/000000/;
+  my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
+
+  $v = int $v;
+  $s = int $s;
+
+  if ($r < 5 || ($r == 5 && $v < 6)) {
+    if ($s % 10) {
+      die "invalid version '$ver'\n";
+    }
+    $s /= 10;
+
+    $ver = sprintf "%d.%03d", $r, $v;
+    $s > 0 and $ver .= sprintf "_%02d", $s;
+
+    return $ver;
+  }
+
+  return sprintf "%d.%d.%d", $r, $v, $s;
+}
+
+sub info
+{
+  $opt{quiet} and return;
+  print @_, "\n";
+}
+
+sub diag
+{
+  $opt{quiet} and return;
+  $opt{diag} and print @_, "\n";
+}
+
+sub warning
+{
+  $opt{quiet} and return;
+  print "*** ", @_, "\n";
+}
+
+sub error
+{
+  print "*** ERROR: ", @_, "\n";
+}
+
+my %given_hints;
+sub hint
+{
+  $opt{quiet} and return;
+  $opt{hints} or return;
+  my $func = shift;
+  exists $hints{$func} or return;
+  $given_hints{$func}++ and return;
+  my $hint = $hints{$func};
+  $hint =~ s/^/   /mg;
+  print "   --- hint for $func ---\n", $hint;
+}
+
+sub usage
+{
+  my($usage) = do { local(@ARGV,$/)=($0); <> } =~ /^=head\d$HS+SYNOPSIS\s*^(.*?)\s*^=/ms;
+  my %M = ( 'I' => '*' );
+  $usage =~ s/^\s*perl\s+\S+/$^X $0/;
+  $usage =~ s/([A-Z])<([^>]+)>/$M{$1}$2$M{$1}/g;
+
+  print <<ENDUSAGE;
+
+Usage: $usage
+
+See perldoc $0 for details.
+
+ENDUSAGE
+
+  exit 2;
+}
+
+__DATA__
+*/
+
+#ifndef _P_P_PORTABILITY_H_
+#define _P_P_PORTABILITY_H_
+
+#ifndef DPPP_NAMESPACE
+#  define DPPP_NAMESPACE DPPP_
+#endif
+
+#define DPPP_CAT2(x,y) CAT2(x,y)
+#define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name)
+
+#ifndef PERL_REVISION
+#  if !defined(__PATCHLEVEL_H_INCLUDED__) && !(defined(PATCHLEVEL) && defined(SUBVERSION))
+#    define PERL_PATCHLEVEL_H_IMPLICIT
+#    include <patchlevel.h>
+#  endif
+#  if !(defined(PERL_VERSION) || (defined(SUBVERSION) && defined(PATCHLEVEL)))
+#    include <could_not_find_Perl_patchlevel.h>
+#  endif
+#  ifndef PERL_REVISION
+#    define PERL_REVISION       (5)
+     /* Replace: 1 */
+#    define PERL_VERSION        PATCHLEVEL
+#    define PERL_SUBVERSION     SUBVERSION
+     /* Replace PERL_PATCHLEVEL with PERL_VERSION */
+     /* Replace: 0 */
+#  endif
+#endif
+
+#define PERL_BCDVERSION ((PERL_REVISION * 0x1000000L) + (PERL_VERSION * 0x1000L) + PERL_SUBVERSION)
+
+/* It is very unlikely that anyone will try to use this with Perl 6
+   (or greater), but who knows.
+ */
+#if PERL_REVISION != 5
+#  error ppport.h only works with Perl version 5
+#endif /* PERL_REVISION != 5 */
+
+#ifdef I_LIMITS
+#  include <limits.h>
+#endif
+
+#ifndef PERL_UCHAR_MIN
+#  define PERL_UCHAR_MIN ((unsigned char)0)
+#endif
+
+#ifndef PERL_UCHAR_MAX
+#  ifdef UCHAR_MAX
+#    define PERL_UCHAR_MAX ((unsigned char)UCHAR_MAX)
+#  else
+#    ifdef MAXUCHAR
+#      define PERL_UCHAR_MAX ((unsigned char)MAXUCHAR)
+#    else
+#      define PERL_UCHAR_MAX ((unsigned char)~(unsigned)0)
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_USHORT_MIN
+#  define PERL_USHORT_MIN ((unsigned short)0)
+#endif
+
+#ifndef PERL_USHORT_MAX
+#  ifdef USHORT_MAX
+#    define PERL_USHORT_MAX ((unsigned short)USHORT_MAX)
+#  else
+#    ifdef MAXUSHORT
+#      define PERL_USHORT_MAX ((unsigned short)MAXUSHORT)
+#    else
+#      ifdef USHRT_MAX
+#        define PERL_USHORT_MAX ((unsigned short)USHRT_MAX)
+#      else
+#        define PERL_USHORT_MAX ((unsigned short)~(unsigned)0)
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_SHORT_MAX
+#  ifdef SHORT_MAX
+#    define PERL_SHORT_MAX ((short)SHORT_MAX)
+#  else
+#    ifdef MAXSHORT    /* Often used in <values.h> */
+#      define PERL_SHORT_MAX ((short)MAXSHORT)
+#    else
+#      ifdef SHRT_MAX
+#        define PERL_SHORT_MAX ((short)SHRT_MAX)
+#      else
+#        define PERL_SHORT_MAX ((short) (PERL_USHORT_MAX >> 1))
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_SHORT_MIN
+#  ifdef SHORT_MIN
+#    define PERL_SHORT_MIN ((short)SHORT_MIN)
+#  else
+#    ifdef MINSHORT
+#      define PERL_SHORT_MIN ((short)MINSHORT)
+#    else
+#      ifdef SHRT_MIN
+#        define PERL_SHORT_MIN ((short)SHRT_MIN)
+#      else
+#        define PERL_SHORT_MIN (-PERL_SHORT_MAX - ((3 & -1) == 3))
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_UINT_MAX
+#  ifdef UINT_MAX
+#    define PERL_UINT_MAX ((unsigned int)UINT_MAX)
+#  else
+#    ifdef MAXUINT
+#      define PERL_UINT_MAX ((unsigned int)MAXUINT)
+#    else
+#      define PERL_UINT_MAX (~(unsigned int)0)
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_UINT_MIN
+#  define PERL_UINT_MIN ((unsigned int)0)
+#endif
+
+#ifndef PERL_INT_MAX
+#  ifdef INT_MAX
+#    define PERL_INT_MAX ((int)INT_MAX)
+#  else
+#    ifdef MAXINT    /* Often used in <values.h> */
+#      define PERL_INT_MAX ((int)MAXINT)
+#    else
+#      define PERL_INT_MAX ((int)(PERL_UINT_MAX >> 1))
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_INT_MIN
+#  ifdef INT_MIN
+#    define PERL_INT_MIN ((int)INT_MIN)
+#  else
+#    ifdef MININT
+#      define PERL_INT_MIN ((int)MININT)
+#    else
+#      define PERL_INT_MIN (-PERL_INT_MAX - ((3 & -1) == 3))
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_ULONG_MAX
+#  ifdef ULONG_MAX
+#    define PERL_ULONG_MAX ((unsigned long)ULONG_MAX)
+#  else
+#    ifdef MAXULONG
+#      define PERL_ULONG_MAX ((unsigned long)MAXULONG)
+#    else
+#      define PERL_ULONG_MAX (~(unsigned long)0)
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_ULONG_MIN
+#  define PERL_ULONG_MIN ((unsigned long)0L)
+#endif
+
+#ifndef PERL_LONG_MAX
+#  ifdef LONG_MAX
+#    define PERL_LONG_MAX ((long)LONG_MAX)
+#  else
+#    ifdef MAXLONG
+#      define PERL_LONG_MAX ((long)MAXLONG)
+#    else
+#      define PERL_LONG_MAX ((long) (PERL_ULONG_MAX >> 1))
+#    endif
+#  endif
+#endif
+
+#ifndef PERL_LONG_MIN
+#  ifdef LONG_MIN
+#    define PERL_LONG_MIN ((long)LONG_MIN)
+#  else
+#    ifdef MINLONG
+#      define PERL_LONG_MIN ((long)MINLONG)
+#    else
+#      define PERL_LONG_MIN (-PERL_LONG_MAX - ((3 & -1) == 3))
+#    endif
+#  endif
+#endif
+
+#if defined(HAS_QUAD) && (defined(convex) || defined(uts))
+#  ifndef PERL_UQUAD_MAX
+#    ifdef ULONGLONG_MAX
+#      define PERL_UQUAD_MAX ((unsigned long long)ULONGLONG_MAX)
+#    else
+#      ifdef MAXULONGLONG
+#        define PERL_UQUAD_MAX ((unsigned long long)MAXULONGLONG)
+#      else
+#        define PERL_UQUAD_MAX (~(unsigned long long)0)
+#      endif
+#    endif
+#  endif
+
+#  ifndef PERL_UQUAD_MIN
+#    define PERL_UQUAD_MIN ((unsigned long long)0L)
+#  endif
+
+#  ifndef PERL_QUAD_MAX
+#    ifdef LONGLONG_MAX
+#      define PERL_QUAD_MAX ((long long)LONGLONG_MAX)
+#    else
+#      ifdef MAXLONGLONG
+#        define PERL_QUAD_MAX ((long long)MAXLONGLONG)
+#      else
+#        define PERL_QUAD_MAX ((long long) (PERL_UQUAD_MAX >> 1))
+#      endif
+#    endif
+#  endif
+
+#  ifndef PERL_QUAD_MIN
+#    ifdef LONGLONG_MIN
+#      define PERL_QUAD_MIN ((long long)LONGLONG_MIN)
+#    else
+#      ifdef MINLONGLONG
+#        define PERL_QUAD_MIN ((long long)MINLONGLONG)
+#      else
+#        define PERL_QUAD_MIN (-PERL_QUAD_MAX - ((3 & -1) == 3))
+#      endif
+#    endif
+#  endif
+#endif
+
+/* This is based on code from 5.003 perl.h */
+#ifdef HAS_QUAD
+#  ifdef cray
+#ifndef IVTYPE
+#  define IVTYPE                         int
+#endif
+
+#ifndef IV_MIN
+#  define IV_MIN                         PERL_INT_MIN
+#endif
+
+#ifndef IV_MAX
+#  define IV_MAX                         PERL_INT_MAX
+#endif
+
+#ifndef UV_MIN
+#  define UV_MIN                         PERL_UINT_MIN
+#endif
+
+#ifndef UV_MAX
+#  define UV_MAX                         PERL_UINT_MAX
+#endif
+
+#    ifdef INTSIZE
+#ifndef IVSIZE
+#  define IVSIZE                         INTSIZE
+#endif
+
+#    endif
+#  else
+#    if defined(convex) || defined(uts)
+#ifndef IVTYPE
+#  define IVTYPE                         long long
+#endif
+
+#ifndef IV_MIN
+#  define IV_MIN                         PERL_QUAD_MIN
+#endif
+
+#ifndef IV_MAX
+#  define IV_MAX                         PERL_QUAD_MAX
+#endif
+
+#ifndef UV_MIN
+#  define UV_MIN                         PERL_UQUAD_MIN
+#endif
+
+#ifndef UV_MAX
+#  define UV_MAX                         PERL_UQUAD_MAX
+#endif
+
+#      ifdef LONGLONGSIZE
+#ifndef IVSIZE
+#  define IVSIZE                         LONGLONGSIZE
+#endif
+
+#      endif
+#    else
+#ifndef IVTYPE
+#  define IVTYPE                         long
+#endif
+
+#ifndef IV_MIN
+#  define IV_MIN                         PERL_LONG_MIN
+#endif
+
+#ifndef IV_MAX
+#  define IV_MAX                         PERL_LONG_MAX
+#endif
+
+#ifndef UV_MIN
+#  define UV_MIN                         PERL_ULONG_MIN
+#endif
+
+#ifndef UV_MAX
+#  define UV_MAX                         PERL_ULONG_MAX
+#endif
+
+#      ifdef LONGSIZE
+#ifndef IVSIZE
+#  define IVSIZE                         LONGSIZE
+#endif
+
+#      endif
+#    endif
+#  endif
+#ifndef IVSIZE
+#  define IVSIZE                         8
+#endif
+
+#ifndef PERL_QUAD_MIN
+#  define PERL_QUAD_MIN                  IV_MIN
+#endif
+
+#ifndef PERL_QUAD_MAX
+#  define PERL_QUAD_MAX                  IV_MAX
+#endif
+
+#ifndef PERL_UQUAD_MIN
+#  define PERL_UQUAD_MIN                 UV_MIN
+#endif
+
+#ifndef PERL_UQUAD_MAX
+#  define PERL_UQUAD_MAX                 UV_MAX
+#endif
+
+#else
+#ifndef IVTYPE
+#  define IVTYPE                         long
+#endif
+
+#ifndef IV_MIN
+#  define IV_MIN                         PERL_LONG_MIN
+#endif
+
+#ifndef IV_MAX
+#  define IV_MAX                         PERL_LONG_MAX
+#endif
+
+#ifndef UV_MIN
+#  define UV_MIN                         PERL_ULONG_MIN
+#endif
+
+#ifndef UV_MAX
+#  define UV_MAX                         PERL_ULONG_MAX
+#endif
+
+#endif
+
+#ifndef IVSIZE
+#  ifdef LONGSIZE
+#    define IVSIZE LONGSIZE
+#  else
+#    define IVSIZE 4 /* A bold guess, but the best we can make. */
+#  endif
+#endif
+#ifndef UVTYPE
+#  define UVTYPE                         unsigned IVTYPE
+#endif
+
+#ifndef UVSIZE
+#  define UVSIZE                         IVSIZE
+#endif
+
+#ifndef sv_setuv
+#  define sv_setuv(sv, uv)                  \
+   STMT_START {                             \
+       UV TeMpUv = uv;                      \
+       if (TeMpUv <= IV_MAX)                \
+           sv_setiv(sv, TeMpUv);            \
+       else                                 \
+           sv_setnv(sv, (double)TeMpUv);    \
+   } STMT_END
+#endif
+
+#ifndef newSVuv
+#  define newSVuv(uv) ((uv) <= IV_MAX ? newSViv((IV)uv) : newSVnv((NV)uv))
+#endif
+#ifndef sv_2uv
+#  define sv_2uv(sv)                     ((PL_Sv = (sv)), (UV) (SvNOK(PL_Sv) ? SvNV(PL_Sv) : sv_2nv(PL_Sv)))
+#endif
+
+#ifndef SvUVX
+#  define SvUVX(sv)                      ((UV)SvIVX(sv))
+#endif
+
+#ifndef SvUVXx
+#  define SvUVXx(sv)                     SvUVX(sv)
+#endif
+
+#ifndef SvUV
+#  define SvUV(sv)                       (SvIOK(sv) ? SvUVX(sv) : sv_2uv(sv))
+#endif
+
+#ifndef SvUVx
+#  define SvUVx(sv)                      ((PL_Sv = (sv)), SvUV(PL_Sv))
+#endif
+
+/* Hint: sv_uv
+ * Always use the SvUVx() macro instead of sv_uv().
+ */
+#ifndef sv_uv
+#  define sv_uv(sv)                      SvUVx(sv)
+#endif
+#ifndef XST_mUV
+#  define XST_mUV(i,v)                   (ST(i) = sv_2mortal(newSVuv(v))  )
+#endif
+
+#ifndef XSRETURN_UV
+#  define XSRETURN_UV(v)                 STMT_START { XST_mUV(0,v);  XSRETURN(1); } STMT_END
+#endif
+#ifndef PUSHu
+#  define PUSHu(u)                       STMT_START { sv_setuv(TARG, (UV)(u)); PUSHTARG;  } STMT_END
+#endif
+
+#ifndef XPUSHu
+#  define XPUSHu(u)                      STMT_START { sv_setuv(TARG, (UV)(u)); XPUSHTARG; } STMT_END
+#endif
+
+#if (PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION <= 5))
+/* Replace: 1 */
+#  define PL_DBsingle               DBsingle
+#  define PL_DBsub                  DBsub
+#  define PL_Sv                     Sv
+#  define PL_compiling              compiling
+#  define PL_copline                copline
+#  define PL_curcop                 curcop
+#  define PL_curstash               curstash
+#  define PL_debstash               debstash
+#  define PL_defgv                  defgv
+#  define PL_diehook                diehook
+#  define PL_dirty                  dirty
+#  define PL_dowarn                 dowarn
+#  define PL_errgv                  errgv
+#  define PL_hexdigit               hexdigit
+#  define PL_hints                  hints
+#  define PL_na                            na
+#  define PL_no_modify              no_modify
+#  define PL_perl_destruct_level    perl_destruct_level
+#  define PL_perldb                 perldb
+#  define PL_ppaddr                 ppaddr
+#  define PL_rsfp_filters           rsfp_filters
+#  define PL_rsfp                   rsfp
+#  define PL_stack_base             stack_base
+#  define PL_stack_sp               stack_sp
+#  define PL_stdingv                stdingv
+#  define PL_sv_arenaroot           sv_arenaroot
+#  define PL_sv_no                  sv_no
+#  define PL_sv_undef               sv_undef
+#  define PL_sv_yes                 sv_yes
+#  define PL_tainted                tainted
+#  define PL_tainting               tainting
+/* Replace: 0 */
+#endif
+
+#ifndef PERL_UNUSED_DECL
+#  ifdef HASATTRIBUTE
+#    if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER)
+#      define PERL_UNUSED_DECL
+#    else
+#      define PERL_UNUSED_DECL __attribute__((unused))
+#    endif
+#  else
+#    define PERL_UNUSED_DECL
+#  endif
+#endif
+#ifndef NOOP
+#  define NOOP                           (void)0
+#endif
+
+#ifndef dNOOP
+#  define dNOOP                          extern int Perl___notused PERL_UNUSED_DECL
+#endif
+
+#ifndef NVTYPE
+#  if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE)
+#    define NVTYPE long double
+#  else
+#    define NVTYPE double
+#  endif
+typedef NVTYPE NV;
+#endif
+
+#ifndef INT2PTR
+
+#  if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE)
+#    define PTRV                  UV
+#    define INT2PTR(any,d)        (any)(d)
+#  else
+#    if PTRSIZE == LONGSIZE
+#      define PTRV                unsigned long
+#    else
+#      define PTRV                unsigned
+#    endif
+#    define INT2PTR(any,d)        (any)(PTRV)(d)
+#  endif
+
+#  define NUM2PTR(any,d)  (any)(PTRV)(d)
+#  define PTR2IV(p)       INT2PTR(IV,p)
+#  define PTR2UV(p)       INT2PTR(UV,p)
+#  define PTR2NV(p)       NUM2PTR(NV,p)
+
+#  if PTRSIZE == LONGSIZE
+#    define PTR2ul(p)     (unsigned long)(p)
+#  else
+#    define PTR2ul(p)     INT2PTR(unsigned long,p)
+#  endif
+
+#endif /* !INT2PTR */
+
+#undef START_EXTERN_C
+#undef END_EXTERN_C
+#undef EXTERN_C
+#ifdef __cplusplus
+#  define START_EXTERN_C extern "C" {
+#  define END_EXTERN_C }
+#  define EXTERN_C extern "C"
+#else
+#  define START_EXTERN_C
+#  define END_EXTERN_C
+#  define EXTERN_C extern
+#endif
+
+#ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN
+#  if defined(__STRICT_ANSI__) && defined(PERL_GCC_PEDANTIC)
+#    define PERL_GCC_BRACE_GROUPS_FORBIDDEN
+#  endif
+#endif
+
+#undef STMT_START
+#undef STMT_END
+#if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus)
+#  define STMT_START   (void)( /* gcc supports ``({ STATEMENTS; })'' */
+#  define STMT_END     )
+#else
+#  if defined(VOIDFLAGS) && (VOIDFLAGS) && (defined(sun) || defined(__sun__)) && !defined(__GNUC__)
+#    define STMT_START if (1)
+#    define STMT_END   else (void)0
+#  else
+#    define STMT_START do
+#    define STMT_END   while (0)
+#  endif
+#endif
+#ifndef boolSV
+#  define boolSV(b)                      ((b) ? &PL_sv_yes : &PL_sv_no)
+#endif
+
+/* DEFSV appears first in 5.004_56 */
+#ifndef DEFSV
+#  define DEFSV                          GvSV(PL_defgv)
+#endif
+
+#ifndef SAVE_DEFSV
+#  define SAVE_DEFSV                     SAVESPTR(GvSV(PL_defgv))
+#endif
+
+/* Older perls (<=5.003) lack AvFILLp */
+#ifndef AvFILLp
+#  define AvFILLp                        AvFILL
+#endif
+#ifndef ERRSV
+#  define ERRSV                          get_sv("@",FALSE)
+#endif
+#ifndef newSVpvn
+#  define newSVpvn(data,len)             ((data)                                              \
+                                    ? ((len) ? newSVpv((data), (len)) : newSVpv("", 0)) \
+                                    : newSV(0))
+#endif
+
+/* Hint: gv_stashpvn
+ * This function's backport doesn't support the length parameter, but
+ * rather ignores it. Portability can only be ensured if the length
+ * parameter is used for speed reasons, but the length can always be
+ * correctly computed from the string argument.
+ */
+#ifndef gv_stashpvn
+#  define gv_stashpvn(str,len,create)    gv_stashpv(str,create)
+#endif
+
+/* Replace: 1 */
+#ifndef get_cv
+#  define get_cv                         perl_get_cv
+#endif
+
+#ifndef get_sv
+#  define get_sv                         perl_get_sv
+#endif
+
+#ifndef get_av
+#  define get_av                         perl_get_av
+#endif
+
+#ifndef get_hv
+#  define get_hv                         perl_get_hv
+#endif
+
+/* Replace: 0 */
+
+#ifdef HAS_MEMCMP
+#ifndef memNE
+#  define memNE(s1,s2,l)                 (memcmp(s1,s2,l))
+#endif
+
+#ifndef memEQ
+#  define memEQ(s1,s2,l)                 (!memcmp(s1,s2,l))
+#endif
+
+#else
+#ifndef memNE
+#  define memNE(s1,s2,l)                 (bcmp(s1,s2,l))
+#endif
+
+#ifndef memEQ
+#  define memEQ(s1,s2,l)                 (!bcmp(s1,s2,l))
+#endif
+
+#endif
+#ifndef MoveD
+#  define MoveD(s,d,n,t)                 memmove((char*)(d),(char*)(s), (n) * sizeof(t))
+#endif
+
+#ifndef CopyD
+#  define CopyD(s,d,n,t)                 memcpy((char*)(d),(char*)(s), (n) * sizeof(t))
+#endif
+
+#ifdef HAS_MEMSET
+#ifndef ZeroD
+#  define ZeroD(d,n,t)                   memzero((char*)(d), (n) * sizeof(t))
+#endif
+
+#else
+#ifndef ZeroD
+#  define ZeroD(d,n,t)                   ((void)memzero((char*)(d), (n) * sizeof(t)),d)
+#endif
+
+#endif
+#ifndef Poison
+#  define Poison(d,n,t)                  (void)memset((char*)(d), 0xAB, (n) * sizeof(t))
+#endif
+#ifndef dUNDERBAR
+#  define dUNDERBAR                      dNOOP
+#endif
+
+#ifndef UNDERBAR
+#  define UNDERBAR                       DEFSV
+#endif
+#ifndef dAX
+#  define dAX                            I32 ax = MARK - PL_stack_base + 1
+#endif
+
+#ifndef dITEMS
+#  define dITEMS                         I32 items = SP - MARK
+#endif
+#ifndef dXSTARG
+#  define dXSTARG                        SV * targ = sv_newmortal()
+#endif
+#ifndef dTHR
+#  define dTHR                           dNOOP
+#endif
+#ifndef dTHX
+#  define dTHX                           dNOOP
+#endif
+
+#ifndef dTHXa
+#  define dTHXa(x)                       dNOOP
+#endif
+#ifndef pTHX
+#  define pTHX                           void
+#endif
+
+#ifndef pTHX_
+#  define pTHX_
+#endif
+
+#ifndef aTHX
+#  define aTHX
+#endif
+
+#ifndef aTHX_
+#  define aTHX_
+#endif
+#ifndef dTHXoa
+#  define dTHXoa(x)                      dTHXa(x)
+#endif
+#ifndef PUSHmortal
+#  define PUSHmortal                     PUSHs(sv_newmortal())
+#endif
+
+#ifndef mPUSHp
+#  define mPUSHp(p,l)                    sv_setpvn_mg(PUSHmortal, (p), (l))
+#endif
+
+#ifndef mPUSHn
+#  define mPUSHn(n)                      sv_setnv_mg(PUSHmortal, (NV)(n))
+#endif
+
+#ifndef mPUSHi
+#  define mPUSHi(i)                      sv_setiv_mg(PUSHmortal, (IV)(i))
+#endif
+
+#ifndef mPUSHu
+#  define mPUSHu(u)                      sv_setuv_mg(PUSHmortal, (UV)(u))
+#endif
+#ifndef XPUSHmortal
+#  define XPUSHmortal                    XPUSHs(sv_newmortal())
+#endif
+
+#ifndef mXPUSHp
+#  define mXPUSHp(p,l)                   STMT_START { EXTEND(sp,1); sv_setpvn_mg(PUSHmortal, (p), (l)); } STMT_END
+#endif
+
+#ifndef mXPUSHn
+#  define mXPUSHn(n)                     STMT_START { EXTEND(sp,1); sv_setnv_mg(PUSHmortal, (NV)(n)); } STMT_END
+#endif
+
+#ifndef mXPUSHi
+#  define mXPUSHi(i)                     STMT_START { EXTEND(sp,1); sv_setiv_mg(PUSHmortal, (IV)(i)); } STMT_END
+#endif
+
+#ifndef mXPUSHu
+#  define mXPUSHu(u)                     STMT_START { EXTEND(sp,1); sv_setuv_mg(PUSHmortal, (UV)(u)); } STMT_END
+#endif
+
+/* Replace: 1 */
+#ifndef call_sv
+#  define call_sv                        perl_call_sv
+#endif
+
+#ifndef call_pv
+#  define call_pv                        perl_call_pv
+#endif
+
+#ifndef call_argv
+#  define call_argv                      perl_call_argv
+#endif
+
+#ifndef call_method
+#  define call_method                    perl_call_method
+#endif
+#ifndef eval_sv
+#  define eval_sv                        perl_eval_sv
+#endif
+
+/* Replace: 0 */
+
+/* Replace perl_eval_pv with eval_pv */
+/* eval_pv depends on eval_sv */
+
+#ifndef eval_pv
+#if defined(NEED_eval_pv)
+static SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error);
+static
+#else
+extern SV* DPPP_(my_eval_pv)(char *p, I32 croak_on_error);
+#endif
+
+#ifdef eval_pv
+#  undef eval_pv
+#endif
+#define eval_pv(a,b) DPPP_(my_eval_pv)(aTHX_ a,b)
+#define Perl_eval_pv DPPP_(my_eval_pv)
+
+#if defined(NEED_eval_pv) || defined(NEED_eval_pv_GLOBAL)
+
+SV*
+DPPP_(my_eval_pv)(char *p, I32 croak_on_error)
+{
+    dSP;
+    SV* sv = newSVpv(p, 0);
+
+    PUSHMARK(sp);
+    eval_sv(sv, G_SCALAR);
+    SvREFCNT_dec(sv);
+
+    SPAGAIN;
+    sv = POPs;
+    PUTBACK;
+
+    if (croak_on_error && SvTRUE(GvSV(errgv)))
+       croak(SvPVx(GvSV(errgv), na));
+
+    return sv;
+}
+
+#endif
+#endif
+#ifndef newRV_inc
+#  define newRV_inc(sv)                  newRV(sv)   /* Replace */
+#endif
+
+#ifndef newRV_noinc
+#if defined(NEED_newRV_noinc)
+static SV * DPPP_(my_newRV_noinc)(SV *sv);
+static
+#else
+extern SV * DPPP_(my_newRV_noinc)(SV *sv);
+#endif
+
+#ifdef newRV_noinc
+#  undef newRV_noinc
+#endif
+#define newRV_noinc(a) DPPP_(my_newRV_noinc)(aTHX_ a)
+#define Perl_newRV_noinc DPPP_(my_newRV_noinc)
+
+#if defined(NEED_newRV_noinc) || defined(NEED_newRV_noinc_GLOBAL)
+SV *
+DPPP_(my_newRV_noinc)(SV *sv)
+{
+  SV *rv = (SV *)newRV(sv);
+  SvREFCNT_dec(sv);
+  return rv;
+}
+#endif
+#endif
+
+/* Hint: newCONSTSUB
+ * Returns a CV* as of perl-5.7.1. This return value is not supported
+ * by Devel::PPPort.
+ */
+
+/* newCONSTSUB from IO.xs is in the core starting with 5.004_63 */
+#if ((PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION < 63))) && ((PERL_VERSION != 4) || (PERL_SUBVERSION != 5))
+#if defined(NEED_newCONSTSUB)
+static void DPPP_(my_newCONSTSUB)(HV *stash, char *name, SV *sv);
+static
+#else
+extern void DPPP_(my_newCONSTSUB)(HV *stash, char *name, SV *sv);
+#endif
+
+#ifdef newCONSTSUB
+#  undef newCONSTSUB
+#endif
+#define newCONSTSUB(a,b,c) DPPP_(my_newCONSTSUB)(aTHX_ a,b,c)
+#define Perl_newCONSTSUB DPPP_(my_newCONSTSUB)
+
+#if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL)
+
+void
+DPPP_(my_newCONSTSUB)(HV *stash, char *name, SV *sv)
+{
+       U32 oldhints = PL_hints;
+       HV *old_cop_stash = PL_curcop->cop_stash;
+       HV *old_curstash = PL_curstash;
+       line_t oldline = PL_curcop->cop_line;
+       PL_curcop->cop_line = PL_copline;
+
+       PL_hints &= ~HINT_BLOCK_SCOPE;
+       if (stash)
+               PL_curstash = PL_curcop->cop_stash = stash;
+
+       newSUB(
+
+#if   ((PERL_VERSION < 3) || ((PERL_VERSION == 3) && (PERL_SUBVERSION < 22)))
+               start_subparse(),
+#elif ((PERL_VERSION == 3) && (PERL_SUBVERSION == 22))
+               start_subparse(0),
+#else  /* 5.003_23  onwards */
+               start_subparse(FALSE, 0),
+#endif
+
+               newSVOP(OP_CONST, 0, newSVpv(name,0)),
+               newSVOP(OP_CONST, 0, &PL_sv_no),   /* SvPV(&PL_sv_no) == "" -- GMB */
+               newSTATEOP(0, Nullch, newSVOP(OP_CONST, 0, sv))
+       );
+
+       PL_hints = oldhints;
+       PL_curcop->cop_stash = old_cop_stash;
+       PL_curstash = old_curstash;
+       PL_curcop->cop_line = oldline;
+}
+#endif
+#endif
+
+/*
+ * Boilerplate macros for initializing and accessing interpreter-local
+ * data from C.  All statics in extensions should be reworked to use
+ * this, if you want to make the extension thread-safe.  See ext/re/re.xs
+ * for an example of the use of these macros.
+ *
+ * Code that uses these macros is responsible for the following:
+ * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts"
+ * 2. Declare a typedef named my_cxt_t that is a structure that contains
+ *    all the data that needs to be interpreter-local.
+ * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t.
+ * 4. Use the MY_CXT_INIT macro such that it is called exactly once
+ *    (typically put in the BOOT: section).
+ * 5. Use the members of the my_cxt_t structure everywhere as
+ *    MY_CXT.member.
+ * 6. Use the dMY_CXT macro (a declaration) in all the functions that
+ *    access MY_CXT.
+ */
+
+#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || \
+    defined(PERL_CAPI)    || defined(PERL_IMPLICIT_CONTEXT)
+
+#ifndef START_MY_CXT
+
+/* This must appear in all extensions that define a my_cxt_t structure,
+ * right after the definition (i.e. at file scope).  The non-threads
+ * case below uses it to declare the data as static. */
+#define START_MY_CXT
+
+#if (PERL_VERSION < 4 || (PERL_VERSION == 4 && PERL_SUBVERSION < 68 ))
+/* Fetches the SV that keeps the per-interpreter data. */
+#define dMY_CXT_SV \
+       SV *my_cxt_sv = get_sv(MY_CXT_KEY, FALSE)
+#else /* >= perl5.004_68 */
+#define dMY_CXT_SV \
+       SV *my_cxt_sv = *hv_fetch(PL_modglobal, MY_CXT_KEY,             \
+                                 sizeof(MY_CXT_KEY)-1, TRUE)
+#endif /* < perl5.004_68 */
+
+/* This declaration should be used within all functions that use the
+ * interpreter-local data. */
+#define dMY_CXT        \
+       dMY_CXT_SV;                                                     \
+       my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*,SvUV(my_cxt_sv))
+
+/* Creates and zeroes the per-interpreter data.
+ * (We allocate my_cxtp in a Perl SV so that it will be released when
+ * the interpreter goes away.) */
+#define MY_CXT_INIT \
+       dMY_CXT_SV;                                                     \
+       /* newSV() allocates one more than needed */                    \
+       my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\
+       Zero(my_cxtp, 1, my_cxt_t);                                     \
+       sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+
+/* This macro must be used to access members of the my_cxt_t structure.
+ * e.g. MYCXT.some_data */
+#define MY_CXT         (*my_cxtp)
+
+/* Judicious use of these macros can reduce the number of times dMY_CXT
+ * is used.  Use is similar to pTHX, aTHX etc. */
+#define pMY_CXT                my_cxt_t *my_cxtp
+#define pMY_CXT_       pMY_CXT,
+#define _pMY_CXT       ,pMY_CXT
+#define aMY_CXT                my_cxtp
+#define aMY_CXT_       aMY_CXT,
+#define _aMY_CXT       ,aMY_CXT
+
+#endif /* START_MY_CXT */
+
+#ifndef MY_CXT_CLONE
+/* Clones the per-interpreter data. */
+#define MY_CXT_CLONE \
+       dMY_CXT_SV;                                                     \
+       my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\
+       Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t);\
+       sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+#endif
+
+#else /* single interpreter */
+
+#ifndef START_MY_CXT
+
+#define START_MY_CXT   static my_cxt_t my_cxt;
+#define dMY_CXT_SV     dNOOP
+#define dMY_CXT                dNOOP
+#define MY_CXT_INIT    NOOP
+#define MY_CXT         my_cxt
+
+#define pMY_CXT                void
+#define pMY_CXT_
+#define _pMY_CXT
+#define aMY_CXT
+#define aMY_CXT_
+#define _aMY_CXT
+
+#endif /* START_MY_CXT */
+
+#ifndef MY_CXT_CLONE
+#define MY_CXT_CLONE   NOOP
+#endif
+
+#endif
+
+#ifndef IVdf
+#  if IVSIZE == LONGSIZE
+#    define    IVdf      "ld"
+#    define    UVuf      "lu"
+#    define    UVof      "lo"
+#    define    UVxf      "lx"
+#    define    UVXf      "lX"
+#  else
+#    if IVSIZE == INTSIZE
+#      define  IVdf      "d"
+#      define  UVuf      "u"
+#      define  UVof      "o"
+#      define  UVxf      "x"
+#      define  UVXf      "X"
+#    endif
+#  endif
+#endif
+
+#ifndef NVef
+#  if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \
+      defined(PERL_PRIfldbl) /* Not very likely, but let's try anyway. */
+#    define NVef          PERL_PRIeldbl
+#    define NVff          PERL_PRIfldbl
+#    define NVgf          PERL_PRIgldbl
+#  else
+#    define NVef          "e"
+#    define NVff          "f"
+#    define NVgf          "g"
+#  endif
+#endif
+
+#ifndef SvPV_nolen
+
+#if defined(NEED_sv_2pv_nolen)
+static char * DPPP_(my_sv_2pv_nolen)(pTHX_ register SV *sv);
+static
+#else
+extern char * DPPP_(my_sv_2pv_nolen)(pTHX_ register SV *sv);
+#endif
+
+#ifdef sv_2pv_nolen
+#  undef sv_2pv_nolen
+#endif
+#define sv_2pv_nolen(a) DPPP_(my_sv_2pv_nolen)(aTHX_ a)
+#define Perl_sv_2pv_nolen DPPP_(my_sv_2pv_nolen)
+
+#if defined(NEED_sv_2pv_nolen) || defined(NEED_sv_2pv_nolen_GLOBAL)
+
+char *
+DPPP_(my_sv_2pv_nolen)(pTHX_ register SV *sv)
+{
+  STRLEN n_a;
+  return sv_2pv(sv, &n_a);
+}
+
+#endif
+
+/* Hint: sv_2pv_nolen
+ * Use the SvPV_nolen() macro instead of sv_2pv_nolen().
+ */
+
+/* SvPV_nolen depends on sv_2pv_nolen */
+#define SvPV_nolen(sv) \
+          ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+           ? SvPVX(sv) : sv_2pv_nolen(sv))
+
+#endif
+
+#ifdef SvPVbyte
+
+/* Hint: SvPVbyte
+ * Does not work in perl-5.6.1, ppport.h implements a version
+ * borrowed from perl-5.7.3.
+ */
+
+#if ((PERL_VERSION < 7) || ((PERL_VERSION == 7) && (PERL_SUBVERSION < 0)))
+
+#if defined(NEED_sv_2pvbyte)
+static char * DPPP_(my_sv_2pvbyte)(pTHX_ register SV *sv, STRLEN *lp);
+static
+#else
+extern char * DPPP_(my_sv_2pvbyte)(pTHX_ register SV *sv, STRLEN *lp);
+#endif
+
+#ifdef sv_2pvbyte
+#  undef sv_2pvbyte
+#endif
+#define sv_2pvbyte(a,b) DPPP_(my_sv_2pvbyte)(aTHX_ a,b)
+#define Perl_sv_2pvbyte DPPP_(my_sv_2pvbyte)
+
+#if defined(NEED_sv_2pvbyte) || defined(NEED_sv_2pvbyte_GLOBAL)
+
+char *
+DPPP_(my_sv_2pvbyte)(pTHX_ register SV *sv, STRLEN *lp)
+{
+  sv_utf8_downgrade(sv,0);
+  return SvPV(sv,*lp);
+}
+
+#endif
+
+/* Hint: sv_2pvbyte
+ * Use the SvPVbyte() macro instead of sv_2pvbyte().
+ */
+
+#undef SvPVbyte
+
+/* SvPVbyte depends on sv_2pvbyte */
+#define SvPVbyte(sv, lp)                                                \
+        ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK)                \
+         ? ((lp = SvCUR(sv)), SvPVX(sv)) : sv_2pvbyte(sv, &lp))
+
+#endif
+
+#else
+
+#  define SvPVbyte          SvPV
+#  define sv_2pvbyte        sv_2pv
+
+#endif
+
+/* sv_2pvbyte_nolen depends on sv_2pv_nolen */
+#ifndef sv_2pvbyte_nolen
+#  define sv_2pvbyte_nolen               sv_2pv_nolen
+#endif
+
+/* Hint: sv_pvn
+ * Always use the SvPV() macro instead of sv_pvn().
+ */
+#ifndef sv_pvn
+#  define sv_pvn(sv, len)                SvPV(sv, len)
+#endif
+
+/* Hint: sv_pvn_force
+ * Always use the SvPV_force() macro instead of sv_pvn_force().
+ */
+#ifndef sv_pvn_force
+#  define sv_pvn_force(sv, len)          SvPV_force(sv, len)
+#endif
+
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(vnewSVpvf)
+#if defined(NEED_vnewSVpvf)
+static SV * DPPP_(my_vnewSVpvf)(pTHX_ const char * pat, va_list * args);
+static
+#else
+extern SV * DPPP_(my_vnewSVpvf)(pTHX_ const char * pat, va_list * args);
+#endif
+
+#ifdef vnewSVpvf
+#  undef vnewSVpvf
+#endif
+#define vnewSVpvf(a,b) DPPP_(my_vnewSVpvf)(aTHX_ a,b)
+#define Perl_vnewSVpvf DPPP_(my_vnewSVpvf)
+
+#if defined(NEED_vnewSVpvf) || defined(NEED_vnewSVpvf_GLOBAL)
+
+SV *
+DPPP_(my_vnewSVpvf)(pTHX_ const char *pat, va_list *args)
+{
+  register SV *sv = newSV(0);
+  sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*));
+  return sv;
+}
+
+#endif
+#endif
+
+/* sv_vcatpvf depends on sv_vcatpvfn */
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_vcatpvf)
+#  define sv_vcatpvf(sv, pat, args)  sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*))
+#endif
+
+/* sv_vsetpvf depends on sv_vsetpvfn */
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_vsetpvf)
+#  define sv_vsetpvf(sv, pat, args)  sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*))
+#endif
+
+/* sv_catpvf_mg depends on sv_vcatpvfn, sv_catpvf_mg_nocontext */
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_catpvf_mg)
+#if defined(NEED_sv_catpvf_mg)
+static void DPPP_(my_sv_catpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_catpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+#endif
+
+#define Perl_sv_catpvf_mg DPPP_(my_sv_catpvf_mg)
+
+#if defined(NEED_sv_catpvf_mg) || defined(NEED_sv_catpvf_mg_GLOBAL)
+
+void
+DPPP_(my_sv_catpvf_mg)(pTHX_ SV *sv, const char *pat, ...)
+{
+  va_list args;
+  va_start(args, pat);
+  sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+  SvSETMAGIC(sv);
+  va_end(args);
+}
+
+#endif
+#endif
+
+/* sv_catpvf_mg_nocontext depends on sv_vcatpvfn */
+#ifdef PERL_IMPLICIT_CONTEXT
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_catpvf_mg_nocontext)
+#if defined(NEED_sv_catpvf_mg_nocontext)
+static void DPPP_(my_sv_catpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_catpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+#endif
+
+#define sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext)
+#define Perl_sv_catpvf_mg_nocontext DPPP_(my_sv_catpvf_mg_nocontext)
+
+#if defined(NEED_sv_catpvf_mg_nocontext) || defined(NEED_sv_catpvf_mg_nocontext_GLOBAL)
+
+void
+DPPP_(my_sv_catpvf_mg_nocontext)(SV *sv, const char *pat, ...)
+{
+  dTHX;
+  va_list args;
+  va_start(args, pat);
+  sv_vcatpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+  SvSETMAGIC(sv);
+  va_end(args);
+}
+
+#endif
+#endif
+#endif
+
+#ifndef sv_catpvf_mg
+#  ifdef PERL_IMPLICIT_CONTEXT
+#    define sv_catpvf_mg   Perl_sv_catpvf_mg_nocontext
+#  else
+#    define sv_catpvf_mg   Perl_sv_catpvf_mg
+#  endif
+#endif
+
+/* sv_vcatpvf_mg depends on sv_vcatpvfn */
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_vcatpvf_mg)
+#  define sv_vcatpvf_mg(sv, pat, args)                                     \
+   STMT_START {                                                            \
+     sv_vcatpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*));  \
+     SvSETMAGIC(sv);                                                       \
+   } STMT_END
+#endif
+
+/* sv_setpvf_mg depends on sv_vsetpvfn, sv_setpvf_mg_nocontext */
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_setpvf_mg)
+#if defined(NEED_sv_setpvf_mg)
+static void DPPP_(my_sv_setpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_setpvf_mg)(pTHX_ SV * sv, const char * pat, ...);
+#endif
+
+#define Perl_sv_setpvf_mg DPPP_(my_sv_setpvf_mg)
+
+#if defined(NEED_sv_setpvf_mg) || defined(NEED_sv_setpvf_mg_GLOBAL)
+
+void
+DPPP_(my_sv_setpvf_mg)(pTHX_ SV *sv, const char *pat, ...)
+{
+  va_list args;
+  va_start(args, pat);
+  sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+  SvSETMAGIC(sv);
+  va_end(args);
+}
+
+#endif
+#endif
+
+/* sv_setpvf_mg_nocontext depends on sv_vsetpvfn */
+#ifdef PERL_IMPLICIT_CONTEXT
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_setpvf_mg_nocontext)
+#if defined(NEED_sv_setpvf_mg_nocontext)
+static void DPPP_(my_sv_setpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+static
+#else
+extern void DPPP_(my_sv_setpvf_mg_nocontext)(SV * sv, const char * pat, ...);
+#endif
+
+#define sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext)
+#define Perl_sv_setpvf_mg_nocontext DPPP_(my_sv_setpvf_mg_nocontext)
+
+#if defined(NEED_sv_setpvf_mg_nocontext) || defined(NEED_sv_setpvf_mg_nocontext_GLOBAL)
+
+void
+DPPP_(my_sv_setpvf_mg_nocontext)(SV *sv, const char *pat, ...)
+{
+  dTHX;
+  va_list args;
+  va_start(args, pat);
+  sv_vsetpvfn(sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+  SvSETMAGIC(sv);
+  va_end(args);
+}
+
+#endif
+#endif
+#endif
+
+#ifndef sv_setpvf_mg
+#  ifdef PERL_IMPLICIT_CONTEXT
+#    define sv_setpvf_mg   Perl_sv_setpvf_mg_nocontext
+#  else
+#    define sv_setpvf_mg   Perl_sv_setpvf_mg
+#  endif
+#endif
+
+/* sv_vsetpvf_mg depends on sv_vsetpvfn */
+#if ((PERL_VERSION > 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION >= 0))) && !defined(sv_vsetpvf_mg)
+#  define sv_vsetpvf_mg(sv, pat, args)                                     \
+   STMT_START {                                                            \
+     sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*));  \
+     SvSETMAGIC(sv);                                                       \
+   } STMT_END
+#endif
+#ifndef SvGETMAGIC
+#  define SvGETMAGIC(x)                  STMT_START { if (SvGMAGICAL(x)) mg_get(x); } STMT_END
+#endif
+#ifndef PERL_MAGIC_sv
+#  define PERL_MAGIC_sv                  '\0'
+#endif
+
+#ifndef PERL_MAGIC_overload
+#  define PERL_MAGIC_overload            'A'
+#endif
+
+#ifndef PERL_MAGIC_overload_elem
+#  define PERL_MAGIC_overload_elem       'a'
+#endif
+
+#ifndef PERL_MAGIC_overload_table
+#  define PERL_MAGIC_overload_table      'c'
+#endif
+
+#ifndef PERL_MAGIC_bm
+#  define PERL_MAGIC_bm                  'B'
+#endif
+
+#ifndef PERL_MAGIC_regdata
+#  define PERL_MAGIC_regdata             'D'
+#endif
+
+#ifndef PERL_MAGIC_regdatum
+#  define PERL_MAGIC_regdatum            'd'
+#endif
+
+#ifndef PERL_MAGIC_env
+#  define PERL_MAGIC_env                 'E'
+#endif
+
+#ifndef PERL_MAGIC_envelem
+#  define PERL_MAGIC_envelem             'e'
+#endif
+
+#ifndef PERL_MAGIC_fm
+#  define PERL_MAGIC_fm                  'f'
+#endif
+
+#ifndef PERL_MAGIC_regex_global
+#  define PERL_MAGIC_regex_global        'g'
+#endif
+
+#ifndef PERL_MAGIC_isa
+#  define PERL_MAGIC_isa                 'I'
+#endif
+
+#ifndef PERL_MAGIC_isaelem
+#  define PERL_MAGIC_isaelem             'i'
+#endif
+
+#ifndef PERL_MAGIC_nkeys
+#  define PERL_MAGIC_nkeys               'k'
+#endif
+
+#ifndef PERL_MAGIC_dbfile
+#  define PERL_MAGIC_dbfile              'L'
+#endif
+
+#ifndef PERL_MAGIC_dbline
+#  define PERL_MAGIC_dbline              'l'
+#endif
+
+#ifndef PERL_MAGIC_mutex
+#  define PERL_MAGIC_mutex               'm'
+#endif
+
+#ifndef PERL_MAGIC_shared
+#  define PERL_MAGIC_shared              'N'
+#endif
+
+#ifndef PERL_MAGIC_shared_scalar
+#  define PERL_MAGIC_shared_scalar       'n'
+#endif
+
+#ifndef PERL_MAGIC_collxfrm
+#  define PERL_MAGIC_collxfrm            'o'
+#endif
+
+#ifndef PERL_MAGIC_tied
+#  define PERL_MAGIC_tied                'P'
+#endif
+
+#ifndef PERL_MAGIC_tiedelem
+#  define PERL_MAGIC_tiedelem            'p'
+#endif
+
+#ifndef PERL_MAGIC_tiedscalar
+#  define PERL_MAGIC_tiedscalar          'q'
+#endif
+
+#ifndef PERL_MAGIC_qr
+#  define PERL_MAGIC_qr                  'r'
+#endif
+
+#ifndef PERL_MAGIC_sig
+#  define PERL_MAGIC_sig                 'S'
+#endif
+
+#ifndef PERL_MAGIC_sigelem
+#  define PERL_MAGIC_sigelem             's'
+#endif
+
+#ifndef PERL_MAGIC_taint
+#  define PERL_MAGIC_taint               't'
+#endif
+
+#ifndef PERL_MAGIC_uvar
+#  define PERL_MAGIC_uvar                'U'
+#endif
+
+#ifndef PERL_MAGIC_uvar_elem
+#  define PERL_MAGIC_uvar_elem           'u'
+#endif
+
+#ifndef PERL_MAGIC_vstring
+#  define PERL_MAGIC_vstring             'V'
+#endif
+
+#ifndef PERL_MAGIC_vec
+#  define PERL_MAGIC_vec                 'v'
+#endif
+
+#ifndef PERL_MAGIC_utf8
+#  define PERL_MAGIC_utf8                'w'
+#endif
+
+#ifndef PERL_MAGIC_substr
+#  define PERL_MAGIC_substr              'x'
+#endif
+
+#ifndef PERL_MAGIC_defelem
+#  define PERL_MAGIC_defelem             'y'
+#endif
+
+#ifndef PERL_MAGIC_glob
+#  define PERL_MAGIC_glob                '*'
+#endif
+
+#ifndef PERL_MAGIC_arylen
+#  define PERL_MAGIC_arylen              '#'
+#endif
+
+#ifndef PERL_MAGIC_pos
+#  define PERL_MAGIC_pos                 '.'
+#endif
+
+#ifndef PERL_MAGIC_backref
+#  define PERL_MAGIC_backref             '<'
+#endif
+
+#ifndef PERL_MAGIC_ext
+#  define PERL_MAGIC_ext                 '~'
+#endif
+
+/* That's the best we can do... */
+#ifndef SvPV_force_nomg
+#  define SvPV_force_nomg                SvPV_force
+#endif
+
+#ifndef SvPV_nomg
+#  define SvPV_nomg                      SvPV
+#endif
+
+#ifndef sv_catpvn_nomg
+#  define sv_catpvn_nomg                 sv_catpvn
+#endif
+
+#ifndef sv_catsv_nomg
+#  define sv_catsv_nomg                  sv_catsv
+#endif
+
+#ifndef sv_setsv_nomg
+#  define sv_setsv_nomg                  sv_setsv
+#endif
+
+#ifndef sv_pvn_nomg
+#  define sv_pvn_nomg                    sv_pvn
+#endif
+
+#ifndef SvIV_nomg
+#  define SvIV_nomg                      SvIV
+#endif
+
+#ifndef SvUV_nomg
+#  define SvUV_nomg                      SvUV
+#endif
+
+#ifndef sv_catpv_mg
+#  define sv_catpv_mg(sv, ptr)          \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_catpv(TeMpSv,ptr);              \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_catpvn_mg
+#  define sv_catpvn_mg(sv, ptr, len)    \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_catpvn(TeMpSv,ptr,len);         \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_catsv_mg
+#  define sv_catsv_mg(dsv, ssv)         \
+   STMT_START {                         \
+     SV *TeMpSv = dsv;                  \
+     sv_catsv(TeMpSv,ssv);              \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_setiv_mg
+#  define sv_setiv_mg(sv, i)            \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_setiv(TeMpSv,i);                \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_setnv_mg
+#  define sv_setnv_mg(sv, num)          \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_setnv(TeMpSv,num);              \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_setpv_mg
+#  define sv_setpv_mg(sv, ptr)          \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_setpv(TeMpSv,ptr);              \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_setpvn_mg
+#  define sv_setpvn_mg(sv, ptr, len)    \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_setpvn(TeMpSv,ptr,len);         \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_setsv_mg
+#  define sv_setsv_mg(dsv, ssv)         \
+   STMT_START {                         \
+     SV *TeMpSv = dsv;                  \
+     sv_setsv(TeMpSv,ssv);              \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_setuv_mg
+#  define sv_setuv_mg(sv, i)            \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_setuv(TeMpSv,i);                \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifndef sv_usepvn_mg
+#  define sv_usepvn_mg(sv, ptr, len)    \
+   STMT_START {                         \
+     SV *TeMpSv = sv;                   \
+     sv_usepvn(TeMpSv,ptr,len);         \
+     SvSETMAGIC(TeMpSv);                \
+   } STMT_END
+#endif
+
+#ifdef USE_ITHREADS
+#ifndef CopFILE
+#  define CopFILE(c)                     ((c)->cop_file)
+#endif
+
+#ifndef CopFILEGV
+#  define CopFILEGV(c)                   (CopFILE(c) ? gv_fetchfile(CopFILE(c)) : Nullgv)
+#endif
+
+#ifndef CopFILE_set
+#  define CopFILE_set(c,pv)              ((c)->cop_file = savepv(pv))
+#endif
+
+#ifndef CopFILESV
+#  define CopFILESV(c)                   (CopFILE(c) ? GvSV(gv_fetchfile(CopFILE(c))) : Nullsv)
+#endif
+
+#ifndef CopFILEAV
+#  define CopFILEAV(c)                   (CopFILE(c) ? GvAV(gv_fetchfile(CopFILE(c))) : Nullav)
+#endif
+
+#ifndef CopSTASHPV
+#  define CopSTASHPV(c)                  ((c)->cop_stashpv)
+#endif
+
+#ifndef CopSTASHPV_set
+#  define CopSTASHPV_set(c,pv)           ((c)->cop_stashpv = ((pv) ? savepv(pv) : Nullch))
+#endif
+
+#ifndef CopSTASH
+#  define CopSTASH(c)                    (CopSTASHPV(c) ? gv_stashpv(CopSTASHPV(c),GV_ADD) : Nullhv)
+#endif
+
+#ifndef CopSTASH_set
+#  define CopSTASH_set(c,hv)             CopSTASHPV_set(c, (hv) ? HvNAME(hv) : Nullch)
+#endif
+
+#ifndef CopSTASH_eq
+#  define CopSTASH_eq(c,hv)              ((hv) && (CopSTASHPV(c) == HvNAME(hv) \
+                                       || (CopSTASHPV(c) && HvNAME(hv) \
+                                       && strEQ(CopSTASHPV(c), HvNAME(hv)))))
+#endif
+
+#else
+#ifndef CopFILEGV
+#  define CopFILEGV(c)                   ((c)->cop_filegv)
+#endif
+
+#ifndef CopFILEGV_set
+#  define CopFILEGV_set(c,gv)            ((c)->cop_filegv = (GV*)SvREFCNT_inc(gv))
+#endif
+
+#ifndef CopFILE_set
+#  define CopFILE_set(c,pv)              CopFILEGV_set((c), gv_fetchfile(pv))
+#endif
+
+#ifndef CopFILESV
+#  define CopFILESV(c)                   (CopFILEGV(c) ? GvSV(CopFILEGV(c)) : Nullsv)
+#endif
+
+#ifndef CopFILEAV
+#  define CopFILEAV(c)                   (CopFILEGV(c) ? GvAV(CopFILEGV(c)) : Nullav)
+#endif
+
+#ifndef CopFILE
+#  define CopFILE(c)                     (CopFILESV(c) ? SvPVX(CopFILESV(c)) : Nullch)
+#endif
+
+#ifndef CopSTASH
+#  define CopSTASH(c)                    ((c)->cop_stash)
+#endif
+
+#ifndef CopSTASH_set
+#  define CopSTASH_set(c,hv)             ((c)->cop_stash = (hv))
+#endif
+
+#ifndef CopSTASHPV
+#  define CopSTASHPV(c)                  (CopSTASH(c) ? HvNAME(CopSTASH(c)) : Nullch)
+#endif
+
+#ifndef CopSTASHPV_set
+#  define CopSTASHPV_set(c,pv)           CopSTASH_set((c), gv_stashpv(pv,GV_ADD))
+#endif
+
+#ifndef CopSTASH_eq
+#  define CopSTASH_eq(c,hv)              (CopSTASH(c) == (hv))
+#endif
+
+#endif /* USE_ITHREADS */
+#ifndef IN_PERL_COMPILETIME
+#  define IN_PERL_COMPILETIME            (PL_curcop == &PL_compiling)
+#endif
+
+#ifndef IN_LOCALE_RUNTIME
+#  define IN_LOCALE_RUNTIME              (PL_curcop->op_private & HINT_LOCALE)
+#endif
+
+#ifndef IN_LOCALE_COMPILETIME
+#  define IN_LOCALE_COMPILETIME          (PL_hints & HINT_LOCALE)
+#endif
+
+#ifndef IN_LOCALE
+#  define IN_LOCALE                      (IN_PERL_COMPILETIME ? IN_LOCALE_COMPILETIME : IN_LOCALE_RUNTIME)
+#endif
+#ifndef IS_NUMBER_IN_UV
+#  define IS_NUMBER_IN_UV                0x01
+#endif
+
+#ifndef IS_NUMBER_GREATER_THAN_UV_MAX
+#  define IS_NUMBER_GREATER_THAN_UV_MAX  0x02
+#endif
+
+#ifndef IS_NUMBER_NOT_INT
+#  define IS_NUMBER_NOT_INT              0x04
+#endif
+
+#ifndef IS_NUMBER_NEG
+#  define IS_NUMBER_NEG                  0x08
+#endif
+
+#ifndef IS_NUMBER_INFINITY
+#  define IS_NUMBER_INFINITY             0x10
+#endif
+
+#ifndef IS_NUMBER_NAN
+#  define IS_NUMBER_NAN                  0x20
+#endif
+
+/* GROK_NUMERIC_RADIX depends on grok_numeric_radix */
+#ifndef GROK_NUMERIC_RADIX
+#  define GROK_NUMERIC_RADIX(sp, send)   grok_numeric_radix(sp, send)
+#endif
+#ifndef PERL_SCAN_GREATER_THAN_UV_MAX
+#  define PERL_SCAN_GREATER_THAN_UV_MAX  0x02
+#endif
+
+#ifndef PERL_SCAN_SILENT_ILLDIGIT
+#  define PERL_SCAN_SILENT_ILLDIGIT      0x04
+#endif
+
+#ifndef PERL_SCAN_ALLOW_UNDERSCORES
+#  define PERL_SCAN_ALLOW_UNDERSCORES    0x01
+#endif
+
+#ifndef PERL_SCAN_DISALLOW_PREFIX
+#  define PERL_SCAN_DISALLOW_PREFIX      0x02
+#endif
+
+#ifndef grok_numeric_radix
+#if defined(NEED_grok_numeric_radix)
+static bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send);
+static
+#else
+extern bool DPPP_(my_grok_numeric_radix)(pTHX_ const char ** sp, const char * send);
+#endif
+
+#ifdef grok_numeric_radix
+#  undef grok_numeric_radix
+#endif
+#define grok_numeric_radix(a,b) DPPP_(my_grok_numeric_radix)(aTHX_ a,b)
+#define Perl_grok_numeric_radix DPPP_(my_grok_numeric_radix)
+
+#if defined(NEED_grok_numeric_radix) || defined(NEED_grok_numeric_radix_GLOBAL)
+bool
+DPPP_(my_grok_numeric_radix)(pTHX_ const char **sp, const char *send)
+{
+#ifdef USE_LOCALE_NUMERIC
+#ifdef PL_numeric_radix_sv
+    if (PL_numeric_radix_sv && IN_LOCALE) {
+        STRLEN len;
+        char* radix = SvPV(PL_numeric_radix_sv, len);
+        if (*sp + len <= send && memEQ(*sp, radix, len)) {
+            *sp += len;
+            return TRUE;
+        }
+    }
+#else
+    /* older perls don't have PL_numeric_radix_sv so the radix
+     * must manually be requested from locale.h
+     */
+#include <locale.h>
+    dTHR;  /* needed for older threaded perls */
+    struct lconv *lc = localeconv();
+    char *radix = lc->decimal_point;
+    if (radix && IN_LOCALE) {
+        STRLEN len = strlen(radix);
+        if (*sp + len <= send && memEQ(*sp, radix, len)) {
+            *sp += len;
+            return TRUE;
+        }
+    }
+#endif /* PERL_VERSION */
+#endif /* USE_LOCALE_NUMERIC */
+    /* always try "." if numeric radix didn't match because
+     * we may have data from different locales mixed */
+    if (*sp < send && **sp == '.') {
+        ++*sp;
+        return TRUE;
+    }
+    return FALSE;
+}
+#endif
+#endif
+
+/* grok_number depends on grok_numeric_radix */
+
+#ifndef grok_number
+#if defined(NEED_grok_number)
+static int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep);
+static
+#else
+extern int DPPP_(my_grok_number)(pTHX_ const char * pv, STRLEN len, UV * valuep);
+#endif
+
+#ifdef grok_number
+#  undef grok_number
+#endif
+#define grok_number(a,b,c) DPPP_(my_grok_number)(aTHX_ a,b,c)
+#define Perl_grok_number DPPP_(my_grok_number)
+
+#if defined(NEED_grok_number) || defined(NEED_grok_number_GLOBAL)
+int
+DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep)
+{
+  const char *s = pv;
+  const char *send = pv + len;
+  const UV max_div_10 = UV_MAX / 10;
+  const char max_mod_10 = UV_MAX % 10;
+  int numtype = 0;
+  int sawinf = 0;
+  int sawnan = 0;
+
+  while (s < send && isSPACE(*s))
+    s++;
+  if (s == send) {
+    return 0;
+  } else if (*s == '-') {
+    s++;
+    numtype = IS_NUMBER_NEG;
+  }
+  else if (*s == '+')
+  s++;
+
+  if (s == send)
+    return 0;
+
+  /* next must be digit or the radix separator or beginning of infinity */
+  if (isDIGIT(*s)) {
+    /* UVs are at least 32 bits, so the first 9 decimal digits cannot
+       overflow.  */
+    UV value = *s - '0';
+    /* This construction seems to be more optimiser friendly.
+       (without it gcc does the isDIGIT test and the *s - '0' separately)
+       With it gcc on arm is managing 6 instructions (6 cycles) per digit.
+       In theory the optimiser could deduce how far to unroll the loop
+       before checking for overflow.  */
+    if (++s < send) {
+      int digit = *s - '0';
+      if (digit >= 0 && digit <= 9) {
+        value = value * 10 + digit;
+        if (++s < send) {
+          digit = *s - '0';
+          if (digit >= 0 && digit <= 9) {
+            value = value * 10 + digit;
+            if (++s < send) {
+              digit = *s - '0';
+              if (digit >= 0 && digit <= 9) {
+                value = value * 10 + digit;
+               if (++s < send) {
+                  digit = *s - '0';
+                  if (digit >= 0 && digit <= 9) {
+                    value = value * 10 + digit;
+                    if (++s < send) {
+                      digit = *s - '0';
+                      if (digit >= 0 && digit <= 9) {
+                        value = value * 10 + digit;
+                        if (++s < send) {
+                          digit = *s - '0';
+                          if (digit >= 0 && digit <= 9) {
+                            value = value * 10 + digit;
+                            if (++s < send) {
+                              digit = *s - '0';
+                              if (digit >= 0 && digit <= 9) {
+                                value = value * 10 + digit;
+                                if (++s < send) {
+                                  digit = *s - '0';
+                                  if (digit >= 0 && digit <= 9) {
+                                    value = value * 10 + digit;
+                                    if (++s < send) {
+                                      /* Now got 9 digits, so need to check
+                                         each time for overflow.  */
+                                      digit = *s - '0';
+                                      while (digit >= 0 && digit <= 9
+                                             && (value < max_div_10
+                                                 || (value == max_div_10
+                                                     && digit <= max_mod_10))) {
+                                        value = value * 10 + digit;
+                                        if (++s < send)
+                                          digit = *s - '0';
+                                        else
+                                          break;
+                                      }
+                                      if (digit >= 0 && digit <= 9
+                                          && (s < send)) {
+                                        /* value overflowed.
+                                           skip the remaining digits, don't
+                                           worry about setting *valuep.  */
+                                        do {
+                                          s++;
+                                        } while (s < send && isDIGIT(*s));
+                                        numtype |=
+                                          IS_NUMBER_GREATER_THAN_UV_MAX;
+                                        goto skip_value;
+                                      }
+                                    }
+                                  }
+                               }
+                              }
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+       }
+      }
+    }
+    numtype |= IS_NUMBER_IN_UV;
+    if (valuep)
+      *valuep = value;
+
+  skip_value:
+    if (GROK_NUMERIC_RADIX(&s, send)) {
+      numtype |= IS_NUMBER_NOT_INT;
+      while (s < send && isDIGIT(*s))  /* optional digits after the radix */
+        s++;
+    }
+  }
+  else if (GROK_NUMERIC_RADIX(&s, send)) {
+    numtype |= IS_NUMBER_NOT_INT | IS_NUMBER_IN_UV; /* valuep assigned below */
+    /* no digits before the radix means we need digits after it */
+    if (s < send && isDIGIT(*s)) {
+      do {
+        s++;
+      } while (s < send && isDIGIT(*s));
+      if (valuep) {
+        /* integer approximation is valid - it's 0.  */
+        *valuep = 0;
+      }
+    }
+    else
+      return 0;
+  } else if (*s == 'I' || *s == 'i') {
+    s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
+    s++; if (s == send || (*s != 'F' && *s != 'f')) return 0;
+    s++; if (s < send && (*s == 'I' || *s == 'i')) {
+      s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
+      s++; if (s == send || (*s != 'I' && *s != 'i')) return 0;
+      s++; if (s == send || (*s != 'T' && *s != 't')) return 0;
+      s++; if (s == send || (*s != 'Y' && *s != 'y')) return 0;
+      s++;
+    }
+    sawinf = 1;
+  } else if (*s == 'N' || *s == 'n') {
+    /* XXX TODO: There are signaling NaNs and quiet NaNs. */
+    s++; if (s == send || (*s != 'A' && *s != 'a')) return 0;
+    s++; if (s == send || (*s != 'N' && *s != 'n')) return 0;
+    s++;
+    sawnan = 1;
+  } else
+    return 0;
+
+  if (sawinf) {
+    numtype &= IS_NUMBER_NEG; /* Keep track of sign  */
+    numtype |= IS_NUMBER_INFINITY | IS_NUMBER_NOT_INT;
+  } else if (sawnan) {
+    numtype &= IS_NUMBER_NEG; /* Keep track of sign  */
+    numtype |= IS_NUMBER_NAN | IS_NUMBER_NOT_INT;
+  } else if (s < send) {
+    /* we can have an optional exponent part */
+    if (*s == 'e' || *s == 'E') {
+      /* The only flag we keep is sign.  Blow away any "it's UV"  */
+      numtype &= IS_NUMBER_NEG;
+      numtype |= IS_NUMBER_NOT_INT;
+      s++;
+      if (s < send && (*s == '-' || *s == '+'))
+        s++;
+      if (s < send && isDIGIT(*s)) {
+        do {
+          s++;
+        } while (s < send && isDIGIT(*s));
+      }
+      else
+      return 0;
+    }
+  }
+  while (s < send && isSPACE(*s))
+    s++;
+  if (s >= send)
+    return numtype;
+  if (len == 10 && memEQ(pv, "0 but true", 10)) {
+    if (valuep)
+      *valuep = 0;
+    return IS_NUMBER_IN_UV;
+  }
+  return 0;
+}
+#endif
+#endif
+
+/*
+ * The grok_* routines have been modified to use warn() instead of
+ * Perl_warner(). Also, 'hexdigit' was the former name of PL_hexdigit,
+ * which is why the stack variable has been renamed to 'xdigit'.
+ */
+
+#ifndef grok_bin
+#if defined(NEED_grok_bin)
+static UV DPPP_(my_grok_bin)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result);
+static
+#else
+extern UV DPPP_(my_grok_bin)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result);
+#endif
+
+#ifdef grok_bin
+#  undef grok_bin
+#endif
+#define grok_bin(a,b,c,d) DPPP_(my_grok_bin)(aTHX_ a,b,c,d)
+#define Perl_grok_bin DPPP_(my_grok_bin)
+
+#if defined(NEED_grok_bin) || defined(NEED_grok_bin_GLOBAL)
+UV
+DPPP_(my_grok_bin)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result)
+{
+    const char *s = start;
+    STRLEN len = *len_p;
+    UV value = 0;
+    NV value_nv = 0;
+
+    const UV max_div_2 = UV_MAX / 2;
+    bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
+    bool overflowed = FALSE;
+
+    if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
+        /* strip off leading b or 0b.
+           for compatibility silently suffer "b" and "0b" as valid binary
+           numbers. */
+        if (len >= 1) {
+            if (s[0] == 'b') {
+                s++;
+                len--;
+            }
+            else if (len >= 2 && s[0] == '0' && s[1] == 'b') {
+                s+=2;
+                len-=2;
+            }
+        }
+    }
+
+    for (; len-- && *s; s++) {
+        char bit = *s;
+        if (bit == '0' || bit == '1') {
+            /* Write it in this wonky order with a goto to attempt to get the
+               compiler to make the common case integer-only loop pretty tight.
+               With gcc seems to be much straighter code than old scan_bin.  */
+          redo:
+            if (!overflowed) {
+                if (value <= max_div_2) {
+                    value = (value << 1) | (bit - '0');
+                    continue;
+                }
+                /* Bah. We're just overflowed.  */
+                warn("Integer overflow in binary number");
+                overflowed = TRUE;
+                value_nv = (NV) value;
+            }
+            value_nv *= 2.0;
+           /* If an NV has not enough bits in its mantissa to
+            * represent a UV this summing of small low-order numbers
+            * is a waste of time (because the NV cannot preserve
+            * the low-order bits anyway): we could just remember when
+            * did we overflow and in the end just multiply value_nv by the
+            * right amount. */
+            value_nv += (NV)(bit - '0');
+            continue;
+        }
+        if (bit == '_' && len && allow_underscores && (bit = s[1])
+            && (bit == '0' || bit == '1'))
+           {
+               --len;
+               ++s;
+                goto redo;
+           }
+        if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
+            warn("Illegal binary digit '%c' ignored", *s);
+        break;
+    }
+
+    if (   ( overflowed && value_nv > 4294967295.0)
+#if UVSIZE > 4
+       || (!overflowed && value > 0xffffffff  )
+#endif
+       ) {
+       warn("Binary number > 0b11111111111111111111111111111111 non-portable");
+    }
+    *len_p = s - start;
+    if (!overflowed) {
+        *flags = 0;
+        return value;
+    }
+    *flags = PERL_SCAN_GREATER_THAN_UV_MAX;
+    if (result)
+        *result = value_nv;
+    return UV_MAX;
+}
+#endif
+#endif
+
+#ifndef grok_hex
+#if defined(NEED_grok_hex)
+static UV DPPP_(my_grok_hex)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result);
+static
+#else
+extern UV DPPP_(my_grok_hex)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result);
+#endif
+
+#ifdef grok_hex
+#  undef grok_hex
+#endif
+#define grok_hex(a,b,c,d) DPPP_(my_grok_hex)(aTHX_ a,b,c,d)
+#define Perl_grok_hex DPPP_(my_grok_hex)
+
+#if defined(NEED_grok_hex) || defined(NEED_grok_hex_GLOBAL)
+UV
+DPPP_(my_grok_hex)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result)
+{
+    const char *s = start;
+    STRLEN len = *len_p;
+    UV value = 0;
+    NV value_nv = 0;
+
+    const UV max_div_16 = UV_MAX / 16;
+    bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
+    bool overflowed = FALSE;
+    const char *xdigit;
+
+    if (!(*flags & PERL_SCAN_DISALLOW_PREFIX)) {
+        /* strip off leading x or 0x.
+           for compatibility silently suffer "x" and "0x" as valid hex numbers.
+        */
+        if (len >= 1) {
+            if (s[0] == 'x') {
+                s++;
+                len--;
+            }
+            else if (len >= 2 && s[0] == '0' && s[1] == 'x') {
+                s+=2;
+                len-=2;
+            }
+        }
+    }
+
+    for (; len-- && *s; s++) {
+       xdigit = strchr((char *) PL_hexdigit, *s);
+        if (xdigit) {
+            /* Write it in this wonky order with a goto to attempt to get the
+               compiler to make the common case integer-only loop pretty tight.
+               With gcc seems to be much straighter code than old scan_hex.  */
+          redo:
+            if (!overflowed) {
+                if (value <= max_div_16) {
+                    value = (value << 4) | ((xdigit - PL_hexdigit) & 15);
+                    continue;
+                }
+                warn("Integer overflow in hexadecimal number");
+                overflowed = TRUE;
+                value_nv = (NV) value;
+            }
+            value_nv *= 16.0;
+           /* If an NV has not enough bits in its mantissa to
+            * represent a UV this summing of small low-order numbers
+            * is a waste of time (because the NV cannot preserve
+            * the low-order bits anyway): we could just remember when
+            * did we overflow and in the end just multiply value_nv by the
+            * right amount of 16-tuples. */
+            value_nv += (NV)((xdigit - PL_hexdigit) & 15);
+            continue;
+        }
+        if (*s == '_' && len && allow_underscores && s[1]
+               && (xdigit = strchr((char *) PL_hexdigit, s[1])))
+           {
+               --len;
+               ++s;
+                goto redo;
+           }
+        if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
+            warn("Illegal hexadecimal digit '%c' ignored", *s);
+        break;
+    }
+
+    if (   ( overflowed && value_nv > 4294967295.0)
+#if UVSIZE > 4
+       || (!overflowed && value > 0xffffffff  )
+#endif
+       ) {
+       warn("Hexadecimal number > 0xffffffff non-portable");
+    }
+    *len_p = s - start;
+    if (!overflowed) {
+        *flags = 0;
+        return value;
+    }
+    *flags = PERL_SCAN_GREATER_THAN_UV_MAX;
+    if (result)
+        *result = value_nv;
+    return UV_MAX;
+}
+#endif
+#endif
+
+#ifndef grok_oct
+#if defined(NEED_grok_oct)
+static UV DPPP_(my_grok_oct)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result);
+static
+#else
+extern UV DPPP_(my_grok_oct)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result);
+#endif
+
+#ifdef grok_oct
+#  undef grok_oct
+#endif
+#define grok_oct(a,b,c,d) DPPP_(my_grok_oct)(aTHX_ a,b,c,d)
+#define Perl_grok_oct DPPP_(my_grok_oct)
+
+#if defined(NEED_grok_oct) || defined(NEED_grok_oct_GLOBAL)
+UV
+DPPP_(my_grok_oct)(pTHX_ char *start, STRLEN *len_p, I32 *flags, NV *result)
+{
+    const char *s = start;
+    STRLEN len = *len_p;
+    UV value = 0;
+    NV value_nv = 0;
+
+    const UV max_div_8 = UV_MAX / 8;
+    bool allow_underscores = *flags & PERL_SCAN_ALLOW_UNDERSCORES;
+    bool overflowed = FALSE;
+
+    for (; len-- && *s; s++) {
+         /* gcc 2.95 optimiser not smart enough to figure that this subtraction
+            out front allows slicker code.  */
+        int digit = *s - '0';
+        if (digit >= 0 && digit <= 7) {
+            /* Write it in this wonky order with a goto to attempt to get the
+               compiler to make the common case integer-only loop pretty tight.
+            */
+          redo:
+            if (!overflowed) {
+                if (value <= max_div_8) {
+                    value = (value << 3) | digit;
+                    continue;
+                }
+                /* Bah. We're just overflowed.  */
+                warn("Integer overflow in octal number");
+                overflowed = TRUE;
+                value_nv = (NV) value;
+            }
+            value_nv *= 8.0;
+           /* If an NV has not enough bits in its mantissa to
+            * represent a UV this summing of small low-order numbers
+            * is a waste of time (because the NV cannot preserve
+            * the low-order bits anyway): we could just remember when
+            * did we overflow and in the end just multiply value_nv by the
+            * right amount of 8-tuples. */
+            value_nv += (NV)digit;
+            continue;
+        }
+        if (digit == ('_' - '0') && len && allow_underscores
+            && (digit = s[1] - '0') && (digit >= 0 && digit <= 7))
+           {
+               --len;
+               ++s;
+                goto redo;
+           }
+        /* Allow \octal to work the DWIM way (that is, stop scanning
+         * as soon as non-octal characters are seen, complain only iff
+         * someone seems to want to use the digits eight and nine). */
+        if (digit == 8 || digit == 9) {
+            if (!(*flags & PERL_SCAN_SILENT_ILLDIGIT))
+                warn("Illegal octal digit '%c' ignored", *s);
+        }
+        break;
+    }
+
+    if (   ( overflowed && value_nv > 4294967295.0)
+#if UVSIZE > 4
+       || (!overflowed && value > 0xffffffff  )
+#endif
+       ) {
+       warn("Octal number > 037777777777 non-portable");
+    }
+    *len_p = s - start;
+    if (!overflowed) {
+        *flags = 0;
+        return value;
+    }
+    *flags = PERL_SCAN_GREATER_THAN_UV_MAX;
+    if (result)
+        *result = value_nv;
+    return UV_MAX;
+}
+#endif
+#endif
+
+#ifdef NO_XSLOCKS
+#  ifdef dJMPENV
+#    define dXCPT             dJMPENV; int rEtV = 0
+#    define XCPT_TRY_START    JMPENV_PUSH(rEtV); if (rEtV == 0)
+#    define XCPT_TRY_END      JMPENV_POP;
+#    define XCPT_CATCH        if (rEtV != 0)
+#    define XCPT_RETHROW      JMPENV_JUMP(rEtV)
+#  else
+#    define dXCPT             Sigjmp_buf oldTOP; int rEtV = 0
+#    define XCPT_TRY_START    Copy(top_env, oldTOP, 1, Sigjmp_buf); rEtV = Sigsetjmp(top_env, 1); if (rEtV == 0)
+#    define XCPT_TRY_END      Copy(oldTOP, top_env, 1, Sigjmp_buf);
+#    define XCPT_CATCH        if (rEtV != 0)
+#    define XCPT_RETHROW      Siglongjmp(top_env, rEtV)
+#  endif
+#endif
+
+#endif /* _P_P_PORTABILITY_H_ */
+
+/* End of File ppport.h */
diff --git a/perl-module/t/Pspp.t b/perl-module/t/Pspp.t
new file mode 100644 (file)
index 0000000..04b76a5
--- /dev/null
@@ -0,0 +1,624 @@
+# -*-perl-*-
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl PSPP.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test::More tests => 37;
+use Text::Diff;
+use File::Temp qw/ tempfile tempdir /;
+BEGIN { use_ok('PSPP') };
+
+#########################
+
+sub compare
+{
+    my $file = shift;
+    my $pattern = shift;
+    return ! diff ("$file", \$pattern);
+}
+
+my $pspp_cmd = $ENV{PSPP_TEST_CMD};
+
+if ( ! $pspp_cmd)
+{
+    $pspp_cmd="pspp";
+}
+
+sub run_pspp_syntax
+{
+    my $tempdir = shift;
+    my $syntax = shift;
+
+    my $syntaxfile = "$tempdir/foo.sps";
+
+    open (FH, ">$syntaxfile");
+    print FH "$syntax";
+    close (FH);
+
+    system ("cd $tempdir; $pspp_cmd -o raw-ascii $syntaxfile");
+}
+
+sub run_pspp_syntax_cmp
+{
+    my $tempdir = shift;
+    my $syntax = shift;
+
+    my $result = shift;
+
+    run_pspp_syntax ($tempdir, $syntax);
+
+    my $diff =  diff ("$tempdir/pspp.list", \$result);
+
+    if ( ! ($diff eq ""))
+    {
+       diag ("$diff");
+    }
+
+    return ($diff eq "");
+}
+
+
+# Insert your test code below, the Test::More module is used here so read
+# its man page ( perldoc Test::More ) for help writing this test script.
+
+{
+  my $d = PSPP::Dict->new();
+  ok (ref $d, "Dictionary Creation");
+  ok ($d->get_var_cnt () == 0);
+
+  $d->set_label ("My Dictionary");
+  $d->set_documents ("These Documents");
+
+  # Tests for variable creation
+
+  my $var0 = PSPP::Var->new ($d, "le");
+  ok (!ref $var0, "Trap illegal variable name");
+  ok ($d->get_var_cnt () == 0);
+
+  $var0 = PSPP::Var->new ($d, "legal");
+  ok (ref $var0, "Accept legal variable name");
+  ok ($d->get_var_cnt () == 1);
+
+  my $var1 = PSPP::Var->new ($d, "legal");
+  ok (!ref $var1, "Trap duplicate variable name");
+  ok ($d->get_var_cnt () == 1);
+
+  $var1 = PSPP::Var->new ($d, "money", 
+                         (fmt=>PSPP::Fmt::DOLLAR, 
+                          width=>4, decimals=>2) );
+  ok (ref $var1, "Accept valid format");
+  ok ($d->get_var_cnt () == 2);
+
+  $d->set_weight ($var1);
+
+
+  # Tests for system file creation
+  # Make sure a system file can be created
+  {
+      my $tempdir = tempdir( CLEANUP => 1 );
+      my $tempfile = "$tempdir/testfile.sav";
+      my $syntaxfile = "$tempdir/syntax.sps";
+      my $sysfile = PSPP::Sysfile->new ("$tempfile", $d);
+      ok (ref $sysfile, "Create sysfile object");
+
+      $sysfile->close ();
+      ok (-s "$tempfile", "Write system file");
+  }
+}
+
+
+# Make sure we can write cases to a file
+{
+  my $d = PSPP::Dict->new();
+  PSPP::Var->new ($d, "id",
+                        (
+                         fmt=>PSPP::Fmt::F, 
+                         width=>2, 
+                         decimals=>0
+                         )
+                        );
+
+  PSPP::Var->new ($d, "name",
+                        (
+                         fmt=>PSPP::Fmt::A, 
+                         width=>20, 
+                         )
+                        );
+
+  $d->set_documents ("This should not appear");
+  $d->clear_documents ();
+  $d->add_document ("This is a document line");
+
+  $d->set_label ("This is the file label");
+
+  # Check that we can write system files
+  {
+      my $tempdir = tempdir( CLEANUP => 1 );
+      my $tempfile = "$tempdir/testfile.sav";
+      my $sysfile = PSPP::Sysfile->new ("$tempfile", $d);
+
+      my $res = $sysfile->append_case ( [34, "frederick"]);
+      ok ($res, "Append Case");
+
+      $res = $sysfile->append_case ( [34, "frederick", "extra"]);
+      ok (!$res, "Appending Case with too many variables");
+
+      $sysfile->close ();
+      ok (-s  "$tempfile", "existance");
+  }
+
+  # Check that sysfiles are closed properly
+  {
+      my $tempdir = tempdir( CLEANUP => 1 );
+      my $tempfile = "$tempdir/testfile.sav";
+      {
+         my $sysfile = PSPP::Sysfile->new ("$tempfile", $d);
+
+         my $res = $sysfile->append_case ( [21, "wheelbarrow"]);
+         ok ($res, "Append Case 2");
+
+         # Don't close.  We want to test that the destructor  does that 
+         # automatically 
+      }
+      ok (-s "$tempfile", "existance2");
+
+    ok (run_pspp_syntax_cmp ($tempdir, <<SYNTAX, <<RESULT), "Check output");
+
+        GET FILE='$tempfile'.
+       DISPLAY DICTIONARY.
+       DISPLAY FILE LABEL.
+       DISPLAY DOCUMENTS.
+       LIST.
+SYNTAX
+1.1 DISPLAY.  
++--------+-------------------------------------------+--------+
+|Variable|Description                                |Position|
+#========#===========================================#========#
+|id      |Format: F2.0                               |       1|
+|        |Measure: Scale                             |        |
+|        |Display Alignment: Right                   |        |
+|        |Display Width: 8                           |        |
++--------+-------------------------------------------+--------+
+|name    |Format: A20                                |       2|
+|        |Measure: Nominal                           |        |
+|        |Display Alignment: Left                    |        |
+|        |Display Width: 20                          |        |
++--------+-------------------------------------------+--------+
+
+File label:
+This is the file label
+
+Documents in the active file:
+
+This is a document line
+
+id                 name
+-- --------------------
+21 wheelbarrow          
+
+RESULT
+
+
+  }
+
+  # Now do some tests to make sure all the variable parameters 
+  # can be written properly.
+
+  {
+      my $tempdir = tempdir( CLEANUP => 1 );
+      my $tempfile = "$tempdir/testfile.sav";      
+      my $dict = PSPP::Dict->new();
+      ok (ref $dict, "Dictionary Creation 2");
+
+      my $int = PSPP::Var->new ($dict, "integer", 
+                               (width=>8, decimals=>0) );
+
+      $int->set_label ("My Integer");
+      
+      $int->add_value_label (99, "Silly");
+      $int->clear_value_labels ();
+      $int->add_value_label (0, "Zero");
+      $int->add_value_label (1, "Unity");
+      $int->add_value_label (2, "Duality");
+
+      my $str = PSPP::Var->new ($dict, "string", 
+                               (fmt=>PSPP::Fmt::A, width=>8) );
+
+
+      $str->set_label ("My String");
+      ok ($str->add_value_label ("xx", "foo"), "Value label for short string");
+      diag ($PSPP::errstr);
+      $str->add_value_label ("yy", "bar");
+
+      $str->set_missing_values ("this", "that");
+
+      my $longstr = PSPP::Var->new ($dict, "longstring", 
+                               (fmt=>PSPP::Fmt::A, width=>9) );
+
+
+      $longstr->set_label ("My Long String");
+      my $re = $longstr->add_value_label ("xxx", "xfoo");
+      ok (($re == 0), "Long strings cant have labels");
+
+      ok ($PSPP::errstr eq "Cannot add label to a long string variable", "Error msg");
+
+      $int->set_missing_values (9, 99);
+
+      my $sysfile = PSPP::Sysfile->new ("$tempfile", $dict);
+
+
+      $sysfile->close ();
+
+      ok (run_pspp_syntax_cmp ($tempdir, <<SYNTAX, <<RESULT), "Check output 2");
+GET FILE='$tempfile'.
+DISPLAY DICTIONARY.
+SYNTAX
+1.1 DISPLAY.  
++----------+-----------------------------------------+--------+
+|Variable  |Description                              |Position|
+#==========#=========================================#========#
+|integer   |My Integer                               |       1|
+|          |Format: F8.0                             |        |
+|          |Measure: Scale                           |        |
+|          |Display Alignment: Right                 |        |
+|          |Display Width: 8                         |        |
+|          |Missing Values: 9; 99                    |        |
+|          +-----+-----------------------------------+        |
+|          |    0|Zero                               |        |
+|          |    1|Unity                              |        |
+|          |    2|Duality                            |        |
++----------+-----+-----------------------------------+--------+
+|string    |My String                                |       2|
+|          |Format: A8                               |        |
+|          |Measure: Nominal                         |        |
+|          |Display Alignment: Left                  |        |
+|          |Display Width: 8                         |        |
+|          |Missing Values: "this    "; "that    "   |        |
+|          +-----+-----------------------------------+        |
+|          |   xx|foo                                |        |
+|          |   yy|bar                                |        |
++----------+-----+-----------------------------------+--------+
+|longstring|My Long String                           |       3|
+|          |Format: A9                               |        |
+|          |Measure: Nominal                         |        |
+|          |Display Alignment: Left                  |        |
+|          |Display Width: 9                         |        |
++----------+-----------------------------------------+--------+
+
+RESULT
+
+  }
+
+}
+
+sub generate_sav_file 
+{
+    my $filename = shift;
+    my $tempdir = shift;
+
+    run_pspp_syntax_cmp ($tempdir, <<SYNTAX, <<RESULT);
+data list notable list /string (a8) longstring (a12) numeric (f10) date (date11) dollar (dollar8.2) datetime (datetime17)
+begin data.
+1111 One   1 1/1/1 1   1/1/1+01:01
+2222 Two   2 2/2/2 2   2/2/2+02:02
+3333 Three 3 3/3/3 3   3/3/3+03:03
+.    .     . .         .
+5555 Five  5 5/5/5 5   5/5/5+05:05
+end data.
+
+
+variable labels string 'A Short String Variable'
+  /longstring 'A Long String Variable'
+  /numeric 'A Numeric Variable'
+  /date 'A Date Variable'
+  /dollar 'A Dollar Variable'
+  /datetime 'A Datetime Variable'.
+
+
+missing values numeric (9, 5, 999).
+
+missing values string ("3333").
+
+add value labels
+  /string '1111' 'ones' '2222' 'twos' '3333' 'threes'
+  /numeric 1 'Unity' 2 'Duality' 3 'Thripality'.
+
+variable attribute
+    variables = numeric
+    attribute=colour[1]('blue') colour[2]('pink') colour[3]('violet')
+    attribute=size('large') nationality('foreign').
+
+
+save outfile='$filename'.
+SYNTAX
+
+RESULT
+
+}
+
+
+# Test to make sure that the dictionary survives the sysfile.
+# Thanks to Rob Messer for reporting this problem
+{
+    my $tempdir = tempdir( CLEANUP => 1 );
+    my $tempfile = "$tempdir/testfile.sav";
+    my $sysfile ;
+
+    {
+       my $d = PSPP::Dict->new();
+
+       PSPP::Var->new ($d, "id",
+                       (
+                        fmt=>PSPP::Fmt::F, 
+                        width=>2, 
+                        decimals=>0
+                        )
+                       );
+
+       $sysfile = PSPP::Sysfile->new ("$tempfile", $d);
+    }
+
+    my $res = $sysfile->append_case ([3]);
+
+    ok ($res, "Dictionary survives sysfile");
+}
+
+
+# Basic reader test
+{
+ my $tempdir = tempdir( CLEANUP => 1 );
+
+ generate_sav_file ("$tempdir/in.sav", "$tempdir");
+
+ my $sf = PSPP::Reader->open ("$tempdir/in.sav");
+
+ my $dict = $sf->get_dict ();
+
+ open (MYFILE, ">$tempdir/out.txt");
+ for ($v = 0 ; $v < $dict->get_var_cnt() ; $v++)
+ {
+    my $var = $dict->get_var ($v);
+    my $name = $var->get_name ();
+    my $label = $var->get_label ();
+
+    print MYFILE "Variable $v is \"$name\", label is \"$label\"\n";
+    
+    my $vl = $var->get_value_labels ();
+
+    print MYFILE "Value Labels:\n";
+    print MYFILE "$_ => $vl->{$_}\n" for keys %$vl;
+ }
+
+ while (my @c = $sf->get_next_case () )
+ {
+    for ($v = 0; $v < $dict->get_var_cnt(); $v++)
+    {
+       print MYFILE "val$v: \"$c[$v]\"\n";
+    }
+    print MYFILE "\n";
+ }
+
+ close (MYFILE);
+
+ok (compare ("$tempdir/out.txt", <<EOF), "Basic reader operation");
+Variable 0 is "string", label is "A Short String Variable"
+Value Labels:
+3333     => threes
+1111     => ones
+2222     => twos
+Variable 1 is "longstring", label is "A Long String Variable"
+Value Labels:
+Variable 2 is "numeric", label is "A Numeric Variable"
+Value Labels:
+1 => Unity
+3 => Thripality
+2 => Duality
+Variable 3 is "date", label is "A Date Variable"
+Value Labels:
+Variable 4 is "dollar", label is "A Dollar Variable"
+Value Labels:
+Variable 5 is "datetime", label is "A Datetime Variable"
+Value Labels:
+val0: "1111    "
+val1: "One         "
+val2: "1"
+val3: "13197686400"
+val4: "1"
+val5: "13197690060"
+
+val0: "2222    "
+val1: "Two         "
+val2: "2"
+val3: "13231987200"
+val4: "2"
+val5: "13231994520"
+
+val0: "3333    "
+val1: "Three       "
+val2: "3"
+val3: "13266028800"
+val4: "3"
+val5: "13266039780"
+
+val0: ".       "
+val1: ".           "
+val2: ""
+val3: ""
+val4: ""
+val5: ""
+
+val0: "5555    "
+val1: "Five        "
+val2: "5"
+val3: "13334630400"
+val4: "5"
+val5: "13334648700"
+
+EOF
+
+}
+
+
+# Check that we can stream one file into another
+{
+ my $tempdir = tempdir( CLEANUP => 1 );
+
+ generate_sav_file ("$tempdir/in.sav", "$tempdir");
+
+ my $input = PSPP::Reader->open ("$tempdir/in.sav");
+
+ my $dict = $input->get_dict ();
+
+ my $output = PSPP::Sysfile->new ("$tempdir/out.sav", $dict);
+
+ while (my (@c) = $input->get_next_case () )
+ {
+   $output->append_case (\@c);
+ }
+
+ $output->close ();
+
+
+ #Check the two files are the same (except for metadata)
+
+ run_pspp_syntax ($tempdir, <<SYNTAX);
+ get file='$tempdir/in.sav'.
+ display dictionary.
+ list.
+
+SYNTAX
+
+ system ("cp $tempdir/pspp.list $tempdir/in.txt");
+
+ run_pspp_syntax ($tempdir, <<SYNTAX);
+ get file='$tempdir/out.sav'.
+ display dictionary.
+ list.
+
+SYNTAX
+ ok (! diff ("$tempdir/pspp.list", "$tempdir/in.txt"), "Streaming of files");
+}
+
+
+
+# Check that the format_value function works properly
+{
+ my $tempdir = tempdir( CLEANUP => 1 );
+
+ run_pspp_syntax ($tempdir, <<SYNTAX);
+
+data list list /d (datetime17).
+begin data.
+11/9/2001+08:20
+end data.
+
+save outfile='$tempdir/dd.sav'.
+
+SYNTAX
+
+ my $sf = PSPP::Reader->open ("$tempdir/dd.sav");
+
+ my $dict = $sf->get_dict ();
+
+ my (@c) = $sf->get_next_case ();
+
+ my $var = $dict->get_var (0);
+ my $val = $c[0];
+ my $formatted = PSPP::format_value ($val, $var);
+ my $str = gmtime ($val - PSPP::PERL_EPOCH);
+ print "Formatted string is \"$formatted\"\n";
+ ok ( $formatted eq "11-SEP-2001 08:20", "format_value function");
+ ok ( $str eq "Tue Sep 11 08:20:00 2001", "Perl representation of time");
+}
+
+
+# Check that attempting to open a non-existent file results in an error
+{
+  my $tempdir = tempdir( CLEANUP => 1 );
+
+  unlink ("$tempdir/no-such-file.sav");
+
+  my $sf = PSPP::Reader->open ("$tempdir/no-such-file.sav");
+
+  ok ( !ref $sf, "Returns undef on opening failure");
+
+  ok ("$PSPP::errstr" eq "Error opening \"$tempdir/no-such-file.sav\" for reading as a system file: No such file or directory.",
+      "Error string on open failure");
+}
+
+
+# Missing value tests. 
+{
+ my $tempdir = tempdir( CLEANUP => 1 );
+
+ generate_sav_file ("$tempdir/in.sav", "$tempdir");
+
+ my $sf = PSPP::Reader->open ("$tempdir/in.sav");
+
+ my $dict = $sf->get_dict ();
+
+
+ my (@c) = $sf->get_next_case ();
+
+ my $stringvar = $dict->get_var (0);
+ my $numericvar = $dict->get_var (2);
+ my $val = $c[0];
+
+ ok ( !PSPP::value_is_missing ($val, $stringvar), "Missing Value Negative String");
+
+ $val = $c[2];
+
+ ok ( !PSPP::value_is_missing ($val, $numericvar), "Missing Value Negative Num");
+
+ @c = $sf->get_next_case (); 
+ @c = $sf->get_next_case (); 
+
+ $val = $c[0];
+ ok ( PSPP::value_is_missing ($val, $stringvar), "Missing Value Positive");
+
+ @c = $sf->get_next_case (); 
+ $val = $c[2];
+ ok ( PSPP::value_is_missing ($val, $numericvar), "Missing Value Positive SYS");
+
+ @c = $sf->get_next_case (); 
+ $val = $c[2];
+ ok ( PSPP::value_is_missing ($val, $numericvar), "Missing Value Positive Num");
+}
+
+
+#Test reading of custom attributes
+{
+    my $tempdir = tempdir( CLEANUP => 1 );
+
+    generate_sav_file ("$tempdir/in.sav", "$tempdir");
+
+    my $sf = PSPP::Reader->open ("$tempdir/in.sav");
+
+    my $dict = $sf->get_dict ();
+
+    my $var = $dict->get_var_by_name ("numeric");
+
+    my $attr = $var->get_attributes ();
+
+    open (MYFILE, ">$tempdir/out.txt");
+
+    foreach $k (keys %$attr)
+    {
+       my $ll = $attr->{$k};
+       print MYFILE "$k =>";
+       print MYFILE map "$_\n", join ', ', @$ll;
+    }
+
+    close (MYFILE);
+
+    ok (compare ("$tempdir/out.txt", <<EOF), "Custom Attributes");
+colour =>blue, pink, violet
+nationality =>foreign
+size =>large
+EOF
+
+}
diff --git a/perl-module/typemap b/perl-module/typemap
new file mode 100644 (file)
index 0000000..cd45c33
--- /dev/null
@@ -0,0 +1,53 @@
+TYPEMAP
+ struct dictionary *   T_PTRREF
+ struct variable *     T_PTRREF
+ struct sysfile_info * T_PTRREF
+ struct sysreader_info * T_PTRREF
+ input_format  INPUT_FMT_SPEC
+ output_format OUTPUT_FMT_SPEC
+
+
+INPUT
+OUTPUT_FMT_SPEC
+ {
+   HV *hv = (HV *) SvRV ($arg);
+   SV** the_format = hv_fetch (hv, \"fmt\", 3, 0);
+   SV** decimals = hv_fetch (hv, \"decimals\", 8, 0);
+   SV** width = hv_fetch (hv, \"width\", 5, 0);
+
+   $var.type = the_format ? SvIV (*the_format) : FMT_F;
+   $var.w    = width ? SvIV (*width) : 8;
+   $var.d    = decimals ? SvIV (*decimals) :
+     fmt_takes_decimals ($var.type) ?
+     MIN (2, fmt_max_output_decimals ($var.type, $var.w)) : 0;
+   if ( ! fmt_check_output (&$var))
+   {
+       char buf[FMT_STRING_LEN_MAX + 1];
+        fmt_to_string (&$var, buf);
+       croak (\"%s is an invalid output format\", buf);
+   }
+
+ }
+
+
+INPUT_FMT_SPEC
+ {
+   HV *hv = (HV *) SvRV ($arg);
+   SV** the_format = hv_fetch (hv, \"fmt\", 3, 0);
+   SV** decimals = hv_fetch (hv, \"decimals\", 8, 0);
+   SV** width = hv_fetch (hv, \"width\", 5, 0);
+
+
+   $var.type = the_format ? SvIV (*the_format) : FMT_F;
+   $var.w    = width ? SvIV (*width) : 8;
+   $var.d    = decimals ? SvIV (*decimals) :
+     fmt_takes_decimals ($var.type) ?
+     MIN (2, fmt_max_input_decimals ($var.type, $var.w)) : 0;
+   if ( ! fmt_check_input (&$var))
+   {
+       char buf[FMT_STRING_LEN_MAX + 1];
+        fmt_to_string (&$var, buf);
+       croak (\"%s is an invalid input format\", buf);
+   }
+
+ }
index 2c3bc865a77dfb02f95900927fc4bba92bdd3609..6698d663953457e8dfcb506bff166130473c2d0d 100644 (file)
@@ -1,14 +1,13 @@
 # British translations for PSPP
-# Copyright (C) 2007 Free Software Foundation, Inc.
+# Copyright (C) 2007, 2008 Free Software Foundation, Inc.
 # This file is distributed under the same licence as the PSPP package.
-# John Darrington <john@darrington.wattle.id.au>, 2007.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: PSPP 0.4.3\n"
+"Project-Id-Version: PSPP 0.7.0\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2008-08-23 08:35+0800\n"
-"PO-Revision-Date: 2007-09-15 08:29+0800\n"
+"POT-Creation-Date: 2009-02-24 13:41+0900\n"
+"PO-Revision-Date: 2008-11-05 08:17+0900\n"
 "Last-Translator: John Darrington <john@darrington.wattle.id.au>\n"
 "Language-Team: John Darrington <john@darrington.wattle.id.au>\n"
 "MIME-Version: 1.0\n"
@@ -101,7 +100,7 @@ msgstr ""
 
 #: src/data/data-in.c:338
 msgid "Unrecognized character in field."
-msgstr ""
+msgstr "Unrecognised character in field."
 
 #: src/data/data-in.c:362 src/data/data-in.c:636
 msgid "Field must have even length."
@@ -129,6 +128,8 @@ msgid ""
 "Unrecognized month format.  Months may be specified as Arabic or Roman "
 "numerals or as at least 3 letters of their English names."
 msgstr ""
+"Unrecognised month format.  Months may be specified as Arabic or Roman \n"
+"numerals or as at least 3 letters of their English names."
 
 #: src/data/data-in.c:850
 #, c-format
@@ -173,6 +174,8 @@ msgid ""
 "Unrecognized weekday name.  At least the first two letters of an English "
 "weekday name must be specified."
 msgstr ""
+"Unrecognised weekday name.  At least the first two letters of an English "
+"weekday name must be specified."
 
 #: src/data/data-in.c:1138
 #, c-format
@@ -216,13 +219,13 @@ msgstr ""
 msgid "scratch"
 msgstr ""
 
-#: src/data/dictionary.c:882
+#: src/data/dictionary.c:905
 msgid ""
 "At least one case in the data file had a weight value that was user-missing, "
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgstr ""
 
-#: src/data/dictionary.c:1180
+#: src/data/dictionary.c:1207
 #, c-format
 msgid "Truncating document line to %d bytes."
 msgstr ""
@@ -319,33 +322,33 @@ msgstr[1] ""
 msgid "%s variables are not compatible with %s format %s."
 msgstr ""
 
-#: src/data/format.c:327 src/data/sys-file-reader.c:639
-#: src/ui/gui/data-editor.glade:1190 src/ui/gui/psppire.glade:2176
-#: src/ui/gui/psppire-var-store.c:605
+#: src/data/format.c:327 src/data/sys-file-reader.c:655
+#: src/ui/gui/data-editor.glade:1196 src/ui/gui/psppire.glade:2136
+#: src/ui/gui/psppire-var-store.c:557
 msgid "String"
 msgstr ""
 
-#: src/data/format.c:327 src/data/sys-file-reader.c:639
-#: src/ui/gui/data-editor.glade:1079 src/ui/gui/psppire.glade:2131
-#: src/ui/gui/psppire-var-store.c:598
+#: src/data/format.c:327 src/data/sys-file-reader.c:655
+#: src/ui/gui/data-editor.glade:1085 src/ui/gui/psppire.glade:2211
+#: src/ui/gui/psppire-var-store.c:550
 msgid "Numeric"
 msgstr ""
 
-#: src/data/format.c:328 src/data/sys-file-reader.c:1145
-#: src/data/sys-file-reader.c:1147
+#: src/data/format.c:328 src/data/sys-file-reader.c:1160
+#: src/data/sys-file-reader.c:1162
 #: src/language/dictionary/apply-dictionary.c:78
 #: src/language/dictionary/apply-dictionary.c:79
-#: src/language/xforms/recode.c:472 src/language/xforms/recode.c:473
-#: src/language/xforms/recode.c:485 src/language/xforms/recode.c:486
+#: src/language/xforms/recode.c:489 src/language/xforms/recode.c:490
+#: src/language/xforms/recode.c:502 src/language/xforms/recode.c:503
 msgid "numeric"
 msgstr ""
 
-#: src/data/format.c:328 src/data/sys-file-reader.c:1145
-#: src/data/sys-file-reader.c:1147
+#: src/data/format.c:328 src/data/sys-file-reader.c:1160
+#: src/data/sys-file-reader.c:1162
 #: src/language/dictionary/apply-dictionary.c:78
 #: src/language/dictionary/apply-dictionary.c:79
-#: src/language/xforms/recode.c:472 src/language/xforms/recode.c:473
-#: src/language/xforms/recode.c:485 src/language/xforms/recode.c:486
+#: src/language/xforms/recode.c:489 src/language/xforms/recode.c:490
+#: src/language/xforms/recode.c:502 src/language/xforms/recode.c:503
 msgid "string"
 msgstr ""
 
@@ -359,22 +362,22 @@ msgid ""
 "Support for Gnumeric files was not compiled into this installation of PSPP"
 msgstr ""
 
-#: src/data/gnumeric-reader.c:366
+#: src/data/gnumeric-reader.c:365
 #, c-format
 msgid "Error opening \"%s\" for reading as a gnumeric file: %s."
 msgstr ""
 
-#: src/data/gnumeric-reader.c:386
+#: src/data/gnumeric-reader.c:385
 #, c-format
 msgid "Invalid cell range \"%s\""
 msgstr ""
 
-#: src/data/gnumeric-reader.c:523 src/data/psql-reader.c:184
+#: src/data/gnumeric-reader.c:522 src/data/psql-reader.c:182
 #, c-format
 msgid "Cannot create variable name from %s"
 msgstr ""
 
-#: src/data/gnumeric-reader.c:535
+#: src/data/gnumeric-reader.c:534
 #, c-format
 msgid "Selected sheet or range of spreadsheet \"%s\" is empty."
 msgstr ""
@@ -484,7 +487,7 @@ msgstr ""
 #: src/data/por-file-reader.c:519
 #, c-format
 msgid "Unrecognized version code `%c'."
-msgstr ""
+msgstr "Unrecognised version code `%c'."
 
 #: src/data/por-file-reader.c:528
 #, c-format
@@ -593,35 +596,35 @@ msgid ""
 "installation of PSPP"
 msgstr ""
 
-#: src/data/psql-reader.c:239
+#: src/data/psql-reader.c:237
 msgid "Memory error whilst opening psql source"
 msgstr ""
 
-#: src/data/psql-reader.c:245
+#: src/data/psql-reader.c:243
 #, c-format
 msgid "Error opening psql source: %s."
 msgstr ""
 
-#: src/data/psql-reader.c:260
+#: src/data/psql-reader.c:258
 #, c-format
 msgid ""
 "Postgres server is version %s. Reading from versions earlier than 8.0 is not "
 "supported."
 msgstr ""
 
-#: src/data/psql-reader.c:280
+#: src/data/psql-reader.c:278
 msgid ""
 "Connection is unencrypted, but unencrypted connections have not been "
 "permitted."
 msgstr ""
 
-#: src/data/psql-reader.c:307 src/data/psql-reader.c:332
-#: src/data/psql-reader.c:342
+#: src/data/psql-reader.c:305 src/data/psql-reader.c:330
+#: src/data/psql-reader.c:340
 #, c-format
 msgid "Error from psql source: %s."
 msgstr ""
 
-#: src/data/psql-reader.c:437
+#: src/data/psql-reader.c:435
 #, c-format
 msgid "Unsupported OID %d.  SYSMIS values will be inserted."
 msgstr ""
@@ -639,7 +642,7 @@ msgstr ""
 msgid "scratch file"
 msgstr ""
 
-#: src/data/settings.c:685
+#: src/data/settings.c:687
 #, c-format
 msgid ""
 "%s: Custom currency string `%s' does not contain exactly three periods or "
@@ -652,352 +655,364 @@ msgstr ""
 
 #. TRANSLATORS: this fragment will be interpolated into
 #. messages in fh_lock() that identify types of files.
-#: src/data/sys-file-reader.c:198 src/data/sys-file-writer.c:190
+#: src/data/sys-file-reader.c:214 src/data/sys-file-writer.c:197
 msgid "system file"
 msgstr ""
 
-#: src/data/sys-file-reader.c:205
+#: src/data/sys-file-reader.c:221
 #, c-format
 msgid "Error opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:244
+#: src/data/sys-file-reader.c:260
 msgid "Misplaced type 4 record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:255
+#: src/data/sys-file-reader.c:271
 #, c-format
 msgid "Unrecognized record type %d."
-msgstr ""
+msgstr "Unrecognised record type %d."
 
-#: src/data/sys-file-reader.c:294
+#: src/data/sys-file-reader.c:310
 #, c-format
 msgid "File header claims %d variable positions but %d were read from file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:334
+#: src/data/sys-file-reader.c:350
 #, c-format
 msgid "Error closing system file \"%s\": %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:399 src/data/sys-file-reader.c:409
+#: src/data/sys-file-reader.c:415 src/data/sys-file-reader.c:425
 msgid "This is not an SPSS system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:428
+#: src/data/sys-file-reader.c:444
 msgid ""
 "Compression bias is not the usual value of 100, or system file uses "
 "unrecognized floating-point format."
 msgstr ""
+"Compression bias is not the usual value of 100, or system file uses \n"
+"unrecognised floating-point format."
 
-#: src/data/sys-file-reader.c:496
+#: src/data/sys-file-reader.c:512
 #, c-format
 msgid "Invalid variable name `%s'."
 msgstr ""
 
-#: src/data/sys-file-reader.c:500
+#: src/data/sys-file-reader.c:516
 #, c-format
 msgid "Bad variable width %d."
 msgstr ""
 
-#: src/data/sys-file-reader.c:504
+#: src/data/sys-file-reader.c:520
 #, c-format
 msgid "Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:512
+#: src/data/sys-file-reader.c:528
 msgid "Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/data/sys-file-reader.c:520
+#: src/data/sys-file-reader.c:536
 #, c-format
 msgid "Variable %s has label of invalid length %zu."
 msgstr ""
 
-#: src/data/sys-file-reader.c:539
+#: src/data/sys-file-reader.c:555
 msgid "Numeric missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
-#: src/data/sys-file-reader.c:554
+#: src/data/sys-file-reader.c:570
 msgid "String missing value indicator field is not 0, 1, 2, or 3."
 msgstr ""
 
-#: src/data/sys-file-reader.c:557
+#: src/data/sys-file-reader.c:573
 #, c-format
 msgid ""
 "Ignoring missing values on long string variable %s, which PSPP does not yet "
 "support."
 msgstr ""
 
-#: src/data/sys-file-reader.c:586
+#: src/data/sys-file-reader.c:602
 msgid "Missing string continuation record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:620
+#: src/data/sys-file-reader.c:636
 #, c-format
 msgid "Unknown variable format %<PRIu8>."
 msgstr ""
 
-#: src/data/sys-file-reader.c:638
+#: src/data/sys-file-reader.c:654
 #, c-format
 msgid "%s variable %s has invalid %s format %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:641
+#: src/data/sys-file-reader.c:657
 msgid "print"
 msgstr ""
 
-#: src/data/sys-file-reader.c:641
+#: src/data/sys-file-reader.c:657
 msgid "write"
 msgstr ""
 
-#: src/data/sys-file-reader.c:645
+#: src/data/sys-file-reader.c:661
 msgid "Suppressing further invalid format warnings."
 msgstr ""
 
-#: src/data/sys-file-reader.c:663
+#: src/data/sys-file-reader.c:679
 msgid "Weighting variable must be numeric."
 msgstr ""
 
-#: src/data/sys-file-reader.c:677
+#: src/data/sys-file-reader.c:693
 msgid "Multiple type 6 (document) records."
 msgstr ""
 
-#: src/data/sys-file-reader.c:681
+#: src/data/sys-file-reader.c:697
 #, c-format
 msgid "Number of document lines (%d) must be greater than 0."
 msgstr ""
 
-#: src/data/sys-file-reader.c:689
+#: src/data/sys-file-reader.c:705
 msgid "Document line contains null byte."
 msgstr ""
 
-#: src/data/sys-file-reader.c:763
+#: src/data/sys-file-reader.c:782
 msgid ""
 "Ignoring value labels for long string variables, which PSPP does not yet "
 "support."
 msgstr ""
 
-#: src/data/sys-file-reader.c:768
+#: src/data/sys-file-reader.c:787
 #, c-format
 msgid "Unrecognized record type 7, subtype %d."
-msgstr ""
+msgstr "Unrecognised record type 7, subtype %d."
 
-#: src/data/sys-file-reader.c:793
+#: src/data/sys-file-reader.c:812
 #, c-format
 msgid "Bad size (%zu) or count (%zu) field on record type 7, subtype 3."
 msgstr ""
 
-#: src/data/sys-file-reader.c:813
+#: src/data/sys-file-reader.c:832
 #, c-format
 msgid ""
 "Floating-point representation indicated by system file (%d) differs from "
 "expected (%d)."
 msgstr ""
 
-#: src/data/sys-file-reader.c:826
+#: src/data/sys-file-reader.c:845
 msgid "little-endian"
 msgstr ""
 
-#: src/data/sys-file-reader.c:826
+#: src/data/sys-file-reader.c:845
 msgid "big-endian"
 msgstr ""
 
-#: src/data/sys-file-reader.c:827
+#: src/data/sys-file-reader.c:846
 #, c-format
 msgid ""
 "Integer format indicated by system file (%s) differs from expected (%s)."
 msgstr ""
 
-#: src/data/sys-file-reader.c:843
+#: src/data/sys-file-reader.c:862
 #, c-format
 msgid "Bad size (%zu) or count (%zu) on extension 4."
 msgstr ""
 
-#: src/data/sys-file-reader.c:847
+#: src/data/sys-file-reader.c:866
 #, c-format
 msgid "File specifies unexpected value %g as SYSMIS."
 msgstr ""
 
-#: src/data/sys-file-reader.c:849
+#: src/data/sys-file-reader.c:868
 #, c-format
 msgid "File specifies unexpected value %g as HIGHEST."
 msgstr ""
 
-#: src/data/sys-file-reader.c:851
+#: src/data/sys-file-reader.c:870
 #, c-format
 msgid "File specifies unexpected value %g as LOWEST."
 msgstr ""
 
-#: src/data/sys-file-reader.c:867
+#: src/data/sys-file-reader.c:886
 #, c-format
 msgid "Bad size %zu on extension 11."
 msgstr ""
 
-#: src/data/sys-file-reader.c:879
+#: src/data/sys-file-reader.c:898
 #, c-format
 msgid "Extension 11 has bad count %zu (for %zu variables)."
 msgstr ""
 
-#: src/data/sys-file-reader.c:900
+#: src/data/sys-file-reader.c:919
 #, c-format
 msgid ""
 "Invalid variable display parameters for variable %zu (%s).  Default "
 "parameters substituted."
 msgstr ""
 
-#: src/data/sys-file-reader.c:946
+#: src/data/sys-file-reader.c:963
 #, c-format
 msgid "Long variable mapping from %s to invalid variable name `%s'."
 msgstr ""
 
-#: src/data/sys-file-reader.c:956
+#: src/data/sys-file-reader.c:973
 #, c-format
 msgid "Duplicate long variable name `%s' within system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1011
+#: src/data/sys-file-reader.c:1026
 #, c-format
 msgid "%s listed as string of invalid length %s in very length string record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1021
+#: src/data/sys-file-reader.c:1036
 #, c-format
 msgid ""
 "%s listed in very long string record with width %s, which requires only one "
 "segment."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1027
+#: src/data/sys-file-reader.c:1042
 #, c-format
 msgid "Very long string %s overflows dictionary."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1041
+#: src/data/sys-file-reader.c:1056
 #, c-format
 msgid ""
 "Very long string with width %ld has segment %d of width %d (expected %d)"
 msgstr ""
 
-#: src/data/sys-file-reader.c:1086
+#: src/data/sys-file-reader.c:1101
 #, c-format
 msgid "Invalid number of labels: %d.  Ignoring labels."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1117
+#: src/data/sys-file-reader.c:1132
 msgid ""
 "Variable index record (type 4) does not immediately follow value label "
 "record (type 3) as it should."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1124
+#: src/data/sys-file-reader.c:1139
 #, c-format
 msgid ""
 "Number of variables associated with a value label (%d) is not between 1 and "
 "the number of variables (%zu)."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1134
+#: src/data/sys-file-reader.c:1149
 #, c-format
 msgid "Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1141
+#: src/data/sys-file-reader.c:1156
 #, c-format
 msgid ""
 "Variables associated with value label are not all of identical type.  "
 "Variable %s is %s, but variable %s is %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1174
+#: src/data/sys-file-reader.c:1189
 #, c-format
 msgid "Duplicate value label for %g on %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1177
+#: src/data/sys-file-reader.c:1192
 #, c-format
 msgid "Duplicate value label for \"%.*s\" on %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1255
+#: src/data/sys-file-reader.c:1230
+#, c-format
+msgid "Error parsing attribute value %s[%d]"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1244
+#, c-format
+msgid "Attribute value %s[%d] is not quoted: %s"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1360
 msgid "File ends in partial case."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1263
+#: src/data/sys-file-reader.c:1368
 #, c-format
 msgid "Error reading case from file %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1360 src/data/sys-file-reader.c:1396
+#: src/data/sys-file-reader.c:1465 src/data/sys-file-reader.c:1501
 msgid "Compressed data is corrupt."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1483
+#: src/data/sys-file-reader.c:1588
 #, c-format
 msgid "Variable index %d not in valid range 1...%d."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1488
+#: src/data/sys-file-reader.c:1593
 #, c-format
 msgid "Variable index %d refers to long string continuation."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1574
+#: src/data/sys-file-reader.c:1661
 #, c-format
-msgid "Suppressed %d additional variable map warnings."
+msgid "Suppressed %d additional related warnings."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1587
+#: src/data/sys-file-reader.c:1702
 #, c-format
 msgid "Variable map refers to unknown variable %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1663
+#: src/data/sys-file-reader.c:1810
 #, c-format
 msgid "System error: %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1665
+#: src/data/sys-file-reader.c:1812
 msgid "Unexpected end of file."
 msgstr ""
 
-#: src/data/sys-file-writer.c:163
+#: src/data/sys-file-writer.c:170
 #, c-format
 msgid "Unknown system file version %d. Treating as version %d."
 msgstr ""
 
-#: src/data/sys-file-writer.c:202
+#: src/data/sys-file-writer.c:209
 #, c-format
 msgid "Error opening \"%s\" for writing as a system file: %s."
 msgstr ""
 
-#: src/data/sys-file-writer.c:737
+#: src/data/sys-file-writer.c:814
 #, c-format
 msgid "An I/O error occurred writing system file \"%s\"."
 msgstr ""
 
-#: src/data/variable.c:209
+#: src/data/variable.c:240
 #, c-format
 msgid ""
 "Character `%c' (in %s) may not appear as the first character in a variable "
 "name."
 msgstr ""
 
-#: src/data/variable.c:221
+#: src/data/variable.c:252
 #, c-format
 msgid "Character `%c' (in %s) may not appear in a variable name."
 msgstr ""
 
-#: src/data/variable.c:249
+#: src/data/variable.c:280
 msgid "Variable name cannot be empty string."
 msgstr ""
 
-#: src/data/variable.c:255
+#: src/data/variable.c:286
 #, c-format
 msgid "Variable name %s exceeds %d-character limit."
 msgstr ""
 
-#: src/data/variable.c:263
+#: src/data/variable.c:294
 #, c-format
 msgid "`%s' may not be used as a variable name because it is a reserved word."
 msgstr ""
@@ -1202,6 +1217,81 @@ msgid ""
 "commands."
 msgstr ""
 
+#: src/language/data-io/combine-files.c:210
+msgid "Cannot specify the active file since no active file has been defined."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:216
+msgid ""
+"This command may not be used after TEMPORARY when the active file is an "
+"input source.  Temporary transformations will be made permanent."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:250
+msgid "Multiple IN subcommands for a single FILE or TABLE."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:302
+#, c-format
+msgid "File %s lacks BY variable %s."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:305
+#, c-format
+msgid "Active file lacks BY variable %s."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:376
+msgid "The BY subcommand is required."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:381
+msgid "BY is required when TABLE is specified."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:386
+msgid "BY is required when SORT is specified."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:525
+#, c-format
+msgid ""
+"Variable %s in file %s has different type or width from the same variable in "
+"earlier file."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:531
+#, c-format
+msgid "In file %s, %s is numeric."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:534
+#, c-format
+msgid "In file %s, %s is a string variable with width %d."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:539
+#, c-format
+msgid "In an earlier file, %s was numeric."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:542
+#, c-format
+msgid "In an earlier file, %s was a string variable with width %d."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:581
+#, c-format
+msgid ""
+"Variable name %s specified on %s subcommand duplicates an existing variable "
+"name."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:737
+#, c-format
+msgid "Encountered %zu sets of duplicate cases in the master file."
+msgstr ""
+
 #: src/language/data-io/data-list.c:128
 msgid "The END subcommand may only be used within INPUT PROGRAM."
 msgstr ""
@@ -1222,85 +1312,86 @@ msgstr ""
 msgid "At least one variable must be specified."
 msgstr ""
 
-#: src/language/data-io/data-list.c:348 src/language/data-io/data-list.c:437
+#: src/language/data-io/data-list.c:349 src/language/data-io/data-list.c:438
 #: src/language/data-io/get-data.c:528
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
-#: src/language/data-io/data-list.c:355
+#: src/language/data-io/data-list.c:356
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
-#: src/language/data-io/data-list.c:362
+#: src/language/data-io/data-list.c:363
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
-#: src/language/data-io/data-list.c:370
+#: src/language/data-io/data-list.c:371
 #, c-format
 msgid "Cannot place variable %s on record %d when RECORDS=%d is specified."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:455
-#: src/language/data-io/data-parser.c:464
+#: src/language/data-io/data-parser.c:458
+#: src/language/data-io/data-parser.c:467
 msgid "Quoted string extends beyond end of line."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:519
+#: src/language/data-io/data-parser.c:522
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:565
+#: src/language/data-io/data-parser.c:568
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:602
+#: src/language/data-io/data-parser.c:605
 #, c-format
 msgid ""
 "Missing value(s) for all variables from %s onward.  These will be filled "
 "with the system-missing value or blanks, as appropriate."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:621
+#: src/language/data-io/data-parser.c:624
 msgid "Record ends in data not part of any field."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:641
-#: src/language/data-io/data-parser.c:682 src/language/data-io/print.c:403
+#: src/language/data-io/data-parser.c:644
+#: src/language/data-io/data-parser.c:685 src/language/data-io/print.c:403
 #: src/language/dictionary/split-file.c:84
-#: src/language/dictionary/sys-file-info.c:161
-#: src/language/dictionary/sys-file-info.c:390
-#: src/language/dictionary/sys-file-info.c:634
-#: src/language/stats/descriptives.c:883 src/ui/gui/dict-display.c:245
+#: src/language/dictionary/sys-file-info.c:162
+#: src/language/dictionary/sys-file-info.c:386
+#: src/language/dictionary/sys-file-info.c:709
+#: src/language/stats/descriptives.c:885 src/ui/gui/dict-display.c:245
 msgid "Variable"
 msgstr ""
 
-#: src/language/data-io/data-parser.c:642 src/language/data-io/print.c:404
+#: src/language/data-io/data-parser.c:645 src/language/data-io/print.c:404
 msgid "Record"
 msgstr ""
 
-#: src/language/data-io/data-parser.c:643 src/language/data-io/print.c:405
-#: src/ui/gui/crosstabs.glade:92 src/ui/gui/psppire-var-sheet.c:107
+#: src/language/data-io/data-parser.c:646 src/language/data-io/print.c:405
+#: src/ui/gui/crosstabs.glade:92 src/ui/gui/psppire-var-sheet.c:518
+#: src/ui/gui/psppire-var-store.c:770
 msgid "Columns"
 msgstr ""
 
-#: src/language/data-io/data-parser.c:644
-#: src/language/data-io/data-parser.c:683 src/language/data-io/print.c:406
+#: src/language/data-io/data-parser.c:647
+#: src/language/data-io/data-parser.c:686 src/language/data-io/print.c:406
 msgid "Format"
 msgstr ""
 
-#: src/language/data-io/data-parser.c:663
+#: src/language/data-io/data-parser.c:666
 #, c-format
 msgid "Reading %d record from %s."
 msgid_plural "Reading %d records from %s."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/language/data-io/data-parser.c:699
+#: src/language/data-io/data-parser.c:702
 #, c-format
 msgid "Reading free-form data from %s."
 msgstr ""
@@ -1317,57 +1408,57 @@ msgstr ""
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:190
+#: src/language/data-io/data-reader.c:191
 msgid ""
 "Unexpected end-of-file while reading data in BEGIN DATA.  This probably "
 "indicates a missing or misformatted END DATA command.  END DATA must appear "
 "by itself on a single line with exactly one space between words."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:215
+#: src/language/data-io/data-reader.c:216
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:218
+#: src/language/data-io/data-reader.c:219
 #, c-format
 msgid "Unexpected end of file reading %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:227
+#: src/language/data-io/data-reader.c:228
 #, c-format
 msgid "Unexpected end of file in partial record reading %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:287
+#: src/language/data-io/data-reader.c:288
 #, c-format
 msgid "Corrupt block descriptor word at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:288
+#: src/language/data-io/data-reader.c:289
 #, c-format
 msgid "Corrupt record descriptor word at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:301
+#: src/language/data-io/data-reader.c:302
 #, c-format
 msgid "Corrupt record size at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:443
+#: src/language/data-io/data-reader.c:444
 msgid "Record exceeds remaining block length."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:517
+#: src/language/data-io/data-reader.c:518
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:520
+#: src/language/data-io/data-reader.c:521
 msgid "Attempt to read beyond END DATA."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:706
+#: src/language/data-io/data-reader.c:707
 msgid ""
 "This command is not valid here since the current input program does not "
 "access the inline file."
@@ -1427,91 +1518,6 @@ msgstr ""
 msgid "expecting COMM or TAPE"
 msgstr ""
 
-#: src/language/data-io/get.c:272 src/language/data-io/get.c:286
-#: src/language/data-io/get.c:311
-#, c-format
-msgid "expecting %s or %s"
-msgstr ""
-
-#: src/language/data-io/get.c:506 src/language/data-io/print.c:178
-msgid "expecting a valid subcommand"
-msgstr ""
-
-#: src/language/data-io/get.c:539
-#, c-format
-msgid ""
-"Cannot rename %s as %s because there already exists a variable named %s.  To "
-"rename variables with overlapping names, use a single RENAME subcommand such "
-"as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
-msgstr ""
-
-#: src/language/data-io/get.c:565
-msgid "`=' expected after variable list."
-msgstr ""
-
-#: src/language/data-io/get.c:572
-#, c-format
-msgid ""
-"Number of variables on left side of `=' (%zu) does not match number of "
-"variables on right side (%zu), in parenthesized group %d of RENAME "
-"subcommand."
-msgstr ""
-
-#: src/language/data-io/get.c:585
-#, c-format
-msgid "Requested renaming duplicates variable name %s."
-msgstr ""
-
-#: src/language/data-io/get.c:615
-msgid "Cannot DROP all variables from dictionary."
-msgstr ""
-
-#: src/language/data-io/get.c:788
-msgid "Cannot specify the active file since no active file has been defined."
-msgstr ""
-
-#: src/language/data-io/get.c:795
-msgid ""
-"MATCH FILES may not be used after TEMPORARY when the active file is an input "
-"source.  Temporary transformations will be made permanent."
-msgstr ""
-
-#: src/language/data-io/get.c:829
-msgid "Multiple IN subcommands for a single FILE or TABLE."
-msgstr ""
-
-#: src/language/data-io/get.c:873
-#, c-format
-msgid "File %s lacks BY variable %s."
-msgstr ""
-
-#: src/language/data-io/get.c:876
-#, c-format
-msgid "Active file lacks BY variable %s."
-msgstr ""
-
-#: src/language/data-io/get.c:946
-msgid "BY is required when TABLE is specified."
-msgstr ""
-
-#: src/language/data-io/get.c:951
-msgid "BY is required when IN is specified."
-msgstr ""
-
-#: src/language/data-io/get.c:1056
-#, c-format
-msgid ""
-"Variable name %s specified on %s subcommand duplicates an existing variable "
-"name."
-msgstr ""
-
-#: src/language/data-io/get.c:1303
-#, c-format
-msgid ""
-"Variable %s in file %s (%s) has different type or width from the same "
-"variable in earlier file (%s)."
-msgstr ""
-
 #: src/language/data-io/get-data.c:62
 #, c-format
 msgid "Unsupported TYPE %s"
@@ -1581,11 +1587,11 @@ msgstr ""
 msgid "Input program did not create any variables."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:287
+#: src/language/data-io/inpt-pgm.c:286
 msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:337
+#: src/language/data-io/inpt-pgm.c:336
 msgid ""
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
@@ -1622,7 +1628,7 @@ msgstr ""
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/language/data-io/list.q:467
+#: src/language/data-io/list.q:468
 msgid "Line"
 msgstr ""
 
@@ -1662,6 +1668,10 @@ msgstr ""
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
+#: src/language/data-io/print.c:178 src/language/data-io/trim.c:54
+msgid "expecting a valid subcommand"
+msgstr ""
+
 #: src/language/data-io/print.c:266
 #, c-format
 msgid "Output calls for %d records but %zu specified on RECORDS subcommand."
@@ -1681,7 +1691,7 @@ msgid_plural "Writing %d records."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:476
+#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:478
 #: src/language/stats/autorecode.c:154 src/language/xforms/select-if.c:60
 msgid "expecting end of command"
 msgstr ""
@@ -1695,6 +1705,44 @@ msgstr ""
 msgid "The expression on PRINT SPACE evaluated to %g."
 msgstr ""
 
+#: src/language/data-io/save.c:223 src/language/data-io/save.c:238
+#: src/language/data-io/save.c:266
+#, c-format
+msgid "expecting %s or %s"
+msgstr ""
+
+#: src/language/data-io/trim.c:88
+#, c-format
+msgid ""
+"Cannot rename %s as %s because there already exists a variable named %s.  To "
+"rename variables with overlapping names, use a single RENAME subcommand such "
+"as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
+msgstr ""
+
+#: src/language/data-io/trim.c:114
+msgid "`=' expected after variable list."
+msgstr ""
+
+#: src/language/data-io/trim.c:121
+#, c-format
+msgid ""
+"Number of variables on left side of `=' (%zu) does not match number of "
+"variables on right side (%zu), in parenthesized group %d of RENAME "
+"subcommand."
+msgstr ""
+"Number of variables on left side of `=' (%zu) does not match number of \n"
+"variables on right side (%zu), in parenthesised group %d of RENAME \n"
+"subcommand."
+
+#: src/language/data-io/trim.c:134
+#, c-format
+msgid "Requested renaming duplicates variable name %s."
+msgstr ""
+
+#: src/language/data-io/trim.c:165
+msgid "Cannot DROP all variables from dictionary."
+msgstr ""
+
 #: src/language/dictionary/apply-dictionary.c:75
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
@@ -1711,10 +1759,18 @@ msgid ""
 "Cannot apply missing values from source file to long string variable %s."
 msgstr ""
 
-#: src/language/dictionary/apply-dictionary.c:126
+#: src/language/dictionary/apply-dictionary.c:129
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
+#: src/language/dictionary/attributes.c:108
+msgid "Attribute array index must be between 1 and 65535."
+msgstr ""
+
+#: src/language/dictionary/attributes.c:189
+msgid "expecting ATTRIBUTE= or DELETE="
+msgstr ""
+
 #: src/language/dictionary/delete-variables.c:40
 msgid ""
 "DELETE VARIABLES may not be used after TEMPORARY.  Temporary transformations "
@@ -1736,7 +1792,7 @@ msgid "`)' expected after output format."
 msgstr ""
 
 #: src/language/dictionary/missing-values.c:56
-#: src/language/stats/aggregate.c:451
+#: src/language/stats/aggregate.c:458
 msgid "expecting `('"
 msgstr ""
 
@@ -1820,7 +1876,7 @@ msgstr ""
 #: src/language/dictionary/modify-variables.c:302
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
-msgstr ""
+msgstr "Unrecognised subcommand name `%s'."
 
 #: src/language/dictionary/modify-variables.c:304
 msgid "Subcommand name expected."
@@ -1869,233 +1925,240 @@ msgid "Renaming would duplicate variable name %s."
 msgstr ""
 
 #: src/language/dictionary/split-file.c:85
-#: src/language/dictionary/sys-file-info.c:563
-#: src/language/stats/crosstabs.q:1155 src/language/stats/crosstabs.q:1182
-#: src/language/stats/crosstabs.q:1202 src/language/stats/crosstabs.q:1224
-#: src/language/stats/examine.q:1198 src/language/stats/frequencies.q:1060
-#: src/language/stats/frequencies.q:1184
+#: src/language/dictionary/sys-file-info.c:480
+#: src/language/dictionary/sys-file-info.c:629
+#: src/language/stats/crosstabs.q:1156 src/language/stats/crosstabs.q:1183
+#: src/language/stats/crosstabs.q:1203 src/language/stats/crosstabs.q:1225
+#: src/language/stats/examine.q:1948 src/language/stats/frequencies.q:1056
+#: src/language/stats/frequencies.q:1180 src/language/stats/reliability.q:572
+#: src/language/stats/reliability.q:583
 msgid "Value"
 msgstr ""
 
 #: src/language/dictionary/split-file.c:86
-#: src/language/dictionary/sys-file-info.c:397
-#: src/language/dictionary/sys-file-info.c:564 src/ui/gui/crosstabs.glade:275
-#: src/ui/gui/psppire.glade:2099 src/ui/gui/psppire-var-sheet.c:104
+#: src/language/dictionary/sys-file-info.c:390
+#: src/language/dictionary/sys-file-info.c:630 src/ui/gui/crosstabs.glade:275
+#: src/ui/gui/psppire.glade:2101 src/ui/gui/psppire-var-sheet.c:515
+#: src/ui/gui/psppire-var-store.c:767
 msgid "Label"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:110
+#: src/language/dictionary/sys-file-info.c:113
 msgid "File:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:112 src/ui/gui/psppire.glade:2052
+#: src/language/dictionary/sys-file-info.c:115 src/ui/gui/psppire.glade:2040
 #: src/ui/gui/recode.glade:841
 msgid "Label:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:116
+#: src/language/dictionary/sys-file-info.c:119
 msgid "No label."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:119
+#: src/language/dictionary/sys-file-info.c:122
 msgid "Created:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:122
+#: src/language/dictionary/sys-file-info.c:125
 msgid "Integer Format:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:124
+#: src/language/dictionary/sys-file-info.c:127
 msgid "Big Endian."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:125
+#: src/language/dictionary/sys-file-info.c:128
 msgid "Little Endian."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:126
-#: src/language/dictionary/sys-file-info.c:134
+#: src/language/dictionary/sys-file-info.c:129
+#: src/language/dictionary/sys-file-info.c:137
 msgid "Unknown."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:127
+#: src/language/dictionary/sys-file-info.c:130
 msgid "Real Format:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:129
+#: src/language/dictionary/sys-file-info.c:132
 msgid "IEEE 754 LE."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:130
+#: src/language/dictionary/sys-file-info.c:133
 msgid "IEEE 754 BE."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:131
+#: src/language/dictionary/sys-file-info.c:134
 msgid "VAX D."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:132
+#: src/language/dictionary/sys-file-info.c:135
 msgid "VAX G."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:133
+#: src/language/dictionary/sys-file-info.c:136
 msgid "IBM 390 Hex Long."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:135
+#: src/language/dictionary/sys-file-info.c:138
 #: src/ui/gui/descriptives-dialog.glade:79 src/ui/gui/recode.glade:940
 msgid "Variables:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:137
+#: src/language/dictionary/sys-file-info.c:140
 msgid "Cases:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:139
+#: src/language/dictionary/sys-file-info.c:142
 msgid "Unknown"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:141
+#: src/language/dictionary/sys-file-info.c:144
 msgid "Type:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:142
+#: src/language/dictionary/sys-file-info.c:145
 msgid "System File."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:143
+#: src/language/dictionary/sys-file-info.c:146
 msgid "Weight:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:148
+#: src/language/dictionary/sys-file-info.c:151
 msgid "Not weighted."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:150
+#: src/language/dictionary/sys-file-info.c:153
 msgid "Mode:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:152
+#: src/language/dictionary/sys-file-info.c:155
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:152
+#: src/language/dictionary/sys-file-info.c:155
 msgid "on"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:152
+#: src/language/dictionary/sys-file-info.c:155
 msgid "off"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:162
-#: src/language/dictionary/sys-file-info.c:395
+#: src/language/dictionary/sys-file-info.c:163
+#: src/language/dictionary/sys-file-info.c:390
 msgid "Description"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:163
-#: src/language/dictionary/sys-file-info.c:393
-#: src/language/dictionary/sys-file-info.c:633
+#: src/language/dictionary/sys-file-info.c:164
+#: src/language/dictionary/sys-file-info.c:392
+#: src/language/dictionary/sys-file-info.c:708
 msgid "Position"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:222
+#: src/language/dictionary/sys-file-info.c:213
 msgid "The active file does not have a file label."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:225
+#: src/language/dictionary/sys-file-info.c:216
 msgid "File label:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:288
+#: src/language/dictionary/sys-file-info.c:291
 msgid "No variables to display."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:307
+#: src/language/dictionary/sys-file-info.c:306
 msgid "Macros not supported."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:317
+#: src/language/dictionary/sys-file-info.c:316
 msgid "The active file dictionary does not contain any documents."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:325
+#: src/language/dictionary/sys-file-info.c:324
 msgid "Documents in the active file:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:477
+#: src/language/dictionary/sys-file-info.c:479
+msgid "Attribute"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:537
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:485
+#: src/language/dictionary/sys-file-info.c:544
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:488
+#: src/language/dictionary/sys-file-info.c:547
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:494
+#: src/language/dictionary/sys-file-info.c:559
 #, c-format
 msgid "Measure: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:495
-#: src/ui/gui/psppire-var-sheet.c:123
+#: src/language/dictionary/sys-file-info.c:560
+#: src/ui/gui/psppire-var-sheet.c:108
 msgid "Nominal"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:496
-#: src/ui/gui/psppire-var-sheet.c:124
+#: src/language/dictionary/sys-file-info.c:561
+#: src/ui/gui/psppire-var-sheet.c:109
 msgid "Ordinal"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:497
-#: src/ui/gui/psppire-var-sheet.c:125
+#: src/language/dictionary/sys-file-info.c:562
+#: src/ui/gui/psppire-var-sheet.c:110
 msgid "Scale"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:500
+#: src/language/dictionary/sys-file-info.c:565
 #, c-format
 msgid "Display Alignment: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:501
-#: src/ui/gui/psppire-var-sheet.c:116
+#: src/language/dictionary/sys-file-info.c:566
+#: src/ui/gui/psppire-var-sheet.c:101
 msgid "Left"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:502
-#: src/ui/gui/psppire-var-sheet.c:118
+#: src/language/dictionary/sys-file-info.c:567
+#: src/ui/gui/psppire-var-sheet.c:103
 msgid "Center"
 msgstr "Centre"
 
-#: src/language/dictionary/sys-file-info.c:503
-#: src/ui/gui/psppire-var-sheet.c:117
+#: src/language/dictionary/sys-file-info.c:568
+#: src/ui/gui/psppire-var-sheet.c:102
 msgid "Right"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:506
+#: src/language/dictionary/sys-file-info.c:571
 #, c-format
 msgid "Display Width: %d"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:517
+#: src/language/dictionary/sys-file-info.c:583
 msgid "Missing Values: "
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:611
+#: src/language/dictionary/sys-file-info.c:686
 msgid "No vectors defined."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:632
+#: src/language/dictionary/sys-file-info.c:707
 msgid "Vector"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:635
+#: src/language/dictionary/sys-file-info.c:710
 msgid "Print Format"
 msgstr ""
 
@@ -2106,20 +2169,15 @@ msgid ""
 "s."
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:629
+#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:631
 msgid "expecting string"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:643
-msgid "expecting integer"
-msgstr ""
-
-#: src/language/dictionary/value-labels.c:170
-#, c-format
-msgid "Value label `%g' is not integer."
+#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:658
+msgid "expecting number"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:184
+#: src/language/dictionary/value-labels.c:182
 msgid "Truncating value label to 60 characters."
 msgstr ""
 
@@ -2226,6 +2284,10 @@ msgid ""
 "Unrecognized date unit \"%.*s\".  Valid date units are \"years\", \"quarters"
 "\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", and \"seconds\"."
 msgstr ""
+"Unrecognised date unit \"%.*s\".  Valid date units are \"years\", \"quarters"
+"\"\n"
+"\"\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", and \"seconds"
+"\"."
 
 #: src/language/expressions/helpers.c:332
 msgid ""
@@ -2274,7 +2336,7 @@ msgstr ""
 msgid "Unknown identifier %s."
 msgstr ""
 
-#: src/language/expressions/parse.c:885 src/language/stats/aggregate.c:509
+#: src/language/expressions/parse.c:885 src/language/stats/aggregate.c:516
 msgid "expecting `)'"
 msgstr ""
 
@@ -2342,8 +2404,7 @@ msgstr ""
 msgid "%s is a PSPP extension."
 msgstr ""
 
-#: src/language/expressions/parse.c:1267 src/ui/terminal/command-line.c:127
-#: src/ui/terminal/command-line.c:146 src/ui/terminal/command-line.c:158
+#: src/language/expressions/parse.c:1267
 #, c-format
 msgid "%s is not yet implemented."
 msgstr ""
@@ -2361,89 +2422,89 @@ msgstr ""
 msgid "expecting format type"
 msgstr ""
 
-#: src/language/lexer/lexer.c:282
+#: src/language/lexer/lexer.c:283
 #, c-format
 msgid "%s does not form a valid number."
 msgstr ""
 
-#: src/language/lexer/lexer.c:386
+#: src/language/lexer/lexer.c:388
 #, c-format
 msgid "Bad character in input: `%c'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:388
+#: src/language/lexer/lexer.c:390
 #, c-format
 msgid "Bad character in input: `\\%o'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:424
+#: src/language/lexer/lexer.c:426
 #, c-format
 msgid "Subcommand %s may only be specified once."
 msgstr ""
 
-#: src/language/lexer/lexer.c:432
+#: src/language/lexer/lexer.c:434
 #, c-format
 msgid "missing required subcommand %s"
 msgstr ""
 
-#: src/language/lexer/lexer.c:461
+#: src/language/lexer/lexer.c:463
 #, c-format
 msgid "Syntax error %s at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:464
+#: src/language/lexer/lexer.c:466
 #, c-format
 msgid "Syntax error at %s."
 msgstr ""
 
-#: src/language/lexer/lexer.c:598 src/language/lexer/lexer.c:615
+#: src/language/lexer/lexer.c:600 src/language/lexer/lexer.c:617
 #, c-format
 msgid "expecting `%s'"
 msgstr ""
 
-#: src/language/lexer/lexer.c:656
-msgid "expecting number"
+#: src/language/lexer/lexer.c:645
+msgid "expecting integer"
 msgstr ""
 
-#: src/language/lexer/lexer.c:668
+#: src/language/lexer/lexer.c:670
 msgid "expecting identifier"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1062
+#: src/language/lexer/lexer.c:1064
 msgid "binary"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1067
+#: src/language/lexer/lexer.c:1069
 msgid "octal"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1072
+#: src/language/lexer/lexer.c:1074
 msgid "hex"
 msgstr ""
 
-#: src/language/lexer/lexer.c:1082
+#: src/language/lexer/lexer.c:1084
 #, c-format
 msgid "String of %s digits has %zu characters, which is not a multiple of %d."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1111
+#: src/language/lexer/lexer.c:1113
 #, c-format
 msgid "`%c' is not a valid %s digit."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1145
+#: src/language/lexer/lexer.c:1147
 msgid "Unterminated string constant."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1199
+#: src/language/lexer/lexer.c:1201
 msgid "Unexpected end of file in string concatenation."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1207
+#: src/language/lexer/lexer.c:1209
 msgid "String expected following `+'."
 msgstr ""
 
-#: src/language/lexer/lexer.c:1220
+#: src/language/lexer/lexer.c:1222
 #, c-format
 msgid "String exceeds 255 characters in length (%zu characters)."
 msgstr ""
@@ -2546,54 +2607,54 @@ msgstr ""
 msgid "Bad bounds in use of TO convention."
 msgstr ""
 
-#: src/language/stats/aggregate.c:209
+#: src/language/stats/aggregate.c:219
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/language/stats/aggregate.c:240
+#: src/language/stats/aggregate.c:247
 msgid "expecting BREAK"
 msgstr ""
 
-#: src/language/stats/aggregate.c:245
+#: src/language/stats/aggregate.c:252
 msgid ""
 "When PRESORTED is specified, specifying sorting directions with (A) or (D) "
 "has no effect.  Output data will be sorted the same way as the input data."
 msgstr ""
 
-#: src/language/stats/aggregate.c:416
+#: src/language/stats/aggregate.c:423
 msgid "expecting aggregation function"
 msgstr ""
 
-#: src/language/stats/aggregate.c:434
+#: src/language/stats/aggregate.c:441
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
-#: src/language/stats/aggregate.c:490
+#: src/language/stats/aggregate.c:497
 #, c-format
 msgid "Missing argument %zu to %s."
 msgstr ""
 
-#: src/language/stats/aggregate.c:499
+#: src/language/stats/aggregate.c:506
 #, c-format
 msgid "Arguments to %s must be of same type as source variables."
 msgstr ""
 
-#: src/language/stats/aggregate.c:521
+#: src/language/stats/aggregate.c:528
 #, c-format
 msgid ""
 "Number of source variables (%zu) does not match number of target variables (%"
 "zu)."
 msgstr ""
 
-#: src/language/stats/aggregate.c:537
+#: src/language/stats/aggregate.c:544
 #, c-format
 msgid ""
 "The value arguments passed to the %s function are out-of-order.  They will "
 "be treated as if they had been specified in the correct order."
 msgstr ""
 
-#: src/language/stats/aggregate.c:607
+#: src/language/stats/aggregate.c:614
 #, c-format
 msgid ""
 "Variable name %s is not unique within the aggregate file dictionary, which "
@@ -2620,49 +2681,51 @@ msgstr ""
 msgid "Variable %s is not dichotomous"
 msgstr ""
 
-#: src/language/stats/binomial.c:177
+#: src/language/stats/binomial.c:183
 msgid "Binomial Test"
 msgstr ""
 
-#: src/language/stats/binomial.c:201
+#: src/language/stats/binomial.c:207
 msgid "Group1"
 msgstr ""
 
-#: src/language/stats/binomial.c:202
+#: src/language/stats/binomial.c:208
 msgid "Group2"
 msgstr ""
 
-#: src/language/stats/binomial.c:203 src/language/stats/chisquare.c:223
-#: src/language/stats/chisquare.c:283 src/language/stats/crosstabs.q:862
-#: src/language/stats/crosstabs.q:1062 src/language/stats/crosstabs.q:1785
-#: src/language/stats/examine.q:918 src/language/stats/frequencies.q:1137
-#: src/language/stats/oneway.q:306 src/language/stats/oneway.q:476
-#: src/language/stats/regression.q:309 src/ui/gui/crosstabs-dialog.c:59
+#: src/language/stats/binomial.c:209 src/language/stats/chisquare.c:223
+#: src/language/stats/chisquare.c:283 src/language/stats/crosstabs.q:863
+#: src/language/stats/crosstabs.q:1063 src/language/stats/crosstabs.q:1786
+#: src/language/stats/examine.q:1207 src/language/stats/frequencies.q:1133
+#: src/language/stats/oneway.q:304 src/language/stats/oneway.q:470
+#: src/language/stats/regression.q:309 src/language/stats/reliability.q:705
+#: src/language/stats/wilcoxon.c:237 src/ui/gui/crosstabs-dialog.c:59
 msgid "Total"
 msgstr ""
 
-#: src/language/stats/binomial.c:235 src/language/stats/chisquare.c:246
-#: src/language/stats/crosstabs.q:1180 src/language/stats/crosstabs.q:1221
+#: src/language/stats/binomial.c:241 src/language/stats/chisquare.c:246
+#: src/language/stats/crosstabs.q:1181 src/language/stats/crosstabs.q:1222
 msgid "Category"
 msgstr ""
 
-#: src/language/stats/binomial.c:236 src/language/stats/crosstabs.q:872
-#: src/language/stats/examine.q:993 src/language/stats/frequencies.q:1405
-#: src/language/stats/npar-summary.c:122 src/language/stats/oneway.q:391
-#: src/language/stats/t-test.q:693 src/language/stats/t-test.q:716
-#: src/language/stats/t-test.q:850 src/language/stats/t-test.q:1387
+#: src/language/stats/binomial.c:242 src/language/stats/crosstabs.q:873
+#: src/language/stats/examine.q:1280 src/language/stats/frequencies.q:1401
+#: src/language/stats/npar-summary.c:122 src/language/stats/oneway.q:386
+#: src/language/stats/reliability.q:708 src/language/stats/t-test.q:693
+#: src/language/stats/t-test.q:716 src/language/stats/t-test.q:850
+#: src/language/stats/t-test.q:1387 src/language/stats/wilcoxon.c:220
 msgid "N"
 msgstr ""
 
-#: src/language/stats/binomial.c:237
+#: src/language/stats/binomial.c:243
 msgid "Observed Prop."
 msgstr ""
 
-#: src/language/stats/binomial.c:238
+#: src/language/stats/binomial.c:244
 msgid "Test Prop."
 msgstr ""
 
-#: src/language/stats/binomial.c:241
+#: src/language/stats/binomial.c:247
 #, c-format
 msgid "Exact Sig. (%d-tailed)"
 msgstr ""
@@ -2691,7 +2754,7 @@ msgstr ""
 msgid "Frequencies"
 msgstr ""
 
-#: src/language/stats/chisquare.c:297
+#: src/language/stats/chisquare.c:297 src/language/stats/wilcoxon.c:288
 msgid "Test Statistics"
 msgstr ""
 
@@ -2699,8 +2762,8 @@ msgstr ""
 msgid "Chi-Square"
 msgstr ""
 
-#: src/language/stats/chisquare.c:312 src/language/stats/crosstabs.q:1156
-#: src/language/stats/oneway.q:279 src/language/stats/oneway.q:694
+#: src/language/stats/chisquare.c:312 src/language/stats/crosstabs.q:1157
+#: src/language/stats/oneway.q:277 src/language/stats/oneway.q:683
 #: src/language/stats/regression.q:302 src/language/stats/t-test.q:1001
 #: src/language/stats/t-test.q:1193 src/language/stats/t-test.q:1286
 msgid "df"
@@ -2736,126 +2799,128 @@ msgstr ""
 msgid "Maximum value (%ld) less than minimum value (%ld)."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:857
+#: src/language/stats/crosstabs.q:858
 msgid "Summary."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:859 src/language/stats/examine.q:981
+#: src/language/stats/crosstabs.q:860 src/language/stats/examine.q:1268
+#: src/language/stats/reliability.q:696
 msgid "Cases"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:860 src/language/stats/examine.q:916
-#: src/language/stats/frequencies.q:1058 src/language/stats/frequencies.q:1406
+#: src/language/stats/crosstabs.q:861 src/language/stats/examine.q:1205
+#: src/language/stats/frequencies.q:1054 src/language/stats/frequencies.q:1402
+#: src/language/stats/reliability.q:699
 msgid "Valid"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:861 src/language/stats/examine.q:917
-#: src/language/stats/frequencies.q:1128 src/language/stats/frequencies.q:1407
-#: src/ui/gui/psppire-var-sheet.c:106
+#: src/language/stats/crosstabs.q:862 src/language/stats/examine.q:1206
+#: src/language/stats/frequencies.q:1124 src/language/stats/frequencies.q:1403
+#: src/ui/gui/psppire-var-sheet.c:517 src/ui/gui/psppire-var-store.c:769
 msgid "Missing"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:873 src/language/stats/examine.q:996
-#: src/language/stats/frequencies.q:1062 src/language/stats/frequencies.q:1063
-#: src/language/stats/frequencies.q:1064
+#: src/language/stats/crosstabs.q:874 src/language/stats/examine.q:1283
+#: src/language/stats/frequencies.q:1058 src/language/stats/frequencies.q:1059
+#: src/language/stats/frequencies.q:1060
 msgid "Percent"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1114
+#: src/language/stats/crosstabs.q:1115
 msgid "count"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1115
+#: src/language/stats/crosstabs.q:1116
 msgid "row %"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1116
+#: src/language/stats/crosstabs.q:1117
 msgid "column %"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1117
+#: src/language/stats/crosstabs.q:1118
 msgid "total %"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1118
+#: src/language/stats/crosstabs.q:1119
 msgid "expected"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1119
+#: src/language/stats/crosstabs.q:1120
 msgid "residual"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1120
+#: src/language/stats/crosstabs.q:1121
 msgid "std. resid."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1121
+#: src/language/stats/crosstabs.q:1122
 msgid "adj. resid."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1151
+#: src/language/stats/crosstabs.q:1152
 msgid "Chi-square tests."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1154 src/language/stats/crosstabs.q:1181
-#: src/language/stats/crosstabs.q:1201 src/language/stats/crosstabs.q:1222
-#: src/language/stats/examine.q:1442 src/ui/gui/checkbox-treeview.c:94
+#: src/language/stats/crosstabs.q:1155 src/language/stats/crosstabs.q:1182
+#: src/language/stats/crosstabs.q:1202 src/language/stats/crosstabs.q:1223
+#: src/language/stats/examine.q:1745 src/ui/gui/checkbox-treeview.c:94
 msgid "Statistic"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1158
+#: src/language/stats/crosstabs.q:1159
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1160
+#: src/language/stats/crosstabs.q:1161
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1162
+#: src/language/stats/crosstabs.q:1163
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1177
+#: src/language/stats/crosstabs.q:1178
 msgid "Symmetric measures."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1183 src/language/stats/crosstabs.q:1225
+#: src/language/stats/crosstabs.q:1184 src/language/stats/crosstabs.q:1226
 msgid "Asymp. Std. Error"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1184 src/language/stats/crosstabs.q:1226
+#: src/language/stats/crosstabs.q:1185 src/language/stats/crosstabs.q:1227
 msgid "Approx. T"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1185 src/language/stats/crosstabs.q:1227
+#: src/language/stats/crosstabs.q:1186 src/language/stats/crosstabs.q:1228
 msgid "Approx. Sig."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1196
+#: src/language/stats/crosstabs.q:1197
 msgid "Risk estimate."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1200
+#: src/language/stats/crosstabs.q:1201
 #, c-format
 msgid "95%% Confidence Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1203 src/language/stats/t-test.q:1005
+#: src/language/stats/crosstabs.q:1204 src/language/stats/t-test.q:1005
 #: src/language/stats/t-test.q:1190 src/language/stats/t-test.q:1289
 msgid "Lower"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1204 src/language/stats/t-test.q:1006
+#: src/language/stats/crosstabs.q:1205 src/language/stats/t-test.q:1006
 #: src/language/stats/t-test.q:1191 src/language/stats/t-test.q:1290
 msgid "Upper"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1218
+#: src/language/stats/crosstabs.q:1219
 msgid "Directional measures."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1223 src/ui/gui/psppire.glade:2223
-#: src/ui/gui/psppire-var-sheet.c:101
+#: src/language/stats/crosstabs.q:1224 src/ui/gui/psppire.glade:2226
+#: src/ui/gui/psppire-var-sheet.c:512 src/ui/gui/psppire-var-store.c:764
 msgid "Type"
 msgstr ""
 
@@ -2989,9 +3054,9 @@ msgstr ""
 msgid "%s Dependent"
 msgstr ""
 
-#: src/language/stats/descriptives.c:102 src/language/stats/examine.q:1556
+#: src/language/stats/descriptives.c:102 src/language/stats/examine.q:1550
 #: src/language/stats/frequencies.q:123 src/language/stats/npar-summary.c:125
-#: src/language/stats/oneway.q:392 src/language/stats/t-test.q:694
+#: src/language/stats/oneway.q:387 src/language/stats/t-test.q:694
 #: src/language/stats/t-test.q:717 src/language/stats/t-test.q:849
 #: src/language/stats/t-test.q:1187 src/ui/gui/descriptives-dialog.c:39
 #: src/ui/gui/frequencies-dialog.c:40
@@ -3006,13 +3071,13 @@ msgstr ""
 msgid "Std Dev"
 msgstr ""
 
-#: src/language/stats/descriptives.c:105 src/language/stats/examine.q:1636
+#: src/language/stats/descriptives.c:105 src/language/stats/examine.q:1581
 #: src/language/stats/frequencies.q:128 src/ui/gui/descriptives-dialog.c:46
 #: src/ui/gui/frequencies-dialog.c:45
 msgid "Variance"
 msgstr ""
 
-#: src/language/stats/descriptives.c:106 src/language/stats/examine.q:1743
+#: src/language/stats/descriptives.c:106 src/language/stats/examine.q:1617
 #: src/language/stats/frequencies.q:129 src/ui/gui/descriptives-dialog.c:47
 #: src/ui/gui/frequencies-dialog.c:50
 msgid "Kurtosis"
@@ -3022,7 +3087,7 @@ msgstr ""
 msgid "S E Kurt"
 msgstr ""
 
-#: src/language/stats/descriptives.c:108 src/language/stats/examine.q:1723
+#: src/language/stats/descriptives.c:108 src/language/stats/examine.q:1612
 #: src/language/stats/frequencies.q:131 src/ui/gui/descriptives-dialog.c:48
 #: src/ui/gui/frequencies-dialog.c:46
 msgid "Skewness"
@@ -3032,22 +3097,22 @@ msgstr ""
 msgid "S E Skew"
 msgstr ""
 
-#: src/language/stats/descriptives.c:110 src/language/stats/examine.q:1684
+#: src/language/stats/descriptives.c:110 src/language/stats/examine.q:1601
 #: src/language/stats/frequencies.q:133 src/ui/gui/descriptives-dialog.c:43
 #: src/ui/gui/frequencies-dialog.c:48
 msgid "Range"
 msgstr ""
 
-#: src/language/stats/descriptives.c:111 src/language/stats/examine.q:1661
+#: src/language/stats/descriptives.c:111 src/language/stats/examine.q:1591
 #: src/language/stats/frequencies.q:134 src/language/stats/npar-summary.c:131
-#: src/language/stats/oneway.q:404 src/ui/gui/descriptives-dialog.c:41
+#: src/language/stats/oneway.q:400 src/ui/gui/descriptives-dialog.c:41
 #: src/ui/gui/frequencies-dialog.c:42
 msgid "Minimum"
 msgstr ""
 
-#: src/language/stats/descriptives.c:112 src/language/stats/examine.q:1672
+#: src/language/stats/descriptives.c:112 src/language/stats/examine.q:1596
 #: src/language/stats/frequencies.q:135 src/language/stats/npar-summary.c:134
-#: src/language/stats/oneway.q:405 src/ui/gui/descriptives-dialog.c:42
+#: src/language/stats/oneway.q:401 src/ui/gui/descriptives-dialog.c:42
 #: src/ui/gui/frequencies-dialog.c:43
 msgid "Maximum"
 msgstr ""
@@ -3084,137 +3149,151 @@ msgstr ""
 msgid "Target"
 msgstr ""
 
-#: src/language/stats/descriptives.c:672
+#: src/language/stats/descriptives.c:673
 #, c-format
 msgid "Z-score of %s"
 msgstr ""
 
-#: src/language/stats/descriptives.c:886
+#: src/language/stats/descriptives.c:888
 msgid "Valid N"
 msgstr ""
 
-#: src/language/stats/descriptives.c:887
+#: src/language/stats/descriptives.c:889
 msgid "Missing N"
 msgstr ""
 
-#: src/language/stats/descriptives.c:914
+#: src/language/stats/descriptives.c:916
 #, c-format
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
 
-#: src/language/stats/examine.q:288 src/language/stats/examine.q:291
-#, c-format
-msgid "%s is not currently supported."
+#: src/language/stats/examine.q:340 src/language/stats/examine.q:493
+#: src/language/stats/examine.q:1055
+msgid "Not creating plot because data set is empty."
 msgstr ""
 
-#: src/language/stats/examine.q:501 src/language/stats/examine.q:514
+#: src/language/stats/examine.q:350
 #, c-format
-msgid "%s and %s are mutually exclusive"
+msgid "Normal Q-Q Plot of %s"
 msgstr ""
 
-#: src/language/stats/examine.q:976
-msgid "Case Processing Summary"
+#: src/language/stats/examine.q:351 src/language/stats/examine.q:356
+msgid "Observed Value"
 msgstr ""
 
-#: src/language/stats/examine.q:1183
-msgid "Extreme Values"
+#: src/language/stats/examine.q:352
+msgid "Expected Normal"
 msgstr ""
 
-#: src/language/stats/examine.q:1199
-msgid "Case Number"
+#: src/language/stats/examine.q:354
+#, c-format
+msgid "Detrended Normal Q-Q Plot of %s"
 msgstr ""
 
-#: src/language/stats/examine.q:1297
-msgid "Highest"
+#: src/language/stats/examine.q:357
+msgid "Dev from Normal"
 msgstr ""
 
-#: src/language/stats/examine.q:1302
-msgid "Lowest"
+#: src/language/stats/examine.q:510
+#, c-format
+msgid "Boxplot of %s vs. %s"
 msgstr ""
 
-#: src/language/stats/examine.q:1443 src/language/stats/oneway.q:394
-#: src/language/stats/oneway.q:692 src/language/stats/regression.q:203
-msgid "Std. Error"
+#: src/language/stats/examine.q:514
+#, c-format
+msgid "Boxplot of %s"
 msgstr ""
 
-#: src/language/stats/examine.q:1445 src/language/stats/oneway.q:408
-#: src/ui/gui/examine.glade:307
-msgid "Descriptives"
-msgstr ""
+#: src/language/stats/examine.q:750 src/language/stats/examine.q:763
+#, c-format
+msgid "%s and %s are mutually exclusive"
+msgstr ""
+
+#: src/language/stats/examine.q:1263 src/language/stats/reliability.q:673
+msgid "Case Processing Summary"
+msgstr ""
 
-#: src/language/stats/examine.q:1574 src/language/stats/oneway.q:399
+#: src/language/stats/examine.q:1555 src/language/stats/oneway.q:395
 #, c-format
 msgid "%g%% Confidence Interval for Mean"
 msgstr ""
 
-#: src/language/stats/examine.q:1580 src/language/stats/oneway.q:401
+#: src/language/stats/examine.q:1561 src/language/stats/oneway.q:397
 msgid "Lower Bound"
 msgstr ""
 
-#: src/language/stats/examine.q:1591 src/language/stats/oneway.q:402
+#: src/language/stats/examine.q:1566 src/language/stats/oneway.q:398
 msgid "Upper Bound"
 msgstr ""
 
-#: src/language/stats/examine.q:1603
+#: src/language/stats/examine.q:1571
 #, c-format
 msgid "5%% Trimmed Mean"
 msgstr ""
 
-#: src/language/stats/examine.q:1614 src/language/stats/frequencies.q:125
+#: src/language/stats/examine.q:1576 src/language/stats/frequencies.q:125
 #: src/ui/gui/frequencies-dialog.c:52
 msgid "Median"
 msgstr ""
 
-#: src/language/stats/examine.q:1648 src/language/stats/npar-summary.c:128
-#: src/language/stats/oneway.q:393 src/language/stats/t-test.q:695
+#: src/language/stats/examine.q:1586 src/language/stats/npar-summary.c:128
+#: src/language/stats/oneway.q:388 src/language/stats/t-test.q:695
 #: src/language/stats/t-test.q:718 src/language/stats/t-test.q:851
 #: src/language/stats/t-test.q:1188
 msgid "Std. Deviation"
 msgstr ""
 
-#: src/language/stats/examine.q:1696
+#: src/language/stats/examine.q:1606
 msgid "Interquartile Range"
 msgstr ""
 
-#: src/language/stats/examine.q:1850
-#, c-format
-msgid "Boxplot of %s vs. %s"
+#: src/language/stats/examine.q:1742 src/language/stats/oneway.q:404
+#: src/ui/gui/examine.glade:310
+msgid "Descriptives"
 msgstr ""
 
-#: src/language/stats/examine.q:1877
-msgid "Boxplot"
+#: src/language/stats/examine.q:1748 src/language/stats/oneway.q:389
+#: src/language/stats/oneway.q:681 src/language/stats/regression.q:203
+msgid "Std. Error"
 msgstr ""
 
-#: src/language/stats/examine.q:1919
+#: src/language/stats/examine.q:1845 src/language/stats/examine.q:1850
+#: src/ui/gui/psppire-data-store.c:746 src/ui/gui/psppire-var-store.c:627
+#: src/ui/gui/psppire-var-store.c:637 src/ui/gui/psppire-var-store.c:647
+#: src/ui/gui/psppire-var-store.c:756
 #, c-format
-msgid "Normal Q-Q Plot of %s"
+msgid "%d"
 msgstr ""
 
-#: src/language/stats/examine.q:1920 src/language/stats/examine.q:1926
-msgid "Observed Value"
+#: src/language/stats/examine.q:1928
+msgid "Highest"
 msgstr ""
 
-#: src/language/stats/examine.q:1921
-msgid "Expected Normal"
+#: src/language/stats/examine.q:1933
+msgid "Lowest"
 msgstr ""
 
-#: src/language/stats/examine.q:1924
-#, c-format
-msgid "Detrended Normal Q-Q Plot of %s"
+#: src/language/stats/examine.q:1940
+msgid "Extreme Values"
 msgstr ""
 
-#: src/language/stats/examine.q:1927
-msgid "Dev from Normal"
+#: src/language/stats/examine.q:1944
+msgid "Case Number"
+msgstr ""
+
+#: src/language/stats/examine.q:2066
+msgid "Tukey's Hinges"
 msgstr ""
 
-#: src/language/stats/examine.q:2046 src/language/stats/examine.q:2068
-#: src/language/stats/frequencies.q:1417 src/language/stats/npar-summary.c:141
-#: src/ui/gui/examine.glade:328
+#: src/language/stats/examine.q:2106 src/language/stats/examine.q:2124
+#: src/language/stats/frequencies.q:1413 src/language/stats/npar-summary.c:141
+#: src/ui/gui/examine.glade:333
 msgid "Percentiles"
 msgstr ""
 
-#: src/language/stats/examine.q:2204
-msgid "Tukey's Hinges"
+#: src/language/stats/examine.q:2113
+#, c-format
+msgid "%g"
 msgstr ""
 
 #: src/language/stats/flip.c:96
@@ -3278,12 +3357,12 @@ msgstr ""
 msgid "Error rewinding FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:488
+#: src/language/stats/flip.c:487
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:491
+#: src/language/stats/flip.c:490
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
@@ -3317,60 +3396,60 @@ msgid ""
 "MIN was specified as %g and MAX as %g.  MIN and MAX will be ignored."
 msgstr ""
 
-#: src/language/stats/frequencies.q:759
+#: src/language/stats/frequencies.q:755
 #, c-format
 msgid "Variable %s specified multiple times on VARIABLES subcommand."
 msgstr ""
 
-#: src/language/stats/frequencies.q:822
+#: src/language/stats/frequencies.q:818
 msgid "`)' expected after GROUPED interval list."
 msgstr ""
 
-#: src/language/stats/frequencies.q:834
+#: src/language/stats/frequencies.q:830
 #, c-format
 msgid "Variables %s specified on GROUPED but not on VARIABLES."
 msgstr ""
 
-#: src/language/stats/frequencies.q:841
+#: src/language/stats/frequencies.q:837
 #, c-format
 msgid "Variables %s specified multiple times on GROUPED subcommand."
 msgstr ""
 
-#: src/language/stats/frequencies.q:1059 src/language/stats/frequencies.q:1152
-#: src/language/stats/frequencies.q:1153 src/language/stats/frequencies.q:1187
+#: src/language/stats/frequencies.q:1055 src/language/stats/frequencies.q:1148
+#: src/language/stats/frequencies.q:1149 src/language/stats/frequencies.q:1183
 msgid "Cum"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1061 src/output/charts/plot-hist.c:126
+#: src/language/stats/frequencies.q:1057 src/output/charts/plot-hist.c:140
 msgid "Frequency"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1082
+#: src/language/stats/frequencies.q:1078
 msgid "Value Label"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1185
+#: src/language/stats/frequencies.q:1181
 msgid "Freq"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1186 src/language/stats/frequencies.q:1188
+#: src/language/stats/frequencies.q:1182 src/language/stats/frequencies.q:1184
 msgid "Pct"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1379
+#: src/language/stats/frequencies.q:1375
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/language/stats/frequencies.q:1421
+#: src/language/stats/frequencies.q:1417
 msgid "50 (Median)"
 msgstr ""
 
-#: src/language/stats/glm.q:148
+#: src/language/stats/glm.q:143
 msgid "Multivariate GLM not yet supported"
 msgstr ""
 
-#: src/language/stats/glm.q:356 src/language/stats/regression.q:1026
+#: src/language/stats/glm.q:262 src/language/stats/regression.q:1000
 msgid "No valid data found. This command was skipped."
 msgstr ""
 
@@ -3382,24 +3461,24 @@ msgstr ""
 msgid "TABLES subcommand may not appear more than once."
 msgstr ""
 
-#: src/language/stats/npar.q:98
+#: src/language/stats/npar.q:108
 msgid "NPAR subcommand not currently implemented."
 msgstr ""
 
-#: src/language/stats/npar.q:236
+#: src/language/stats/npar.q:251
 #, c-format
 msgid ""
 "The specified value of HI (%d) is lower than the specified value of LO (%d)"
 msgstr ""
 
-#: src/language/stats/npar.q:291
+#: src/language/stats/npar.q:306
 #, c-format
 msgid ""
 "%d expected values were given, but the specified range (%d-%d) requires "
 "exactly %d values."
 msgstr ""
 
-#: src/language/stats/npar.q:425 src/language/stats/t-test.q:496
+#: src/language/stats/npar.q:443 src/language/stats/t-test.q:496
 #, c-format
 msgid ""
 "PAIRED was specified but the number of variables preceding WITH (%zu) did "
@@ -3422,98 +3501,98 @@ msgstr ""
 msgid "75th"
 msgstr ""
 
-#: src/language/stats/oneway.q:169
+#: src/language/stats/oneway.q:170
 msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
-#: src/language/stats/oneway.q:178
+#: src/language/stats/oneway.q:179
 #, c-format
 msgid "Coefficients for contrast %zu do not total zero"
 msgstr ""
 
-#: src/language/stats/oneway.q:244
+#: src/language/stats/oneway.q:242
 #, c-format
 msgid "`%s' is not a variable name"
 msgstr ""
 
-#: src/language/stats/oneway.q:278 src/language/stats/regression.q:301
+#: src/language/stats/oneway.q:276 src/language/stats/regression.q:301
 msgid "Sum of Squares"
 msgstr ""
 
-#: src/language/stats/oneway.q:280 src/language/stats/regression.q:303
+#: src/language/stats/oneway.q:278 src/language/stats/regression.q:303
 msgid "Mean Square"
 msgstr ""
 
-#: src/language/stats/oneway.q:281 src/language/stats/regression.q:304
+#: src/language/stats/oneway.q:279 src/language/stats/regression.q:304
 #: src/language/stats/t-test.q:998
 msgid "F"
 msgstr ""
 
-#: src/language/stats/oneway.q:282 src/language/stats/oneway.q:542
+#: src/language/stats/oneway.q:280 src/language/stats/oneway.q:532
 #: src/language/stats/regression.q:206 src/language/stats/regression.q:305
 msgid "Significance"
 msgstr ""
 
-#: src/language/stats/oneway.q:304
+#: src/language/stats/oneway.q:302
 msgid "Between Groups"
 msgstr ""
 
-#: src/language/stats/oneway.q:305
+#: src/language/stats/oneway.q:303
 msgid "Within Groups"
 msgstr ""
 
-#: src/language/stats/oneway.q:352 src/language/stats/regression.q:330
+#: src/language/stats/oneway.q:347 src/language/stats/regression.q:330
 msgid "ANOVA"
 msgstr ""
 
-#: src/language/stats/oneway.q:539
+#: src/language/stats/oneway.q:529
 msgid "Levene Statistic"
 msgstr ""
 
-#: src/language/stats/oneway.q:540
+#: src/language/stats/oneway.q:530
 msgid "df1"
 msgstr ""
 
-#: src/language/stats/oneway.q:541
+#: src/language/stats/oneway.q:531
 msgid "df2"
 msgstr ""
 
-#: src/language/stats/oneway.q:545
+#: src/language/stats/oneway.q:534
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/language/stats/oneway.q:613
+#: src/language/stats/oneway.q:602
 msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/language/stats/oneway.q:615 src/language/stats/oneway.q:690
+#: src/language/stats/oneway.q:604 src/language/stats/oneway.q:679
 msgid "Contrast"
 msgstr ""
 
-#: src/language/stats/oneway.q:688
+#: src/language/stats/oneway.q:677
 msgid "Contrast Tests"
 msgstr ""
 
-#: src/language/stats/oneway.q:691
+#: src/language/stats/oneway.q:680
 msgid "Value of Contrast"
 msgstr ""
 
-#: src/language/stats/oneway.q:693 src/language/stats/regression.q:205
+#: src/language/stats/oneway.q:682 src/language/stats/regression.q:205
 #: src/language/stats/t-test.q:1000 src/language/stats/t-test.q:1192
 #: src/language/stats/t-test.q:1285
 msgid "t"
 msgstr ""
 
-#: src/language/stats/oneway.q:695 src/language/stats/t-test.q:1002
+#: src/language/stats/oneway.q:684 src/language/stats/t-test.q:1002
 #: src/language/stats/t-test.q:1194 src/language/stats/t-test.q:1287
 msgid "Sig. (2-tailed)"
 msgstr ""
 
-#: src/language/stats/oneway.q:739
+#: src/language/stats/oneway.q:728
 msgid "Assume equal variances"
 msgstr ""
 
-#: src/language/stats/oneway.q:743
+#: src/language/stats/oneway.q:732
 msgid "Does not assume equal"
 msgstr ""
 
@@ -3527,46 +3606,46 @@ msgstr ""
 msgid "%s of %s"
 msgstr ""
 
-#: src/language/stats/rank.q:602
+#: src/language/stats/rank.q:601
 msgid "Cannot create new rank variable.  All candidates in use."
 msgstr ""
 
-#: src/language/stats/rank.q:695
+#: src/language/stats/rank.q:694
 msgid "Variables Created By RANK"
 msgstr ""
 
-#: src/language/stats/rank.q:719
+#: src/language/stats/rank.q:718
 #, c-format
 msgid "%s into %s(%s of %s using %s BY %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:730
+#: src/language/stats/rank.q:729
 #, c-format
 msgid "%s into %s(%s of %s BY %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:744
+#: src/language/stats/rank.q:743
 #, c-format
 msgid "%s into %s(%s of %s using %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:754
+#: src/language/stats/rank.q:753
 #, c-format
 msgid "%s into %s(%s of %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:767
+#: src/language/stats/rank.q:766
 msgid ""
 "FRACTION has been specified, but NORMAL and PROPORTION rank functions have "
 "not been requested.  The FRACTION subcommand will be ignored."
 msgstr ""
 
-#: src/language/stats/rank.q:860
+#: src/language/stats/rank.q:857
 #, c-format
 msgid "Variable %s already exists."
 msgstr ""
 
-#: src/language/stats/rank.q:865
+#: src/language/stats/rank.q:862
 msgid "Too many variables in INTO clause."
 msgstr ""
 
@@ -3622,30 +3701,107 @@ msgstr ""
 msgid "Coefficient Correlations"
 msgstr ""
 
-#: src/language/stats/regression.q:807
+#: src/language/stats/regression.q:812
 msgid ""
 "The dependent variable is equal to the independent variable.The least "
 "squares line is therefore Y=X.Standard errors and related statistics may be "
 "meaningless."
 msgstr ""
 
-#: src/language/stats/regression.q:931
+#: src/language/stats/regression.q:904
 msgid "Dependent variable must be numeric."
 msgstr ""
 
+#: src/language/stats/reliability.q:429
+msgid "Reliability Statistics"
+msgstr ""
+
+#: src/language/stats/reliability.q:472
+msgid "Item-Total Statistics"
+msgstr ""
+
+#: src/language/stats/reliability.q:494
+msgid "Scale Mean if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:497
+msgid "Scale Variance if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:500
+msgid "Corrected Item-Total Correlation"
+msgstr ""
+
+#: src/language/stats/reliability.q:503
+msgid "Cronbach's Alpha if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:550 src/language/stats/reliability.q:566
+msgid "Cronbach's Alpha"
+msgstr ""
+
+#: src/language/stats/reliability.q:553
+msgid "N of items"
+msgstr ""
+
+#: src/language/stats/reliability.q:569
+msgid "Part 1"
+msgstr ""
+
+#: src/language/stats/reliability.q:575 src/language/stats/reliability.q:586
+msgid "N of Items"
+msgstr ""
+
+#: src/language/stats/reliability.q:580
+msgid "Part 2"
+msgstr ""
+
+#: src/language/stats/reliability.q:591
+msgid "Total N of Items"
+msgstr ""
+
+#: src/language/stats/reliability.q:594
+msgid "Correlation Between Forms"
+msgstr ""
+
+#: src/language/stats/reliability.q:598
+msgid "Spearman-Brown Coefficient"
+msgstr ""
+
+#: src/language/stats/reliability.q:601
+msgid "Equal Length"
+msgstr ""
+
+#: src/language/stats/reliability.q:604
+msgid "Unequal Length"
+msgstr ""
+
+#: src/language/stats/reliability.q:608
+msgid "Guttman Split-Half Coefficient"
+msgstr ""
+
+#: src/language/stats/reliability.q:702
+msgid "Excluded"
+msgstr ""
+
+#: src/language/stats/reliability.q:711
+#, c-format
+msgid "%%"
+msgstr ""
+
 #: src/language/stats/sort-cases.c:64
 msgid "Buffer limit must be at least 2."
 msgstr ""
 
-#: src/language/stats/sort-criteria.c:69
+#: src/language/stats/sort-criteria.c:74
 msgid "`A' or `D' expected inside parentheses."
 msgstr ""
 
-#: src/language/stats/sort-criteria.c:74
+#: src/language/stats/sort-criteria.c:79
 msgid "`)' expected."
 msgstr ""
 
-#: src/language/stats/sort-criteria.c:85
+#: src/language/stats/sort-criteria.c:92
 #, c-format
 msgid "Variable %s specified twice in sort criteria."
 msgstr ""
@@ -3771,6 +3927,50 @@ msgstr ""
 msgid "%s & %s"
 msgstr ""
 
+#: src/language/stats/wilcoxon.c:207
+msgid "Ranks"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:221
+msgid "Mean Rank"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:222
+msgid "Sum of Ranks"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:234
+msgid "Negative Ranks"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:235
+msgid "Positive Ranks"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:236
+msgid "Ties"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:301
+msgid "Z"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:302
+msgid "Asymp. Sig (2-tailed)"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:306
+msgid "Exact Sig (2-tailed)"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:307
+msgid "Exact Sig (1-tailed)"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:310
+msgid "Point Probability"
+msgstr ""
+
 #: src/language/syntax-file.c:88
 #, c-format
 msgid "opening \"%s\" as syntax file"
@@ -3842,32 +4042,37 @@ msgstr ""
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
-#: src/language/utilities/include.c:91
+#: src/language/utilities/include.c:92
 msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
 msgstr ""
 
-#: src/language/utilities/include.c:108
+#: src/language/utilities/include.c:109
 msgid "Expecting YES or NO after CD."
 msgstr ""
 
-#: src/language/utilities/include.c:125
+#: src/language/utilities/include.c:126
 msgid "Expecting CONTINUE or STOP after ERROR."
 msgstr ""
 
-#: src/language/utilities/include.c:132
+#: src/language/utilities/include.c:133
 #, c-format
 msgid "Unexpected token: `%s'."
 msgstr ""
 
-#: src/language/utilities/include.c:177
+#: src/language/utilities/include.c:178
 msgid "expecting file name"
 msgstr ""
 
-#: src/language/utilities/include.c:189
+#: src/language/utilities/include.c:190
 #, c-format
 msgid "Can't find `%s' in include file search path."
 msgstr ""
 
+#: src/language/utilities/include.c:198
+#, c-format
+msgid "Unable to open `%s': %s."
+msgstr ""
+
 #: src/language/utilities/permissions.c:73
 #, c-format
 msgid "Expecting %s or %s."
@@ -4027,20 +4232,20 @@ msgstr ""
 msgid "   (Entered %s)"
 msgstr ""
 
-#: src/language/xforms/compute.c:146 src/language/xforms/compute.c:194
+#: src/language/xforms/compute.c:149 src/language/xforms/compute.c:203
 #, c-format
 msgid ""
 "When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
 "s."
 msgstr ""
 
-#: src/language/xforms/compute.c:150 src/language/xforms/compute.c:201
+#: src/language/xforms/compute.c:153 src/language/xforms/compute.c:210
 #, c-format
 msgid ""
 "When executing COMPUTE: %g is not a valid value as an index into vector %s."
 msgstr ""
 
-#: src/language/xforms/compute.c:344
+#: src/language/xforms/compute.c:354
 #, c-format
 msgid "There is no vector named %s."
 msgstr ""
@@ -4049,44 +4254,44 @@ msgstr ""
 msgid "Destination cannot be a string variable."
 msgstr ""
 
-#: src/language/xforms/recode.c:246
+#: src/language/xforms/recode.c:251
 msgid ""
 "Inconsistent target variable types.  Target variables must be all numeric or "
 "all string."
 msgstr ""
 
-#: src/language/xforms/recode.c:267
+#: src/language/xforms/recode.c:272
 msgid "CONVERT requires string input values and numeric output values."
 msgstr ""
 
-#: src/language/xforms/recode.c:317
+#: src/language/xforms/recode.c:329
 msgid "THRU is not allowed with string variables."
 msgstr ""
 
-#: src/language/xforms/recode.c:391
+#: src/language/xforms/recode.c:407
 msgid "expecting output value"
 msgstr ""
 
-#: src/language/xforms/recode.c:440
+#: src/language/xforms/recode.c:456
 #, c-format
 msgid ""
 "%zu variable(s) cannot be recoded into %zu variable(s).  Specify the same "
 "number of variables as source and target variables."
 msgstr ""
 
-#: src/language/xforms/recode.c:455
+#: src/language/xforms/recode.c:471
 #, c-format
 msgid ""
 "There is no variable named %s.  (All string variables specified on INTO must "
 "already exist.  Use the STRING command to create a string variable.)"
 msgstr ""
 
-#: src/language/xforms/recode.c:470
+#: src/language/xforms/recode.c:487
 #, c-format
 msgid "INTO is required with %s input values and %s output values."
 msgstr ""
 
-#: src/language/xforms/recode.c:483
+#: src/language/xforms/recode.c:500
 #, c-format
 msgid "Type mismatch.  Cannot store %s data in %s variable %s."
 msgstr ""
@@ -4112,28 +4317,28 @@ msgstr ""
 msgid "The filter variable may not be scratch."
 msgstr ""
 
-#: src/libpspp/hash.c:614
+#: src/libpspp/hash.c:545
 #, c-format
 msgid "hash table:"
 msgstr ""
 
-#: src/math/percentiles.c:41
+#: src/math/percentiles.c:35
 msgid "HAverage"
 msgstr ""
 
-#: src/math/percentiles.c:42
+#: src/math/percentiles.c:36
 msgid "Weighted Average"
 msgstr ""
 
-#: src/math/percentiles.c:43
+#: src/math/percentiles.c:37
 msgid "Rounded"
 msgstr ""
 
-#: src/math/percentiles.c:44
+#: src/math/percentiles.c:38
 msgid "Empirical"
 msgstr ""
 
-#: src/math/percentiles.c:45
+#: src/math/percentiles.c:39
 msgid "Empirical with averaging"
 msgstr ""
 
@@ -4283,7 +4488,7 @@ msgstr ""
 msgid "creating \"%s\""
 msgstr ""
 
-#: src/output/charts/plot-hist.c:124
+#: src/output/charts/plot-hist.c:138
 msgid "HISTOGRAM"
 msgstr ""
 
@@ -4301,12 +4506,12 @@ msgstr ""
 msgid "unknown configuration parameter `%s' for HTML device driver"
 msgstr ""
 
-#: src/output/journal.c:68
+#: src/output/journal.c:69
 #, c-format
 msgid "error writing \"%s\""
 msgstr ""
 
-#: src/output/journal.c:90
+#: src/output/journal.c:94
 #, c-format
 msgid "error creating \"%s\""
 msgstr ""
@@ -4341,7 +4546,7 @@ msgstr ""
 msgid "reading \"%s\""
 msgstr ""
 
-#: src/output/output.c:332 src/ui/gui/message-dialog.c:97
+#: src/output/output.c:332 src/ui/gui/message-dialog.c:95
 #, c-format
 msgid "syntax error"
 msgstr ""
@@ -4402,7 +4607,7 @@ msgstr ""
 #: src/output/output.c:719
 #, c-format
 msgid "cannot initialize output driver `%s' of class `%s'"
-msgstr ""
+msgstr "cannot initialise output driver `%s' of class `%s'"
 
 #: src/output/output.c:765
 #, c-format
@@ -4578,7 +4783,7 @@ msgstr ""
 
 #: src/ui/gui/crosstabs-dialog.c:53 src/ui/gui/crosstabs-dialog.c:64
 #: src/ui/gui/crosstabs-dialog.c:99 src/ui/gui/crosstabs-dialog.c:107
-#: src/ui/gui/psppire-var-store.c:591 src/ui/gui/var-display.c:14
+#: src/ui/gui/psppire-var-store.c:543 src/ui/gui/var-display.c:14
 msgid "None"
 msgstr ""
 
@@ -4614,7 +4819,7 @@ msgstr ""
 msgid "Format..."
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:138 src/ui/gui/examine.glade:246
+#: src/ui/gui/crosstabs.glade:138 src/ui/gui/examine.glade:247
 #: src/ui/gui/regression.glade:31
 msgid "Statistics..."
 msgstr ""
@@ -4631,7 +4836,7 @@ msgstr ""
 msgid "Pivot"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:778
+#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:781
 msgid "Ascending"
 msgstr ""
 
@@ -4660,960 +4865,932 @@ msgstr ""
 msgid "Style of bevel around the custom entry button"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:150
-msgid "Transformations Pending"
+#: src/ui/gui/data-editor.glade:18 src/ui/gui/output-viewer.glade:22
+#: src/ui/gui/syntax-editor.glade:14
+msgid "_File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:323
-msgid "_Labels"
+#: src/ui/gui/data-editor.glade:33 src/ui/gui/data-editor.glade:59
+#: src/ui/gui/syntax-editor.glade:32 src/ui/gui/syntax-editor.glade:62
+msgid "_Syntax"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:324
-msgid "Show/hide value labels"
+#: src/ui/gui/data-editor.glade:40 src/ui/gui/data-editor.glade:66
+#: src/ui/gui/data-editor.glade:286 src/ui/gui/data-editor.glade:304
+#: src/ui/gui/syntax-editor.glade:41 src/ui/gui/syntax-editor.glade:71
+msgid "_Data"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:342 src/ui/gui/data-editor.c:361
-#: src/ui/gui/data-editor.c:1507 src/ui/gui/data-editor.c:1561
-msgid "Clear"
+#: src/ui/gui/data-editor.glade:78
+msgid "_Import Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:343
-msgid "Delete the cases at the selected position(s)"
+#: src/ui/gui/data-editor.glade:111
+msgid "Recently Used Da_ta"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:362
-msgid "Delete the variables at the selected position(s)"
+#: src/ui/gui/data-editor.glade:118
+msgid "Recently Used _Files"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:377
-msgid "Insert _Variable"
+#: src/ui/gui/data-editor.glade:142 src/ui/gui/output-viewer.glade:55
+#: src/ui/gui/syntax-editor.glade:118
+msgid "_Edit"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:378
-msgid "Create a new variable at the current position"
+#: src/ui/gui/data-editor.glade:150 src/ui/gui/data-editor.glade:807
+#: src/ui/gui/psppire-data-window.c:746 src/ui/gui/psppire-data-window.c:836
+msgid "Insert Variable"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:396
-msgid "Insert Ca_se"
+#: src/ui/gui/data-editor.glade:158
+msgid "Insert Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:397
-msgid "Create a new case at the current position"
+#: src/ui/gui/data-editor.glade:166 src/ui/gui/data-editor.glade:745
+msgid "Go To Case"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:417
-msgid "_Goto Case"
+#: src/ui/gui/data-editor.glade:207
+msgid "Cl_ear Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:418
-msgid "Jump to a Case in the Data Sheet"
+#: src/ui/gui/data-editor.glade:215
+msgid "_Clear Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:437
-msgid "_Weights"
+#: src/ui/gui/data-editor.glade:228
+msgid "_Find"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:438
-msgid "Weight cases by variable"
+#: src/ui/gui/data-editor.glade:239
+msgid "_View"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:447 src/ui/gui/data-editor.glade:319
-msgid "_Transpose"
+#: src/ui/gui/data-editor.glade:246
+msgid "_Status Bar"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:448
-msgid "Transpose the cases with the variables"
+#: src/ui/gui/data-editor.glade:259
+msgid "_Fonts"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:459
-msgid "S_plit"
+#: src/ui/gui/data-editor.glade:266
+msgid "_Grid Lines"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:460
-msgid "Split the active file"
+#: src/ui/gui/data-editor.glade:274
+msgid "Value _Labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:470
-msgid "_Sort"
+#: src/ui/gui/data-editor.glade:293 src/ui/gui/data-editor.glade:584
+msgid "_Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:471
-msgid "Sort cases in the active file"
+#: src/ui/gui/data-editor.glade:311
+msgid "_Sort Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:479 src/ui/gui/data-editor.glade:340
-msgid "Select _Cases"
+#: src/ui/gui/data-editor.glade:319
+msgid "_Transpose"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:480
-msgid "Select cases from the active file"
+#: src/ui/gui/data-editor.glade:332
+msgid "S_plit File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:489 src/ui/gui/data-editor.glade:369
-msgid "_Compute"
+#: src/ui/gui/data-editor.glade:340
+msgid "Select _Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:490
-msgid "Compute new values for a variable"
+#: src/ui/gui/data-editor.glade:347
+msgid "_Weight Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:498
-msgid "Oneway _ANOVA"
+#: src/ui/gui/data-editor.glade:359
+msgid "_Transform"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:499
-msgid "Perform one way analysis of variance"
+#: src/ui/gui/data-editor.glade:369
+msgid "_Compute"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:507 src/ui/gui/data-editor.glade:496
-msgid "_Independent Samples T Test"
+#: src/ui/gui/data-editor.glade:377
+msgid "Ran_k Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:508
-msgid "Calculate T Test for samples from independent groups"
+#: src/ui/gui/data-editor.glade:389
+msgid "Recode into _Same Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:517 src/ui/gui/data-editor.glade:504
-msgid "_Paired Samples T Test"
+#: src/ui/gui/data-editor.glade:396
+msgid "Recode into _Different Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:518
-msgid "Calculate T Test for paired samples"
+#: src/ui/gui/data-editor.glade:409
+msgid "_Run Pending Transforms"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:527
-msgid "One _Sample T Test"
-msgstr ""
+#: src/ui/gui/data-editor.glade:422
+msgid "_Analyze"
+msgstr "_Analyse"
 
-#: src/ui/gui/data-editor.c:528
-msgid "Calculate T Test for sample from a single distribution"
+#: src/ui/gui/data-editor.glade:432
+msgid "_Descriptive Statistics"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:537 src/ui/gui/data-editor.glade:593
-msgid "Data File _Comments"
+#: src/ui/gui/data-editor.glade:442
+msgid "_Frequencies"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:538
-msgid "Commentary text for the data file"
+#: src/ui/gui/data-editor.glade:450 src/ui/gui/oneway.glade:179
+msgid "_Descriptives"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:546 src/ui/gui/data-editor.glade:228
-msgid "_Find"
+#: src/ui/gui/data-editor.glade:458
+msgid "_Explore"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:547
-msgid "Find Case"
+#: src/ui/gui/data-editor.glade:466
+msgid "_Crosstabs"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:556 src/ui/gui/data-editor.glade:377
-msgid "Ran_k Cases"
+#: src/ui/gui/data-editor.glade:478
+msgid "Compare _Means"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:557
-msgid "Rank Cases"
+#: src/ui/gui/data-editor.glade:488
+msgid "_One Sample T Test"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:566 src/ui/gui/data-editor.glade:389
-msgid "Recode into _Same Variables"
+#: src/ui/gui/data-editor.glade:496
+msgid "_Independent Samples T Test"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:567
-msgid "Recode values into the same Variables"
+#: src/ui/gui/data-editor.glade:504
+msgid "_Paired Samples T Test"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:576 src/ui/gui/data-editor.glade:396
-msgid "Recode into _Different Variables"
+#: src/ui/gui/data-editor.glade:512
+msgid "One Way _ANOVA"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:577
-msgid "Recode values into different Variables"
+#: src/ui/gui/data-editor.glade:524
+msgid "Bivariate _Correlation"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:586 src/ui/gui/data-editor.glade:293
-#: src/ui/gui/data-editor.glade:584
-msgid "_Variables"
+#: src/ui/gui/data-editor.glade:532
+msgid "Linear _Regression"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:587
-msgid "Jump to Variable"
+#: src/ui/gui/data-editor.glade:540
+msgid "_Non-Parametric Statistics"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:595 src/ui/gui/data-editor.glade:450
-#: src/ui/gui/oneway.glade:179
-msgid "_Descriptives"
+#: src/ui/gui/data-editor.glade:550
+msgid "_Chi-Square"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:596
-msgid "Calculate descriptive statistics (mean, variance, ...)"
+#: src/ui/gui/data-editor.glade:558
+msgid "_Binomial"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:605 src/ui/gui/data-editor.glade:442
-msgid "_Frequencies"
+#: src/ui/gui/data-editor.glade:574
+msgid "_Utilities"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:606
-msgid "Generate frequency statistics"
+#: src/ui/gui/data-editor.glade:593
+msgid "Data File _Comments"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:614 src/ui/gui/data-editor.glade:466
-msgid "_Crosstabs"
+#: src/ui/gui/data-editor.glade:604 src/ui/gui/output-viewer.glade:78
+#: src/ui/gui/syntax-editor.glade:209
+msgid "_Windows"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:615
-msgid "Generate crosstabulations"
+#: src/ui/gui/data-editor.glade:611 src/ui/gui/output-viewer.glade:88
+#: src/ui/gui/syntax-editor.glade:218
+msgid "_Minimize All Windows"
+msgstr "_Minimise All Windows"
+
+#: src/ui/gui/data-editor.glade:618
+msgid "_Split"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:624 src/ui/gui/data-editor.glade:458
-msgid "_Explore"
+#: src/ui/gui/data-editor.glade:629 src/ui/gui/output-viewer.glade:99
+#: src/ui/gui/syntax-editor.glade:229
+msgid "_Help"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:625
-msgid "Examine Data by Factors"
+#: src/ui/gui/data-editor.glade:636 src/ui/gui/output-viewer.glade:106
+#: src/ui/gui/syntax-editor.glade:237
+msgid "_Reference Manual"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:634 src/ui/gui/data-editor.glade:532
-msgid "Linear _Regression"
+#: src/ui/gui/data-editor.glade:643 src/ui/gui/output-viewer.glade:113
+#: src/ui/gui/syntax-editor.glade:244
+msgid "_About"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:635
-msgid "Estimate parameters of the linear model"
+#: src/ui/gui/data-editor.glade:667 src/ui/gui/psppire-data-window.c:354
+msgid "Open"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1027
-msgid "Font Selection"
+#: src/ui/gui/data-editor.glade:677 src/ui/gui/psppire-data-window.c:490
+msgid "Save"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1099
-msgid "No Split"
+#: src/ui/gui/data-editor.glade:687
+msgid "Print"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1108
-msgid "Split by "
+#: src/ui/gui/data-editor.glade:697
+msgid "Recall"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1133
-msgid "Filter off"
+#: src/ui/gui/data-editor.glade:715
+msgid "Undo"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1145
-#, c-format
-msgid "Filter by %s"
+#: src/ui/gui/data-editor.glade:725
+msgid "Redo"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1163
-msgid "Weights off"
+#: src/ui/gui/data-editor.glade:755
+msgid "Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1175
-#, c-format
-msgid "Weight by %s"
+#: src/ui/gui/data-editor.glade:775
+msgid "Find"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1198 src/ui/gui/data-editor.c:1440
-#: src/ui/gui/data-editor.glade:660
-msgid "Open"
+#: src/ui/gui/data-editor.glade:795 src/ui/gui/psppire-data-window.c:800
+msgid "Insert Case"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1199
-msgid "Open a data file"
+#: src/ui/gui/data-editor.glade:827
+msgid "Split File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1207 src/ui/gui/data-editor.c:1323
-#: src/ui/gui/data-editor.glade:670
-msgid "Save"
+#: src/ui/gui/data-editor.glade:838
+msgid "Weight Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1208 src/ui/gui/data-editor.c:1218
-msgid "Save data to file"
+#: src/ui/gui/data-editor.glade:850
+msgid "Select Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1217
-msgid "Save As"
+#: src/ui/gui/data-editor.glade:870 src/ui/gui/data-editor.glade:1458
+#: src/ui/gui/data-editor.glade:1639
+msgid "Value Labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1226 src/ui/gui/recode-dialog.c:928
-#: src/ui/gui/recode-dialog.c:1023
-msgid "New"
+#: src/ui/gui/data-editor.glade:881
+msgid "Use Sets"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1227
-msgid "New data file"
+#: src/ui/gui/data-editor.glade:913
+msgid "Information Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1235
-msgid "_Import Text Data"
+#: src/ui/gui/data-editor.glade:932
+msgid "Processor Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1236
-msgid "Import text data file"
+#: src/ui/gui/data-editor.glade:957
+msgid "Case Counter Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1331 src/ui/gui/data-editor.c:1448
-msgid "System Files (*.sav)"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:1337 src/ui/gui/data-editor.c:1454
-msgid "Portable Files (*.por) "
-msgstr ""
-
-#: src/ui/gui/data-editor.c:1343 src/ui/gui/data-editor.c:1460
-#: src/ui/gui/syntax-editor.c:138 src/ui/gui/syntax-editor.c:522
-msgid "All Files"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:1351
-msgid "System File"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:1356
-msgid "Portable File"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:1498
-msgid "Sort Ascending"
+#: src/ui/gui/data-editor.glade:982
+msgid "Filter Use Status Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1501
-msgid "Sort Descending"
+#: src/ui/gui/data-editor.glade:1008
+msgid "Weight Status Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1504 src/ui/gui/data-editor.glade:150
-#: src/ui/gui/data-editor.glade:801
-msgid "Insert Variable"
+#: src/ui/gui/data-editor.glade:1034
+msgid "Split File Status Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1558 src/ui/gui/data-editor.glade:789
-msgid "Insert Case"
+#: src/ui/gui/data-editor.glade:1064
+msgid "Variable Type"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:18 src/ui/gui/output-viewer.glade:22
-#: src/ui/gui/syntax-editor.glade:39
-msgid "_File"
+#: src/ui/gui/data-editor.glade:1100 src/ui/gui/psppire-var-store.c:551
+msgid "Comma"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:33 src/ui/gui/data-editor.glade:59
-#: src/ui/gui/syntax-editor.glade:57 src/ui/gui/syntax-editor.glade:87
-msgid "_Syntax"
+#: src/ui/gui/data-editor.glade:1116 src/ui/gui/psppire-var-store.c:552
+msgid "Dot"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:40 src/ui/gui/data-editor.glade:66
-#: src/ui/gui/data-editor.glade:286 src/ui/gui/data-editor.glade:304
-#: src/ui/gui/syntax-editor.glade:66 src/ui/gui/syntax-editor.glade:96
-msgid "_Data"
+#: src/ui/gui/data-editor.glade:1132
+msgid "Scientific notation"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:78
-msgid "_Import Delimited Text Data"
+#: src/ui/gui/data-editor.glade:1148 src/ui/gui/psppire-var-store.c:554
+msgid "Date"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:111
-msgid "Recently Used Da_ta"
+#: src/ui/gui/data-editor.glade:1164 src/ui/gui/psppire-var-store.c:555
+msgid "Dollar"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:118
-msgid "Recently Used _Files"
+#: src/ui/gui/data-editor.glade:1180
+msgid "Custom currency"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:142 src/ui/gui/output-viewer.glade:55
-#: src/ui/gui/syntax-editor.glade:143
-msgid "_Edit"
+#: src/ui/gui/data-editor.glade:1274
+msgid "positive"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:158
-msgid "Insert Cases"
+#: src/ui/gui/data-editor.glade:1280
+msgid "negative"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:166 src/ui/gui/data-editor.glade:738
-msgid "Go To Case"
+#: src/ui/gui/data-editor.glade:1293
+msgid "Sample"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:207
-msgid "Cl_ear Variables"
+#: src/ui/gui/data-editor.glade:1343
+msgid "Width:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:215
-msgid "_Clear Cases"
+#: src/ui/gui/data-editor.glade:1387
+msgid "Decimal Places:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:239
-msgid "_View"
+#: src/ui/gui/data-editor.glade:1556
+msgid "Value Label:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:246
-msgid "_Status Bar"
+#: src/ui/gui/data-editor.glade:1569 src/ui/gui/psppire.glade:2548
+#: src/ui/gui/recode.glade:185
+msgid "Value:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:259
-msgid "_Fonts"
+#: src/ui/gui/data-editor.glade:1706 src/ui/gui/examine.glade:432
+#: src/ui/gui/t-test.glade:460
+msgid "Missing Values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:266
-msgid "_Grid Lines"
+#: src/ui/gui/data-editor.glade:1724
+msgid "_Range plus one optional discrete missing value"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:274
-msgid "Value _Labels"
+#: src/ui/gui/data-editor.glade:1749
+msgid "_Low:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:311
-msgid "_Sort Cases"
+#: src/ui/gui/data-editor.glade:1778
+msgid "_High:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:332
-msgid "S_plit File"
+#: src/ui/gui/data-editor.glade:1819
+msgid "Di_screte value:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:347
-msgid "_Weight Cases"
+#: src/ui/gui/data-editor.glade:1866
+msgid "_No missing values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:359
-msgid "_Transform"
+#: src/ui/gui/data-editor.glade:1884
+msgid "_Discrete missing values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:409
-msgid "_Run Pending Transforms"
+#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
+msgid "Standard deviation"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:422
-msgid "_Analyze"
+#: src/ui/gui/descriptives-dialog.c:45
+msgid "Standard error"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:432
-msgid "_Descriptive Statistics"
+#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
+msgid "Statistics:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:478
-msgid "Compare _Means"
+#: src/ui/gui/descriptives-dialog.glade:184
+msgid "Exclude entire case if any selected variable is missing"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:488
-msgid "_One Sample T Test"
+#: src/ui/gui/descriptives-dialog.glade:194
+msgid "Include user-missing data in analysis"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:512
-msgid "One Way _ANOVA"
+#: src/ui/gui/descriptives-dialog.glade:207
+msgid "Save Z-scores of selected variables as new variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:524
-msgid "Bivariate _Correlation"
+#: src/ui/gui/descriptives-dialog.glade:223
+msgid "Options:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:540
-msgid "_Non-Parametric Statistics"
+#: src/ui/gui/examine.glade:49
+msgid "Label Cases by:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:550
-msgid "_Chi-Square"
+#: src/ui/gui/examine.glade:100
+msgid "Factor List:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:558
-msgid "_Binomial"
+#: src/ui/gui/examine.glade:150
+msgid "Dependent List:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:574
-msgid "_Utilities"
+#: src/ui/gui/examine.glade:257 src/ui/gui/t-test.glade:69
+#: src/ui/gui/t-test.glade:629 src/ui/gui/t-test.glade:780
+msgid "Options..."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:604 src/ui/gui/output-viewer.glade:78
-#: src/ui/gui/syntax-editor.glade:234
-msgid "_Windows"
+#: src/ui/gui/examine.glade:320
+msgid "Extremes"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:611 src/ui/gui/output-viewer.glade:88
-#: src/ui/gui/syntax-editor.glade:243
-msgid "_Minimize All Windows"
+#: src/ui/gui/examine.glade:388
+msgid "Exclude cases listwise"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:622 src/ui/gui/output-viewer.glade:99
-#: src/ui/gui/syntax-editor.glade:254
-msgid "_Help"
+#: src/ui/gui/examine.glade:399
+msgid "Exclude cases pairwise"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:629 src/ui/gui/output-viewer.glade:106
-#: src/ui/gui/syntax-editor.glade:262
-msgid "_Reference Manual"
+#: src/ui/gui/examine.glade:414
+msgid "Repeat values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:636 src/ui/gui/output-viewer.glade:113
-#: src/ui/gui/syntax-editor.glade:269
-msgid "_About"
+#: src/ui/gui/find-dialog.c:658
+#, c-format
+msgid "Bad regular expression: %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:680
-msgid "Print"
+#: src/ui/gui/frequencies-dialog.c:44
+msgid "Standard error of the mean"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:690
-msgid "Recall"
+#: src/ui/gui/frequencies-dialog.c:47
+msgid "Standard error of the skewness"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:708
-msgid "Undo"
+#: src/ui/gui/frequencies-dialog.c:51
+msgid "Standard error of the kurtosis"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:718
-msgid "Redo"
+#: src/ui/gui/frequencies.glade:98 src/ui/gui/psppire.glade:277
+#: src/ui/gui/rank.glade:103
+msgid "Variable(s):"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:748
-msgid "Variables"
+#: src/ui/gui/frequencies.glade:168
+msgid "Display Frequency Table"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:769
-msgid "Find"
+#: src/ui/gui/frequencies.glade:264
+msgid "Ascending Order"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:821
-msgid "Split File"
+#: src/ui/gui/frequencies.glade:275
+msgid "Descending Order"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:832
-msgid "Weight Cases"
+#: src/ui/gui/frequencies.glade:290
+msgid "Ascending Counts"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:844
-msgid "Select Cases"
+#: src/ui/gui/frequencies.glade:305
+msgid "Descending Counts"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:864 src/ui/gui/data-editor.glade:1452
-#: src/ui/gui/data-editor.glade:1633
-msgid "Value Labels"
+#: src/ui/gui/frequencies.glade:323
+msgid "Order by"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:875
-msgid "Use Sets"
+#: src/ui/gui/frequencies.glade:355
+msgid "Supress tables with more than N categories"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:907
-msgid "Information Area"
+#: src/ui/gui/frequencies.glade:371
+msgid "Maximum no of categories"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:926
-msgid "Processor Area"
+#: src/ui/gui/helper.c:173
+msgid "Sorry. The help system hasn't yet been implemented."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:951
-msgid "Case Counter Area"
+#: src/ui/gui/helper.c:216
+#, c-format
+msgid "Cannot open reference manual: %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:976
-msgid "Filter Use Status Area"
+#: src/ui/gui/main.c:42
+msgid "Don't show the splash screen"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1002
-msgid "Weight Status Area"
+#: src/ui/gui/main.c:158
+msgid "PSPPIRE --- A user interface for PSPP"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1028
-msgid "Split File Status Area"
+#: src/ui/gui/main.c:160
+msgid "Miscellaneous options:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1058
-msgid "Variable Type"
-msgstr ""
+#: src/ui/gui/main.c:162 src/ui/terminal/main.c:125
+msgid "Options affecting syntax and behavior:"
+msgstr "Options affecting syntax and behaviour:"
 
-#: src/ui/gui/data-editor.glade:1094 src/ui/gui/psppire-var-store.c:599
-msgid "Comma"
+#: src/ui/gui/message-dialog.c:99
+msgid "data file error"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1110 src/ui/gui/psppire-var-store.c:600
-msgid "Dot"
+#: src/ui/gui/message-dialog.c:104
+msgid "PSPP error"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1126
-msgid "Scientific notation"
+#: src/ui/gui/message-dialog.c:112
+msgid "syntax warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1142 src/ui/gui/psppire-var-store.c:602
-msgid "Date"
+#: src/ui/gui/message-dialog.c:116
+msgid "data file warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1158 src/ui/gui/psppire-var-store.c:603
-msgid "Dollar"
+#: src/ui/gui/message-dialog.c:121
+msgid "PSPP warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1174
-msgid "Custom currency"
+#: src/ui/gui/message-dialog.c:130
+msgid "syntax information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1268
-msgid "positive"
+#: src/ui/gui/message-dialog.c:134
+msgid "data file information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1274
-msgid "negative"
+#: src/ui/gui/message-dialog.c:139
+msgid "PSPP information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1287
-msgid "Sample"
-msgstr ""
+#: src/ui/gui/message-dialog.c:215
+msgid "The PSPP processing engine reported the following message:"
+msgid_plural "The PSPP processing engine reported the following messages:"
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/ui/gui/data-editor.glade:1337
-msgid "Width:"
-msgstr ""
+#: src/ui/gui/message-dialog.c:222
+#, c-format
+msgid "The PSPP processing engine reported %d message."
+msgid_plural "The PSPP processing engine reported %d messages."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/ui/gui/data-editor.glade:1381
-msgid "Decimal Places:"
-msgstr ""
+#: src/ui/gui/message-dialog.c:229
+#, c-format
+msgid "%d of these messages are displayed below."
+msgid_plural "%d of these messages are displayed below."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/ui/gui/data-editor.glade:1550
-msgid "Value Label:"
+#: src/ui/gui/message-dialog.glade:8
+msgid "Messages Reported"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1563 src/ui/gui/psppire.glade:2544
-#: src/ui/gui/recode.glade:185
-msgid "Value:"
+#: src/ui/gui/message-dialog.glade:42
+msgid ""
+"The PSPP processor reported # errors.  The first # and last # are shown "
+"below:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1700 src/ui/gui/examine.glade:423
-#: src/ui/gui/t-test.glade:460
-msgid "Missing Values"
+#: src/ui/gui/message-dialog.glade:94
+msgid "gtk-close"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1718
-msgid "_Range plus one optional discrete missing value"
+#: src/ui/gui/missing-val-dialog.c:114 src/ui/gui/missing-val-dialog.c:159
+msgid "Incorrect value for variable type"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1743
-msgid "_Low:"
+#: src/ui/gui/missing-val-dialog.c:135 src/ui/gui/missing-val-dialog.c:142
+msgid "Incorrect range specification"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1772
-msgid "_High:"
+#: src/ui/gui/oneway-anova-dialog.c:332
+#, c-format
+msgid "Contrast %d of %d"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1813
-msgid "Di_screte value:"
+#: src/ui/gui/oneway.glade:30
+msgid "_Factor:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1860
-msgid "_No missing values"
+#: src/ui/gui/oneway.glade:66
+msgid "Dependent _Variable(s):"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1878
-msgid "_Discrete missing values"
+#: src/ui/gui/oneway.glade:190
+msgid "_Homogeneity"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
-msgid "Standard deviation"
+#: src/ui/gui/oneway.glade:226
+msgid "_Contrasts..."
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.c:45
-msgid "Standard error"
+#: src/ui/gui/oneway.glade:309
+msgid "gtk-go-back"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
-msgid "Statistics:"
+#: src/ui/gui/oneway.glade:320
+msgid "gtk-go-forward"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:184
-msgid "Exclude entire case if any selected variable is missing"
+#: src/ui/gui/oneway.glade:343
+msgid "_Coefficients:"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:194
-msgid "Include user-missing data in analysis"
+#: src/ui/gui/oneway.glade:389
+msgid "Coefficient Total: "
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:207
-msgid "Save Z-scores of selected variables as new variables"
+#: src/ui/gui/oneway.glade:422
+msgid "Contrast 1 of 1"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:223
-msgid "Options:"
+#: src/ui/gui/output-viewer.glade:32
+msgid "gtk-save"
 msgstr ""
 
-#: src/ui/gui/examine.glade:132
-msgid "Dependent List:"
+#: src/ui/gui/output-viewer.glade:41
+msgid "gtk-save-as"
 msgstr ""
 
-#: src/ui/gui/examine.glade:180
-msgid "Factor List:"
+#: src/ui/gui/output-viewer.glade:65
+msgid "gtk-copy"
 msgstr ""
 
-#: src/ui/gui/examine.glade:218
-msgid "Label Cases by:"
+#: src/ui/gui/psppire-buttonbox.c:143
+msgid "Buttons"
 msgstr ""
 
-#: src/ui/gui/examine.glade:255 src/ui/gui/t-test.glade:69
-#: src/ui/gui/t-test.glade:629 src/ui/gui/t-test.glade:780
-msgid "Options..."
+#: src/ui/gui/psppire-buttonbox.c:144
+msgid "The mask that decides what buttons appear in the button box"
 msgstr ""
 
-#: src/ui/gui/examine.glade:316
-msgid "Extremes"
+#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:429
+msgid "Continue"
 msgstr ""
 
-#: src/ui/gui/examine.glade:382
-msgid "Exclude cases listwise"
+#: src/ui/gui/psppire-buttonbox.c:427
+msgid "OK"
 msgstr ""
 
-#: src/ui/gui/examine.glade:392
-msgid "Exclude cases pairwise"
+#: src/ui/gui/psppire-buttonbox.c:428
+msgid "Go To"
 msgstr ""
 
-#: src/ui/gui/examine.glade:406
-msgid "Repeat values"
+#: src/ui/gui/psppire-buttonbox.c:430
+msgid "Cancel"
 msgstr ""
 
-#: src/ui/gui/find-dialog.c:659
-#, c-format
-msgid "Bad regular expression: %s"
+#: src/ui/gui/psppire-buttonbox.c:431
+msgid "Help"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:44
-msgid "Standard error of the mean"
+#: src/ui/gui/psppire-buttonbox.c:432
+msgid "Reset"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:47
-msgid "Standard error of the skewness"
+#: src/ui/gui/psppire-buttonbox.c:433
+msgid "Paste"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:51
-msgid "Standard error of the kurtosis"
+#: src/ui/gui/psppire.c:228
+msgid "_Reset"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:98 src/ui/gui/psppire.glade:265
-#: src/ui/gui/rank.glade:67
-msgid "Variable(s):"
+#: src/ui/gui/psppire.c:229
+msgid "_Select"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:168
-msgid "Display Frequency Table"
+#: src/ui/gui/psppire-data-editor.c:946
+msgid "Data View"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:264
-msgid "Ascending Order"
+#: src/ui/gui/psppire-data-editor.c:949
+msgid "Variable View"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:275
-msgid "Descending Order"
+#: src/ui/gui/psppire-data-store.c:737
+msgid "var"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:290
-msgid "Ascending Counts"
+#: src/ui/gui/psppire-data-window.c:195
+msgid "Transformations Pending"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:305
-msgid "Descending Counts"
+#: src/ui/gui/psppire-data-window.c:211
+msgid "Filter off"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:323
-msgid "Order by"
+#: src/ui/gui/psppire-data-window.c:223
+#, c-format
+msgid "Filter by %s"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:355
-msgid "Supress tables with more than N categories"
+#: src/ui/gui/psppire-data-window.c:244
+msgid "No Split"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:371
-msgid "Maximum no of categories"
+#: src/ui/gui/psppire-data-window.c:253
+msgid "Split by "
 msgstr ""
 
-#: src/ui/gui/helper.c:139
-msgid "Sorry. The help system hasn't yet been implemented."
+#: src/ui/gui/psppire-data-window.c:281
+msgid "Weights off"
 msgstr ""
 
-#: src/ui/gui/helper.c:165
+#: src/ui/gui/psppire-data-window.c:293
 #, c-format
-msgid "Cannot open reference manual: %s"
-msgstr ""
-
-#: src/ui/gui/message-dialog.c:101
-msgid "data file error"
+msgid "Weight by %s"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:106
-msgid "PSPP error"
+#: src/ui/gui/psppire-data-window.c:362 src/ui/gui/psppire-data-window.c:498
+msgid "System Files (*.sav)"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:114
-msgid "syntax warning"
+#: src/ui/gui/psppire-data-window.c:368 src/ui/gui/psppire-data-window.c:504
+msgid "Portable Files (*.por) "
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:118
-msgid "data file warning"
+#: src/ui/gui/psppire-data-window.c:374 src/ui/gui/psppire-data-window.c:510
+#: src/ui/gui/psppire-syntax-window.c:340
+#: src/ui/gui/psppire-syntax-window.c:441
+msgid "All Files"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:123
-msgid "PSPP warning"
+#: src/ui/gui/psppire-data-window.c:518
+msgid "System File"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:132
-msgid "syntax information"
+#: src/ui/gui/psppire-data-window.c:523
+msgid "Portable File"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:136
-msgid "data file information"
+#: src/ui/gui/psppire-data-window.c:668
+msgid "Font Selection"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:141
-msgid "PSPP information"
+#: src/ui/gui/psppire-data-window.c:735
+msgid "Sort Ascending"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:209
-msgid "The PSPP processing engine reported the following message:"
-msgid_plural "The PSPP processing engine reported the following messages:"
-msgstr[0] ""
-msgstr[1] ""
-
-#: src/ui/gui/message-dialog.c:216
-#, c-format
-msgid "The PSPP processing engine reported %d message."
-msgid_plural "The PSPP processing engine reported %d messages."
-msgstr[0] ""
-msgstr[1] ""
+#: src/ui/gui/psppire-data-window.c:741
+msgid "Sort Descending"
+msgstr ""
 
-#: src/ui/gui/message-dialog.c:223
-#, c-format
-msgid "%d of these messages are displayed below."
-msgid_plural "%d of these messages are displayed below."
-msgstr[0] ""
-msgstr[1] ""
+#: src/ui/gui/psppire-data-window.c:749 src/ui/gui/psppire-data-window.c:803
+#: src/ui/gui/psppire-data-window.c:839 src/ui/gui/psppire-data-window.c:1195
+#: src/ui/gui/psppire-data-window.c:1213
+msgid "Clear"
+msgstr ""
 
-#: src/ui/gui/message-dialog.glade:8
-msgid "Messages Reported"
+#: src/ui/gui/psppire-data-window.c:1097
+msgid "Open a data file"
 msgstr ""
 
-#: src/ui/gui/message-dialog.glade:42
-msgid ""
-"The PSPP processor reported # errors.  The first # and last # are shown "
-"below:"
+#: src/ui/gui/psppire-data-window.c:1112
+msgid "New data file"
 msgstr ""
 
-#: src/ui/gui/message-dialog.glade:94
-msgid "gtk-close"
+#: src/ui/gui/psppire-data-window.c:1127
+msgid "Import text data file"
 msgstr ""
 
-#: src/ui/gui/missing-val-dialog.c:115 src/ui/gui/missing-val-dialog.c:160
-msgid "Incorrect value for variable type"
+#: src/ui/gui/psppire-data-window.c:1143 src/ui/gui/psppire-data-window.c:1160
+msgid "Save data to file"
 msgstr ""
 
-#: src/ui/gui/missing-val-dialog.c:136 src/ui/gui/missing-val-dialog.c:143
-msgid "Incorrect range specification"
+#: src/ui/gui/psppire-data-window.c:1159
+msgid "Save As"
 msgstr ""
 
-#: src/ui/gui/oneway-anova-dialog.c:335
-#, c-format
-msgid "Contrast %d of %d"
+#: src/ui/gui/psppire-data-window.c:1176
+msgid "Show/hide value labels"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:30
-msgid "_Factor:"
+#: src/ui/gui/psppire-data-window.c:1196
+msgid "Delete the cases at the selected position(s)"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:66
-msgid "Dependent _Variable(s):"
+#: src/ui/gui/psppire-data-window.c:1214
+msgid "Delete the variables at the selected position(s)"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:190
-msgid "_Homogeneity"
+#: src/ui/gui/psppire-data-window.c:1232
+msgid "Create a new variable at the current position"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:226
-msgid "_Contrasts..."
+#: src/ui/gui/psppire-data-window.c:1247
+msgid "Create a new case at the current position"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:309
-msgid "gtk-go-back"
+#: src/ui/gui/psppire-data-window.c:1263
+msgid "Jump to a Case in the Data Sheet"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:320
-msgid "gtk-go-forward"
+#: src/ui/gui/psppire-data-window.c:1279
+msgid "Weight cases by variable"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:343
-msgid "_Coefficients:"
+#: src/ui/gui/psppire-data-window.c:1293
+msgid "Transpose the cases with the variables"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:389
-msgid "Coefficient Total: "
+#: src/ui/gui/psppire-data-window.c:1307
+msgid "Split the active file"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:422
-msgid "Contrast 1 of 1"
+#: src/ui/gui/psppire-data-window.c:1322
+msgid "Sort cases in the active file"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:32
-msgid "gtk-save"
+#: src/ui/gui/psppire-data-window.c:1336
+msgid "Select cases from the active file"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:41
-msgid "gtk-save-as"
+#: src/ui/gui/psppire-data-window.c:1350
+msgid "Compute new values for a variable"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:65
-msgid "gtk-copy"
+#: src/ui/gui/psppire-data-window.c:1364
+msgid "Perform one way analysis of variance"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:143
-msgid "Buttons"
+#: src/ui/gui/psppire-data-window.c:1379
+msgid "Calculate T Test for samples from independent groups"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:144
-msgid "The mask that decides what buttons appear in the button box"
+#: src/ui/gui/psppire-data-window.c:1393
+msgid "Calculate T Test for paired samples"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:429
-msgid "Continue"
+#: src/ui/gui/psppire-data-window.c:1407
+msgid "Calculate T Test for sample from a single distribution"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:427
-msgid "OK"
+#: src/ui/gui/psppire-data-window.c:1422
+msgid "Commentary text for the data file"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:428
-msgid "Go To"
+#: src/ui/gui/psppire-data-window.c:1448
+msgid "Rank Cases"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:430
-msgid "Cancel"
+#: src/ui/gui/psppire-data-window.c:1462
+msgid "Recode values into the same variables"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:431
-msgid "Help"
+#: src/ui/gui/psppire-data-window.c:1476
+msgid "Recode values into different variables"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:432
-msgid "Reset"
+#: src/ui/gui/psppire-data-window.c:1490
+msgid "Jump to variable"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:433
-msgid "Paste"
+#: src/ui/gui/psppire-data-window.c:1503
+msgid "Calculate descriptive statistics (mean, variance, ...)"
 msgstr ""
 
-#: src/ui/gui/psppire.c:194
-msgid "_Reset"
+#: src/ui/gui/psppire-data-window.c:1517
+msgid "Generate frequency statistics"
 msgstr ""
 
-#: src/ui/gui/psppire.c:195
-msgid "_Select"
+#: src/ui/gui/psppire-data-window.c:1531
+msgid "Generate crosstabulations"
 msgstr ""
 
-#: src/ui/gui/psppire-data-editor.c:604
-msgid "Data View"
+#: src/ui/gui/psppire-data-window.c:1546
+msgid "Examine Data by Factors"
 msgstr ""
 
-#: src/ui/gui/psppire-data-editor.c:607
-msgid "Variable View"
+#: src/ui/gui/psppire-data-window.c:1560
+msgid "Estimate parameters of the linear model"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:828
-msgid "var"
+#: src/ui/gui/psppire-data-window.c:1712
+msgid "Split the window vertically and horizontally"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:949 src/ui/gui/psppire-var-store.c:840
-#, c-format
-msgid "%ld"
+#: src/ui/gui/psppire-data-window.c:1751
+msgid "Data Editor"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:11
+#: src/ui/gui/psppire.glade:10
 msgid ""
 "This is beta status software.  Please report bugs to bug-gnu-pspp@gnu.org"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:73 src/ui/gui/psppire.glade:154
-#: src/ui/gui/weight-cases-dialog.c:80
+#: src/ui/gui/psppire.glade:72 src/ui/gui/psppire.glade:155
+#: src/ui/gui/weight-cases-dialog.c:79
 msgid "Do not weight cases"
 msgstr ""
 
@@ -5621,15 +5798,15 @@ msgstr ""
 msgid "Weight cases by"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:107
+#: src/ui/gui/psppire.glade:108
 msgid "Frequency Variable"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:147
+#: src/ui/gui/psppire.glade:148
 msgid "Current Status: "
 msgstr ""
 
-#: src/ui/gui/psppire.glade:314
+#: src/ui/gui/psppire.glade:244
 msgid "Name Variable:"
 msgstr ""
 
@@ -5637,281 +5814,298 @@ msgstr ""
 msgid "Analyze all cases.  Do not create groups."
 msgstr "Analyse all cases.  Do not create groups."
 
-#: src/ui/gui/psppire.glade:439
+#: src/ui/gui/psppire.glade:440
 msgid "Compare groups."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:452
+#: src/ui/gui/psppire.glade:454
 msgid "Organize output by groups."
-msgstr ""
+msgstr "Organise output by groups."
 
-#: src/ui/gui/psppire.glade:499
+#: src/ui/gui/psppire.glade:502
 msgid "Groups based on:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:563
+#: src/ui/gui/psppire.glade:565
 msgid "Sort the file by grouping variables."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:574
+#: src/ui/gui/psppire.glade:577
 msgid "File is already sorted."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:618
+#: src/ui/gui/psppire.glade:622
 msgid "Current Status : "
 msgstr ""
 
-#: src/ui/gui/psppire.glade:626
+#: src/ui/gui/psppire.glade:630
 msgid "Analysis by groups is off"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:725
+#: src/ui/gui/psppire.glade:729
 msgid "Sort by:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:788
+#: src/ui/gui/psppire.glade:792
 msgid "Descending"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:804
+#: src/ui/gui/psppire.glade:809
 msgid "Sort Order"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:873
+#: src/ui/gui/psppire.glade:878
 msgid "Target Variable:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:904
+#: src/ui/gui/psppire.glade:909
 msgid "Type & Label"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:943
+#: src/ui/gui/psppire.glade:949
 msgid "="
 msgstr ""
 
-#: src/ui/gui/psppire.glade:989
+#: src/ui/gui/psppire.glade:995
 msgid "Numeric Expressions:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1043
+#: src/ui/gui/psppire.glade:1049
 msgid "Functions:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1107 src/ui/gui/psppire.glade:1253
+#: src/ui/gui/psppire.glade:1112 src/ui/gui/psppire.glade:1516
 #: src/ui/gui/recode.glade:731
 msgid "If..."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1223
-msgid "All Cases"
+#: src/ui/gui/psppire.glade:1345
+msgid "Use filter variable"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1239
-msgid "If condition is satisfied"
+#: src/ui/gui/psppire.glade:1398
+msgid "Based on time or case range"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1291
-msgid "Random sample of cases"
+#: src/ui/gui/psppire.glade:1411
+msgid "Range..."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1305
-msgid "Sample..."
+#: src/ui/gui/psppire.glade:1450
+msgid "Random sample of cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1343
-msgid "Based on time or case range"
+#: src/ui/gui/psppire.glade:1464
+msgid "Sample..."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1356
-msgid "Range..."
+#: src/ui/gui/psppire.glade:1502
+msgid "If condition is satisfied"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1394
-msgid "Use filter variable"
+#: src/ui/gui/psppire.glade:1551
+msgid "All Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1556
+#: src/ui/gui/psppire.glade:1566
 msgid "Select"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1586
+#: src/ui/gui/psppire.glade:1595
 msgid "Filtered"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1596
+#: src/ui/gui/psppire.glade:1606
 msgid "Deleted"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1613
+#: src/ui/gui/psppire.glade:1624
 msgid "Unselected Cases Are"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1678
+#: src/ui/gui/psppire.glade:1689
 msgid "Comments:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1720
+#: src/ui/gui/psppire.glade:1731
 msgid "Display comments in output"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1734
+#: src/ui/gui/psppire.glade:1746
 msgid "Column Number: 0"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1810
+#: src/ui/gui/psppire.glade:1822
 msgid "Variable Information:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1836
-msgid ""
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-msgstr ""
-
-#: src/ui/gui/psppire.glade:1900
-msgid "Observation"
+#: src/ui/gui/psppire.glade:1931
+msgid "First case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1910
+#: src/ui/gui/psppire.glade:1944
 msgid "Last case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1923
-msgid "First case"
+#: src/ui/gui/psppire.glade:1957
+msgid "Observation"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2081
+#: src/ui/gui/psppire.glade:2021
 msgid "Use expression as label"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2187 src/ui/gui/psppire-var-sheet.c:102
+#: src/ui/gui/psppire.glade:2147 src/ui/gui/psppire-var-sheet.c:513
+#: src/ui/gui/psppire-var-store.c:765
 msgid "Width"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2274
+#: src/ui/gui/psppire.glade:2277
 msgid "Goto Case Number:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2410
+#: src/ui/gui/psppire.glade:2414
 msgid "Sample Size"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2513
+#: src/ui/gui/psppire.glade:2517
 msgid "Variable:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2570
+#: src/ui/gui/psppire.glade:2574
 msgid "Search value labels"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2593
+#: src/ui/gui/psppire.glade:2598
 msgid "Regular expression Match"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2603
+#: src/ui/gui/psppire.glade:2609
 msgid "Search substrings"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2615
+#: src/ui/gui/psppire.glade:2622
 msgid "Wrap around"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2627
+#: src/ui/gui/psppire.glade:2635
 msgid "Search backward"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:100
+#: src/ui/gui/psppire-output-window.c:267
+msgid "Output Viewer"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:249
+#, c-format
+msgid "Saved file \"%s\""
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:278
+#, c-format
+msgid "Save contents of syntax editor to %s?"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:326
+msgid "Save Syntax"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:334
+#: src/ui/gui/psppire-syntax-window.c:435
+msgid "Syntax Files (*.sps) "
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:427
+msgid "Open Syntax"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:615
+msgid "Syntax Editor"
+msgstr ""
+
+#: src/ui/gui/psppire-var-sheet.c:511 src/ui/gui/psppire-var-store.c:763
 msgid "Name"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:103
+#: src/ui/gui/psppire-var-sheet.c:514 src/ui/gui/psppire-var-store.c:766
 msgid "Decimals"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:105
+#: src/ui/gui/psppire-var-sheet.c:516 src/ui/gui/psppire-var-store.c:768
 msgid "Values"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:108
+#: src/ui/gui/psppire-var-sheet.c:519 src/ui/gui/psppire-var-store.c:771
 msgid "Align"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:109
+#: src/ui/gui/psppire-var-sheet.c:520 src/ui/gui/psppire-var-store.c:772
 msgid "Measure"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:601
+#: src/ui/gui/psppire-var-store.c:553
 msgid "Scientific"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:604
+#: src/ui/gui/psppire-var-store.c:556
 msgid "Custom"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:675 src/ui/gui/psppire-var-store.c:685
-#: src/ui/gui/psppire-var-store.c:695
+#: src/ui/gui/psppire-window.c:92
 #, c-format
-msgid "%d"
+msgid "%s %s PSPPIRE %s"
 msgstr ""
 
-#: src/ui/gui/rank.glade:111
+#: src/ui/gui/rank.glade:57
 msgid "By:"
 msgstr ""
 
-#: src/ui/gui/rank.glade:197
+#: src/ui/gui/rank.glade:196
 msgid "_Smallest Value"
 msgstr ""
 
-#: src/ui/gui/rank.glade:209
+#: src/ui/gui/rank.glade:208
 msgid "_Largest Value"
 msgstr ""
 
-#: src/ui/gui/rank.glade:228
+#: src/ui/gui/rank.glade:227
 msgid "Assign rank 1 to:"
 msgstr ""
 
-#: src/ui/gui/rank.glade:246
+#: src/ui/gui/rank.glade:245
 msgid "_Display summary tables"
 msgstr ""
 
-#: src/ui/gui/rank.glade:262
+#: src/ui/gui/rank.glade:261
 msgid "Rank T_ypes"
 msgstr ""
 
-#: src/ui/gui/rank.glade:273
+#: src/ui/gui/rank.glade:272
 msgid "_Ties..."
 msgstr ""
 
-#: src/ui/gui/rank.glade:343
-msgid "Ntiles"
+#: src/ui/gui/rank.glade:339
+msgid "Sum of case weights"
 msgstr ""
 
-#: src/ui/gui/rank.glade:376
-msgid "Rank"
+#: src/ui/gui/rank.glade:355
+msgid "Fractional rank as %"
 msgstr ""
 
-#: src/ui/gui/rank.glade:386
-msgid "Savage score"
+#: src/ui/gui/rank.glade:369
+msgid "Fractional rank"
 msgstr ""
 
-#: src/ui/gui/rank.glade:400
-msgid "Fractional rank"
+#: src/ui/gui/rank.glade:383
+msgid "Savage score"
 msgstr ""
 
-#: src/ui/gui/rank.glade:414
-msgid "Fractional rank as %"
+#: src/ui/gui/rank.glade:397
+msgid "Rank"
 msgstr ""
 
-#: src/ui/gui/rank.glade:428
-msgid "Sum of case weights"
+#: src/ui/gui/rank.glade:411
+msgid "Ntiles"
 msgstr ""
 
 #: src/ui/gui/rank.glade:450
@@ -5922,63 +6116,67 @@ msgstr ""
 msgid "Normal Scores"
 msgstr ""
 
-#: src/ui/gui/rank.glade:495
+#: src/ui/gui/rank.glade:494
 msgid "Blom"
 msgstr ""
 
-#: src/ui/gui/rank.glade:506
+#: src/ui/gui/rank.glade:505
 msgid "Tukey"
 msgstr ""
 
-#: src/ui/gui/rank.glade:520
+#: src/ui/gui/rank.glade:519
 msgid "Rankit"
 msgstr ""
 
-#: src/ui/gui/rank.glade:534
+#: src/ui/gui/rank.glade:533
 msgid "Van der Wärden"
 msgstr ""
 
-#: src/ui/gui/rank.glade:551
+#: src/ui/gui/rank.glade:550
 msgid "Proportion Estimation Formula"
 msgstr ""
 
-#: src/ui/gui/rank.glade:614
+#: src/ui/gui/rank.glade:612
 msgid "_Mean"
 msgstr ""
 
-#: src/ui/gui/rank.glade:626
+#: src/ui/gui/rank.glade:624
 msgid "_Low"
 msgstr ""
 
-#: src/ui/gui/rank.glade:642
+#: src/ui/gui/rank.glade:640
 msgid "_High"
 msgstr ""
 
-#: src/ui/gui/rank.glade:660
+#: src/ui/gui/rank.glade:658
 msgid "_Sequential ranks to unique values"
 msgstr ""
 
-#: src/ui/gui/rank.glade:680
+#: src/ui/gui/rank.glade:678
 msgid "Rank Assigned to Ties"
 msgstr ""
 
-#: src/ui/gui/recode-dialog.c:879
+#: src/ui/gui/recode-dialog.c:878
 msgid "Recode into Different Variables"
 msgstr ""
 
-#: src/ui/gui/recode-dialog.c:882
+#: src/ui/gui/recode-dialog.c:881
 msgid "Recode into Same Variables"
 msgstr ""
 
-#: src/ui/gui/recode-dialog.c:913 src/ui/gui/recode-dialog.c:1015
+#: src/ui/gui/recode-dialog.c:912 src/ui/gui/recode-dialog.c:1014
 msgid "Old"
 msgstr ""
 
-#: src/ui/gui/recode-dialog.c:1274
+#: src/ui/gui/recode-dialog.c:927 src/ui/gui/recode-dialog.c:1022
+msgid "New"
+msgstr ""
+
+#: src/ui/gui/recode-dialog.c:1270
 msgid "Recode into Different Variables: Old and New Values "
 msgstr ""
 
-#: src/ui/gui/recode-dialog.c:1275
+#: src/ui/gui/recode-dialog.c:1271
 msgid "Recode into Same Variables: Old and New Values"
 msgstr ""
 
@@ -6094,59 +6292,38 @@ msgstr ""
 msgid "Residuals"
 msgstr ""
 
-#: src/ui/gui/select-cases-dialog.c:81
+#: src/ui/gui/select-cases-dialog.c:80
 #, c-format
 msgid "Approximately %3d%% of all cases."
 msgstr ""
 
-#: src/ui/gui/select-cases-dialog.c:82
+#: src/ui/gui/select-cases-dialog.c:81
 #, c-format
 msgid "Exactly %3d cases from the first %3d cases."
 msgstr ""
 
-#: src/ui/gui/select-cases-dialog.c:222
+#: src/ui/gui/select-cases-dialog.c:221
 #, c-format
 msgid "%d thru %d"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:77
-#, c-format
-msgid "Save contents of syntax editor to %s?"
-msgstr ""
-
-#: src/ui/gui/syntax-editor.c:124
-msgid "Save Syntax"
-msgstr ""
-
-#: src/ui/gui/syntax-editor.c:132 src/ui/gui/syntax-editor.c:516
-msgid "Syntax Files (*.sps) "
-msgstr ""
-
-#: src/ui/gui/syntax-editor.c:508
-msgid "Open Syntax"
-msgstr ""
-
-#: src/ui/gui/syntax-editor.glade:10
-msgid "Psppire Syntax Editor"
-msgstr ""
-
-#: src/ui/gui/syntax-editor.glade:188
+#: src/ui/gui/syntax-editor.glade:163
 msgid "_Run"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:197
+#: src/ui/gui/syntax-editor.glade:172
 msgid "All"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:205
+#: src/ui/gui/syntax-editor.glade:180
 msgid "Selection"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:213
+#: src/ui/gui/syntax-editor.glade:188
 msgid "Current Line"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:222
+#: src/ui/gui/syntax-editor.glade:197
 msgid "To End"
 msgstr ""
 
@@ -6156,37 +6333,37 @@ msgid ""
 "because GTK+ version 2.10.0 or later was not available."
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:488
+#: src/ui/gui/text-data-import-dialog.c:486
 #, c-format
 msgid "Could not open \"%s\": %s"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:504
+#: src/ui/gui/text-data-import-dialog.c:502
 #, c-format
 msgid "Error reading \"%s\": %s"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:507
+#: src/ui/gui/text-data-import-dialog.c:505
 #, c-format
 msgid ""
 "Failed to read \"%s\", because it contains a line over %d bytes long and "
 "therefore appears not to be a text file."
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:521
+#: src/ui/gui/text-data-import-dialog.c:519
 #, c-format
 msgid "\"%s\" is empty."
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:566
+#: src/ui/gui/text-data-import-dialog.c:564
 msgid "Import Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:617
+#: src/ui/gui/text-data-import-dialog.c:615
 msgid "Importing Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:768
+#: src/ui/gui/text-data-import-dialog.c:766
 msgid ""
 "This assistant will guide you through the process of importing data into "
 "PSPP from a text file with one line per case,  in which fields are separated "
@@ -6194,21 +6371,21 @@ msgid ""
 "\n"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:774
+#: src/ui/gui/text-data-import-dialog.c:772
 #, c-format
 msgid "The selected file contains %zu line of text.  "
 msgid_plural "The selected file contains %zu lines of text.  "
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/ui/gui/text-data-import-dialog.c:782
+#: src/ui/gui/text-data-import-dialog.c:780
 #, c-format
 msgid "The selected file contains approximately %lu line of text.  "
 msgid_plural "The selected file contains approximately %lu lines of text.  "
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/ui/gui/text-data-import-dialog.c:788
+#: src/ui/gui/text-data-import-dialog.c:786
 #, c-format
 msgid ""
 "Only the first %zu line of the file will be shown for preview purposes in "
@@ -6219,16 +6396,16 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/ui/gui/text-data-import-dialog.c:795
+#: src/ui/gui/text-data-import-dialog.c:793
 msgid "You may choose below how much of the file should actually be imported."
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:1515
-#: src/ui/gui/text-data-import-dialog.c:1759
+#: src/ui/gui/text-data-import-dialog.c:1540
+#: src/ui/gui/text-data-import-dialog.c:1782
 msgid "This input line has too few separators to fill in this field."
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:1750
+#: src/ui/gui/text-data-import-dialog.c:1773
 #, c-format
 msgid "Field content \"%.*s\" cannot be parsed in format %s."
 msgstr ""
@@ -6284,44 +6461,44 @@ msgstr ""
 msgid "Choose Separators"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:274
-msgid "_Space"
+#: src/ui/gui/text-data-import.glade:287
+msgid "C_ustom"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:285
-msgid "Ta_b"
+#: src/ui/gui/text-data-import.glade:302
+msgid "Slas_h (/)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:300
-msgid "Ban_g (!)"
+#: src/ui/gui/text-data-import.glade:319
+msgid "Semicolo_n (;)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:315
-msgid "_Colon (:)"
+#: src/ui/gui/text-data-import.glade:336
+msgid "P_ipe (|)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:330
-msgid "Co_mma (,)"
+#: src/ui/gui/text-data-import.glade:351
+msgid "H_yphen (-)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:347
-msgid "H_yphen (-)"
+#: src/ui/gui/text-data-import.glade:368
+msgid "Co_mma (,)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:364
-msgid "P_ipe (|)"
+#: src/ui/gui/text-data-import.glade:385
+msgid "_Colon (:)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:379
-msgid "Semicolo_n (;)"
+#: src/ui/gui/text-data-import.glade:400
+msgid "Ban_g (!)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:396
-msgid "Slas_h (/)"
+#: src/ui/gui/text-data-import.glade:415
+msgid "Ta_b"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:413
-msgid "C_ustom"
+#: src/ui/gui/text-data-import.glade:430
+msgid "_Space"
 msgstr ""
 
 #: src/ui/gui/text-data-import.glade:444
@@ -6329,43 +6506,36 @@ msgid "<b>Separators</b>"
 msgstr ""
 
 #: src/ui/gui/text-data-import.glade:475
-msgid "Quote separator characters with"
-msgstr ""
-
-#: src/ui/gui/text-data-import.glade:489
-msgid ""
-"\"'\n"
-"\"\n"
-"'\n"
+msgid "Doubled quote mark treated as escape"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:514
-msgid "Doubled quote mark treated as escape"
+#: src/ui/gui/text-data-import.glade:512
+msgid "Quote separator characters with"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:533
+#: src/ui/gui/text-data-import.glade:529
 msgid "<b>Quoting</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:584
+#: src/ui/gui/text-data-import.glade:580
 msgid "<b>Fields Preview</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:601
+#: src/ui/gui/text-data-import.glade:597
 msgid "Adjust Variable Formats"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:610
+#: src/ui/gui/text-data-import.glade:606
 msgid ""
 "Check the data formats displayed below and fix any that are incorrect.  You "
 "may set other variable properties now or later."
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:653
+#: src/ui/gui/text-data-import.glade:649
 msgid "<b>Variables</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:700
+#: src/ui/gui/text-data-import.glade:696
 msgid "<b>Data Preview</b>"
 msgstr ""
 
@@ -6411,132 +6581,104 @@ msgstr ""
 msgid "Confidence Interval: %2d %%"
 msgstr ""
 
-#: src/ui/gui/t-test-paired-samples.c:228
+#: src/ui/gui/t-test-paired-samples.c:225
 msgid "Var 1"
 msgstr ""
 
-#: src/ui/gui/t-test-paired-samples.c:229
+#: src/ui/gui/t-test-paired-samples.c:226
 msgid "Var 2"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:89
+#: src/ui/gui/variable-info-dialog.c:88
 #, c-format
 msgid "Label: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:98
+#: src/ui/gui/variable-info-dialog.c:97
 #, c-format
 msgid "Type: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:102
+#: src/ui/gui/variable-info-dialog.c:101
 #, c-format
 msgid "Missing Values: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:107
+#: src/ui/gui/variable-info-dialog.c:106
 #, c-format
 msgid "Measurement Level: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:121
+#: src/ui/gui/variable-info-dialog.c:120
 msgid "Value Labels:\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:133
+#: src/ui/gui/variable-info-dialog.c:132
 #, c-format
 msgid "%s %s\n"
 msgstr ""
 
-#: src/ui/gui/weight-cases-dialog.c:86
+#: src/ui/gui/weight-cases-dialog.c:85
 #, c-format
 msgid "Weight cases by %s"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:142
-#, c-format
-msgid "Syntax%d"
+#: src/ui/source-init-opts.c:42
+msgid ""
+"set to `compatible' if you want output calculated from broken algorithms"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:143 src/ui/gui/window-manager.c:178
-#, c-format
-msgid "%s --- PSPP Syntax Editor"
+#: src/ui/source-init-opts.c:43
+msgid "Append DIR to include path"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:146
-#, c-format
-msgid "Untitled%d"
+#: src/ui/source-init-opts.c:44
+msgid "Clear include path"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:147 src/ui/gui/window-manager.c:181
-#, c-format
-msgid "%s --- PSPP Data Editor"
+#: src/ui/source-init-opts.c:45
+msgid "Disable execution of .pspp/rc at startup"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:150
-#, c-format
-msgid "Output%d"
+#: src/ui/source-init-opts.c:46
+msgid "Set configuration directory to DIR"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:151
-#, c-format
-msgid "%s --- PSPP Output"
+#: src/ui/source-init-opts.c:47
+msgid "Don't allow some unsafe operations"
+msgstr ""
+
+#: src/ui/source-init-opts.c:48
+msgid "Set to `compatible' if you want only to accept SPSS compatible syntax"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:230
+#: src/ui/source-init-opts.c:83
 #, c-format
-msgid ""
-"PSPP, a program for statistical analysis of sample data.\n"
-"\n"
-"Usage: %s [OPTION]... FILE...\n"
-"\n"
-"If a long option shows an argument as mandatory, then it is mandatory\n"
-"for the equivalent short option also.  Similarly for optional arguments.\n"
-"\n"
-"Configuration:\n"
-"  -a, --algorithm={compatible|enhanced}\n"
-"                            set to `compatible' if you want output\n"
-"                            calculated from broken algorithms\n"
-"  -B, --config-dir=DIR      set configuration directory to DIR\n"
-"  -o, --device=DEVICE       select output driver DEVICE and disable "
-"defaults\n"
-"\n"
-"Input and output:\n"
-"  -e, --error-file=FILE     send error messages to FILE (appended)\n"
-"  -f, --out-file=FILE       send output to FILE (overwritten)\n"
-"  -p, --pipe                read syntax from stdin, send output to stdout\n"
-"  -I-, --no-include         clear include path\n"
-"  -I, --include=DIR         append DIR to include path\n"
-"\n"
-"Language modifiers:\n"
-"  -i, --interactive         interpret syntax in interactive mode\n"
-"  -n, --edit                just check syntax; don't actually run the code\n"
-"  -r, --no-statrc           disable execution of .pspp/rc at startup\n"
-"  -s, --safer               don't allow some unsafe operations\n"
-"  -x, --syntax={compatible|enhanced}\n"
-"                            set to `compatible' if you want only to accept\n"
-"                            spss compatible syntax\n"
-"\n"
-"Informative output:\n"
-"  -h, --help                print this help, then exit\n"
-"  -l, --list                print a list of known driver classes, then exit\n"
-"  -V, --version             show PSPP version, then exit\n"
-"  -v, --verbose             increments verbosity level\n"
-"\n"
-"Non-option arguments:\n"
-" FILE                       syntax file to execute\n"
-" KEY=VALUE                  overrides macros in output initialization file\n"
-"\n"
+msgid "Algorithm must be either \"compatible\" or \"enhanced\"."
 msgstr ""
 
-#: src/ui/terminal/command-line.c:265
+#: src/ui/source-init-opts.c:124
 #, c-format
-msgid ""
-"\n"
-"Report bugs to <%s>.\n"
+msgid "Syntax must be either \"compatible\" or \"enhanced\"."
+msgstr ""
+
+#: src/ui/terminal/main.c:115
+msgid "PSPP --- A program for statistical analysis"
+msgstr ""
+
+#: src/ui/terminal/main.c:116
+msgid "FILE1, FILE2 ... FILEn"
 msgstr ""
 
-#: src/ui/terminal/main.c:130
+#: src/ui/terminal/main.c:119 src/ui/terminal/terminal-opts.c:177
+msgid "Options affecting input and output locations:"
+msgstr ""
+
+#: src/ui/terminal/main.c:122
+msgid "Diagnositic options:"
+msgstr ""
+
+#: src/ui/terminal/main.c:156
 msgid ""
 "Stopping syntax file processing here to avoid a cascade of dependent command "
 "failures."
@@ -6574,6 +6716,30 @@ msgstr ""
 msgid "could not access definition for terminal `%s'"
 msgstr ""
 
+#: src/ui/terminal/terminal-opts.c:41
+msgid "Increase diagnostic verbosity level"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:68
+msgid "Send error messages to FILE (appended)"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:71
+msgid "Select output driver DEVICE and disable defaults"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:74
+msgid "Print a list of known driver classes, then exit"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:76
+msgid "Start an interactive session"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:178
+msgid "Diagnostic options:"
+msgstr ""
+
 #~ msgid ""
 #~ "   This program is free software: you can redistribute it and/or modify\n"
 #~ "   it under the terms of the GNU General Public License as published by\n"
index 1ffbdf3e24ae70d8673cff1887b6d3fe2222c6ab..56f7590410f8d042761b9cdbf64949d219c4c669 100644 (file)
@@ -2,14 +2,42 @@
 
 # PSPP
 
-include $(top_srcdir)/src/math/automake.mk
 include $(top_srcdir)/src/libpspp/automake.mk
 include $(top_srcdir)/src/data/automake.mk
+
+
+
+AM_CPPFLAGS += -I$(top_srcdir)/src -I$(top_srcdir)/lib -DPKGDATADIR=\"$(pkgdatadir)\"
+
+
+lib_LTLIBRARIES = src/libpspp-core.la src/libpspp.la
+src_libpspp_core_la_SOURCES = 
+
+
+src_libpspp_core_la_LDFLAGS = -release @VERSION@
+
+src_libpspp_core_la_LIBADD = \
+       src/data/libdata.la \
+       src/libpspp/libpspp.la \
+       $(LIBXML2_LIBS) $(PG_LIBS) \
+       gl/libgl.la
+
+src_libpspp_la_SOURCES = 
+
+src_libpspp_la_LDFLAGS = -release @VERSION@
+
+src_libpspp_la_LIBADD = \
+       src/language/liblanguage.la \
+       src/math/libpspp-math.la \
+       src/output/liboutput.la \
+       gl/libgl.la
+
+
+include $(top_srcdir)/src/math/automake.mk
 include $(top_srcdir)/src/output/automake.mk
 include $(top_srcdir)/src/language/automake.mk
-
 include $(top_srcdir)/src/ui/automake.mk
 
-AM_CPPFLAGS += -I$(top_srcdir)/src -I$(top_srcdir)/lib -DPKGDATADIR=\"$(pkgdatadir)\"
+
 
 EXTRA_DIST += src/OChangeLog
diff --git a/src/data/attributes.c b/src/data/attributes.c
new file mode 100644 (file)
index 0000000..aa12829
--- /dev/null
@@ -0,0 +1,298 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 <data/attributes.h>
+#include <assert.h>
+#include <string.h>
+#include <libpspp/array.h>
+#include <libpspp/hash-functions.h>
+#include "xalloc.h"
+
+/* A custom attribute of the sort maintained by the DATAFILE
+   ATTRIBUTE and VARIABLE ATTRIBUTE commands.
+
+   Attributes have a name (the rules for which are the same as
+   those for PSPP variable names) and one or more values, each of
+   which is a string.  An attribute may be part of one attribute
+   set. */
+struct attribute
+  {
+    struct hmap_node node;      /* Used by attrset. */
+    char *name;                 /* Name. */
+    char **values;              /* Each value. */
+    size_t n_values;            /* Number of values. */
+    size_t allocated_values;    /* Amount of allocated space for values. */
+  };
+
+/* Creates and returns a new attribute with the given NAME.  The
+   attribute initially has no values.  (Attributes with no values
+   cannot be saved to system files, so at least one value should
+   be added before the attribute is made available to the PSPP
+   user.) */
+struct attribute *
+attribute_create (const char *name)
+{
+  struct attribute *attr = xmalloc (sizeof *attr);
+  attr->name = xstrdup (name);
+  attr->values = NULL;
+  attr->n_values = 0;
+  attr->allocated_values = 0;
+  return attr;
+}
+
+/* Creates and returns a new attribute with the same name and
+   values as ORIG. */
+struct attribute *
+attribute_clone (const struct attribute *orig)
+{
+  struct attribute *attr;
+  size_t i;
+
+  attr = attribute_create (orig->name);
+  for (i = 0; i < orig->n_values; i++)
+    attribute_add_value (attr, orig->values[i]);
+  return attr;
+}
+
+/* Destroys ATTR and frees all associated memory.
+
+   This function must not be called if ATTR is part of an
+   attribute set.  Use attrset_delete() instead. */
+void
+attribute_destroy (struct attribute *attr)
+{
+  if (attr != NULL)
+    {
+      size_t i;
+
+      for (i = 0; i < attr->n_values; i++)
+        free (attr->values[i]);
+      free (attr->values);
+      free (attr->name);
+      free (attr);
+    }
+}
+
+/* Returns the name of ATTR.  The caller must not free or modify
+   the returned string. */
+const char *
+attribute_get_name (const struct attribute *attr)
+{
+  return attr->name;
+}
+
+/* Returns ATTR's value with the given INDEX, or a null pointer
+   if INDEX is greater than or equal to the number of values in
+   ATTR (that is, attributes are numbered starting from 0).  The
+   caller must not free or modify the returned string.  */
+const char *
+attribute_get_value (const struct attribute *attr, size_t index)
+{
+  return index < attr->n_values ? attr->values[index] : NULL;
+}
+
+/* Returns ATTR's number of values. */
+size_t
+attribute_get_n_values (const struct attribute *attrs)
+{
+  return attrs->n_values;
+}
+
+/* Adds a copy of VALUE as a new value to ATTR.  The caller
+   retains ownership of VALUE. */
+void
+attribute_add_value (struct attribute *attr, const char *value)
+{
+  if (attr->n_values >= attr->allocated_values)
+    attr->values = x2nrealloc (attr->values, &attr->allocated_values,
+                               sizeof *attr->values);
+  attr->values[attr->n_values++] = xstrdup (value);
+}
+
+/* Adds or replaces the value with the given INDEX in ATTR by a
+   copy of VALUE.  The caller retains ownership of VALUE.
+
+   If INDEX is an existing value index, that value is replaced.
+   If no value index numbered INDEX exists in ATTR, then it is
+   added, and any values intermediate between the last maximum
+   index and INDEX are set to the empty string. */
+void
+attribute_set_value (struct attribute *attr, size_t index, const char *value)
+{
+  if (index < attr->n_values)
+    {
+      /* Replace existing value. */
+      free (attr->values[index]);
+      attr->values[index] = xstrdup (value);
+    }
+  else
+    {
+      /* Add new value. */
+      while (index > attr->n_values)
+        attribute_add_value (attr, "");
+      attribute_add_value (attr, value);
+    }
+
+}
+
+/* Deletes the value with the given INDEX from ATTR.  Any values
+   with higher-numbered indexes are shifted down into the gap
+   that this creates.
+
+   If INDEX is greater than the maximum index, this has no effect.*/
+void
+attribute_del_value (struct attribute *attr, size_t index)
+{
+  if (index < attr->n_values)
+    {
+      free (attr->values[index]);
+      remove_element (attr->values, attr->n_values, sizeof *attr->values,
+                      index);
+      attr->n_values--;
+    }
+}
+\f
+/* Initializes SET as a new, initially empty attibute set. */
+void
+attrset_init (struct attrset *set)
+{
+  hmap_init (&set->map);
+}
+
+/* Initializes NEW_SET as a new attribute set whose contents are
+   initially the same as that of OLD_SET. */
+void
+attrset_clone (struct attrset *new_set, const struct attrset *old_set)
+{
+  struct attribute *old_attr;
+
+  attrset_init (new_set);
+  HMAP_FOR_EACH (old_attr, struct attribute, node, &old_set->map)
+    {
+      struct attribute *new_attr = attribute_clone (old_attr);
+      hmap_insert (&new_set->map, &new_attr->node,
+                   hmap_node_hash (&old_attr->node));
+    }
+}
+
+/* Frees the storage associated with SET, if SET is nonnull.
+   (Does not free SET itself.) */
+void
+attrset_destroy (struct attrset *set)
+{
+  if (set != NULL)
+    {
+      struct attribute *attr, *next;
+
+      HMAP_FOR_EACH_SAFE (attr, next, struct attribute, node, &set->map)
+        attribute_destroy (attr);
+      hmap_destroy (&set->map);
+    }
+}
+
+/* Returns the number of attributes in SET. */
+size_t
+attrset_count (const struct attrset *set)
+{
+  return hmap_count (&set->map);
+}
+
+/* Returns the attribute in SET whose name matches NAME
+   case-insensitively, or a null pointer if SET does not contain
+   an attribute with that name. */
+struct attribute *
+attrset_lookup (struct attrset *set, const char *name)
+{
+  struct attribute *attr;
+  HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node,
+                           hsh_hash_case_string (name), &set->map)
+    if (!strcasecmp (attribute_get_name (attr), name))
+      break;
+  return attr;
+}
+
+/* Adds ATTR to SET, which must not already contain an attribute
+   with the same name (matched case insensitively).  Ownership of
+   ATTR is transferred to SET. */
+void
+attrset_add (struct attrset *set, struct attribute *attr)
+{
+  const char *name = attribute_get_name (attr);
+  assert (attrset_lookup (set, name) == NULL);
+  hmap_insert (&set->map, &attr->node, hsh_hash_case_string (name));
+}
+
+/* Deletes any attribute from SET that matches NAME
+   (case-insensitively). */
+void
+attrset_delete (struct attrset *set, const char *name)
+{
+  struct attribute *attr = attrset_lookup (set, name);
+  if (attr != NULL)
+    {
+      hmap_delete (&set->map, &attr->node);
+      attribute_destroy (attr);
+    }
+}
+
+/* Deletes all attributes from SET. */
+void
+attrset_clear (struct attrset *set)
+{
+  attrset_destroy (set);
+  attrset_init (set);
+}
+
+static struct attribute *iterator_data (struct attrset_iterator *iterator)
+{
+  return HMAP_NULLABLE_DATA (iterator->node, struct attribute, node);
+}
+
+/* Returns the first attribute in SET and initializes ITERATOR.
+   If SET is empty, returns a null pointer.
+
+   The caller must not destroy the returned attribute, but it may
+   add or remove values.
+
+   Attributes are visited in no particular order.  Calling
+   attrset_add() during iteration can cause some attributes to
+   be visited more than once and others not at all. */
+struct attribute *
+attrset_first (const struct attrset *set, struct attrset_iterator *iterator)
+{
+  iterator->node = hmap_first (&set->map);
+  return iterator_data (iterator);
+}
+
+/* Returns the next attribute in SET and advances ITERATOR, which
+   should have been initialized by calling attrset_first().  If
+   all the attributes in SET have already been visited, returns a
+   null pointer.
+
+   The caller must not destroy the returned attribute, but it may
+   add or remove values.
+
+   Attributes are visited in no particular order.  Calling
+   attrset_add() during iteration can cause some attributes to
+   be visited more than once and others not at all. */
+struct attribute *
+attrset_next (const struct attrset *set, struct attrset_iterator *iterator)
+{
+  iterator->node = hmap_next (&set->map, iterator->node);
+  return iterator_data (iterator);
+}
diff --git a/src/data/attributes.h b/src/data/attributes.h
new file mode 100644 (file)
index 0000000..87cb772
--- /dev/null
@@ -0,0 +1,70 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 DATA_ATTRIBUTES_H
+#define DATA_ATTRIBUTES_H 1
+
+#include <libpspp/hmapx.h>
+
+/* This header supports custom attribute of the sort maintained
+   by the DATAFILE ATTRIBUTE and VARIABLE ATTRIBUTE commands.
+
+   Attributes have a name (the rules for which are the same as
+   those for PSPP variable names) and one or more values, each of
+   which is a string.  An attribute may be part of one attribute
+   set.
+
+   An attribute set is an unordered collection of attributes
+   with names that are unique (case-insensitively). */
+
+struct attribute *attribute_create (const char *name);
+struct attribute *attribute_clone (const struct attribute *);
+void attribute_destroy (struct attribute *);
+
+const char *attribute_get_name (const struct attribute *);
+const char *attribute_get_value (const struct attribute *, size_t index);
+void attribute_add_value (struct attribute *, const char *);
+void attribute_set_value (struct attribute *, size_t index, const char *);
+void attribute_del_value (struct attribute *, size_t index);
+size_t attribute_get_n_values (const struct attribute *);
+
+struct attrset 
+  {
+    struct hmap map;
+  };
+
+void attrset_init (struct attrset *);
+void attrset_clone (struct attrset *, const struct attrset *);
+void attrset_destroy (struct attrset *);
+
+size_t attrset_count (const struct attrset *);
+
+struct attribute *attrset_lookup (struct attrset *, const char *);
+void attrset_add (struct attrset *, struct attribute *);
+void attrset_delete (struct attrset *, const char *);
+void attrset_clear (struct attrset *);
+
+struct attrset_iterator
+  {
+    struct hmap_node *node;
+  };
+struct attribute *attrset_first (const struct attrset *,
+                                 struct attrset_iterator *);
+struct attribute *attrset_next (const struct attrset *,
+                                struct attrset_iterator *);
+
+
+#endif /* data/attributes.h */
index a6d18581483500dee9fb219cfa1e91bce800669a..4169a57ef0c3b08e75de85794576f371e965276c 100644 (file)
@@ -1,20 +1,23 @@
 
-noinst_LIBRARIES += src/data/libdata.a
+noinst_LTLIBRARIES += src/data/libdata.la
 
-src_data_libdata_a_CPPFLAGS = $(LIBXML2_CFLAGS) $(PG_CFLAGS) $(AM_CPPFLAGS) 
+src_data_libdata_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(PG_CFLAGS) $(AM_CPPFLAGS) 
 
+src_data_libdata_la_LIBADD =   $(LIBXML2_LIBS) $(PG_LIBS)
 
-src_data_libdata_a_SOURCES = \
+src_data_libdata_la_SOURCES = \
        src/data/any-reader.c \
        src/data/any-reader.h \
        src/data/any-writer.c \
        src/data/any-writer.h \
+       src/data/attributes.c \
+       src/data/attributes.h \
        src/data/calendar.c \
        src/data/calendar.h \
        src/data/case-map.c \
        src/data/case-map.h \
-       src/data/case-ordering.c \
-       src/data/case-ordering.h \
+       src/data/case-matcher.c \
+       src/data/case-matcher.h \
        src/data/case.c \
        src/data/casegrouper.c \
        src/data/casegrouper.h \
@@ -85,6 +88,8 @@ src_data_libdata_a_SOURCES = \
        src/data/short-names.h \
        src/data/sparse-cases.c \
        src/data/sparse-cases.h \
+       src/data/subcase.c \
+       src/data/subcase.h \
        src/data/sys-file-private.c \
        src/data/sys-file-private.h \
        src/data/sys-file-reader.c \
index 7a91f497ca9e66f7f5c76fbf9a5d5cd1653b016a..cff621f3456df7438562aa57d79dd78c4905fb3e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <data/casereader.h>
+#include <data/casewriter.h>
 #include <data/dictionary.h>
 #include <data/variable.h>
 #include <data/case.h>
@@ -36,6 +38,9 @@ struct case_map
                            corresponding source index. */
   };
 
+static struct ccase *translate_case (struct ccase *, void *map_);
+static bool destroy_case_map (void *map_);
+
 /* Creates and returns an empty map. */
 static struct case_map *
 create_case_map (size_t n)
@@ -78,20 +83,30 @@ case_map_destroy (struct case_map *map)
     }
 }
 
-/* Maps from SRC to DST, applying case map MAP. */
-void
-case_map_execute (const struct case_map *map,
-                  const struct ccase *src, struct ccase *dst)
-{
-  size_t dst_idx;
+/* If MAP is nonnull, returns a new case that is the result of
+   applying case map MAP to SRC, and unrefs SRC.
 
-  case_create (dst, map->value_cnt);
-  for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++)
+   If MAP is null, returns SRC unchanged. */
+struct ccase *
+case_map_execute (const struct case_map *map, struct ccase *src)
+{
+  if (map != NULL)
     {
-      int src_idx = map->map[dst_idx];
-      if (src_idx != -1)
-        *case_data_rw_idx (dst, dst_idx) = *case_data_idx (src, src_idx);
+      struct ccase *dst;
+      size_t dst_idx;
+
+      dst = case_create (map->value_cnt);
+      for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++)
+        {
+          int src_idx = map->map[dst_idx];
+          if (src_idx != -1)
+            *case_data_rw_idx (dst, dst_idx) = *case_data_idx (src, src_idx);
+        }
+      case_unref (src);
+      return dst;
     }
+  else
+    return src;
 }
 
 /* Returns the number of `union value's in cases created by
@@ -102,6 +117,62 @@ case_map_get_value_cnt (const struct case_map *map)
   return map->value_cnt;
 }
 
+/* Creates and returns a new casereader whose cases are produced
+   by reading from SUBREADER and executing the actions of MAP.  
+   The casereader will have as many `union value's as MAP.  When
+   the new casereader is destroyed, MAP will be destroyed too.
+
+   After this function is called, SUBREADER must not ever again
+   be referenced directly.  It will be destroyed automatically
+   when the returned casereader is destroyed. */
+struct casereader *
+case_map_create_input_translator (struct case_map *map,
+                                  struct casereader *subreader) 
+{
+    return casereader_create_translator (subreader,
+                                         case_map_get_value_cnt (map),
+                                         translate_case,
+                                         destroy_case_map,
+                                         map);
+}
+
+/* Creates and returns a new casewriter.  Cases written to the
+   new casewriter will be passed through MAP and written to
+   SUBWRITER.  The casewriter will have as many `union value's as
+   MAP.  When the new casewriter is destroyed, MAP will be
+   destroyed too.
+
+   After this function is called, SUBWRITER must not ever again
+   be referenced directly.  It will be destroyed automatically
+   when the returned casewriter is destroyed. */
+struct casewriter *
+case_map_create_output_translator (struct case_map *map,
+                                   struct casewriter *subwriter) 
+{
+    return casewriter_create_translator (subwriter,
+                                         case_map_get_value_cnt (map),
+                                         translate_case,
+                                         destroy_case_map,
+                                         map);
+}
+
+/* Casereader/casewriter translation callback. */
+static struct ccase *
+translate_case (struct ccase *input, void *map_)
+{
+  struct case_map *map = map_;
+  return case_map_execute (map, input);
+}
+
+/* Casereader/casewriter destruction callback. */
+static bool
+destroy_case_map (void *map_)
+{
+  struct case_map *map = map_;
+  case_map_destroy (map);
+  return true;
+}
+
 /* Creates and returns a case_map that can be used to compact
    cases for dictionary D.
 
index 86d448ec9d8aefe6b45efdf9463313e212814cf4..010dd487274d537af9035a53b5e5dac21ef12a91 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <stddef.h>
 
 struct case_map;
-struct dictionary;
+struct casereader;
+struct casewriter;
 struct ccase;
+struct dictionary;
 
 struct case_map *case_map_create (void);
 void case_map_destroy (struct case_map *);
-void case_map_execute (const struct case_map *,
-                       const struct ccase *, struct ccase *);
+struct ccase *case_map_execute (const struct case_map *, struct ccase *);
 
 size_t case_map_get_value_cnt (const struct case_map *);
 
+struct casereader *case_map_create_input_translator (struct case_map *,
+                                                    struct casereader *);
+struct casewriter *case_map_create_output_translator (struct case_map *,
+                                                      struct casewriter *);
+
 /* For mapping cases for one version of a dictionary to those in
    a modified version of the same dictionary. */
 void case_map_prepare_dict (const struct dictionary *);
diff --git a/src/data/case-matcher.c b/src/data/case-matcher.c
new file mode 100644 (file)
index 0000000..a1251cb
--- /dev/null
@@ -0,0 +1,154 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 <data/case-matcher.h>
+
+#include <stdlib.h>
+
+#include <data/case.h>
+#include <data/subcase.h>
+#include <data/value.h>
+#include <libpspp/assertion.h>
+
+#include "xalloc.h"
+
+struct case_matcher_input
+  {
+    struct subcase by_vars;
+    struct ccase **data;
+    bool *is_minimal;
+  };
+
+struct case_matcher
+  {
+    struct case_matcher_input *inputs;
+    size_t n_inputs, allocated_inputs;
+    union value *by_values;
+  };
+
+/* Creates and returns a new case matcher. */
+struct case_matcher *
+case_matcher_create (void)
+{
+  struct case_matcher *cm = xmalloc (sizeof *cm);
+  cm->inputs = NULL;
+  cm->n_inputs = 0;
+  cm->allocated_inputs = 0;
+  cm->by_values = NULL;
+  return cm;
+}
+
+/* Adds a new input file to case matcher CM.
+   case_matcher_match() will compare the variables specified in
+   BY in case *DATA and set *IS_MINIMAL appropriately.
+   (The caller may change the case that *DATA points to from one
+   call to the next.)
+
+   All of the BY subcases provided to this function for a given
+   CM must be conformable (see subcase_conformable()). */
+void
+case_matcher_add_input (struct case_matcher *cm, const struct subcase *by,
+                        struct ccase **data, bool *is_minimal)
+{
+  struct case_matcher_input *input;
+
+  if (cm->n_inputs == 0)
+    cm->by_values = xmalloc (subcase_get_n_values (by)
+                             * sizeof *cm->by_values);
+  else
+    assert (subcase_conformable (by, &cm->inputs[0].by_vars));
+
+  if (cm->n_inputs >= cm->allocated_inputs)
+    cm->inputs = x2nrealloc (cm->inputs, &cm->allocated_inputs,
+                             sizeof *cm->inputs);
+  input = &cm->inputs[cm->n_inputs++];
+  subcase_clone (&input->by_vars, by);
+  input->data = data;
+  input->is_minimal = is_minimal;
+}
+
+/* Destroys case matcher CM. */
+void
+case_matcher_destroy (struct case_matcher *cm)
+{
+  if (cm != NULL)
+    {
+      size_t i;
+
+      for (i = 0; i < cm->n_inputs; i++)
+        {
+          struct case_matcher_input *input = &cm->inputs[i];
+          subcase_destroy (&input->by_vars);
+        }
+      free (cm->inputs);
+      free (cm);
+    }
+}
+
+static int
+compare_BY_3way (struct case_matcher_input *a, struct case_matcher_input *b)
+{
+  return subcase_compare_3way (&a->by_vars, *a->data, &b->by_vars, *b->data);
+}
+
+/* Compares the values of the BY variables in all of the nonnull
+   cases provided to case_matcher_add_input() for CM, sets
+   *IS_MINIMAL for each one to true if it has the minimum BY
+   values among those cases or to false if its BY values are
+   greater than the minimum.  Also sets *IS_MINIMAL to false for
+   null cases.  Sets *BY to the BY values extracted from the
+   minimum case.  (The caller must not free *BY.)
+
+   Returns true if at least one of the cases is nonnull, false
+   if they are all null.*/
+bool
+case_matcher_match (struct case_matcher *cm, union value **by)
+{
+  struct case_matcher_input *file, *min;
+
+  min = NULL;
+  for (file = cm->inputs; file < &cm->inputs[cm->n_inputs]; file++)
+    if (*file->data != NULL)
+      {
+        int cmp = min != NULL ? compare_BY_3way (min, file) : 1;
+        if (cmp < 0)
+          *file->is_minimal = false;
+        else
+          {
+            *file->is_minimal = true;
+            if (cmp > 0)
+              min = file;
+          }
+      }
+    else
+      *file->is_minimal = false;
+
+  if (min != NULL)
+    {
+      for (file = cm->inputs; file < min; file++)
+        *file->is_minimal = false;
+      subcase_extract (&min->by_vars, *min->data, cm->by_values);
+      *by = cm->by_values;
+      return true;
+    }
+  else
+    {
+      *by = NULL;
+      return false;
+    }
+}
diff --git a/src/data/case-matcher.h b/src/data/case-matcher.h
new file mode 100644 (file)
index 0000000..fa40781
--- /dev/null
@@ -0,0 +1,33 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 DATA_CASE_MATCHER_H
+#define DATA_CASE_MATCHER_H 1
+
+#include <stdbool.h>
+
+struct ccase;
+struct subcase;
+union value;
+
+struct case_matcher *case_matcher_create (void);
+void case_matcher_add_input (struct case_matcher *, const struct subcase *,
+                             struct ccase **, bool *is_minimal);
+void case_matcher_destroy (struct case_matcher *);
+
+bool case_matcher_match (struct case_matcher *, union value **by);
+
+#endif /* data/case-matcher.h */
diff --git a/src/data/case-ordering.c b/src/data/case-ordering.c
deleted file mode 100644 (file)
index c4a716e..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 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 <data/case-ordering.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <data/dictionary.h>
-#include <data/variable.h>
-
-#include "xalloc.h"
-
-/* One key used for sorting. */
-struct sort_key
-  {
-    const struct variable *var;       /* Variable. */
-    enum sort_direction dir;    /* Sort direction. */
-  };
-
-/* A set of criteria for ordering cases. */
-struct case_ordering
-  {
-    /* Sort keys. */
-    struct sort_key *keys;
-    size_t key_cnt;
-  };
-
-/* Creates and returns a new case ordering for comparing cases
-   that represent dictionary DICT.  The case ordering initially
-   contains no variables, so that all cases will compare as
-   equal. */
-struct case_ordering *
-case_ordering_create (void)
-{
-  struct case_ordering *co = xmalloc (sizeof *co);
-  co->keys = NULL;
-  co->key_cnt = 0;
-  return co;
-}
-
-/* Creates and returns a clone of case ordering ORIG. */
-struct case_ordering *
-case_ordering_clone (const struct case_ordering *orig)
-{
-  struct case_ordering *co = xmalloc (sizeof *co);
-  co->keys = xmemdup (orig->keys, orig->key_cnt * sizeof *orig->keys);
-  co->key_cnt = orig->key_cnt;
-  return co;
-}
-
-/* Destroys case ordering CO. */
-void
-case_ordering_destroy (struct case_ordering *co)
-{
-  if (co != NULL)
-    {
-      free (co->keys);
-      free (co);
-    }
-}
-
-/* Compares cases A and B given case ordering CO and returns a
-   strcmp()-type result. */
-int
-case_ordering_compare_cases (const struct ccase *a, const struct ccase *b,
-                             const struct case_ordering *co)
-{
-  size_t i;
-
-  for (i = 0; i < co->key_cnt; i++)
-    {
-      const struct sort_key *key = &co->keys[i];
-      int width = var_get_width (key->var);
-      int cmp;
-
-      if (width == 0)
-        {
-          double af = case_num (a, key->var);
-          double bf = case_num (b, key->var);
-          if (af == bf)
-            continue;
-          cmp = af > bf ? 1 : -1;
-        }
-      else
-        {
-          const char *as = case_str (a, key->var);
-          const char *bs = case_str (b, key->var);
-          cmp = memcmp (as, bs, width);
-          if (cmp == 0)
-            continue;
-        }
-
-      return key->dir == SRT_ASCEND ? cmp : -cmp;
-    }
-  return 0;
-}
-
-/* Adds VAR to case ordering CO as an additional sort key in sort
-   direction DIR.  Returns true if successful, false if VAR was
-   already part of the ordering for CO. */
-bool
-case_ordering_add_var (struct case_ordering *co,
-                       const struct variable *var, enum sort_direction dir)
-{
-  struct sort_key *key;
-  size_t i;
-
-  for (i = 0; i < co->key_cnt; i++)
-    if (var_get_case_index (co->keys[i].var) == var_get_case_index (var))
-      return false;
-
-  co->keys = xnrealloc (co->keys, co->key_cnt + 1, sizeof *co->keys);
-  key = &co->keys[co->key_cnt++];
-  key->var = var;
-  key->dir = dir;
-  return true;
-}
-
-/* Returns the number of variables used for ordering within
-   CO. */
-size_t
-case_ordering_get_var_cnt (const struct case_ordering *co)
-{
-  return co->key_cnt;
-}
-
-/* Returns sort variable IDX within CO.  An IDX of 0 returns the
-   primary sort key (the one added first), an IDX of 1 returns
-   the secondary sort key, and so on.  IDX must be less than the
-   number of sort variables. */
-const struct variable *
-case_ordering_get_var (const struct case_ordering *co, size_t idx)
-{
-  assert (idx < co->key_cnt);
-  return co->keys[idx].var;
-}
-
-/* Returns the sort direction for sort variable IDX within CO. */
-enum sort_direction
-case_ordering_get_direction (const struct case_ordering *co, size_t idx)
-{
-  assert (idx < co->key_cnt);
-  return co->keys[idx].dir;
-}
-
-/* Stores an array listing all of the variables used for sorting
-   within CO into *VARS and the number of variables into
-   *VAR_CNT.  The caller is responsible for freeing *VARS when it
-   is no longer needed. */
-void
-case_ordering_get_vars (const struct case_ordering *co,
-                        const struct variable ***vars, size_t *var_cnt)
-{
-  size_t i;
-
-  *var_cnt = co->key_cnt;
-  *vars = xnmalloc (*var_cnt, sizeof **vars);
-  for (i = 0; i < co->key_cnt; i++)
-    (*vars)[i] = co->keys[i].var;
-}
-
diff --git a/src/data/case-ordering.h b/src/data/case-ordering.h
deleted file mode 100644 (file)
index f49f265..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 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/>. */
-
-/* Sort order for comparing cases. */
-
-#ifndef DATA_CASE_ORDERING_H
-#define DATA_CASE_ORDERING_H 1
-
-#include <stddef.h>
-#include <data/case.h>
-
-struct dictionary;
-
-/* Sort direction. */
-enum sort_direction
-  {
-    SRT_ASCEND,                        /* A, B, C, ..., X, Y, Z. */
-    SRT_DESCEND                        /* Z, Y, X, ..., C, B, A. */
-  };
-
-/* Creation and destruction. */
-struct case_ordering *case_ordering_create (void);
-struct case_ordering *case_ordering_clone (const struct case_ordering *);
-void case_ordering_destroy (struct case_ordering *);
-
-/* Modification. */
-bool case_ordering_add_var (struct case_ordering *,
-                            const struct variable *, enum sort_direction);
-
-/* Comparing cases. */
-int case_ordering_compare_cases (const struct ccase *, const struct ccase *,
-                                 const struct case_ordering *);
-
-/* Inspection. */
-size_t case_ordering_get_value_cnt (const struct case_ordering *);
-size_t case_ordering_get_var_cnt (const struct case_ordering *);
-const struct variable *case_ordering_get_var (const struct case_ordering *,
-                                              size_t);
-enum sort_direction case_ordering_get_direction (const struct case_ordering *,
-                                                 size_t);
-void case_ordering_get_vars (const struct case_ordering *,
-                             const struct variable ***, size_t *);
-
-#endif /* data/case-ordering.h */
index e6fc3de46c834e6acc48566d8c35847150244be3..ca3a6a564421552f4241900caa2fb21708f08226 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -198,25 +198,23 @@ case_tmpfile_get_values (const struct case_tmpfile *ctf,
           && do_read (ctf, sizeof *values * value_cnt, values));
 }
 
-/* Reads the case numbered CASE_IDX from CTF into C.
-   Returns true if successful, false if CTF is tainted or an I/O
-   error occurs during the operation.
+/* Reads the case numbered CASE_IDX from CTF.
+   Returns the case if successful or a null pointer if CTF is
+   tainted or an I/O error occurs during the operation.
 
    The results of this function are undefined if the case read
    from CTF had not previously been written. */
-bool
-case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx,
-                       struct ccase *c)
+struct ccase *
+case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx)
 {
-  case_create (c, ctf->value_cnt);
+  struct ccase *c = case_create (ctf->value_cnt);
   if (case_tmpfile_get_values (ctf, case_idx, 0,
                                case_data_all_rw (c), ctf->value_cnt))
-    return true;
+    return c;
   else
     {
-      case_destroy (c);
-      case_nullify (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 }
 
@@ -246,7 +244,7 @@ case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx,
 {
   bool ok = case_tmpfile_put_values (ctf, case_idx, 0,
                                      case_data_all (c), ctf->value_cnt);
-  case_destroy (c);
+  case_unref (c);
   return ok;
 }
 
index 3a92debc6e8e1b40be4e98342b96cd50a3487629..a5916249d45fcf04ca55d2845da70ff8ebe618f2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -38,11 +38,10 @@ bool case_tmpfile_error (const struct case_tmpfile *);
 void case_tmpfile_force_error (struct case_tmpfile *);
 const struct taint *case_tmpfile_get_taint (const struct case_tmpfile *);
 
-bool case_tmpfile_get_values (const struct case_tmpfile *,
+ bool case_tmpfile_get_values (const struct case_tmpfile *,
                               casenumber, size_t start_value,
                               union value[], size_t value_cnt);
-bool case_tmpfile_get_case (const struct case_tmpfile *,
-                            casenumber, struct ccase *);
+struct ccase *case_tmpfile_get_case (const struct case_tmpfile *, casenumber);
 
 bool case_tmpfile_put_values (struct case_tmpfile *,
                               casenumber, size_t start_value,
index cec6f21e1be8ae94ccd7dcbc2284841e91ec9cab..4432579e5d2e1284102a9ab7854d79db3b10d197 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
@@ -20,6 +20,7 @@
 
 #include <assert.h>
 #include <limits.h>
+#include <stddef.h>
 #include <stdlib.h>
 
 #include <data/value.h>
 #include "minmax.h"
 #include "xalloc.h"
 
-/* Reference-counted case implementation. */
-struct case_data
-  {
-    size_t value_cnt;                   /* Number of values. */
-    unsigned ref_cnt;                   /* Reference count. */
-    union value values[1];              /* Values. */
-  };
-
-/* Ensures that C does not share data with any other case. */
-static void
-case_unshare (struct ccase *c)
-{
-  if (c->case_data->ref_cnt > 1)
-    {
-      struct case_data *cd = c->case_data;
-      cd->ref_cnt--;
-      case_create (c, cd->value_cnt);
-      memcpy (c->case_data->values, cd->values,
-              sizeof *cd->values * cd->value_cnt);
-    }
-}
-
-/* Returns the number of bytes needed by a case with VALUE_CNT
+/* Returns the number of bytes needed by a case with N_VALUES
    values. */
 static size_t
-case_size (size_t value_cnt)
+case_size (size_t n_values)
 {
-  return (offsetof (struct case_data, values)
-          + value_cnt * sizeof (union value));
+  return offsetof (struct ccase, values) + n_values * sizeof (union value);
 }
 
-/* Initializes C as a null case. */
-void
-case_nullify (struct ccase *c)
+/* Returns true if case C contains COUNT cases starting at index
+   OFS, false if any of those values are out of range for case
+   C. */
+static inline bool UNUSED
+range_is_valid (const struct ccase *c, size_t ofs, size_t count)
 {
-  c->case_data = NULL;
+  return (count <= c->n_values
+          && ofs <= c->n_values
+          && ofs + count <= c->n_values);
 }
 
-/* Returns true iff C is a null case. */
-bool
-case_is_null (const struct ccase *c)
-{
-  return c->case_data == NULL;
-}
-
-/* Initializes C as a new case that can store VALUE_CNT values.
+/* Creates and returns a new case that can store N_VALUES values.
    The values have indeterminate contents until explicitly
    written. */
-void
-case_create (struct ccase *c, size_t value_cnt)
+struct ccase *
+case_create (size_t n_values)
 {
-  if (!case_try_create (c, value_cnt))
+  struct ccase *c = case_try_create (n_values);
+  if (c == NULL)
     xalloc_die ();
+  return c;
 }
 
-/* Initializes CLONE as a copy of ORIG. */
-void
-case_clone (struct ccase *clone, const struct ccase *orig)
+/* Like case_create, but returns a null pointer if not enough
+   memory is available. */
+struct ccase *
+case_try_create (size_t n_values)
 {
-  assert (orig->case_data->ref_cnt > 0);
-
-  if (clone != orig)
-    *clone = *orig;
-  orig->case_data->ref_cnt++;
-#ifdef DEBUGGING
-  case_unshare (clone);
-#endif
-}
-
-/* Replaces DST by SRC and nullifies SRC.
-   DST and SRC must be initialized cases at entry. */
-void
-case_move (struct ccase *dst, struct ccase *src)
-{
-  assert (src->case_data->ref_cnt > 0);
-
-  if (dst != src)
+  struct ccase *c = malloc (case_size (n_values));
+  if (c)
     {
-      *dst = *src;
-      case_nullify (src);
+      c->n_values = n_values;
+      c->ref_cnt = 1;
     }
+  return c;
 }
 
-/* Destroys case C. */
-void
-case_destroy (struct ccase *c)
+/* Resizes case C, which must not be shared, to N_VALUES union
+   values.  If N_VALUES is greater than the current size of case
+   C, then the newly added values have indeterminate content that
+   the caller is responsible for initializing.  Returns the new
+   case. */
+struct ccase *
+case_resize (struct ccase *c, size_t n_values)
 {
-  struct case_data *cd;
-
-  cd = c->case_data;
-  if (cd != NULL && --cd->ref_cnt == 0)
+  assert (!case_is_shared (c));
+  if (n_values != c->n_values)
     {
-      memset (cd->values, 0xcc, sizeof *cd->values * cd->value_cnt);
-      cd->value_cnt = 0xdeadbeef;
-      free (cd);
+      c->n_values = n_values;
+      return xrealloc (c, case_size (n_values));
     }
+  else
+    return c;
 }
 
-/* Returns the number of union values in C. */
-size_t
-case_get_value_cnt (const struct ccase *c)
-{
-  return c->case_data->value_cnt;
-}
+/* case_unshare_and_resize(C, N) is equivalent to
+   case_resize(case_unshare(C), N), but it is faster if case C is
+   shared.
 
-/* Resizes case C to NEW_CNT union values. */
-void
-case_resize (struct ccase *c, size_t new_cnt)
+   Returns the new case.*/
+struct ccase *
+case_unshare_and_resize (struct ccase *c, size_t n_values)
 {
-  size_t old_cnt = case_get_value_cnt (c);
-  if (old_cnt != new_cnt)
+  if (!case_is_shared (c))
+    return case_resize (c, n_values);
+  else
     {
-      struct ccase new;
-
-      case_create (&new, new_cnt);
-      case_copy (&new, 0, c, 0, MIN (old_cnt, new_cnt));
-      case_swap (&new, c);
-      case_destroy (&new);
+      struct ccase *new = case_create (n_values);
+      case_copy (new, 0, c, 0, MIN (n_values, c->n_values));
+      c->ref_cnt--;
+      return new;
     }
 }
 
-/* Swaps cases A and B. */
-void
-case_swap (struct ccase *a, struct ccase *b)
-{
-  struct case_data *t = a->case_data;
-  a->case_data = b->case_data;
-  b->case_data = t;
-}
+/* Copies N_VALUES values from SRC (starting at SRC_IDX) to DST
+   (starting at DST_IDX).
 
-/* Attempts to create C as a new case that holds VALUE_CNT
-   values.  Returns true if successful, false if memory
-   allocation failed. */
-bool
-case_try_create (struct ccase *c, size_t value_cnt)
-{
-  c->case_data = malloc (case_size (value_cnt));
-  if (c->case_data != NULL)
-    {
-      c->case_data->value_cnt = value_cnt;
-      c->case_data->ref_cnt = 1;
-      return true;
-    }
-
-  return false;
-}
-
-/* Tries to initialize CLONE as a copy of ORIG.
-   Returns true if successful, false if memory allocation
-   failed. */
-bool
-case_try_clone (struct ccase *clone, const struct ccase *orig)
-{
-  case_clone (clone, orig);
-  return true;
-}
-
-/* Copies VALUE_CNT values from SRC (starting at SRC_IDX) to DST
-   (starting at DST_IDX). */
+   DST must not be shared. */
 void
 case_copy (struct ccase *dst, size_t dst_idx,
            const struct ccase *src, size_t src_idx,
-           size_t value_cnt)
+           size_t n_values)
 {
-  assert (dst->case_data->ref_cnt > 0);
-  assert (dst_idx + value_cnt <= dst->case_data->value_cnt);
+  assert (!case_is_shared (dst));
+  assert (range_is_valid (dst, dst_idx, n_values));
+  assert (range_is_valid (src, src_idx, n_values));
 
-  assert (src->case_data->ref_cnt > 0);
-  assert (src_idx + value_cnt <= src->case_data->value_cnt);
-
-  if (dst->case_data != src->case_data || dst_idx != src_idx)
-    {
-      case_unshare (dst);
-      memmove (dst->case_data->values + dst_idx,
-               src->case_data->values + src_idx,
-               sizeof *dst->case_data->values * value_cnt);
-    }
+  if (dst != src || dst_idx != src_idx)
+    memmove (dst->values + dst_idx, src->values + src_idx,
+             sizeof *dst->values * n_values);
 }
 
-/* Copies VALUE_CNT values out of case C to VALUES, starting at
+/* Copies N_VALUES values out of case C to VALUES, starting at
    the given START_IDX. */
 void
 case_copy_out (const struct ccase *c,
-               size_t start_idx, union value *values, size_t value_cnt)
+               size_t start_idx, union value *values, size_t n_values)
 {
-  assert (c->case_data->ref_cnt > 0);
-  assert (value_cnt <= c->case_data->value_cnt);
-  assert (start_idx + value_cnt <= c->case_data->value_cnt);
-
-  memcpy (values, c->case_data->values + start_idx,
-          value_cnt * sizeof *values);
+  assert (range_is_valid (c, start_idx, n_values));
+  memcpy (values, c->values + start_idx, n_values * sizeof *values);
 }
 
-/* Copies VALUE_CNT values from VALUES into case C, staring at
-   the given START_IDX. */
+/* Copies N_VALUES values from VALUES into case C, starting at
+   the given START_IDX.
+
+   C must not be shared. */
 void
 case_copy_in (struct ccase *c,
-              size_t start_idx, const union value *values, size_t value_cnt)
+              size_t start_idx, const union value *values, size_t n_values)
 {
-  assert (c->case_data->ref_cnt > 0);
-  assert (value_cnt <= c->case_data->value_cnt);
-  assert (start_idx + value_cnt <= c->case_data->value_cnt);
-
-  case_unshare (c);
-  memcpy (c->case_data->values + start_idx, values,
-          value_cnt * sizeof *values);
+  assert (!case_is_shared (c));
+  assert (range_is_valid (c, start_idx, n_values));
+  memcpy (c->values + start_idx, values, n_values * sizeof *values);
 }
 
 /* Returns a pointer to the `union value' used for the
@@ -247,30 +163,21 @@ case_data (const struct ccase *c, const struct variable *v)
   return case_data_idx (c, var_get_case_index (v));
 }
 
-/* Returns the numeric value of the `union value' in C for
-   variable V.
-   Case C must be drawn from V's dictionary. */
-double
-case_num (const struct ccase *c, const struct variable *v)
+/* Returns a pointer to the `union value' used for the element of
+   C numbered IDX.  The caller must not modify the returned
+   data. */
+const union value *
+case_data_idx (const struct ccase *c, size_t idx)
 {
-  return case_num_idx (c, var_get_case_index (v));
+  assert (idx < c->n_values);
+  return &c->values[idx];
 }
 
-/* Returns the string value of the `union value' in C for
-   variable V.
-   Case C must be drawn from V's dictionary.
-   (Note that the value is not null-terminated.)
-   The caller must not modify the return value. */
-const char *
-case_str (const struct ccase *c, const struct variable *v)
-{
-  return case_str_idx (c, var_get_case_index (v));
-}
+/* Returns a pointer to the `union value' used for the element of
+   C for variable V.  Case C must be drawn from V's dictionary.
+   The caller is allowed to modify the returned data.
 
-/* Returns a pointer to the `union value' used for the
-   element of C for variable V.
-   Case C must be drawn from V's dictionary.
-   The caller is allowed to modify the returned data. */
+   Case C must not be shared. */
 union value *
 case_data_rw (struct ccase *c, const struct variable *v)
 {
@@ -279,14 +186,24 @@ case_data_rw (struct ccase *c, const struct variable *v)
 
 /* Returns a pointer to the `union value' used for the
    element of C numbered IDX.
-   The caller must not modify the returned data. */
-const union value *
-case_data_idx (const struct ccase *c, size_t idx)
+   The caller is allowed to modify the returned data.
+
+   Case C must not be shared. */
+union value *
+case_data_rw_idx (struct ccase *c, size_t idx)
 {
-  assert (c->case_data->ref_cnt > 0);
-  assert (idx < c->case_data->value_cnt);
+  assert (!case_is_shared (c));
+  assert (idx < c->n_values);
+  return &c->values[idx];
+}
 
-  return &c->case_data->values[idx];
+/* Returns the numeric value of the `union value' in C for
+   variable V.
+   Case C must be drawn from V's dictionary. */
+double
+case_num (const struct ccase *c, const struct variable *v)
+{
+  return case_num_idx (c, var_get_case_index (v));
 }
 
 /* Returns the numeric value of the `union value' in C numbered
@@ -294,57 +211,53 @@ case_data_idx (const struct ccase *c, size_t idx)
 double
 case_num_idx (const struct ccase *c, size_t idx)
 {
-  assert (c->case_data->ref_cnt > 0);
-  assert (idx < c->case_data->value_cnt);
-
-  return c->case_data->values[idx].f;
+  assert (idx < c->n_values);
+  return c->values[idx].f;
 }
 
-/* Returns the string value of the `union value' in C numbered
-   IDX.
-   (Note that the value is not null-terminated.)
-   The caller must not modify the return value. */
+/* Returns the string value of the `union value' in C for
+   variable V.  Case C must be drawn from V's dictionary.  The
+   caller must not modify the return value.
+
+   Like all "union value"s, the return value is not
+   null-terminated. */
 const char *
-case_str_idx (const struct ccase *c, size_t idx)
+case_str (const struct ccase *c, const struct variable *v)
 {
-  assert (c->case_data->ref_cnt > 0);
-  assert (idx < c->case_data->value_cnt);
-
-  return c->case_data->values[idx].s;
+  return case_str_idx (c, var_get_case_index (v));
 }
 
-/* Returns a pointer to the `union value' used for the
-   element of C numbered IDX.
-   The caller is allowed to modify the returned data. */
-union value *
-case_data_rw_idx (struct ccase *c, size_t idx)
-{
-  assert (c->case_data->ref_cnt > 0);
-  assert (idx < c->case_data->value_cnt);
+/* Returns the string value of the `union value' in C numbered
+   IDX.  The caller must not modify the return value.
 
-  case_unshare (c);
-  return &c->case_data->values[idx];
+   Like all "union value"s, the return value is not
+   null-terminated. */
+const char *
+case_str_idx (const struct ccase *c, size_t idx)
+{
+  assert (idx < c->n_values);
+  return c->values[idx].s;
 }
 
-/* Compares the values of the VAR_CNT variables in VP
+/* Compares the values of the N_VARS variables in VP
    in cases A and B and returns a strcmp()-type result. */
 int
 case_compare (const struct ccase *a, const struct ccase *b,
-              const struct variable *const *vp, size_t var_cnt)
+              const struct variable *const *vp, size_t n_vars)
 {
-  return case_compare_2dict (a, b, vp, vp, var_cnt);
+  return case_compare_2dict (a, b, vp, vp, n_vars);
 }
 
-/* Compares the values of the VAR_CNT variables in VAP in case CA
-   to the values of the VAR_CNT variables in VBP in CB
+/* Compares the values of the N_VARS variables in VAP in case CA
+   to the values of the N_VARS variables in VBP in CB
    and returns a strcmp()-type result. */
 int
 case_compare_2dict (const struct ccase *ca, const struct ccase *cb,
                     const struct variable *const *vap,
-               const struct variable *const *vbp,
-                    size_t var_cnt)
+                    const struct variable *const *vbp,
+                    size_t n_vars)
 {
-  for (; var_cnt-- > 0; vap++, vbp++)
+  for (; n_vars-- > 0; vap++, vbp++)
     {
       const struct variable *va = *vap;
       const struct variable *vb = *vbp;
@@ -375,26 +288,34 @@ case_compare_2dict (const struct ccase *ca, const struct ccase *cb,
 /* Returns a pointer to the array of `union value's used for C.
    The caller must *not* modify the returned data.
 
-   NOTE: This function breaks the case abstraction.  It should
-   *not* be used often.  Prefer the other case functions. */
+   This function breaks the case abstraction.  It should *not* be
+   commonly used.  Prefer the other case functions. */
 const union value *
 case_data_all (const struct ccase *c)
 {
-  assert (c->case_data->ref_cnt > 0);
-
-  return c->case_data->values;
+  return c->values;
 }
 
 /* Returns a pointer to the array of `union value's used for C.
    The caller is allowed to modify the returned data.
 
-   NOTE: This function breaks the case abstraction.  It should
-   *not* be used often.  Prefer the other case functions. */
+   Case C must not be shared.
+
+   This function breaks the case abstraction.  It should *not* be
+   commonly used.  Prefer the other case functions. */
 union value *
 case_data_all_rw (struct ccase *c)
 {
-  assert (c->case_data->ref_cnt > 0);
+  assert (!case_is_shared (c));
+  return c->values;
+}
 
-  case_unshare (c);
-  return c->case_data->values;
+/* Internal helper function for case_unshare. */
+struct ccase *
+case_unshare__ (struct ccase *old)
+{
+  struct ccase *new = case_create (old->n_values);
+  memcpy (new->values, old->values, old->n_values * sizeof old->values[0]);
+  --old->ref_cnt;
+  return new;
 }
index aa5b9dd0d7c99397ebcb00c7ef908f6ee6a4d719..6f53772688c3c1f29ba15cc0475735d0803762e4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
@@ -20,6 +20,8 @@
 #include <limits.h>
 #include <stddef.h>
 #include <stdbool.h>
+#include <stdlib.h>
+#include <libpspp/compiler.h>
 #include "value.h"
 
 struct variable;
@@ -29,57 +31,130 @@ struct variable;
 #define CASENUMBER_MAX LONG_MAX
 typedef long int casenumber;
 
-/* Opaque structure that represents a case.  Use accessor
-   functions instead of accessing any members directly.  Use
-   case_move() or case_clone() instead of copying.  */
+/* Reference-counted case implementation.
+
+   A newly created case has a single owner (the code that created
+   it), represented by an initial reference count of 1.  Other
+   code that receives the case may keep a virtual copy of it by
+   calling case_ref, which increments the case's reference count.
+   When this is done, the case becomes shared between its
+   original owner and each piece of code that incremented the
+   reference count.
+
+   A shared case (one whose reference count is greater than 1)
+   must not be modified, because this would make the case change
+   in the view of every reference count holder, not just the one
+   that intended to change the case.  Because the purpose of
+   keeping the reference count is to make a virtual copy of the
+   case, this is undesirable behavior.  The case_unshare function
+   provides a solution, by making a new, unshared copy of a
+   shared case. */
 struct ccase
   {
-    struct case_data *case_data;        /* Actual data. */
+    size_t n_values;            /* Number of values. */
+    size_t ref_cnt;             /* Reference count. */
+    union value values[1];      /* Values. */
   };
 
-void case_nullify (struct ccase *);
-bool case_is_null (const struct ccase *);
+struct ccase *case_create (size_t n_values) MALLOC_LIKE;
+struct ccase *case_try_create (size_t n_values) MALLOC_LIKE;
 
-void case_create (struct ccase *, size_t value_cnt);
-void case_clone (struct ccase *, const struct ccase *);
-void case_move (struct ccase *, struct ccase *);
-void case_destroy (struct ccase *);
+static inline struct ccase *case_unshare (struct ccase *) WARN_UNUSED_RESULT;
+static inline struct ccase *case_ref (const struct ccase *);
+static inline void case_unref (struct ccase *);
+static inline bool case_is_shared (const struct ccase *);
 
-size_t case_get_value_cnt (const struct ccase *);
+static inline size_t case_get_value_cnt (const struct ccase *);
 
-void case_resize (struct ccase *, size_t new_cnt);
-void case_swap (struct ccase *, struct ccase *);
-
-bool case_try_create (struct ccase *, size_t value_cnt);
-bool case_try_clone (struct ccase *, const struct ccase *);
+struct ccase *case_resize (struct ccase *, size_t new_cnt) WARN_UNUSED_RESULT;
+struct ccase *case_unshare_and_resize (struct ccase *, size_t new_cnt)
+  WARN_UNUSED_RESULT;
 
 void case_copy (struct ccase *dst, size_t dst_idx,
                 const struct ccase *src, size_t src_idx,
                 size_t cnt);
 
 void case_copy_out (const struct ccase *,
-                       size_t start_idx, union value *, size_t value_cnt);
+                    size_t start_idx, union value *, size_t n_values);
 void case_copy_in (struct ccase *,
-                       size_t start_idx, const union value *, size_t value_cnt);
+                   size_t start_idx, const union value *, size_t n_values);
 
 const union value *case_data (const struct ccase *, const struct variable *);
-double case_num (const struct ccase *, const struct variable *);
-const char *case_str (const struct ccase *, const struct variable *);
+const union value *case_data_idx (const struct ccase *, size_t idx);
 union value *case_data_rw (struct ccase *, const struct variable *);
+union value *case_data_rw_idx (struct ccase *, size_t idx);
 
-const union value *case_data_idx (const struct ccase *, size_t idx);
+double case_num (const struct ccase *, const struct variable *);
 double case_num_idx (const struct ccase *, size_t idx);
+
+const char *case_str (const struct ccase *, const struct variable *);
 const char *case_str_idx (const struct ccase *, size_t idx);
-union value *case_data_rw_idx (struct ccase *, size_t idx);
 
 int case_compare (const struct ccase *, const struct ccase *,
-                  const struct variable *const *, size_t var_cnt);
+                  const struct variable *const *, size_t n_vars);
 int case_compare_2dict (const struct ccase *, const struct ccase *,
                         const struct variable *const *,
                        const struct variable *const *,
-                        size_t var_cnt);
+                        size_t n_vars);
 
 const union value *case_data_all (const struct ccase *);
 union value *case_data_all_rw (struct ccase *);
+\f
+struct ccase *case_unshare__ (struct ccase *);
+
+/* If C is a shared case, that is, if it has a reference count
+   greater than 1, makes a new unshared copy and returns it,
+   decrementing C's reference count.  If C is not shared (its
+   reference count is 1), returns C.
+
+   This function should be used before attempting to modify any
+   of the data in a case that might be shared, e.g.:
+        c = case_unshare (c);              // Make sure that C is not shared.
+        case_data_rw (c, myvar)->f = 1;    // Modify data in C.
+*/
+static inline struct ccase *
+case_unshare (struct ccase *c)
+{
+  if (case_is_shared (c))
+    c = case_unshare__ (c);
+  return c;
+}
+
+/* Increments case C's reference count and returns C.  Afterward,
+   case C is shared among its reference count holders. */
+static inline struct ccase *
+case_ref (const struct ccase *c_)
+{
+  struct ccase *c = (struct ccase *) c_;
+  c->ref_cnt++;
+  return c;
+}
+
+/* Decrements case C's reference count.  Frees C if its
+   reference count drops to 0.
+
+   If C is a null pointer, this function has no effect. */
+static inline void
+case_unref (struct ccase *c)
+{
+  if (c != NULL && !--c->ref_cnt)
+    free (c);
+}
+
+/* Returns true if case C is shared.  A case that is shared
+   cannot be modified directly.  Instead, an unshared copy must
+   first be made with case_unshare(). */
+static inline bool
+case_is_shared (const struct ccase *c)
+{
+  return c->ref_cnt > 1;
+}
+
+/* Returns the number of union values in C. */
+static inline size_t
+case_get_value_cnt (const struct ccase *c)
+{
+  return c->n_values;
+}
 
 #endif /* data/case.h */
index f5974a404b0a6200729548a62a2d67afde684323..86788ba7a3bc5afcbd280ad2be920685a48e76db 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <stdlib.h>
 
-#include <data/case-ordering.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
 #include <data/dictionary.h>
+#include <data/subcase.h>
 #include <libpspp/taint.h>
 
 #include "xalloc.h"
@@ -80,28 +80,26 @@ casegrouper_get_next_group (struct casegrouper *grouper,
   if (grouper->same_group != NULL)
     {
       struct casewriter *writer;
-      struct ccase group_case, tmp;
+      struct ccase *group_case, *tmp;
 
-      if (!casereader_read (grouper->reader, &group_case))
+      group_case = casereader_read (grouper->reader);
+      if (group_case == NULL)
         {
           *reader = NULL;
           return false;
         }
 
       writer = autopaging_writer_create (casereader_get_value_cnt (grouper->reader));
-      case_clone (&tmp, &group_case);
-      casewriter_write (writer, &tmp);
+      case_ref (group_case);
+      casewriter_write (writer, group_case);
 
-      while (casereader_peek (grouper->reader, 0, &tmp)
-             && grouper->same_group (&group_case, &tmp, grouper->aux))
+      while ((tmp = casereader_peek (grouper->reader, 0)) != NULL
+             && grouper->same_group (group_case, tmp, grouper->aux))
         {
-          struct ccase discard;
-          casereader_read (grouper->reader, &discard);
-          case_destroy (&discard);
-          casewriter_write (writer, &tmp);
+          case_unref (casereader_read (grouper->reader));
+          casewriter_write (writer, tmp);
         }
-      case_destroy (&tmp);
-      case_destroy (&group_case);
+      case_unref (group_case);
 
       *reader = casewriter_make_reader (writer);
       return true;
@@ -159,13 +157,6 @@ casegrouper_destroy (struct casegrouper *grouper)
 /* Casegrouper based on equal values of variables from case to
    case. */
 
-/* Casegrouper based on equal variables. */
-struct casegrouper_vars
-  {
-    const struct variable **vars; /* Variables to compare. */
-    size_t var_cnt;               /* Number of variables. */
-  };
-
 static bool casegrouper_vars_same_group (const struct ccase *,
                                          const struct ccase *,
                                          void *);
@@ -180,15 +171,12 @@ casegrouper_create_vars (struct casereader *reader,
                          const struct variable *const *vars,
                          size_t var_cnt)
 {
-  if (var_cnt > 0)
+  if (var_cnt > 0) 
     {
-      struct casegrouper_vars *cv = xmalloc (sizeof *cv);
-      cv->vars = xmemdup (vars, sizeof *vars * var_cnt);
-      cv->var_cnt = var_cnt;
-      return casegrouper_create_func (reader,
-                                      casegrouper_vars_same_group,
-                                      casegrouper_vars_destroy,
-                                      cv);
+      struct subcase *sc = xmalloc (sizeof *sc);
+      subcase_init_vars (sc, vars, var_cnt);
+      return casegrouper_create_func (reader, casegrouper_vars_same_group,
+                                      casegrouper_vars_destroy, sc); 
     }
   else
     return casegrouper_create_func (reader, NULL, NULL, NULL);
@@ -210,39 +198,41 @@ casegrouper_create_splits (struct casereader *reader,
 
 /* Creates and returns a casegrouper that reads data from READER
    and breaks it into contiguous groups of cases that have equal
-   values for the variables used for sorting in CO.  If CO is
-   empty (contains no sort keys), then all the cases will be put
+   values for the variables used for sorting in SC.  If SC is
+   empty (contains no fields), then all the cases will be put
    into a single group. */
 struct casegrouper *
-casegrouper_create_case_ordering (struct casereader *reader,
-                                  const struct case_ordering *co)
+casegrouper_create_subcase (struct casereader *reader,
+                            const struct subcase *sc)
 {
-  const struct variable **vars;
-  size_t var_cnt;
-  struct casegrouper *grouper;
-
-  case_ordering_get_vars (co, &vars, &var_cnt);
-  grouper = casegrouper_create_vars (reader, vars, var_cnt);
-  free (vars);
-
-  return grouper;
+  if (subcase_get_n_fields (sc) > 0) 
+    {
+      struct subcase *sc_copy = xmalloc (sizeof *sc);
+      subcase_clone (sc_copy, sc);
+      return casegrouper_create_func (reader, casegrouper_vars_same_group,
+                                      casegrouper_vars_destroy, sc_copy); 
+    }
+  else
+    return casegrouper_create_func (reader, NULL, NULL, NULL);
 }
 
 /* "same_group" function for an equal-variables casegrouper. */
 static bool
 casegrouper_vars_same_group (const struct ccase *a, const struct ccase *b,
-                             void *cv_)
+                             void *sc_)
 {
-  struct casegrouper_vars *cv = cv_;
-  return case_compare (a, b, cv->vars, cv->var_cnt) == 0;
+  struct subcase *sc = sc_;
+  return subcase_equal (sc, a, sc, b);
 }
 
 /* "destroy" for an equal-variables casegrouper. */
 static void
-casegrouper_vars_destroy (void *cv_)
+casegrouper_vars_destroy (void *sc_)
 {
-  struct casegrouper_vars *cv = cv_;
-  free (cv->vars);
-  free (cv);
+  struct subcase *sc = sc_;
+  if (sc != NULL) 
+    {
+      subcase_destroy (sc);
+      free (sc); 
+    }
 }
-
index 8b5f7b56ae5ba9fbd7a3090dadd4bf8caa48016c..f367d23cdb2b23af870be7f35276b4e3b6b0f66b 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 
-struct case_ordering;
 struct casereader;
 struct ccase;
 struct dictionary;
+struct subcase;
 struct variable;
 
 struct casegrouper *
@@ -45,8 +45,8 @@ struct casegrouper *casegrouper_create_vars (struct casereader *,
                                              size_t var_cnt);
 struct casegrouper *casegrouper_create_splits (struct casereader *,
                                                const struct dictionary *);
-struct casegrouper *casegrouper_create_case_ordering (struct casereader *,
-                                                      const struct case_ordering *);
+struct casegrouper *casegrouper_create_subcase (struct casereader *,
+                                                const struct subcase *);
 bool casegrouper_get_next_group (struct casegrouper *, struct casereader **);
 bool casegrouper_destroy (struct casegrouper *);
 
index f789244dd53b13d4b41a3498c89112bb5ea2cdf7..4f7ece7e03bac3dc6950a2d977a0203ddad62c23 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -247,7 +247,8 @@ caseinit_mark_for_init (struct caseinit *ci, const struct dictionary *d)
   init_list_mark (&ci->left_values, &ci->preinited_values, LEAVE_LEFT, d);
 }
 
-/* Initializes variables in C as described by CI. */
+/* Initializes variables in *C as described by CI.
+   C must not be shared. */
 void
 caseinit_init_vars (const struct caseinit *ci, struct ccase *c)
 {
index 37e1dc85c828f1908fef2454f3ecad872b9a6d68..d387f5ea03181794e790ea297a3b10cdc8337880 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -86,22 +86,22 @@ casereader_create_filter_func (struct casereader *subreader,
 }
 
 /* Internal read function for filtering casereader. */
-static bool
-casereader_filter_read (struct casereader *reader UNUSED, void *filter_,
-                        struct ccase *c)
+static struct ccase *
+casereader_filter_read (struct casereader *reader UNUSED, void *filter_)
 
 {
   struct casereader_filter *filter = filter_;
   for (;;)
     {
-      if (!casereader_read (filter->subreader, c))
-        return false;
+      struct ccase *c = casereader_read (filter->subreader);
+      if (c == NULL)
+        return NULL;
       else if (filter->include (c, filter->aux))
-        return true;
+        return c;
       else if (filter->exclude != NULL)
         casewriter_write (filter->exclude, c);
       else
-        case_destroy (c);
+        case_unref (c);
     }
 }
 
@@ -115,12 +115,12 @@ casereader_filter_destroy (struct casereader *reader, void *filter_)
      casewriter, if there is one. */
   if (filter->exclude != NULL)
     {
-      struct ccase c;
-      while (casereader_read (filter->subreader, &c))
-        if (filter->include (&c, filter->aux))
-          case_destroy (&c);
+      struct ccase *c;
+      while ((c = casereader_read (filter->subreader)) != NULL)
+        if (filter->include (c, filter->aux))
+          case_unref (c);
         else
-          casewriter_write (filter->exclude, &c);
+          casewriter_write (filter->exclude, c);
     }
 
   casereader_destroy (filter->subreader);
@@ -245,6 +245,7 @@ struct casereader_filter_missing
     struct variable **vars;     /* Variables whose values to filter. */
     size_t var_cnt;             /* Number of variables. */
     enum mv_class class;        /* Types of missing values to filter. */
+    casenumber *n_missing;
   };
 
 static bool casereader_filter_missing_include (const struct ccase *, void *);
@@ -264,6 +265,9 @@ static bool casereader_filter_missing_destroy (void *);
    read or, if that never occurs, until the filtering casereader
    is destroyed.
 
+   If N_MISSING is non-null, then after reading, it will be filled
+   with the totla number of dropped cases.
+
    After this function is called, READER must not ever again
    be referenced directly.  It will be destroyed automatically
    when the filtering casereader is destroyed. */
@@ -271,6 +275,7 @@ struct casereader *
 casereader_create_filter_missing (struct casereader *reader,
                                   const struct variable **vars, size_t var_cnt,
                                   enum mv_class class,
+                                 casenumber *n_missing,
                                   struct casewriter *exclude)
 {
   if (var_cnt > 0 && class != MV_NEVER)
@@ -279,6 +284,8 @@ casereader_create_filter_missing (struct casereader *reader,
       cfm->vars = xmemdup (vars, sizeof *vars * var_cnt);
       cfm->var_cnt = var_cnt;
       cfm->class = class;
+      cfm->n_missing = n_missing;
+      if (n_missing) *n_missing = 0;
       return casereader_create_filter_func (reader,
                                             casereader_filter_missing_include,
                                             casereader_filter_missing_destroy,
@@ -302,7 +309,11 @@ casereader_filter_missing_include (const struct ccase *c, void *cfm_)
       struct variable *var = cfm->vars[i];
       const union value *value = case_data (c, var);
       if (var_is_value_missing (var, value, cfm->class))
-        return false;
+       {
+         if ( cfm->n_missing )
+           (*cfm->n_missing)++;
+         return false;
+       }
     }
   return true;
 }
index 772209810a85e44c2f7474a44fcb25235735cf98..31d8a6d4c88e67a6afab5ad2e372c0b5041f6790 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -43,20 +43,23 @@ struct casereader_class
   {
     /* Mandatory.
 
-       Reads the next case from READER into case C, which the
-       casereader must create and which the client is responsible
-       for destroying.  If successful, returns true and advances
-       READER to the next case, so that the next call to this
-       function will read the next case.  The case just read will
-       never be read again by a call to this function for READER.
+       Reads the next case from READER.  If successful, returns
+       the case and advances READER, so that the next call to
+       this function will read the following case.  The case just
+       read will never be read again by a call to this function
+       for READER.
 
-       At end of file or upon an I/O error, returns false.  After
-       false is returned once, this function will not be called
-       again for the given READER.
+       If a case is successfully returned, the client is
+       responsible for calling case_unref upon it when it is no
+       longer needed.
+
+       At end of file or upon an I/O error, returns a null
+       pointer.  After null is returned once, this function will
+       not be called again for the given READER.
 
        If an I/O error occurs, this function should call
        casereader_force_error on READER. */
-    bool (*read) (struct casereader *reader, void *aux, struct ccase *c);
+    struct ccase *(*read) (struct casereader *reader, void *aux);
 
     /* Mandatory.
 
@@ -88,22 +91,23 @@ struct casereader_class
        (But it might be easier to use the random-access
        casereader wrapper instead.)
 
-       Reads the case at 0-based offset IDX from the beginning of
-       READER into case C, which the casereader must create and
-       which the client is responsible for destroying.
+       Reads and returns the case at 0-based offset IDX from the
+       beginning of READER.  If a case is successfully returned,
+       the client is responsible for calling case_unref upon it
+       when it is no longer needed.
 
-       At end of file or upon an I/O error, returns false.  If
-       this function returns false, then it will never be called
-       again for an equal or greater value of IDX, and the "read"
-       member function will never be called to advance as far as
-       IDX cases further into the casereader.  That is, returning
-       false indicates that the casereader has fewer than IDX
-       cases left.
+       At end of file or upon an I/O error, returns a null
+       pointer.  If this function returns null, then it will
+       never be called again for an equal or greater value of
+       IDX, and the "read" member function will never be called
+       to advance as far as IDX cases further into the
+       casereader.  That is, returning null indicates that the
+       casereader has fewer than IDX cases left.
 
        If an I/O error occurs, this function should call
        casereader_force_error on READER. */
-    bool (*peek) (struct casereader *reader, void *aux, casenumber idx,
-                  struct ccase *c);
+    struct ccase *(*peek) (struct casereader *reader, void *aux,
+                           casenumber idx);
   };
 
 struct casereader *
@@ -119,21 +123,22 @@ struct casereader_random_class
     /* Mandatory.
 
        Reads the case at 0-based offset IDX from the beginning of
-       READER into case C, which the casereader must create and
-       which the client is responsible for destroying.
-
-       At end of file or upon an I/O error, returns false.  If
-       this function returns false, then it will never be called
-       again for an equal or greater value of IDX, and the "read"
-       member function will never be called to advance as far as
-       IDX cases further into the casereader.  That is, returning
-       false indicates that the casereader has fewer than IDX
-       cases.
+       READER.  If a case is successfully returned, the client is
+       responsible for calling case_unref upon it when it is no
+       longer needed.
+
+       At end of file or upon an I/O error, returns a null
+       pointer.  If this function returns null, then it will
+       never be called again for an equal or greater value of
+       IDX, and the "read" member function will never be called
+       to advance as far as IDX cases further into the
+       casereader.  That is, returning null indicates that the
+       casereader has fewer than IDX cases.
 
        If an I/O error occurs, this function should call
        casereader_force_error on READER. */
-    bool (*read) (struct casereader *reader, void *aux, casenumber idx,
-                  struct ccase *c);
+    struct ccase *(*read) (struct casereader *reader, void *aux,
+                           casenumber idx);
 
     /* Mandatory.
 
index 229dac2e54944c8df81088080c1032330c0cd84e..d55e18e50292282a6e7c6d95233802fb42c920ed 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -15,9 +15,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-
+#include <data/val-type.h>
 #include <data/casereader.h>
-
 #include <stdlib.h>
 
 #include <data/casereader-provider.h>
@@ -33,7 +32,7 @@ struct casereader_translator
   {
     struct casereader *subreader; /* Source of input cases. */
 
-    void (*translate) (struct ccase *input, struct ccase *output, void *aux);
+    struct ccase *(*translate) (struct ccase *input, void *aux);
     bool (*destroy) (void *aux);
     void *aux;
   };
@@ -42,9 +41,9 @@ static const struct casereader_class casereader_translator_class;
 
 /* Creates and returns a new casereader whose cases are produced
    by reading from SUBREADER and passing through TRANSLATE, which
-   must create case OUTPUT, with OUTPUT_VALUE_CNT values, and
-   populate it based on INPUT and auxiliary data AUX.  TRANSLATE
-   must also destroy INPUT.
+   must return the translated case, with OUTPUT_VALUE_CNT values,
+   and populate it based on INPUT and auxiliary data AUX.
+   TRANSLATE must destroy its input case.
 
    When the translating casereader is destroyed, DESTROY will be
    called to allow any state maintained by TRANSLATE to be freed.
@@ -55,9 +54,8 @@ static const struct casereader_class casereader_translator_class;
 struct casereader *
 casereader_create_translator (struct casereader *subreader,
                               size_t output_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+                              struct ccase *(*translate) (struct ccase *input,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux)
 {
@@ -76,20 +74,15 @@ casereader_create_translator (struct casereader *subreader,
 }
 
 /* Internal read function for translating casereader. */
-static bool
+static struct ccase *
 casereader_translator_read (struct casereader *reader UNUSED,
-                            void *ct_, struct ccase *c)
+                            void *ct_)
 {
   struct casereader_translator *ct = ct_;
-  struct ccase tmp;
-
-  if (casereader_read (ct->subreader, &tmp))
-    {
-      ct->translate (&tmp, c, ct->aux);
-      return true;
-    }
-  else
-    return false;
+  struct ccase *tmp = casereader_read (ct->subreader);
+  if (tmp)
+    tmp = ct->translate (tmp, ct->aux);
+  return tmp;
 }
 
 /* Internal destroy function for translating casereader. */
@@ -110,18 +103,84 @@ static const struct casereader_class casereader_translator_class =
     NULL,
     NULL,
   };
+
 \f
-struct casereader_arithmetic_sequence 
-  {
-    int value_ofs;
-    double first;
-    double increment;
-    casenumber n;
-  };
 
-static void cas_translate (struct ccase *input, struct ccase *output,
-                           void *aux);
-static bool cas_destroy (void *aux);
+struct casereader_append_numeric
+{
+  int value_ofs;
+  casenumber n;
+  new_value_func *func;
+  void *aux;
+  void (*destroy) (void *aux);
+};
+
+static bool can_destroy (void *can_);
+
+static struct ccase *can_translate (struct ccase *, void *can_);
+
+/* Creates and returns a new casereader whose cases are produced
+   by reading from SUBREADER and appending an additional value,
+   generated by FUNC.  AUX is an optional parameter which
+   gets passed to FUNC. FUNC will also receive N as it, which is
+   the ordinal number of the case in the reader.  DESTROY is an
+   optional parameter used to destroy AUX.
+
+   After this function is called, SUBREADER must not ever again
+   be referenced directly.  It will be destroyed automatically
+   when the translating casereader is destroyed. */
+struct casereader *
+casereader_create_append_numeric (struct casereader *subreader,
+                                 new_value_func func, void *aux,
+                                 void (*destroy) (void *aux))
+{
+  struct casereader_append_numeric *can = xmalloc (sizeof *can);
+  can->value_ofs = casereader_get_value_cnt (subreader);
+  can->n = 0;
+  can->aux = aux;
+  can->func = func;
+  can->destroy = destroy;
+  return casereader_create_translator (subreader, can->value_ofs + 1,
+                                       can_translate, can_destroy, can);
+}
+
+
+static struct ccase *
+can_translate (struct ccase *c, void *can_)
+{
+  struct casereader_append_numeric *can = can_;
+  double new_value = can->func (c, can->n++, can->aux);
+  c = case_unshare_and_resize (c, can->value_ofs + 1);
+  case_data_rw_idx (c, can->value_ofs)->f = new_value;
+  return c;
+}
+
+static bool
+can_destroy (void *can_)
+{
+  struct casereader_append_numeric *can = can_;
+  if (can->destroy)
+    can->destroy (can->aux);
+  free (can);
+  return true;
+}
+
+\f
+
+struct arithmetic_sequence
+{
+  double first;
+  double increment;
+};
+
+static double
+next_arithmetic (const struct ccase *c UNUSED,
+                casenumber n,
+                void *aux)
+{
+  struct arithmetic_sequence *as = aux;
+  return n * as->increment + as->first;
+}
 
 /* Creates and returns a new casereader whose cases are produced
    by reading from SUBREADER and appending an additional value,
@@ -136,32 +195,160 @@ struct casereader *
 casereader_create_arithmetic_sequence (struct casereader *subreader,
                                        double first, double increment)
 {
-  /* This could be implemented with a great deal more efficiency
-     and generality.  However, this implementation is easy. */
-  struct casereader_arithmetic_sequence *cas = xmalloc (sizeof *cas);
-  cas->value_ofs = casereader_get_value_cnt (subreader);
-  cas->first = first;
-  cas->increment = increment;
-  cas->n = 0;
-  return casereader_create_translator (subreader, cas->value_ofs + 1,
-                                       cas_translate, cas_destroy, cas);
+  struct arithmetic_sequence *as = xzalloc (sizeof *as);
+  as->first = first;
+  as->increment = increment;
+  return casereader_create_append_numeric (subreader, next_arithmetic,
+                                          as, free);
 }
 
-static void
-cas_translate (struct ccase *input, struct ccase *output, void *cas_)
+
+\f
+
+struct casereader_append_rank
+{
+  struct casereader *clone;
+  casenumber n;
+  const struct variable *var;
+  const struct variable *weight;
+  int value_ofs;
+  casenumber n_common;
+  double mean_rank;
+  double cc;
+  distinct_func *distinct;
+  void *aux;
+  enum rank_error *err;
+  double prev_value;
+};
+
+static bool car_destroy (void *car_);
+
+static struct ccase *car_translate (struct ccase *input, void *car_);
+
+/* Creates and returns a new casereader whose cases are produced
+   by reading from SUBREADER and appending an additional value,
+   which is the rank of the observation.   W is the weight variable
+   of the dictionary containing V, or NULL if there is no weight
+   variable.
+
+   The following preconditions must be met:
+
+   1.    SUBREADER must be sorted on V.
+
+   2.    The weight variables, must be non-negative.
+
+   If either of these preconditions are not satisfied, then the rank
+   variables may not be correct.  In this case, if ERR is non-null,
+   it will be set according to the erroneous conditions encountered.
+
+   If DISTINCT_CALLBACK is non-null, then  it will be called exactly
+   once for every case containing a distinct value of V.  AUX is
+   an auxilliary pointer passed to DISTINCT_CALLBACK.
+
+   After this function is called, SUBREADER must not ever again
+   be referenced directly.  It will be destroyed automatically
+   when the translating casereader is destroyed. */
+struct casereader *
+casereader_create_append_rank (struct casereader *subreader,
+                              const struct variable *v,
+                              const struct variable *w,
+                              enum rank_error *err,
+                              distinct_func *distinct_callback,
+                              void *aux
+                              )
 {
-  struct casereader_arithmetic_sequence *cas = cas_;
-  case_nullify (output);
-  case_move (output, input);
-  case_resize (output, cas->value_ofs + 1);
-  case_data_rw_idx (output, cas->value_ofs)->f
-    = cas->first + cas->increment * cas->n++;
+  struct casereader_append_rank *car = xmalloc (sizeof *car);
+  car->value_ofs = casereader_get_value_cnt (subreader);
+  car->weight = w;
+  car->var = v;
+  car->n = 0;
+  car->n_common = 1;
+  car->cc = 0.0;
+  car->clone = casereader_clone (subreader);
+  car->distinct = distinct_callback;
+  car->aux = aux;
+  car->err = err;
+  car->prev_value = SYSMIS;
+
+  return casereader_create_translator (subreader, car->value_ofs + 1,
+                                       car_translate, car_destroy, car);
 }
 
+
 static bool
-cas_destroy (void *cas_) 
+car_destroy (void *car_)
 {
-  struct casereader_arithmetic_sequence *cas = cas_;
-  free (cas);
+  struct casereader_append_rank *car = car_;
+  casereader_destroy (car->clone);
+  free (car);
   return true;
 }
+
+static struct ccase *
+car_translate (struct ccase *input, void *car_)
+{
+  struct casereader_append_rank *car = car_;
+
+  const double value = case_data (input, car->var)->f;
+
+  if ( car->prev_value != SYSMIS)
+    {
+      if (car->err && value < car->prev_value)
+       *car->err |= RANK_ERR_UNSORTED;
+    }
+
+  if ( car->n_common == 1)
+    {
+      double vxx = SYSMIS;
+      casenumber k = 0;
+      double weight = 1.0;
+      if (car->weight)
+       {
+         weight = case_data (input, car->weight)->f;
+         if ( car->err && weight < 0 )
+           *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
+       }
+
+      do
+       {
+         struct ccase *c = casereader_peek (car->clone, car->n + ++k);
+         if (c == NULL)
+           break;
+         vxx = case_data (c, car->var)->f;
+
+         if ( vxx == value)
+           {
+             if (car->weight)
+               {
+                 double w = case_data (c, car->weight)->f;
+
+                 if ( car->err && w < 0 )
+                   *car->err |= RANK_ERR_NEGATIVE_WEIGHT;
+
+                 weight += w;
+               }
+             else
+               weight += 1.0;
+             car->n_common++;
+           }
+          case_unref (c);
+       }
+      while (vxx == value);
+      car->mean_rank = car->cc + (weight + 1) / 2.0;
+      car->cc += weight;
+
+      if (car->distinct)
+       car->distinct (value, car->n_common, weight, car->aux);
+    }
+  else
+    car->n_common--;
+
+  car->n++;
+
+  input = case_unshare_and_resize (input, car->value_ofs + 1);
+  case_data_rw_idx (input, car->value_ofs)->f = car->mean_rank ;
+  car->prev_value = value;
+  return input;
+}
+
+
index ee7facb769dbf9a26dc96d717f1249a7fe44879b..3d27a9192a79bd63466e25e2059ce91824e94241 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -43,18 +43,17 @@ struct casereader
 
 static void insert_shim (struct casereader *);
 
-/* Creates a new case in C and reads the next case from READER
-   into it.  The caller owns C and must destroy C when its data
-   is no longer needed.  Return true if successful, false when
-   cases have been exhausted or upon detection of an I/O error.
-   In the latter case, C is set to the null case.
+/* Reads and returns the next case from READER.  The caller owns
+   the returned case and must call case_unref on it when its data
+   is no longer needed.  Returns a null pointer if cases have
+   been exhausted or upon detection of an I/O error.
 
    The case returned is effectively consumed: it can never be
    read again through READER.  If this is inconvenient, READER
    may be cloned in advance with casereader_clone, or
    casereader_peek may be used instead. */
-bool
-casereader_read (struct casereader *reader, struct ccase *c)
+struct ccase *
+casereader_read (struct casereader *reader)
 {
   if (reader->case_cnt != 0)
     {
@@ -69,17 +68,18 @@ casereader_read (struct casereader *reader, struct ccase *c)
          case_cnt after calling ->read, then this would actually
          drop two cases from case_cnt instead of one, and we'd
          lose the last case in the casereader. */
+      struct ccase *c;
       if (reader->case_cnt != CASENUMBER_MAX)
         reader->case_cnt--;
-      if (reader->class->read (reader, reader->aux, c))
+      c = reader->class->read (reader, reader->aux);
+      if (c != NULL)
         {
           assert (case_get_value_cnt (c) >= reader->value_cnt);
-          return true;
+          return c;
         }
     }
   reader->case_cnt = 0;
-  case_nullify (c);
-  return false;
+  return NULL;
 }
 
 /* Destroys READER.
@@ -160,27 +160,27 @@ casereader_swap (struct casereader *a, struct casereader *b)
     }
 }
 
-/* Creates a new case in C and reads the (IDX + 1)'th case from
-   READER into it.  The caller owns C and must destroy C when its
-   data is no longer needed.  Return true if successful, false
-   when cases have been exhausted or upon detection of an I/O
-   error.  In the latter case, C is set to the null case. */
-bool
-casereader_peek (struct casereader *reader, casenumber idx, struct ccase *c)
+/* Reads and returns the (IDX + 1)'th case from READER.  The
+   caller owns the returned case and must call case_unref on it
+   when it is no longer needed.  Returns a null pointer if cases
+   have been exhausted or upon detection of an I/O error. */
+struct ccase *
+casereader_peek (struct casereader *reader, casenumber idx)
 {
   if (idx < reader->case_cnt)
     {
+      struct ccase *c;
       if (reader->class->peek == NULL)
         insert_shim (reader);
-      if (reader->class->peek (reader, reader->aux, idx, c))
-        return true;
+      c = reader->class->peek (reader, reader->aux, idx);
+      if (c != NULL)
+        return c;
       else if (casereader_error (reader))
         reader->case_cnt = 0;
     }
   if (reader->case_cnt > idx)
     reader->case_cnt = idx;
-  case_nullify (c);
-  return false;
+  return NULL;
 }
 
 /* Returns true if no cases remain to be read from READER, or if
@@ -191,13 +191,18 @@ casereader_peek (struct casereader *reader, casenumber idx, struct ccase *c)
 bool
 casereader_is_empty (struct casereader *reader)
 {
-  struct ccase c;
-  if (reader->case_cnt == 0 || !casereader_peek (reader, 0, &c))
+  if (reader->case_cnt == 0)
     return true;
   else
     {
-      case_destroy (&c);
-      return false;
+      struct ccase *c = casereader_peek (reader, 0);
+      if (c == NULL)
+        return true;
+      else
+        {
+          case_unref (c);
+          return false;
+        }
     }
 }
 
@@ -263,11 +268,11 @@ casereader_count_cases (struct casereader *reader)
   if (reader->case_cnt == CASENUMBER_MAX)
     {
       casenumber n_cases = 0;
-      struct ccase c;
+      struct ccase *c;
 
       struct casereader *clone = casereader_clone (reader);
 
-      for (; casereader_read (clone, &c); case_destroy (&c))
+      for (; (c = casereader_read (clone)) != NULL; case_unref (c))
         n_cases++;
 
       casereader_destroy (clone);
@@ -289,12 +294,12 @@ casereader_get_value_cnt (struct casereader *reader)
 void
 casereader_transfer (struct casereader *reader, struct casewriter *writer)
 {
-  struct ccase c;
+  struct ccase *c;
 
   taint_propagate (casereader_get_taint (reader),
                    casewriter_get_taint (writer));
-  while (casereader_read (reader, &c))
-    casewriter_write (writer, &c);
+  while ((c = casereader_read (reader)) != NULL)
+    casewriter_write (writer, c);
   casereader_destroy (reader);
 }
 
@@ -475,22 +480,20 @@ advance_random_reader (struct casereader *reader,
 }
 
 /* struct casereader_class "read" function for random reader. */
-static bool
-random_reader_read (struct casereader *reader, void *br_, struct ccase *c)
+static struct ccase *
+random_reader_read (struct casereader *reader, void *br_)
 {
   struct random_reader *br = br_;
   struct random_reader_shared *shared = br->shared;
-
-  if (shared->class->read (reader, shared->aux,
-                           br->offset - shared->min_offset, c))
+  struct ccase *c = shared->class->read (reader, shared->aux,
+                                         br->offset - shared->min_offset);
+  if (c != NULL)
     {
       br->offset++;
       heap_changed (shared->readers, &br->heap_node);
       advance_random_reader (reader, shared);
-      return true;
     }
-  else
-    return false;
+  return c;
 }
 
 /* struct casereader_class "destroy" function for random
@@ -529,15 +532,14 @@ random_reader_clone (struct casereader *reader, void *br_)
 }
 
 /* struct casereader_class "peek" function for random reader. */
-static bool
-random_reader_peek (struct casereader *reader, void *br_,
-                    casenumber idx, struct ccase *c)
+static struct ccase *
+random_reader_peek (struct casereader *reader, void *br_, casenumber idx)
 {
   struct random_reader *br = br_;
   struct random_reader_shared *shared = br->shared;
 
   return shared->class->read (reader, shared->aux,
-                              br->offset - shared->min_offset + idx, c);
+                              br->offset - shared->min_offset + idx);
 }
 
 /* Casereader class for random reader. */
@@ -607,24 +609,27 @@ prime_buffer (struct shim *b, casenumber case_cnt)
 {
   while (casewindow_get_case_cnt (b->window) < case_cnt)
     {
-      struct ccase tmp;
-      if (!casereader_read (b->subreader, &tmp))
+      struct ccase *tmp = casereader_read (b->subreader);
+      if (tmp == NULL)
         return false;
-      casewindow_push_head (b->window, &tmp);
+      casewindow_push_head (b->window, tmp);
     }
   return true;
 }
 
 /* Reads the case at the given 0-based OFFSET from the front of
-   the window into C.  Returns true if successful, false if
-   OFFSET is beyond the end of file or upon I/O error. */
-static bool
+   the window into C.  Returns the case if successful, or a null
+   pointer if OFFSET is beyond the end of file or upon I/O error.
+   The caller must call case_unref() on the returned case when it
+   is no longer needed. */
+static struct ccase *
 shim_read (struct casereader *reader UNUSED, void *b_,
-           casenumber offset, struct ccase *c)
+           casenumber offset)
 {
   struct shim *b = b_;
-  return (prime_buffer (b, offset + 1)
-          && casewindow_get_case (b->window, offset, c));
+  if (!prime_buffer (b, offset + 1))
+    return NULL;
+  return casewindow_get_case (b->window, offset);
 }
 
 /* Destroys B. */
index ba65cb18e7cc7f7d9f54dce259a44e9774e10542..deab5641661ec111d6751254f77c73ca77200550 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -59,7 +59,7 @@ struct dictionary;
 struct casereader;
 struct casewriter;
 
-bool casereader_read (struct casereader *, struct ccase *);
+struct ccase *casereader_read (struct casereader *);
 bool casereader_destroy (struct casereader *);
 
 struct casereader *casereader_clone (const struct casereader *);
@@ -68,8 +68,7 @@ void casereader_split (struct casereader *,
 struct casereader *casereader_rename (struct casereader *);
 void casereader_swap (struct casereader *, struct casereader *);
 
-bool casereader_peek (struct casereader *, casenumber, struct ccase *)
-     WARN_UNUSED_RESULT;
+struct ccase *casereader_peek (struct casereader *, casenumber);
 bool casereader_is_empty (struct casereader *);
 
 bool casereader_error (const struct casereader *);
@@ -98,6 +97,7 @@ struct casereader *
 casereader_create_filter_missing (struct casereader *,
                                   const struct variable **vars, size_t var_cnt,
                                   enum mv_class,
+                                 casenumber *n_missing,
                                   struct casewriter *exclude);
 
 struct casereader *
@@ -106,14 +106,38 @@ casereader_create_counter (struct casereader *, casenumber *counter,
 
 struct casereader *
 casereader_create_translator (struct casereader *, size_t output_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+                              struct ccase *(*translate) (struct ccase *,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux);
 
+/* A function which creates a numberic value from an existing case */
+typedef double new_value_func (const struct ccase *, casenumber, void *);
+
+struct casereader *
+casereader_create_append_numeric (struct casereader *subreader,
+                                 new_value_func func, void *aux,
+                                 void (*destroy) (void *aux));
+
 struct casereader *
 casereader_create_arithmetic_sequence (struct casereader *,
                                        double first, double increment);
 
+enum rank_error
+  {
+    RANK_ERR_NONE = 0,
+    RANK_ERR_NEGATIVE_WEIGHT = 0x01,
+    RANK_ERR_UNSORTED = 0x02
+  };
+
+
+typedef void distinct_func (double v, casenumber n, double w, void *aux);
+
+struct casereader *
+casereader_create_append_rank (struct casereader *,
+                              const struct variable *v, const struct variable *w,
+                              enum rank_error *err,
+                              distinct_func *distinct_callback, void *aux);
+
+
 #endif /* data/casereader.h */
index 0d735c4dc3ff909c41278df40b5179bdb3dbd2cd..d2be9cfaad3b2a6a0653834dae5b173f0e93568a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -52,7 +52,7 @@ struct casewindow_class
     void (*destroy) (void *aux);
     void (*push_head) (void *aux, struct ccase *);
     void (*pop_tail) (void *aux, casenumber cnt);
-    bool (*get_case) (void *aux, casenumber ofs, struct ccase *);
+    struct ccase *(*get_case) (void *aux, casenumber ofs);
     casenumber (*get_case_cnt) (const void *aux);
   };
 
@@ -120,11 +120,11 @@ casewindow_to_disk (struct casewindow *old)
   new = do_casewindow_create (taint_clone (old->taint), old->value_cnt, 0);
   while (casewindow_get_case_cnt (old) > 0 && !casewindow_error (new))
     {
-      struct ccase c;
-      if (!casewindow_get_case (old, 0, &c))
+      struct ccase *c = casewindow_get_case (old, 0);
+      if (c == NULL)
         break;
       casewindow_pop_tail (old, 1);
-      casewindow_push_head (new, &c);
+      casewindow_push_head (new, c);
     }
   casewindow_swap (old, new);
   casewindow_destroy (new);
@@ -147,7 +147,7 @@ casewindow_push_head (struct casewindow *cw, struct ccase *c)
         }
     }
   else
-    case_destroy (c);
+    case_unref (c);
 }
 
 /* Deletes CASE_CNT cases at the tail of casewindow CW. */
@@ -158,23 +158,19 @@ casewindow_pop_tail (struct casewindow *cw, casenumber case_cnt)
     cw->class->pop_tail (cw->aux, case_cnt);
 }
 
-/* Copies the case that is CASE_IDX cases away from CW's tail
-   into C.  Returns true if successful, false on an I/O error or
-   if CW is otherwise tainted.  On failure, nullifies case C. */
-bool
-casewindow_get_case (const struct casewindow *cw_, casenumber case_idx,
-                     struct ccase *c)
+/* Returns the case that is CASE_IDX cases away from CW's tail
+   into C, or a null pointer on an I/O error or if CW is
+   otherwise tainted.  The caller must call case_unref() on the
+   returned case when it is no longer needed. */
+struct ccase *
+casewindow_get_case (const struct casewindow *cw_, casenumber case_idx)
 {
   struct casewindow *cw = (struct casewindow *) cw_;
 
   assert (case_idx >= 0 && case_idx < casewindow_get_case_cnt (cw));
-  if (!casewindow_error (cw))
-    return cw->class->get_case (cw->aux, case_idx, c);
-  else
-    {
-      case_nullify (c);
-      return false;
-    }
+  if (casewindow_error (cw))
+    return NULL;
+  return cw->class->get_case (cw->aux, case_idx);
 }
 
 /* Returns the number of cases in casewindow CW. */
@@ -218,7 +214,7 @@ casewindow_get_taint (const struct casewindow *cw)
 struct casewindow_memory
   {
     struct deque deque;
-    struct ccase *cases;
+    struct ccase **cases;
   };
 
 static void *
@@ -234,7 +230,7 @@ casewindow_memory_destroy (void *cwm_)
 {
   struct casewindow_memory *cwm = cwm_;
   while (!deque_is_empty (&cwm->deque))
-    case_destroy (&cwm->cases[deque_pop_front (&cwm->deque)]);
+    case_unref (cwm->cases[deque_pop_front (&cwm->deque)]);
   free (cwm->cases);
   free (cwm);
 }
@@ -245,7 +241,7 @@ casewindow_memory_push_head (void *cwm_, struct ccase *c)
   struct casewindow_memory *cwm = cwm_;
   if (deque_is_full (&cwm->deque))
     cwm->cases = deque_expand (&cwm->deque, cwm->cases, sizeof *cwm->cases);
-  case_move (&cwm->cases[deque_push_back (&cwm->deque)], c);
+  cwm->cases[deque_push_back (&cwm->deque)] = c;
 }
 
 static void
@@ -254,15 +250,14 @@ casewindow_memory_pop_tail (void *cwm_, casenumber case_cnt)
   struct casewindow_memory *cwm = cwm_;
   assert (deque_count (&cwm->deque) >= case_cnt);
   while (case_cnt-- > 0)
-    case_destroy (&cwm->cases[deque_pop_front (&cwm->deque)]);
+    case_unref (cwm->cases[deque_pop_front (&cwm->deque)]);
 }
 
-static bool
-casewindow_memory_get_case (void *cwm_, casenumber ofs, struct ccase *c)
+static struct ccase *
+casewindow_memory_get_case (void *cwm_, casenumber ofs)
 {
   struct casewindow_memory *cwm = cwm_;
-  case_clone (c, &cwm->cases[deque_front (&cwm->deque, ofs)]);
-  return true;
+  return case_ref (cwm->cases[deque_front (&cwm->deque, ofs)]);
 }
 
 static casenumber
@@ -325,11 +320,11 @@ casewindow_file_pop_tail (void *cwf_, casenumber cnt)
     cwf->head = cwf->tail = 0;
 }
 
-static bool
-casewindow_file_get_case (void *cwf_, casenumber ofs, struct ccase *c)
+static struct ccase *
+casewindow_file_get_case (void *cwf_, casenumber ofs)
 {
   struct casewindow_file *cwf = cwf_;
-  return case_tmpfile_get_case (cwf->file, cwf->tail + ofs, c);
+  return case_tmpfile_get_case (cwf->file, cwf->tail + ofs);
 }
 
 static casenumber
index c3971231afe2d1ed5ac5f89e9b5b61e719505212..f0a200f449de014638d8114ce9b50cb13a4fccb8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -37,8 +37,8 @@ bool casewindow_destroy (struct casewindow *);
 
 void casewindow_push_head (struct casewindow *, struct ccase *);
 void casewindow_pop_tail (struct casewindow *, casenumber cnt);
-bool casewindow_get_case (const struct casewindow *, casenumber case_idx,
-                          struct ccase *);
+struct ccase *casewindow_get_case (const struct casewindow *,
+                                   casenumber case_idx);
 size_t casewindow_get_value_cnt (const struct casewindow *);
 casenumber casewindow_get_case_cnt (const struct casewindow *);
 
index 8298af465da9d88640fe813f6a12309c6effd036..1680fe28303c5f1fa4dd6d8887954a39b494abc9 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -23,7 +23,8 @@ struct casewriter_class
   {
     /* Mandatory.
 
-       Writes case C to WRITER.  Destroys C before returning.
+       Writes case C to WRITER.  Ownership of C is transferred to
+       WRITER.
 
        If an I/O error occurs, this function should call
        casewriter_force_error on WRITER.  Some I/O error
index c630b44cda397dfffccd980c9d3ecb65255e91dc..a19533d65d330b6f443b308522e88d61b25687f6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@ struct casewriter_translator
   {
     struct casewriter *subwriter;
 
-    void (*translate) (struct ccase *input, struct ccase *output, void *aux);
+    struct ccase *(*translate) (struct ccase *, void *aux);
     bool (*destroy) (void *aux);
     void *aux;
   };
@@ -37,10 +37,15 @@ struct casewriter_translator
 static const struct casewriter_class casewriter_translator_class;
 
 /* Creates and returns a new casewriter whose cases are passed
-   through TRANSLATE, which must create case OUTPUT, with
-   OUTPUT_VALUE_CNT values, and populate it based on INPUT and
-   auxiliary data AUX.  The translated cases are then written to
-   SUBWRITER.  TRANSLATE must also destroy INPUT.
+   through TRANSLATE, which must return a case with
+   OUTPUT_VALUE_CNT values, based on INPUT and auxiliary data
+   AUX.  (TRANSLATE may also return a null pointer, in which case
+   no case is written to the output.)  The translated cases are
+   then written to SUBWRITER.
+
+   TRANSLATE takes ownership of each case passed to it.  Thus, it
+   should either unref each case and return a new case, or
+   (unshare and then) modify and return the same case.
 
    When the translating casewriter is destroyed, DESTROY will be
    called to allow any state maintained by TRANSLATE to be freed.
@@ -51,9 +56,8 @@ static const struct casewriter_class casewriter_translator_class;
 struct casewriter *
 casewriter_create_translator (struct casewriter *subwriter,
                               size_t translated_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+                              struct ccase *(*translate) (struct ccase *,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux)
 {
@@ -75,10 +79,9 @@ casewriter_translator_write (struct casewriter *writer UNUSED,
                              void *ct_, struct ccase *c)
 {
   struct casewriter_translator *ct = ct_;
-  struct ccase tmp;
-
-  ct->translate (c, &tmp, ct->aux);
-  casewriter_write (ct->subwriter, &tmp);
+  c = ct->translate (c, ct->aux);
+  if (c != NULL)
+    casewriter_write (ct->subwriter, c);
 }
 
 static void
index a30e50e20983f1f88adde7463931ad3ad4430c1b..56e6c291cbb4fb7722a310eeb504b20bb58a37d5 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -91,8 +91,7 @@ casewriter_get_value_cnt (const struct casewriter *writer)
 struct casereader *
 casewriter_make_reader (struct casewriter *writer)
 {
-  struct casereader *reader;
-  reader = writer->class->convert_to_reader (writer, writer->aux);
+  struct casereader *reader = writer->class->convert_to_reader (writer, writer->aux);
   taint_propagate (writer->taint, casereader_get_taint (reader));
   taint_destroy (writer->taint);
   free (writer);
@@ -241,27 +240,29 @@ casewriter_window_convert_to_reader (struct casewriter *writer UNUSED,
                                      void *window_)
 {
   struct casewindow *window = window_;
-  struct casereader *reader;
-  reader = casereader_create_random (casewindow_get_value_cnt (window),
-                                     casewindow_get_case_cnt (window),
-                                     &casereader_window_class, window);
+  struct casereader *reader =
+    casereader_create_random (casewindow_get_value_cnt (window),
+                             casewindow_get_case_cnt (window),
+                             &casereader_window_class, window);
+
   taint_propagate (casewindow_get_taint (window),
                    casereader_get_taint (reader));
   return reader;
 }
 
-/* Reads the case at the given 0-based OFFSET from the front of
-   WINDOW into C.  Returns true if successful, false if
-   OFFSET is beyond the end of file or upon I/O error. */
-static bool
+/* Reads and returns the case at the given 0-based OFFSET from
+   the front of WINDOW into C.  Returns a null pointer if OFFSET
+   is beyond the end of file or upon I/O error.  The caller must
+   call case_unref() on the returned case when it is no longer
+   needed.*/
+static struct ccase *
 casereader_window_read (struct casereader *reader UNUSED, void *window_,
-                        casenumber offset, struct ccase *c)
+                        casenumber offset)
 {
   struct casewindow *window = window_;
   if (offset >= casewindow_get_case_cnt (window))
-    return false;
-  else
-    return casewindow_get_case (window, offset, c);
+    return NULL;
+  return casewindow_get_case (window, offset);
 }
 
 /* Destroys casewindow reader WINDOW. */
index a92c417680116ffbfc438e52877b64f74f62e1e3..05ef707246060eb215e0b880d6c4e9b0117013d1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -43,9 +43,8 @@ struct casewriter *autopaging_writer_create (size_t value_cnt);
 \f
 struct casewriter *
 casewriter_create_translator (struct casewriter *, size_t translated_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+                              struct ccase *(*translate) (struct ccase *input,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux);
 
index 1620bc7f7f9b3c27bbde71737eeb860d5c4f7cf0..6771b5836f85d4aa29aa5e9ec0561a11a1aa628f 100644 (file)
@@ -108,7 +108,7 @@ cat_value_find (const struct variable *v, const union value *val)
     {
       candidate = obs_vals->vals + i;
       assert (candidate != NULL);
-      if (!compare_values (candidate, val, var_get_width (v)))
+      if (!compare_values_short (candidate, val, v))
        {
          return i;
        }
index 7d4df5adccb9310d6964e5a544a3880ef5238968..3a8d67cc22f66b28eb0bed54126e5a3255449064 100644 (file)
@@ -22,7 +22,7 @@
 #include <libpspp/float-format.h>
 #include <libpspp/integer-format.h>
 #include <libpspp/str.h>
-
+#include <data/format.h>
 
 enum fmt_type;
 union value;
index da85d963c17dfb166ff93ab1e7882f1ee8105d42..83a2a677e3b55a671a10f71d5347fb2b4b312b07 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
 #include <data/sparse-cases.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
-#include <libpspp/model-checker.h>
 #include <libpspp/range-map.h>
 #include <libpspp/range-set.h>
 #include <libpspp/taint.h>
@@ -390,21 +389,22 @@ datasheet_move_columns (struct datasheet *ds,
   axis_move (ds->columns, old_start, new_start, cnt);
 }
 
-/* Retrieves the contents of the given ROW in datasheet DS into
-   newly created case C.  Returns true if successful, false on
-   I/O error. */
-bool
-datasheet_get_row (const struct datasheet *ds, casenumber row, struct ccase *c)
+/* Retrieves and returns the contents of the given ROW in
+   datasheet DS.  The caller owns the returned case and must
+   unref it when it is no longer needed.  Returns a null pointer
+   on I/O error. */
+struct ccase *
+datasheet_get_row (const struct datasheet *ds, casenumber row)
 {
   size_t column_cnt = datasheet_get_column_cnt (ds);
-  case_create (c, column_cnt);
+  struct ccase *c = case_create (column_cnt);
   if (rw_case ((struct datasheet *) ds, OP_READ,
                row, 0, column_cnt, case_data_all_rw (c)))
-    return true;
+    return c;
   else
     {
-      case_destroy (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 }
 
@@ -418,7 +418,7 @@ datasheet_put_row (struct datasheet *ds, casenumber row, struct ccase *c)
   size_t column_cnt = datasheet_get_column_cnt (ds);
   bool ok = rw_case (ds, OP_WRITE, row, 0, column_cnt,
                      (union value *) case_data_all (c));
-  case_destroy (c);
+  case_unref (c);
   return ok;
 }
 
@@ -446,13 +446,15 @@ datasheet_put_value (struct datasheet *ds, casenumber row, size_t column,
                   (union value *) value);
 }
 
-/* Inserts the CNT cases at C, which are destroyed, into
-   datasheet DS just before row BEFORE.  Returns true if
-   successful, false on I/O error.  On failure, datasheet DS is
-   not modified. */
+/* Inserts the CNT cases at C into datasheet DS just before row
+   BEFORE.  Returns true if successful, false on I/O error.  On
+   failure, datasheet DS is not modified.
+
+   Regardless of success, this function unrefs all of the cases
+   in C. */
 bool
 datasheet_insert_rows (struct datasheet *ds,
-                       casenumber before, struct ccase c[],
+                       casenumber before, struct ccase *c[],
                        casenumber cnt)
 {
   casenumber added = 0;
@@ -477,10 +479,10 @@ datasheet_insert_rows (struct datasheet *ds,
 
       /* Initialize the new rows. */
       for (i = 0; i < phy_cnt; i++)
-        if (!datasheet_put_row (ds, before + i, &c[i]))
+        if (!datasheet_put_row (ds, before + i, c[i]))
           {
             while (++i < cnt)
-              case_destroy (&c[i]);
+              case_unref (c[i]);
             datasheet_delete_rows (ds, before - added, phy_cnt + added);
             return false;
           }
@@ -541,20 +543,20 @@ datasheet_make_reader (struct datasheet *ds)
 }
 
 /* "read" function for the datasheet random casereader. */
-static bool
+static struct ccase *
 datasheet_reader_read (struct casereader *reader UNUSED, void *ds_,
-                       casenumber case_idx, struct ccase *c)
+                       casenumber case_idx)
 {
   struct datasheet *ds = ds_;
-  if (case_idx >= datasheet_get_row_cnt (ds))
-    return false;
-  else if (datasheet_get_row (ds, case_idx, c))
-    return true;
-  else
+  if (case_idx < datasheet_get_row_cnt (ds))
     {
-      taint_set_taint (ds->taint);
-      return false;
+      struct ccase *c = datasheet_get_row (ds, case_idx);
+      if (c == NULL)
+        taint_set_taint (ds->taint);
+      return c;
     }
+  else
+    return NULL;
 }
 
 /* "destroy" function for the datasheet random casereader. */
@@ -694,7 +696,7 @@ axis_clone (const struct axis *old)
   for (node = tower_first (&old->log_to_phy); node != NULL;
        node = tower_next (&old->log_to_phy, node))
     {
-      unsigned long int size = tower_node_get_height (node);
+      unsigned long int size = tower_node_get_size (node);
       struct axis_group *group = tower_data (node, struct axis_group, logical);
       tower_insert (&new->log_to_phy, size, make_axis_group (group->phy_start),
                     NULL);
@@ -718,7 +720,7 @@ axis_hash (const struct axis *axis, struct md4_ctx *ctx)
     {
       struct axis_group *group = tower_data (tn, struct axis_group, logical);
       unsigned long int phy_start = group->phy_start;
-      unsigned long int size = tower_node_get_height (tn);
+      unsigned long int size = tower_node_get_size (tn);
 
       md4_process_bytes (&phy_start, sizeof phy_start, ctx);
       md4_process_bytes (&size, sizeof size, ctx);
@@ -922,7 +924,7 @@ split_axis (struct axis *axis, unsigned long int where)
   if (where > group_start)
     {
       unsigned long int size_1 = where - group_start;
-      unsigned long int size_2 = tower_node_get_height (group_node) - size_1;
+      unsigned long int size_2 = tower_node_get_size (group_node) - size_1;
       struct tower_node *next = tower_next (&axis->log_to_phy, group_node);
       struct tower_node *new = make_axis_group (group->phy_start + size_1);
       tower_resize (&axis->log_to_phy, group_node, size_1);
@@ -962,11 +964,11 @@ merge_axis_nodes (struct axis *axis, struct tower_node *node,
   if (next != NULL)
     {
       struct axis_group *next_group = axis_group_from_tower_node (next);
-      unsigned long this_height = tower_node_get_height (node);
+      unsigned long this_height = tower_node_get_size (node);
 
       if (group->phy_start + this_height == next_group->phy_start)
         {
-          unsigned long next_height = tower_node_get_height (next);
+          unsigned long next_height = tower_node_get_size (next);
           tower_resize (t, node, this_height + next_height);
           if (other_node != NULL && *other_node == next)
             *other_node = tower_next (t, *other_node);
@@ -980,11 +982,11 @@ merge_axis_nodes (struct axis *axis, struct tower_node *node,
   if (prev != NULL)
     {
       struct axis_group *prev_group = axis_group_from_tower_node (prev);
-      unsigned long prev_height = tower_node_get_height (prev);
+      unsigned long prev_height = tower_node_get_size (prev);
 
       if (prev_group->phy_start + prev_height == group->phy_start)
         {
-          unsigned long this_height = tower_node_get_height (node);
+          unsigned long this_height = tower_node_get_size (node);
           group->phy_start = prev_group->phy_start;
           tower_resize (t, node, this_height + prev_height);
           if (other_node != NULL && *other_node == prev)
@@ -1008,7 +1010,7 @@ check_axis_merged (const struct axis *axis UNUSED)
     if (prev != NULL)
       {
         struct axis_group *prev_group = axis_group_from_tower_node (prev);
-        unsigned long prev_height = tower_node_get_height (prev);
+        unsigned long prev_height = tower_node_get_size (prev);
         struct axis_group *node_group = axis_group_from_tower_node (node);
         assert (prev_group->phy_start + prev_height != node_group->phy_start);
       }
@@ -1137,15 +1139,12 @@ source_read (const struct source *source,
     return sparse_cases_read (source->data, row, column, values, value_cnt);
   else
     {
-      struct ccase c;
-      bool ok;
-
-      assert (source->backing != NULL);
-      ok = casereader_peek (source->backing, row, &c);
+      struct ccase *c = casereader_peek (source->backing, row);
+      bool ok = c != NULL;
       if (ok)
         {
-          case_copy_out (&c, column, values, value_cnt);
-          case_destroy (&c);
+          case_copy_out (c, column, values, value_cnt);
+          case_unref (c);
         }
       return ok;
     }
@@ -1170,9 +1169,10 @@ source_write (struct source *source,
     ok = sparse_cases_write (source->data, row, column, values, value_cnt);
   else
     {
-      struct ccase c;
+      struct ccase *c;
+
       if (row < source->backing_rows)
-        ok = casereader_peek (source->backing, row, &c);
+        c = case_unshare (casereader_peek (source->backing, row));
       else
         {
           /* It's not one of the backed rows.  Ideally, this
@@ -1183,15 +1183,16 @@ source_write (struct source *source,
              levels, so that we in fact usually write the full
              contents of new, unbacked rows in multiple calls to
              this function.  Make this work. */
-          case_create (&c, column_cnt);
-          ok = true;
+          c = case_create (column_cnt);
         }
+      ok = c != NULL;
+
       if (ok)
         {
-          case_copy_in (&c, column, values, value_cnt);
+          case_copy_in (c, column, values, value_cnt);
           ok = sparse_cases_write (source->data, row, 0,
-                                   case_data_all (&c), column_cnt);
-          case_destroy (&c);
+                                   case_data_all (c), column_cnt);
+          case_unref (c);
         }
     }
   return ok;
@@ -1232,36 +1233,10 @@ source_has_backing (const struct source *source)
 #define MAX_ROWS 5
 #define MAX_COLS 5
 
-/* Hashes the structure of datasheet DS and returns the hash.
-   We use MD4 because it is much faster than MD5 or SHA-1 but its
-   collision resistance is just as good. */
-static unsigned int
-hash_datasheet (const struct datasheet *ds)
-{
-  unsigned int hash[DIV_RND_UP (20, sizeof (unsigned int))];
-  struct md4_ctx ctx;
-  struct range_map_node *r;
-
-  md4_init_ctx (&ctx);
-  axis_hash (ds->columns, &ctx);
-  axis_hash (ds->rows, &ctx);
-  for (r = range_map_first (&ds->sources); r != NULL;
-       r = range_map_next (&ds->sources, r))
-    {
-      unsigned long int start = range_map_node_get_start (r);
-      unsigned long int end = range_map_node_get_end (r);
-      md4_process_bytes (&start, sizeof start, &ctx);
-      md4_process_bytes (&end, sizeof end, &ctx);
-    }
-  md4_process_bytes (&ds->column_min_alloc, sizeof ds->column_min_alloc,
-                      &ctx);
-  md4_finish_ctx (&ctx, hash);
-  return hash[0];
-}
 
 /* Clones the structure and contents of ODS into a new datasheet,
    and returns the new datasheet. */
-static struct datasheet *
+struct datasheet *
 clone_datasheet (const struct datasheet *ods)
 {
   struct datasheet *ds;
@@ -1286,426 +1261,33 @@ clone_datasheet (const struct datasheet *ods)
   return ds;
 }
 
-/* lazy_casereader callback function to instantiate a casereader
-   from the datasheet. */
-static struct casereader *
-lazy_callback (void *ds_)
-{
-  struct datasheet *ds = ds_;
-  return datasheet_make_reader (ds);
-}
-
-/* Checks that READER contains the ROW_CNT rows and COLUMN_CNT
-   columns of data in ARRAY, reporting any errors via MC. */
-static void
-check_datasheet_casereader (struct mc *mc, struct casereader *reader,
-                            double array[MAX_ROWS][MAX_COLS],
-                            size_t row_cnt, size_t column_cnt)
-{
-  if (casereader_get_case_cnt (reader) != row_cnt)
-    {
-      if (casereader_get_case_cnt (reader) == CASENUMBER_MAX
-          && casereader_count_cases (reader) == row_cnt)
-        mc_error (mc, "datasheet casereader has unknown case count");
-      else
-        mc_error (mc, "casereader row count (%lu) does not match "
-                  "expected (%zu)",
-                  (unsigned long int) casereader_get_case_cnt (reader),
-                  row_cnt);
-    }
-  else if (casereader_get_value_cnt (reader) != column_cnt)
-    mc_error (mc, "casereader column count (%zu) does not match "
-              "expected (%zu)",
-              casereader_get_value_cnt (reader), column_cnt);
-  else
-    {
-      struct ccase c;
-      size_t row;
-
-      for (row = 0; row < row_cnt; row++)
-        {
-          size_t col;
-
-          if (!casereader_read (reader, &c))
-            {
-              mc_error (mc, "casereader_read failed reading row %zu of %zu "
-                        "(%zu columns)", row, row_cnt, column_cnt);
-              return;
-            }
-
-          for (col = 0; col < column_cnt; col++)
-            if (case_num_idx (&c, col) != array[row][col])
-              mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
-                        "%g != %g",
-                        row, col, row_cnt, column_cnt,
-                        case_num_idx (&c, col), array[row][col]);
-
-         case_destroy (&c);
-        }
-
-      if (casereader_read (reader, &c))
-        mc_error (mc, "casereader has extra cases (expected %zu)", row_cnt);
-    }
-}
-
-/* Checks that datasheet DS contains has ROW_CNT rows, COLUMN_CNT
-   columns, and the same contents as ARRAY, reporting any
-   mismatches via mc_error.  Then, adds DS to MC as a new state. */
-static void
-check_datasheet (struct mc *mc, struct datasheet *ds,
-                 double array[MAX_ROWS][MAX_COLS],
-                 size_t row_cnt, size_t column_cnt)
-{
-  struct datasheet *ds2;
-  struct casereader *reader;
-  unsigned long int serial = 0;
-
-  assert (row_cnt < MAX_ROWS);
-  assert (column_cnt < MAX_COLS);
-
-  /* If it is a duplicate hash, discard the state before checking
-     its consistency, to save time. */
-  if (mc_discard_dup_state (mc, hash_datasheet (ds)))
-    {
-      datasheet_destroy (ds);
-      return;
-    }
-
-  /* Check contents of datasheet via datasheet functions. */
-  if (row_cnt != datasheet_get_row_cnt (ds))
-    mc_error (mc, "row count (%lu) does not match expected (%zu)",
-              (unsigned long int) datasheet_get_row_cnt (ds), row_cnt);
-  else if (column_cnt != datasheet_get_column_cnt (ds))
-    mc_error (mc, "column count (%zu) does not match expected (%zu)",
-              datasheet_get_column_cnt (ds), column_cnt);
-  else
-    {
-      size_t row, col;
-
-      for (row = 0; row < row_cnt; row++)
-        for (col = 0; col < column_cnt; col++)
-          {
-            union value v;
-            if (!datasheet_get_value (ds, row, col, &v, 1))
-              NOT_REACHED ();
-            if (v.f != array[row][col])
-              mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: %g != %g",
-                        row, col, row_cnt, column_cnt, v.f, array[row][col]);
-          }
-    }
-
-  /* Check that datasheet contents are correct when read through
-     casereader. */
-  ds2 = clone_datasheet (ds);
-  reader = datasheet_make_reader (ds2);
-  check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt);
-  casereader_destroy (reader);
-
-  /* Check that datasheet contents are correct when read through
-     casereader with lazy_casereader wrapped around it.  This is
-     valuable because otherwise there is no non-GUI code that
-     uses the lazy_casereader. */
-  ds2 = clone_datasheet (ds);
-  reader = lazy_casereader_create (column_cnt, row_cnt,
-                                   lazy_callback, ds2, &serial);
-  check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt);
-  if (lazy_casereader_destroy (reader, serial))
-    {
-      /* Lazy casereader was never instantiated.  This will
-         only happen if there are no rows (because in that case
-         casereader_read never gets called). */
-      datasheet_destroy (ds2);
-      if (row_cnt != 0)
-        mc_error (mc, "lazy casereader not instantiated, but should "
-                  "have been (size %zu,%zu)", row_cnt, column_cnt);
-    }
-  else
-    {
-      /* Lazy casereader was instantiated.  This is the common
-         case, in which some casereader operation
-         (casereader_read in this case) was performed on the
-         lazy casereader. */
-      casereader_destroy (reader);
-      if (row_cnt == 0)
-        mc_error (mc, "lazy casereader instantiated, but should not "
-                  "have been (size %zu,%zu)", row_cnt, column_cnt);
-    }
-
-  mc_add_state (mc, ds);
-}
-
-/* Extracts the contents of DS into DATA. */
-static void
-extract_data (const struct datasheet *ds, double data[MAX_ROWS][MAX_COLS])
-{
-  size_t column_cnt = datasheet_get_column_cnt (ds);
-  size_t row_cnt = datasheet_get_row_cnt (ds);
-  size_t row, col;
-
-  assert (row_cnt < MAX_ROWS);
-  assert (column_cnt < MAX_COLS);
-  for (row = 0; row < row_cnt; row++)
-    for (col = 0; col < column_cnt; col++)
-      {
-        union value v;
-        if (!datasheet_get_value (ds, row, col, &v, 1))
-          NOT_REACHED ();
-        data[row][col] = v.f;
-      }
-}
-
-/* Clones the structure and contents of ODS into *DS,
-   and the contents of ODATA into DATA. */
-static void
-clone_model (const struct datasheet *ods, double odata[MAX_ROWS][MAX_COLS],
-             struct datasheet **ds, double data[MAX_ROWS][MAX_COLS])
-{
-  *ds = clone_datasheet (ods);
-  memcpy (data, odata, MAX_ROWS * MAX_COLS * sizeof **data);
-}
 
-/* "init" function for struct mc_class. */
-static void
-datasheet_mc_init (struct mc *mc)
+/* Hashes the structure of datasheet DS and returns the hash.
+   We use MD4 because it is much faster than MD5 or SHA-1 but its
+   collision resistance is just as good. */
+unsigned int
+hash_datasheet (const struct datasheet *ds)
 {
-  struct datasheet_test_params *params = mc_get_aux (mc);
-  struct datasheet *ds;
+  unsigned int hash[DIV_RND_UP (20, sizeof (unsigned int))];
+  struct md4_ctx ctx;
+  struct range_map_node *r;
 
-  if (params->backing_rows == 0 && params->backing_cols == 0)
-    {
-      /* Create unbacked datasheet. */
-      ds = datasheet_create (NULL);
-      mc_name_operation (mc, "empty datasheet");
-      check_datasheet (mc, ds, NULL, 0, 0);
-    }
-  else
+  md4_init_ctx (&ctx);
+  axis_hash (ds->columns, &ctx);
+  axis_hash (ds->rows, &ctx);
+  for (r = range_map_first (&ds->sources); r != NULL;
+       r = range_map_next (&ds->sources, r))
     {
-      /* Create datasheet with backing. */
-      struct casewriter *writer;
-      struct casereader *reader;
-      double data[MAX_ROWS][MAX_COLS];
-      int row;
-
-      assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
-      assert (params->backing_cols > 0 && params->backing_cols <= MAX_COLS);
-
-      writer = mem_writer_create (params->backing_cols);
-      for (row = 0; row < params->backing_rows; row++)
-        {
-          struct ccase c;
-          int col;
-
-          case_create (&c, params->backing_cols);
-          for (col = 0; col < params->backing_cols; col++)
-            {
-              double value = params->next_value++;
-              data[row][col] = value;
-              case_data_rw_idx (&c, col)->f = value;
-            }
-          casewriter_write (writer, &c);
-        }
-      reader = casewriter_make_reader (writer);
-      assert (reader != NULL);
-
-      ds = datasheet_create (reader);
-      mc_name_operation (mc, "datasheet with (%d,%d) backing",
-                         params->backing_rows, params->backing_cols);
-      check_datasheet (mc, ds, data,
-                       params->backing_rows, params->backing_cols);
+      unsigned long int start = range_map_node_get_start (r);
+      unsigned long int end = range_map_node_get_end (r);
+      md4_process_bytes (&start, sizeof start, &ctx);
+      md4_process_bytes (&end, sizeof end, &ctx);
     }
+  md4_process_bytes (&ds->column_min_alloc, sizeof ds->column_min_alloc,
+                      &ctx);
+  md4_finish_ctx (&ctx, hash);
+  return hash[0];
 }
 
-/* "mutate" function for struct mc_class. */
-static void
-datasheet_mc_mutate (struct mc *mc, const void *ods_)
-{
-  struct datasheet_test_params *params = mc_get_aux (mc);
-
-  const struct datasheet *ods = ods_;
-  double odata[MAX_ROWS][MAX_COLS];
-  double data[MAX_ROWS][MAX_COLS];
-  size_t column_cnt = datasheet_get_column_cnt (ods);
-  size_t row_cnt = datasheet_get_row_cnt (ods);
-  size_t pos, new_pos, cnt;
-
-  extract_data (ods, odata);
-
-  /* Insert all possible numbers of columns in all possible
-     positions. */
-  for (pos = 0; pos <= column_cnt; pos++)
-    for (cnt = 0; cnt <= params->max_cols - column_cnt; cnt++)
-      if (mc_include_state (mc))
-        {
-          struct datasheet *ds;
-          union value new[MAX_COLS];
-          size_t i, j;
-
-          mc_name_operation (mc, "insert %zu columns at %zu", cnt, pos);
-          clone_model (ods, odata, &ds, data);
-
-          for (i = 0; i < cnt; i++)
-            new[i].f = params->next_value++;
-
-          if (!datasheet_insert_columns (ds, new, cnt, pos))
-            mc_error (mc, "datasheet_insert_columns failed");
-
-          for (i = 0; i < row_cnt; i++)
-            {
-              insert_range (&data[i][0], column_cnt, sizeof data[i][0],
-                            pos, cnt);
-              for (j = 0; j < cnt; j++)
-                data[i][pos + j] = new[j].f;
-            }
-
-          check_datasheet (mc, ds, data, row_cnt, column_cnt + cnt);
-        }
-
-  /* Delete all possible numbers of columns from all possible
-     positions. */
-  for (pos = 0; pos < column_cnt; pos++)
-    for (cnt = 0; cnt < column_cnt - pos; cnt++)
-      if (mc_include_state (mc))
-        {
-          struct datasheet *ds;
-          size_t i;
-
-          mc_name_operation (mc, "delete %zu columns at %zu", cnt, pos);
-          clone_model (ods, odata, &ds, data);
-
-          datasheet_delete_columns (ds, pos, cnt);
-
-          for (i = 0; i < row_cnt; i++)
-            remove_range (&data[i], column_cnt, sizeof *data[i], pos, cnt);
-
-          check_datasheet (mc, ds, data, row_cnt, column_cnt - cnt);
-        }
-
-  /* Move all possible numbers of columns from all possible
-     existing positions to all possible new positions. */
-  for (pos = 0; pos < column_cnt; pos++)
-    for (cnt = 0; cnt < column_cnt - pos; cnt++)
-      for (new_pos = 0; new_pos < column_cnt - cnt; new_pos++)
-        if (mc_include_state (mc))
-          {
-            struct datasheet *ds;
-            size_t i;
-
-            clone_model (ods, odata, &ds, data);
-            mc_name_operation (mc, "move %zu columns from %zu to %zu",
-                               cnt, pos, new_pos);
-
-            datasheet_move_columns (ds, pos, new_pos, cnt);
-
-            for (i = 0; i < row_cnt; i++)
-              move_range (&data[i], column_cnt, sizeof data[i][0],
-                          pos, new_pos, cnt);
-
-            check_datasheet (mc, ds, data, row_cnt, column_cnt);
-          }
-
-  /* Insert all possible numbers of rows in all possible
-     positions. */
-  for (pos = 0; pos <= row_cnt; pos++)
-    for (cnt = 0; cnt <= params->max_rows - row_cnt; cnt++)
-      if (mc_include_state (mc))
-        {
-          struct datasheet *ds;
-          struct ccase c[MAX_ROWS];
-          size_t i, j;
-
-          clone_model (ods, odata, &ds, data);
-          mc_name_operation (mc, "insert %zu rows at %zu", cnt, pos);
-
-          for (i = 0; i < cnt; i++)
-            {
-              case_create (&c[i], column_cnt);
-              for (j = 0; j < column_cnt; j++)
-                case_data_rw_idx (&c[i], j)->f = params->next_value++;
-            }
-
-          insert_range (data, row_cnt, sizeof data[pos], pos, cnt);
-          for (i = 0; i < cnt; i++)
-            for (j = 0; j < column_cnt; j++)
-              data[i + pos][j] = case_num_idx (&c[i], j);
-
-          if (!datasheet_insert_rows (ds, pos, c, cnt))
-            mc_error (mc, "datasheet_insert_rows failed");
-
-          check_datasheet (mc, ds, data, row_cnt + cnt, column_cnt);
-        }
-
-  /* Delete all possible numbers of rows from all possible
-     positions. */
-  for (pos = 0; pos < row_cnt; pos++)
-    for (cnt = 0; cnt < row_cnt - pos; cnt++)
-      if (mc_include_state (mc))
-        {
-          struct datasheet *ds;
-
-          clone_model (ods, odata, &ds, data);
-          mc_name_operation (mc, "delete %zu rows at %zu", cnt, pos);
-
-          datasheet_delete_rows (ds, pos, cnt);
-
-          remove_range (&data[0], row_cnt, sizeof data[0], pos, cnt);
-
-          check_datasheet (mc, ds, data, row_cnt - cnt, column_cnt);
-        }
-
-  /* Move all possible numbers of rows from all possible existing
-     positions to all possible new positions. */
-  for (pos = 0; pos < row_cnt; pos++)
-    for (cnt = 0; cnt < row_cnt - pos; cnt++)
-      for (new_pos = 0; new_pos < row_cnt - cnt; new_pos++)
-        if (mc_include_state (mc))
-          {
-            struct datasheet *ds;
-
-            clone_model (ods, odata, &ds, data);
-            mc_name_operation (mc, "move %zu rows from %zu to %zu",
-                               cnt, pos, new_pos);
-
-            datasheet_move_rows (ds, pos, new_pos, cnt);
-
-            move_range (&data[0], row_cnt, sizeof data[0],
-                        pos, new_pos, cnt);
-
-            check_datasheet (mc, ds, data, row_cnt, column_cnt);
-          }
-}
-
-/* "destroy" function for struct mc_class. */
-static void
-datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
-{
-  struct datasheet *ds = ds_;
-  datasheet_destroy (ds);
-}
 
-/* Executes the model checker on the datasheet test driver with
-   the given OPTIONS and passing in the given PARAMS, which must
-   point to a modifiable "struct datasheet_test_params".  If any
-   value in PARAMS is out of range, it will be adjusted into the
-   valid range before running the test.
 
-   Returns the results of the model checking run. */
-struct mc_results *
-datasheet_test (struct mc_options *options, void *params_)
-{
-  struct datasheet_test_params *params = params_;
-  static const struct mc_class datasheet_mc_class =
-    {
-      datasheet_mc_init,
-      datasheet_mc_mutate,
-      datasheet_mc_destroy,
-    };
-
-  params->next_value = 1;
-  params->max_rows = MIN (params->max_rows, MAX_ROWS);
-  params->max_cols = MIN (params->max_cols, MAX_COLS);
-  params->backing_rows = MIN (params->backing_rows, params->max_rows);
-  params->backing_cols = MIN (params->backing_cols, params->max_cols);
-
-  mc_options_set_aux (options, params);
-  return mc_run (&datasheet_mc_class, options);
-}
index 3d106193d1cd393bec14297a392bb68bac983604..860f236ec33af74b1ba8f53be1d5108a40644501 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ void datasheet_move_columns (struct datasheet *,
 /* Rows. */
 casenumber datasheet_get_row_cnt (const struct datasheet *);
 bool datasheet_insert_rows (struct datasheet *,
-                            casenumber before, struct ccase[],
+                            casenumber before, struct ccase *[],
                             casenumber cnt);
 void datasheet_delete_rows (struct datasheet *,
                             casenumber first, casenumber cnt);
@@ -59,28 +59,14 @@ void datasheet_move_rows (struct datasheet *,
                           size_t cnt);
 
 /* Data. */
-bool datasheet_get_row (const struct datasheet *, casenumber, struct ccase *);
+struct ccase *datasheet_get_row (const struct datasheet *, casenumber);
 bool datasheet_put_row (struct datasheet *, casenumber, struct ccase *);
 bool datasheet_get_value (const struct datasheet *, casenumber, size_t column,
                           union value *, int width);
 bool datasheet_put_value (struct datasheet *, casenumber, size_t column,
                           const union value *, int width);
 
-/* Testing. */
-struct mc_options;
-
-struct datasheet_test_params
-  {
-    /* Parameters. */
-    int max_rows;
-    int max_cols;
-    int backing_rows;
-    int backing_cols;
-
-    /* State. */
-    int next_value;
-  };
-
-struct mc_results *datasheet_test (struct mc_options *options, void *params);
 
+unsigned int hash_datasheet (const struct datasheet *ds);
+struct datasheet *clone_datasheet (const struct datasheet *ds);
 #endif /* data/datasheet.h */
index 526bb280a0956b70a24f4db3e32943f98701a7ec..ba840898112494b775d02c810edee29c98037fee 100644 (file)
 #include <stdlib.h>
 #include <ctype.h>
 
-#include "case.h"
-#include "category.h"
-#include "identifier.h"
-#include "settings.h"
-#include "value-labels.h"
-#include "vardict.h"
-#include "variable.h"
-#include "vector.h"
+#include <data/attributes.h>
+#include <data/case.h>
+#include <data/category.h>
+#include <data/identifier.h>
+#include <data/settings.h>
+#include <data/value-labels.h>
+#include <data/vardict.h>
+#include <data/variable.h>
+#include <data/vector.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
@@ -61,11 +62,25 @@ struct dictionary
     struct string documents;    /* Documents, as a string. */
     struct vector **vector;     /* Vectors of variables. */
     size_t vector_cnt;          /* Number of vectors. */
+    struct attrset attributes;  /* Custom attributes. */
     const struct dict_callbacks *callbacks; /* Callbacks on dictionary
                                               modification */
     void *cb_data ;                  /* Data passed to callbacks */
+
+    void (*changed) (struct dictionary *, void *); /* Generic change callback */
+    void *changed_data;
   };
 
+void
+dict_set_change_callback (struct dictionary *d,
+                         void (*changed) (struct dictionary *, void*),
+                         void *data)
+{
+  d->changed = changed;
+  d->changed_data = data;
+}
+
+
 /* Print a representation of dictionary D to stdout, for
    debugging purposes. */
 void
@@ -115,6 +130,7 @@ dict_create (void)
 
   d->name_tab = hsh_create (8, compare_vars_by_name, hash_var_by_name,
                             NULL, NULL);
+  attrset_init (&d->attributes);
   return d;
 }
 
@@ -178,6 +194,8 @@ dict_clone (const struct dictionary *s)
   for (i = 0; i < s->vector_cnt; i++)
     d->vector[i] = vector_clone (s->vector[i], s, d);
 
+  dict_set_attributes (d, dict_get_attributes (s));
+
   return d;
 }
 
@@ -208,6 +226,7 @@ dict_clear (struct dictionary *d)
   d->label = NULL;
   ds_destroy (&d->documents);
   dict_clear_vectors (d);
+  attrset_clear (&d->attributes);
 }
 
 /* Destroys the aux data for every variable in D, by calling
@@ -235,6 +254,7 @@ dict_destroy (struct dictionary *d)
 
       dict_clear (d);
       hsh_destroy (d->name_tab);
+      attrset_destroy (&d->attributes);
       free (d);
     }
 }
@@ -327,6 +347,7 @@ add_var (struct dictionary *d, struct variable *v)
   d->var[d->var_cnt++] = v;
   hsh_force_insert (d->name_tab, v);
 
+  if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_added )
     d->callbacks->var_added (d, var_get_dict_index (v), d->cb_data);
 
@@ -454,6 +475,7 @@ set_var_dict_index (struct variable *v, int dict_index)
   vdi.dict_index = dict_index;
   var_set_vardict (v, &vdi);
 
+  if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_changed )
     d->callbacks->var_changed (d, dict_index, d->cb_data);
 }
@@ -528,7 +550,7 @@ dict_delete_var (struct dictionary *d, struct variable *v)
   var_clear_vardict (v);
   var_destroy (v);
 
-
+  if ( d->changed ) d->changed (d, d->changed_data);
   if (d->callbacks &&  d->callbacks->var_deleted )
     d->callbacks->var_deleted (d, dict_index, case_index, val_cnt, d->cb_data);
 }
@@ -659,6 +681,7 @@ dict_rename_var (struct dictionary *d, struct variable *v,
   if (settings_get_algorithm () == ENHANCED)
     var_clear_short_names (v);
 
+  if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_changed )
     d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
 }
@@ -898,6 +921,7 @@ dict_set_weight (struct dictionary *d, struct variable *v)
 
   d->weight = v;
 
+  if (d->changed) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->weight_changed )
     d->callbacks->weight_changed (d,
                                  v ? var_get_dict_index (v) : -1,
@@ -926,6 +950,7 @@ dict_set_filter (struct dictionary *d, struct variable *v)
 
   d->filter = v;
 
+  if (d->changed) d->changed (d, d->changed_data);
   if ( d->callbacks && d->callbacks->filter_changed )
     d->callbacks->filter_changed (d,
                                  v ? var_get_dict_index (v) : -1,
@@ -1080,6 +1105,7 @@ dict_unset_split_var (struct dictionary *d, struct variable *v)
                                &v, compare_var_ptrs, NULL);
   if (orig_count != d->split_cnt)
     {
+      if (d->changed) d->changed (d, d->changed_data);
       /* We changed the set of split variables so invoke the
          callback. */
       if (d->callbacks &&  d->callbacks->split_changed)
@@ -1107,6 +1133,7 @@ dict_set_split_vars (struct dictionary *d,
     d->split = NULL;
    }
 
+  if (d->changed) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->split_changed )
     d->callbacks->split_changed (d, d->cb_data);
 }
@@ -1285,6 +1312,32 @@ dict_clear_vectors (struct dictionary *d)
   d->vector_cnt = 0;
 }
 
+/* Returns D's attribute set.  The caller may examine or modify
+   the attribute set, but must not destroy it.  Destroying D or
+   calling dict_set_attributes for D will also destroy D's
+   attribute set. */
+struct attrset *
+dict_get_attributes (const struct dictionary *d) 
+{
+  return (struct attrset *) &d->attributes;
+}
+
+/* Replaces D's attributes set by a copy of ATTRS. */
+void
+dict_set_attributes (struct dictionary *d, const struct attrset *attrs)
+{
+  attrset_destroy (&d->attributes);
+  attrset_clone (&d->attributes, attrs);
+}
+
+/* Returns true if D has at least one attribute in its attribute
+   set, false if D's attribute set is empty. */
+bool
+dict_has_attributes (const struct dictionary *d) 
+{
+  return attrset_count (&d->attributes) > 0;
+}
+
 /* Called from variable.c to notify the dictionary that some property of
    the variable has changed */
 void
@@ -1295,6 +1348,10 @@ dict_var_changed (const struct variable *v)
       const struct vardict_info *vdi = var_get_vardict (v);
       struct dictionary *d = vdi->dict;
 
+      if ( NULL == d)
+       return;
+
+      if (d->changed ) d->changed (d, d->changed_data);
       if ( d->callbacks && d->callbacks->var_changed )
        d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
     }
@@ -1315,7 +1372,27 @@ dict_var_resized (const struct variable *v, int delta)
 
       dict_pad_values (d, var_get_case_index(v) + 1, delta);
 
+      if (d->changed) d->changed (d, d->changed_data);
       if ( d->callbacks && d->callbacks->var_resized )
        d->callbacks->var_resized (d, var_get_dict_index (v), delta, d->cb_data);
     }
 }
+
+/* Called from variable.c to notify the dictionary that the variable's display width
+   has changed */
+void
+dict_var_display_width_changed (const struct variable *v)
+{
+  if ( var_has_vardict (v))
+    {
+      const struct vardict_info *vdi = var_get_vardict (v);
+      struct dictionary *d;
+
+      d = vdi->dict;
+
+      if (d->changed) d->changed (d, d->changed_data);
+      if ( d->callbacks && d->callbacks->var_display_width_changed )
+       d->callbacks->var_display_width_changed (d, var_get_dict_index (v), d->cb_data);
+    }
+}
+
index 0ab259d35b539f1635345a307572817944986d50..18bf3f781081a14fc23fe2d3568efa1e96b31a9c 100644 (file)
@@ -142,6 +142,11 @@ const struct vector *dict_lookup_vector (const struct dictionary *,
                                          const char *name);
 void dict_clear_vectors (struct dictionary *);
 
+/* Attributes. */
+struct attrset *dict_get_attributes (const struct dictionary *);
+void dict_set_attributes (struct dictionary *, const struct attrset *);
+bool dict_has_attributes (const struct dictionary *);
+
 /* Functions to be called upon dictionary changes. */
 struct dict_callbacks
  {
@@ -152,12 +157,18 @@ struct dict_callbacks
   void (*weight_changed) (struct dictionary *, int, void *);
   void (*filter_changed) (struct dictionary *, int, void *);
   void (*split_changed) (struct dictionary *, void *);
+  void (*var_display_width_changed) (struct dictionary *, int, void *);
  };
 
 void dict_set_callbacks (struct dictionary *, const struct dict_callbacks *,
                         void *);
 void dict_copy_callbacks (struct dictionary *, const struct dictionary *);
 
+void dict_set_change_callback (struct dictionary *d,
+                              void (*changed) (struct dictionary *, void*),
+                              void *data);
+
+
 /* Debug use only. */
 void dict_dump (const struct dictionary *);
 
index 48a27876a76c16352cbce667000fbfe532bd4b62..23249f6a85c10f261e04a6efd20551c926357e9d 100644 (file)
@@ -17,8 +17,8 @@
 #ifndef FORMAT_GUESSER_H
 #define FORMAT_GUESSER_H 1
 
+#include <libpspp/str.h>
 struct fmt_spec;
-struct substring;
 
 struct fmt_guesser *fmt_guesser_create (void);
 void fmt_guesser_destroy (struct fmt_guesser *);
index 522176640489c87ec57027115582cbb9bc26d617..5a0c75ede293a21052228a87b295a070398f3e1f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -60,8 +60,7 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 
 static void gnm_file_casereader_destroy (struct casereader *, void *);
 
-static bool gnm_file_casereader_read (struct casereader *, void *,
-                                     struct ccase *);
+static struct ccase *gnm_file_casereader_read (struct casereader *, void *);
 
 static const struct casereader_class gnm_file_casereader_class =
   {
@@ -172,7 +171,7 @@ struct gnumeric_reader
 
   size_t value_cnt;
   struct dictionary *dict;
-  struct ccase first_case;
+  struct ccase *first_case;
   bool used_first_case;
 };
 
@@ -193,7 +192,7 @@ gnm_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
     xmlFreeTextReader (r->xtr);
 
   if ( ! r->used_first_case )
-    case_destroy (&r->first_case);
+    case_unref (r->first_case);
 
   free (r);
 }
@@ -537,15 +536,15 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
       goto error;
     }
 
-  case_create (&r->first_case, r->value_cnt);
-  memset (case_data_rw_idx (&r->first_case, 0)->s,
+  r->first_case = case_create (r->value_cnt);
+  memset (case_data_rw_idx (r->first_case, 0)->s,
          ' ', MAX_SHORT_STRING * r->value_cnt);
 
   for ( i = 0 ; i < n_var_specs ; ++i )
     {
       const struct variable *var = dict_get_var (r->dict, i);
 
-      convert_xml_string_to_value (&r->first_case, var,
+      convert_xml_string_to_value (r->first_case, var,
                                   var_spec[i].first_value);
     }
 
@@ -580,12 +579,12 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 };
 
 
-/* Reads one case from READER's file into C.  Returns true only
-   if successful. */
-static bool
-gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_,
-                          struct ccase *c)
+/* Reads and returns one case from READER's file.  Returns a null
+   pointer on failure. */
+static struct ccase *
+gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_)
 {
+  struct ccase *c;
   int ret = 0;
 
   struct gnumeric_reader *r = r_;
@@ -593,12 +592,11 @@ gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_,
 
   if ( !r->used_first_case )
     {
-      *c = r->first_case;
       r->used_first_case = true;
-      return true;
+      return r->first_case;
     }
 
-  case_create (c, r->value_cnt);
+  c = case_create (r->value_cnt);
 
   memset (case_data_rw_idx (c, 0)->s, ' ', MAX_SHORT_STRING * r->value_cnt);
 
@@ -632,7 +630,13 @@ gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_,
 
     }
 
-  return (ret == 1);
+  if (ret == 1)
+    return c;
+  else
+    {
+      case_unref (c);
+      return NULL;
+    }
 }
 
 
index 2cb8911fe29770559eee3c7c2ea702e88a38b9f5..65d10aea2477640a826077a7da01c17f78fbf665 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -117,13 +117,12 @@ instantiate_lazy_casereader (struct casereader *reader,
   casereader_destroy (subreader);
 }
 
-static bool
-lazy_casereader_read (struct casereader *reader, void *lc_,
-                      struct ccase *c)
+static struct ccase *
+lazy_casereader_read (struct casereader *reader, void *lc_)
 {
   struct lazy_casereader *lc = lc_;
   instantiate_lazy_casereader (reader, lc);
-  return casereader_read (reader, c);
+  return casereader_read (reader);
 }
 
 static void
@@ -143,13 +142,12 @@ lazy_casereader_clone (struct casereader *reader, void *lc_)
   return casereader_clone (reader);
 }
 
-static bool
-lazy_casereader_peek (struct casereader *reader, void *lc_,
-                      casenumber idx, struct ccase *c)
+static struct ccase *
+lazy_casereader_peek (struct casereader *reader, void *lc_, casenumber idx)
 {
   struct lazy_casereader *lc = lc_;
   instantiate_lazy_casereader (reader, lc);
-  return casereader_peek (reader, idx, c);
+  return casereader_peek (reader, idx);
 }
 
 static const struct casereader_class lazy_casereader_class =
index 52c0cc8edab5dca9a59b142d83cf9c3466489a3e..7e65c28bdf51c11b54f1f6ddd9eced26670b6197 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -814,28 +814,30 @@ read_documents (struct pfm_reader *r, struct dictionary *dict)
     }
 }
 
-/* Reads one case from portable file R into C. */
-static bool
-por_file_casereader_read (struct casereader *reader, void *r_, struct ccase *c)
+/* Reads and returns one case from portable file R.  Returns a
+   null pointer on failure. */
+static struct ccase *
+por_file_casereader_read (struct casereader *reader, void *r_)
 {
   struct pfm_reader *r = r_;
+  struct ccase *volatile c;
   size_t i;
   size_t idx;
 
-  case_create (c, casereader_get_value_cnt (reader));
+  c = case_create (casereader_get_value_cnt (reader));
   setjmp (r->bail_out);
   if (!r->ok)
     {
       casereader_force_error (reader);
-      case_destroy (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 
   /* Check for end of file. */
   if (r->cc == 'Z')
     {
-      case_destroy (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 
   idx = 0;
@@ -857,7 +859,7 @@ por_file_casereader_read (struct casereader *reader, void *r_, struct ccase *c)
         }
     }
 
-  return true;
+  return c;
 }
 
 /* Returns true if FILE is an SPSS portable file,
index f227fd9a7761b1381c04c3598281cad5de114aae..326514f9a78e58adc9ebd46205565a0262e394f5 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -34,7 +34,6 @@ struct pfm_read_info
 
 struct dictionary;
 struct file_handle;
-struct ccase;
 struct casereader *pfm_open_reader (struct file_handle *,
                                     struct dictionary **,
                                     struct pfm_read_info *);
index a50563ab964275df30365263005dbd1ded5262c9..8de293c5eafb2904d065db08f7779c9980f4e805 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -464,7 +464,7 @@ por_file_casewriter_write (struct casewriter *writer, void *w_,
   else
     casewriter_force_error (writer);
 
-  case_destroy (c);
+  case_unref (c);
 }
 
 static void
index b018a4f4206ad47611823c81891a5a30c33d8b6e..04eaaa2c724865a9887c043f7b3a7bb49ed71f4a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -38,7 +38,6 @@ struct pfm_write_options
 
 struct file_handle;
 struct dictionary;
-struct ccase;
 struct casewriter *pfm_open_writer (struct file_handle *, struct dictionary *,
                                     struct pfm_write_options);
 struct pfm_write_options pfm_writer_default_options (void);
index 98414a2b0478dbb5e44f306e0f99d90684b86003..32839e6413356c42b61d238a6634c7c1894775ca 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -79,7 +79,7 @@ struct dataset {
   /* Cases just before ("lagging") the current one. */
   int n_lag;                   /* Number of cases to lag. */
   struct deque lag;             /* Deque of lagged cases. */
-  struct ccase *lag_cases;      /* Lagged cases managed by deque. */
+  struct ccase **lag_cases;     /* Lagged cases managed by deque. */
 
   /* Procedure data. */
   enum
@@ -92,6 +92,10 @@ struct dataset {
   proc_state;
   casenumber cases_written;       /* Cases output so far. */
   bool ok;                    /* Error status. */
+
+  void (*callback) (void *); /* Callback for when the dataset changes */
+  void *cb_data;
+
 }; /* struct dataset */
 
 
@@ -99,9 +103,24 @@ static void add_case_limit_trns (struct dataset *ds);
 static void add_filter_trns (struct dataset *ds);
 
 static void update_last_proc_invocation (struct dataset *ds);
+
+static void
+dataset_set_unsaved (const struct dataset *ds)
+{
+  if (ds->callback) ds->callback (ds->cb_data);
+}
+
 \f
 /* Public functions. */
 
+void
+dataset_set_callback (struct dataset *ds, void (*cb) (void *), void *cb_data)
+{
+  ds->callback = cb;
+  ds->cb_data = cb_data;
+}
+
+
 /* Returns the last time the data was read. */
 time_t
 time_of_last_procedure (struct dataset *ds)
@@ -203,15 +222,15 @@ proc_is_open (const struct dataset *ds)
 }
 
 /* "read" function for procedure casereader. */
-static bool
-proc_casereader_read (struct casereader *reader UNUSED, void *ds_,
-                      struct ccase *c)
+static struct ccase *
+proc_casereader_read (struct casereader *reader UNUSED, void *ds_)
 {
   struct dataset *ds = ds_;
   enum trns_result retval = TRNS_DROP_CASE;
+  struct ccase *c;
 
   assert (ds->proc_state == PROC_OPEN);
-  for (;;)
+  for (; ; case_unref (c))
     {
       casenumber case_nr;
 
@@ -219,58 +238,47 @@ proc_casereader_read (struct casereader *reader UNUSED, void *ds_,
       if (retval == TRNS_ERROR)
         ds->ok = false;
       if (!ds->ok)
-        return false;
+        return NULL;
 
       /* Read a case from source. */
-      if (!casereader_read (ds->source, c))
-        return false;
-      case_resize (c, dict_get_next_value_idx (ds->dict));
+      c = casereader_read (ds->source);
+      if (c == NULL)
+        return NULL;
+      c = case_unshare_and_resize (c, dict_get_next_value_idx (ds->dict));
       caseinit_init_vars (ds->caseinit, c);
 
       /* Execute permanent transformations.  */
       case_nr = ds->cases_written + 1;
       retval = trns_chain_execute (ds->permanent_trns_chain, TRNS_CONTINUE,
-                                   c, case_nr);
+                                   &c, case_nr);
       caseinit_update_left_vars (ds->caseinit, c);
       if (retval != TRNS_CONTINUE)
-        {
-          case_destroy (c);
-          continue;
-        }
+        continue;
 
       /* Write case to collection of lagged cases. */
       if (ds->n_lag > 0)
         {
           while (deque_count (&ds->lag) >= ds->n_lag)
-            case_destroy (&ds->lag_cases[deque_pop_back (&ds->lag)]);
-          case_clone (&ds->lag_cases[deque_push_front (&ds->lag)], c);
+            case_unref (ds->lag_cases[deque_pop_back (&ds->lag)]);
+          ds->lag_cases[deque_push_front (&ds->lag)] = case_ref (c);
         }
 
       /* Write case to replacement active file. */
       ds->cases_written++;
       if (ds->sink != NULL)
-        {
-          struct ccase tmp;
-          if (ds->compactor != NULL)
-            case_map_execute (ds->compactor, c, &tmp);
-          else
-            case_clone (&tmp, c);
-          casewriter_write (ds->sink, &tmp);
-        }
+        casewriter_write (ds->sink,
+                          case_map_execute (ds->compactor, case_ref (c)));
 
       /* Execute temporary transformations. */
       if (ds->temporary_trns_chain != NULL)
         {
           retval = trns_chain_execute (ds->temporary_trns_chain, TRNS_CONTINUE,
-                                       c, ds->cases_written);
+                                       &c, ds->cases_written);
           if (retval != TRNS_CONTINUE)
-            {
-              case_destroy (c);
-              continue;
-            }
+            continue;
         }
 
-      return true;
+      return c;
     }
 }
 
@@ -279,13 +287,13 @@ static void
 proc_casereader_destroy (struct casereader *reader, void *ds_)
 {
   struct dataset *ds = ds_;
-  struct ccase c;
+  struct ccase *c;
 
   /* Make sure transformations happen for every input case, in
      case they have side effects, and ensure that the replacement
      active file gets all the cases it should. */
-  while (casereader_read (reader, &c))
-    case_destroy (&c);
+  while ((c = casereader_read (reader)) != NULL)
+    case_unref (c);
 
   ds->proc_state = PROC_CLOSED;
   ds->ok = casereader_destroy (ds->source) && ds->ok;
@@ -304,9 +312,11 @@ proc_commit (struct dataset *ds)
   assert (ds->proc_state == PROC_CLOSED);
   ds->proc_state = PROC_COMMITTED;
 
+  dataset_set_unsaved (ds);
+
   /* Free memory for lagged cases. */
   while (!deque_is_empty (&ds->lag))
-    case_destroy (&ds->lag_cases[deque_pop_back (&ds->lag)]);
+    case_unref (ds->lag_cases[deque_pop_back (&ds->lag)]);
   free (ds->lag_cases);
 
   /* Dictionary from before TEMPORARY becomes permanent. */
@@ -361,14 +371,14 @@ update_last_proc_invocation (struct dataset *ds)
 \f
 /* Returns a pointer to the lagged case from N_BEFORE cases before the
    current one, or NULL if there haven't been that many cases yet. */
-struct ccase *
+const struct ccase *
 lagged_case (const struct dataset *ds, int n_before)
 {
   assert (n_before >= 1);
   assert (n_before <= ds->n_lag);
 
   if (n_before <= deque_count (&ds->lag))
-    return &ds->lag_cases[deque_front (&ds->lag, n_before - 1)];
+    return ds->lag_cases[deque_front (&ds->lag, n_before - 1)];
   else
     return NULL;
 }
@@ -521,12 +531,23 @@ proc_cancel_all_transformations (struct dataset *ds)
   return ok;
 }
 \f
+
+static void
+dict_callback (struct dictionary *d UNUSED, void *ds_)
+{
+  struct dataset *ds = ds_;
+  dataset_set_unsaved (ds);
+}
+
 /* Initializes procedure handling. */
 struct dataset *
 create_dataset (void)
 {
   struct dataset *ds = xzalloc (sizeof(*ds));
   ds->dict = dict_create ();
+
+  dict_set_change_callback (ds->dict, dict_callback, ds);
+
   ds->caseinit = caseinit_create ();
   proc_cancel_all_transformations (ds);
   return ds;
@@ -596,6 +617,7 @@ proc_set_active_file (struct dataset *ds,
 
   dict_destroy (ds->dict);
   ds->dict = dict;
+  dict_set_change_callback (ds->dict, dict_callback, ds);
 
   proc_set_active_file_data (ds, source);
 }
@@ -679,7 +701,7 @@ add_case_limit_trns (struct dataset *ds)
    *CASES_REMAINING. */
 static int
 case_limit_trns_proc (void *cases_remaining_,
-                      struct ccase *c UNUSED, casenumber case_nr UNUSED)
+                      struct ccase **c UNUSED, casenumber case_nr UNUSED)
 {
   size_t *cases_remaining = cases_remaining_;
   if (*cases_remaining > 0)
@@ -718,11 +740,11 @@ add_filter_trns (struct dataset *ds)
 /* FILTER transformation. */
 static int
 filter_trns_proc (void *filter_var_,
-                  struct ccase *c UNUSED, casenumber case_nr UNUSED)
+                  struct ccase **c UNUSED, casenumber case_nr UNUSED)
 
 {
   struct variable *filter_var = filter_var_;
-  double f = case_num (c, filter_var);
+  double f = case_num (*c, filter_var);
   return (f != 0.0 && !var_is_num_missing (filter_var, f, MV_ANY)
           ? TRNS_CONTINUE : TRNS_DROP_CASE);
 }
index 336725dfdeca934a1a315db32a666d5a17c7ac4f..09d44eeef612bdda52072acbf8be40a8982f9a46 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -82,7 +82,9 @@ struct dictionary *dataset_dict (const struct dataset *ds);
 const struct casereader *dataset_source (const struct dataset *ds);
 
 
-struct ccase *lagged_case (const struct dataset *ds, int n_before);
+const struct ccase *lagged_case (const struct dataset *ds, int n_before);
 void dataset_need_lag (struct dataset *ds, int n_before);
 
+void dataset_set_callback (struct dataset *ds, void (*cb) (void *), void *);
+
 #endif /* procedure.h */
index 9df82b558c69252930fdf932e0ed5575edd3cfa8..be6d0a60759c45746ea01b660ca35e8f1770064b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@
 
 #if !PSQL_SUPPORT
 struct casereader *
-psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
+psql_open_reader (struct psql_read_info *info UNUSED, struct dictionary **dict UNUSED)
 {
   msg (ME, _("Support for reading postgres databases was not compiled into this installation of PSPP"));
 
@@ -75,8 +75,7 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
 
 static void psql_casereader_destroy (struct casereader *reader UNUSED, void *r_);
 
-static bool psql_casereader_read (struct casereader *, void *,
-                                 struct ccase *);
+static struct ccase *psql_casereader_read (struct casereader *, void *);
 
 static const struct casereader_class psql_casereader_class =
   {
@@ -109,8 +108,7 @@ struct psql_reader
 };
 
 
-static bool set_value (struct psql_reader *r,
-                      struct ccase *cc);
+static struct ccase *set_value (struct psql_reader *r);
 
 
 
@@ -550,9 +548,8 @@ psql_casereader_destroy (struct casereader *reader UNUSED, void *r_)
 
 
 
-static bool
-psql_casereader_read (struct casereader *reader UNUSED, void *r_,
-                     struct ccase *cc)
+static struct ccase *
+psql_casereader_read (struct casereader *reader UNUSED, void *r_)
 {
   struct psql_reader *r = r_;
 
@@ -562,24 +559,24 @@ psql_casereader_read (struct casereader *reader UNUSED, void *r_,
        return false;
     }
 
-  return set_value (r, cc);
+  return set_value (r);
 }
 
-static bool
-set_value (struct psql_reader *r,
-          struct ccase *c)
+static struct ccase *
+set_value (struct psql_reader *r)
 {
-  int i;
+  struct ccase *c;
   int n_vars;
+  int i;
 
   assert (r->res);
 
   n_vars = PQnfields (r->res);
 
   if ( r->tuple >= PQntuples (r->res))
-    return false;
+    return NULL;
 
-  case_create (c, r->value_cnt);
+  c = case_create (r->value_cnt);
   memset (case_data_rw_idx (c, 0)->s, ' ', MAX_SHORT_STRING * r->value_cnt);
 
 
@@ -874,7 +871,7 @@ set_value (struct psql_reader *r,
 
   r->tuple++;
 
-  return true;
+  return c;
 }
 
 #endif
index 44531a494f5465bd439aec03bcd6044e640581c1..a5ee005efda9b521db198cdbc4dda387c1fa2646 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
 
 struct dictionary;
 struct file_handle;
-struct ccase;
 struct casereader *scratch_reader_open (struct file_handle *,
                                         struct dictionary **);
 
index ed88af6db4ee1a211443702de9545c251546abc7..952860cdc5887e00104be1239f089f3eaf2c71b8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -99,15 +99,8 @@ scratch_writer_casewriter_write (struct casewriter *w UNUSED, void *writer_,
                                  struct ccase *c)
 {
   struct scratch_writer *writer = writer_;
-  struct ccase tmp;
-  if (writer->compactor)
-    {
-      case_map_execute (writer->compactor, c, &tmp);
-      case_destroy (c);
-    }
-  else
-    case_move (&tmp, c);
-  casewriter_write (writer->subwriter, &tmp);
+  casewriter_write (writer->subwriter,
+                    case_map_execute (writer->compactor, c));
 }
 
 /* Closes WRITER. */
index d2fda12a915c85341d304974b9eee75cc555948f..a9c7a4d9c1e8dfd96974aa142eb44a014e1dc0c8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
 
 struct dictionary;
 struct file_handle;
-struct ccase;
 struct casewriter *scratch_writer_open (struct file_handle *,
                                         const struct dictionary *);
 
index a87e3eff800f78a552232125fbec7af7ebe67527..ad509fb028059a6c6d3af86445a1bdb9e744ce08 100644 (file)
@@ -149,6 +149,8 @@ settings_init (int *width, int *length)
   settings_set_epoch (-1);
   i18n_init ();
   the_settings.styles = fmt_create ();
+
+  settings_set_decimal_char (get_system_decimal ());
 }
 
 void
index e39a63a0fefc766546362d7e1bb412454539f10f..2f2bbe99b46edd2ad28e5bebb75015581b3267f9 100644 (file)
@@ -19,6 +19,9 @@
 
 #include <stdbool.h>
 #include <stddef.h>
+#include <data/format.h>
+#include <libpspp/float-format.h>
+#include <libpspp/integer-format.h>
 
 struct settings;
 
index 1f3fb092a12b77a0601cdce75717a4e87d883a24..13fcf7888feae74f2a2489f7ea5df3c678807a71 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <data/case.h>
 #include <data/settings.h>
 #include <data/case-tmpfile.h>
 #include <libpspp/assertion.h>
@@ -49,7 +50,7 @@ sparse_cases_create (size_t column_cnt)
   sc->column_cnt = column_cnt;
   sc->default_columns = NULL;
   sc->max_memory_cases = settings_get_workspace_cases (column_cnt);
-  sc->memory = sparse_array_create (sizeof (struct ccase));
+  sc->memory = sparse_array_create (sizeof (struct ccase *));
   sc->disk = NULL;
   sc->disk_cases = NULL;
   return sc;
@@ -76,12 +77,15 @@ sparse_cases_clone (const struct sparse_cases *old)
   if (old->memory != NULL)
     {
       unsigned long int idx;
-      struct ccase *c;
+      struct ccase **cp;
 
-      new->memory = sparse_array_create (sizeof (struct ccase));
-      for (c = sparse_array_scan (old->memory, NULL, &idx); c != NULL;
-           c = sparse_array_scan (old->memory, &idx, &idx))
-        case_clone (sparse_array_insert (new->memory, idx), c);
+      new->memory = sparse_array_create (sizeof (struct ccase *));
+      for (cp = sparse_array_scan (old->memory, NULL, &idx); cp != NULL;
+           cp = sparse_array_scan (old->memory, &idx, &idx))
+        {
+          struct ccase **ncp = sparse_array_insert (new->memory, idx);
+          *ncp = case_ref (*cp);
+        }
     }
   else
     new->memory = NULL;
@@ -98,15 +102,16 @@ sparse_cases_clone (const struct sparse_cases *old)
           unsigned long int start = range_set_node_get_start (node);
           unsigned long int end = range_set_node_get_end (node);
           unsigned long int idx;
-          struct ccase c;
-
-          for (idx = start; idx < end; idx++)
-            if (!case_tmpfile_get_case (old->disk, idx, &c)
-                || !case_tmpfile_put_case (new->disk, idx, &c))
-              {
-                sparse_cases_destroy (new);
-                return NULL;
-              }
+
+          for (idx = start; idx < end; idx++) 
+            {
+              struct ccase *c = case_tmpfile_get_case (old->disk, idx);
+              if (c == NULL || !case_tmpfile_put_case (new->disk, idx, c))
+                {
+                  sparse_cases_destroy (new);
+                  return NULL;
+                }
+            }
         }
     }
   else
@@ -127,10 +132,10 @@ sparse_cases_destroy (struct sparse_cases *sc)
       if (sc->memory != NULL)
         {
           unsigned long int idx;
-          struct ccase *c;
-          for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
-               c = sparse_array_scan (sc->memory, &idx, &idx))
-            case_destroy (c);
+          struct ccase **cp;
+          for (cp = sparse_array_scan (sc->memory, NULL, &idx); cp != NULL;
+               cp = sparse_array_scan (sc->memory, &idx, &idx))
+            case_unref (*cp);
           sparse_array_destroy (sc->memory);
         }
       free (sc->default_columns);
@@ -154,7 +159,7 @@ static bool
 dump_sparse_cases_to_disk (struct sparse_cases *sc)
 {
   unsigned long int idx;
-  struct ccase *c;
+  struct ccase **cp;
 
   assert (sc->memory != NULL);
   assert (sc->disk == NULL);
@@ -162,10 +167,10 @@ dump_sparse_cases_to_disk (struct sparse_cases *sc)
   sc->disk = case_tmpfile_create (sc->column_cnt);
   sc->disk_cases = range_set_create ();
 
-  for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
-       c = sparse_array_scan (sc->memory, &idx, &idx))
+  for (cp = sparse_array_scan (sc->memory, NULL, &idx); cp != NULL;
+       cp = sparse_array_scan (sc->memory, &idx, &idx))
     {
-      if (!case_tmpfile_put_case (sc->disk, idx, c))
+      if (!case_tmpfile_put_case (sc->disk, idx, *cp))
         {
           case_tmpfile_destroy (sc->disk);
           sc->disk = NULL;
@@ -202,13 +207,20 @@ sparse_cases_read (struct sparse_cases *sc, casenumber row, size_t column,
 
   if (sparse_cases_contains_row (sc, row))
     {
-      struct ccase c;
+      struct ccase *c;
       if (sc->memory != NULL)
-        case_clone (&c, sparse_array_get (sc->memory, row));
-      else if (!case_tmpfile_get_case (sc->disk, row, &c))
-        return false;
-      case_copy_out (&c, column, values, value_cnt);
-      case_destroy (&c);
+        {
+          struct ccase **cp = sparse_array_get (sc->memory, row);
+          c = case_ref (*cp);
+        }
+      else
+        {
+          c = case_tmpfile_get_case (sc->disk, row);
+          if (c == NULL)
+            return false;
+        }
+      case_copy_out (c, column, values, value_cnt);
+      case_unref (c);
     }
   else
     {
@@ -225,20 +237,24 @@ static bool
 write_disk_case (struct sparse_cases *sc, casenumber row, size_t column,
                  const union value values[], size_t value_cnt)
 {
-  struct ccase c;
+  struct ccase *c;
   bool ok;
 
   /* Get current case data. */
   if (column == 0 && value_cnt == sc->column_cnt)
-    case_create (&c, sc->column_cnt);
-  else if (!case_tmpfile_get_case (sc->disk, row, &c))
-    return false;
+    c = case_create (sc->column_cnt);
+  else
+    {
+      c = case_tmpfile_get_case (sc->disk, row);
+      if (c == NULL)
+        return false;
+    }
 
   /* Copy in new data. */
-  case_copy_in (&c, column, values, value_cnt);
+  case_copy_in (c, column, values, value_cnt);
 
   /* Write new case. */
-  ok = case_tmpfile_put_case (sc->disk, row, &c);
+  ok = case_tmpfile_put_case (sc->disk, row, c);
   if (ok)
     range_set_insert (sc->disk_cases, row, 1);
 
@@ -255,8 +271,11 @@ sparse_cases_write (struct sparse_cases *sc, casenumber row, size_t column,
 {
   if (sc->memory != NULL)
     {
-      struct ccase *c = sparse_array_get (sc->memory, row);
-      if (c == NULL)
+      struct ccase *c, **cp;
+      cp = sparse_array_get (sc->memory, row);
+      if (cp != NULL)
+        c = *cp = case_unshare (*cp);
+      else
         {
           if (sparse_array_count (sc->memory) >= sc->max_memory_cases)
             {
@@ -265,8 +284,8 @@ sparse_cases_write (struct sparse_cases *sc, casenumber row, size_t column,
               return write_disk_case (sc, row, column, values, value_cnt);
             }
 
-          c = sparse_array_insert (sc->memory, row);
-          case_create (c, sc->column_cnt);
+          cp = sparse_array_insert (sc->memory, row);
+          c = *cp = case_create (sc->column_cnt);
           if (sc->default_columns != NULL
               && (column != 0 || value_cnt != sc->column_cnt))
             case_copy_in (c, 0, sc->default_columns, sc->column_cnt);
@@ -302,12 +321,15 @@ sparse_cases_write_columns (struct sparse_cases *sc, size_t start_column,
   /* Set individual rows. */
   if (sc->memory != NULL)
     {
-      struct ccase *c;
+      struct ccase **cp;
       unsigned long int idx;
 
-      for (c = sparse_array_scan (sc->memory, NULL, &idx); c != NULL;
-           c = sparse_array_scan (sc->memory, &idx, &idx))
-        case_copy_in (c, start_column, values, value_cnt);
+      for (cp = sparse_array_scan (sc->memory, NULL, &idx); cp != NULL;
+           cp = sparse_array_scan (sc->memory, &idx, &idx))
+        {
+          *cp = case_unshare (*cp);
+          case_copy_in (*cp, start_column, values, value_cnt);
+        }
     }
   else
     {
diff --git a/src/data/subcase.c b/src/data/subcase.c
new file mode 100644 (file)
index 0000000..d4b1378
--- /dev/null
@@ -0,0 +1,320 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 <data/subcase.h>
+#include <stdlib.h>
+#include <data/case.h>
+#include <data/variable.h>
+#include <libpspp/assertion.h>
+
+#include "xalloc.h"
+
+/* Initializes SC as a subcase that contains no fields. */
+void
+subcase_init_empty (struct subcase *sc)
+{
+  sc->fields = NULL;
+  sc->n_fields = 0;
+  sc->n_values = 0;
+}
+
+/* Initializes SC as a subcase with fields extracted from the
+   N_VARS variables in VARS, with ascending sort order. */
+void
+subcase_init_vars (struct subcase *sc,
+                   const struct variable *const *vars, size_t n_vars)
+{
+  size_t i;
+
+  sc->fields = xnmalloc (n_vars, sizeof *sc->fields);
+  sc->n_fields = n_vars;
+  sc->n_values = 0;
+  for (i = 0; i < n_vars; i++)
+    {
+      struct subcase_field *field = &sc->fields[i];
+      field->case_index = var_get_case_index (vars[i]);
+      field->width = var_get_width (vars[i]);
+      field->direction = SC_ASCEND;
+      sc->n_values += value_cnt_from_width (field->width);
+    }
+}
+
+/* Initializes SC as a subcase with a single field extracted
+   from VAR, with the sort order specified by DIRECTION.  */
+void
+subcase_init_var (struct subcase *sc, const struct variable *var,
+                  enum subcase_direction direction)
+{
+  subcase_init_empty (sc);
+  subcase_add_var (sc, var, direction);
+}
+
+/* Removes all the fields from SC. */
+void
+subcase_clear (struct subcase *sc)
+{
+  sc->n_fields = 0;
+  sc->n_values = 0;
+}
+
+/* Initializes SC with the same fields as ORIG. */
+void
+subcase_clone (struct subcase *sc, const struct subcase *orig)
+{
+  sc->fields = xmemdup (orig->fields, orig->n_fields * sizeof *orig->fields);
+  sc->n_fields = orig->n_fields;
+  sc->n_values = orig->n_values;
+}
+
+/* Frees the memory owned by SC (but not SC itself). */
+void
+subcase_destroy (struct subcase *sc)
+{
+  free (sc->fields);
+}
+
+/* Add a field for VAR to SC, with DIRECTION as the sort order.
+   Returns true if successful, false if VAR already has a field
+   in SC. */
+bool
+subcase_add_var (struct subcase *sc, const struct variable *var,
+                 enum subcase_direction direction)
+{
+  size_t case_index = var_get_case_index (var);
+  struct subcase_field *field;
+  size_t i;
+
+  for (i = 0; i < sc->n_fields; i++)
+    if (sc->fields[i].case_index == case_index)
+      return false;
+
+  sc->fields = xnrealloc (sc->fields, sc->n_fields + 1, sizeof *sc->fields);
+  field = &sc->fields[sc->n_fields++];
+  field->case_index = case_index;
+  field->width = var_get_width (var);
+  field->direction = direction;
+  sc->n_values += value_cnt_from_width (field->width);
+  return true;
+}
+
+/* Returns true if and only if A and B are conformable, which
+   means that they have the same number of fields and that each
+   corresponding field in A and B have the same width. */
+bool
+subcase_conformable (const struct subcase *a, const struct subcase *b)
+{
+  size_t i;
+
+  if (a == b)
+    return true;
+  if (a->n_values != b->n_values || a->n_fields != b->n_fields)
+    return false;
+  for (i = 0; i < a->n_fields; i++)
+    if (a->fields[i].width != b->fields[i].width)
+      return false;
+  return true;
+}
+
+/* Copies the fields represented by SC from C into VALUES.
+   VALUES must have space for at least subcase_get_n_values(SC)
+   array elements. */
+void
+subcase_extract (const struct subcase *sc, const struct ccase *c,
+                 union value values[])
+{
+  size_t i;
+
+  for (i = 0; i < sc->n_fields; i++)
+    {
+      const struct subcase_field *field = &sc->fields[i];
+      value_copy (values, case_data_idx (c, field->case_index), field->width);
+      values += value_cnt_from_width (field->width);
+    }
+}
+
+/* Copies the data in VALUES into the fields in C represented by
+   SC.  VALUES must have at least subcase_get_n_values(SC) array
+   elements, and C must be large enough to contain all the fields
+   in SC. */
+void
+subcase_inject (const struct subcase *sc,
+                const union value values[], struct ccase *c)
+{
+  size_t i;
+
+  for (i = 0; i < sc->n_fields; i++)
+    {
+      const struct subcase_field *field = &sc->fields[i];
+      value_copy (case_data_rw_idx (c, field->case_index), values,
+                  field->width);
+      values += value_cnt_from_width (field->width);
+    }
+}
+
+/* Copies the fields in SRC represented by SRC_SC into the
+   corresponding fields in DST respresented by DST_SC.  SRC_SC
+   and DST_SC must be conformable (as tested by
+   subcase_conformable()).
+
+   DST must not be shared. */
+void
+subcase_copy (const struct subcase *src_sc, const struct ccase *src,
+              const struct subcase *dst_sc, struct ccase *dst)
+{
+  size_t i;
+
+  expensive_assert (subcase_conformable (src_sc, dst_sc));
+  for (i = 0; i < src_sc->n_fields; i++)
+    {
+      const struct subcase_field *src_field = &src_sc->fields[i];
+      const struct subcase_field *dst_field = &dst_sc->fields[i];
+      value_copy (case_data_rw_idx (dst, dst_field->case_index),
+                  case_data_idx (src, src_field->case_index),
+                  src_field->width);
+    }
+}
+
+/* Compares the fields in A specified in A_SC against the fields
+   in B specified in B_SC.  Returns -1, 0, or 1 if A's fields are
+   lexicographically less than, equal to, or greater than B's
+   fields, respectively.
+
+   A_SC and B_SC must be conformable (as tested by
+   subcase_conformable()). */
+int
+subcase_compare_3way (const struct subcase *a_sc, const struct ccase *a,
+                      const struct subcase *b_sc, const struct ccase *b)
+{
+  size_t i;
+
+  expensive_assert (subcase_conformable (a_sc, b_sc));
+  for (i = 0; i < a_sc->n_fields; i++)
+    {
+      const struct subcase_field *a_field = &a_sc->fields[i];
+      const struct subcase_field *b_field = &b_sc->fields[i];
+      int cmp = value_compare_3way (case_data_idx (a, a_field->case_index),
+                                    case_data_idx (b, b_field->case_index),
+                                    a_field->width);
+      if (cmp != 0)
+        return a_field->direction == SC_ASCEND ? cmp : -cmp;
+    }
+  return 0;
+}
+
+/* Compares the values in A against the values in B specified by
+   SC's fields.  Returns -1, 0, or 1 if A's values are
+   lexicographically less than, equal to, or greater than B's
+   values, respectively. */
+int
+subcase_compare_3way_xc (const struct subcase *sc,
+                         const union value a[], const struct ccase *b)
+{
+  size_t i;
+
+  for (i = 0; i < sc->n_fields; i++)
+    {
+      const struct subcase_field *field = &sc->fields[i];
+      int cmp = value_compare_3way (a, case_data_idx (b, field->case_index),
+                                    field->width);
+      if (cmp != 0)
+        return field->direction == SC_ASCEND ? cmp : -cmp;
+      a += value_cnt_from_width (field->width);
+    }
+  return 0;
+}
+
+/* Compares the values in A specified by SC's fields against the
+   values in B.  Returns -1, 0, or 1 if A's values are
+   lexicographically less than, equal to, or greater than B's
+   values, respectively. */
+int
+subcase_compare_3way_cx (const struct subcase *sc,
+                         const struct ccase *a, const union value b[])
+{
+  return -subcase_compare_3way_xc (sc, b, a);
+}
+
+/* Compares the values in A against the values in B, using SC to
+   obtain the number and width of each value.  Returns -1, 0, or
+   1 if A's values are lexicographically less than, equal to, or
+   greater than B's values, respectively. */
+int
+subcase_compare_3way_xx (const struct subcase *sc,
+                         const union value a[], const union value b[])
+{
+  size_t i;
+
+  for (i = 0; i < sc->n_fields; i++)
+    {
+      const struct subcase_field *field = &sc->fields[i];
+      size_t n_values;
+      int cmp;
+
+      cmp = value_compare_3way (a, b, field->width);
+      if (cmp != 0)
+        return field->direction == SC_ASCEND ? cmp : -cmp;
+
+      n_values = value_cnt_from_width (field->width);
+      a += n_values;
+      b += n_values;
+    }
+  return 0;
+}
+
+/* Compares the fields in A specified in A_SC against the fields
+   in B specified in B_SC.  Returns true if the fields' values
+   are equal, false otherwise.
+
+   A_SC and B_SC must be conformable (as tested by
+   subcase_conformable()). */
+bool
+subcase_equal (const struct subcase *a_sc, const struct ccase *a,
+               const struct subcase *b_sc, const struct ccase *b)
+{
+  return subcase_compare_3way (a_sc, a, b_sc, b) == 0;
+}
+
+/* Compares the values in A against the values in B specified by
+   SC's fields.  Returns true if A's values are equal to B's
+   values, otherwise false. */
+bool
+subcase_equal_xc (const struct subcase *sc,
+                  const union value a[], const struct ccase *b)
+{
+  return subcase_compare_3way_xc (sc, a, b) == 0;
+}
+
+/* Compares the values in A specified by SC's fields against the
+   values in B.  Returns true if A's values are equal to B's
+   values, otherwise false. */
+bool
+subcase_equal_cx (const struct subcase *sc,
+                  const struct ccase *a, const union value b[])
+{
+  return subcase_compare_3way_cx (sc, a, b) == 0;
+}
+
+/* Compares the values in A against the values in B, using SC to
+   obtain the number and width of each value.  Returns true if
+   A's values are equal to B's values, otherwise false. */
+bool
+subcase_equal_xx (const struct subcase *sc,
+                  const union value a[], const union value b[])
+{
+  return subcase_compare_3way_xx (sc, a, b) == 0;
+}
+
diff --git a/src/data/subcase.h b/src/data/subcase.h
new file mode 100644 (file)
index 0000000..d50d074
--- /dev/null
@@ -0,0 +1,119 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 DATA_SUBCASE_H
+#define DATA_SUBCASE_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct ccase;
+union value;
+struct variable;
+
+/* Sort order. */
+enum subcase_direction
+  {
+    SC_ASCEND,                 /* A, B, C, ..., X, Y, Z. */
+    SC_DESCEND                 /* Z, Y, X, ..., C, B, A. */
+  };
+
+/* A value within a case. */
+struct subcase_field
+  {
+    size_t case_index;          /* Starting position in the case. */
+    int width;                  /* 0=numeric, otherwise string width. */
+    enum subcase_direction direction; /* Sort order. */
+  };
+
+/* A subcase specifies how to draw values from a case. */
+struct subcase
+  {
+    struct subcase_field *fields;
+    size_t n_fields;
+    size_t n_values;
+  };
+
+void subcase_init_empty (struct subcase *);
+void subcase_init_vars (struct subcase *,
+                        const struct variable *const *, size_t n_vars);
+void subcase_init_var (struct subcase *,
+                       const struct variable *, enum subcase_direction);
+void subcase_clone (struct subcase *, const struct subcase *);
+void subcase_clear (struct subcase *);
+void subcase_destroy (struct subcase *);
+
+bool subcase_add_var (struct subcase *, const struct variable *,
+                      enum subcase_direction);
+
+static inline bool subcase_is_empty (const struct subcase *);
+static inline size_t subcase_get_n_fields (const struct subcase *);
+static inline size_t subcase_get_n_values (const struct subcase *);
+
+static inline enum subcase_direction subcase_get_direction (
+  const struct subcase *, size_t idx);
+
+bool subcase_conformable (const struct subcase *, const struct subcase *);
+
+void subcase_extract (const struct subcase *, const struct ccase *,
+                      union value *values);
+void subcase_inject (const struct subcase *,
+                     const union value *values, struct ccase *);
+void subcase_copy (const struct subcase *src_sc, const struct ccase *src,
+                   const struct subcase *dst_sc, struct ccase *dst);
+
+int subcase_compare_3way (const struct subcase *a_sc, const struct ccase *a,
+                          const struct subcase *b_sc, const struct ccase *b);
+int subcase_compare_3way_xc (const struct subcase *,
+                             const union value *a, const struct ccase *b);
+int subcase_compare_3way_cx (const struct subcase *,
+                             const struct ccase *a, const union value *b);
+int subcase_compare_3way_xx (const struct subcase *,
+                             const union value *a, const union value *b);
+bool subcase_equal (const struct subcase *a_sc, const struct ccase *a,
+                    const struct subcase *b_sc, const struct ccase *b);
+bool subcase_equal_xc (const struct subcase *,
+                       const union value *a, const struct ccase *b);
+bool subcase_equal_cx (const struct subcase *,
+                       const struct ccase *a, const union value *b);
+bool subcase_equal_xx (const struct subcase *,
+                       const union value *a, const union value *b);
+
+static inline enum subcase_direction
+subcase_get_direction (const struct subcase *sc, size_t idx)
+{
+  return sc->fields[idx].direction;
+}
+
+static inline bool
+subcase_is_empty (const struct subcase *sc)
+{
+  return sc->n_fields == 0;
+}
+
+static inline size_t
+subcase_get_n_fields (const struct subcase *sc)
+{
+  return sc->n_fields;
+}
+
+static inline size_t
+subcase_get_n_values (const struct subcase *sc)
+{
+  return sc->n_values;
+}
+
+#endif /* data/subcase.h */
index e8349abf19077bf2c7751853f4d82d8f9c21bd53..84d7f83c4c422a502272ec47039aeb3bb5889b18 100644 (file)
@@ -34,6 +34,7 @@
 #include <libpspp/hash.h>
 #include <libpspp/array.h>
 
+#include <data/attributes.h>
 #include <data/case.h>
 #include <data/casereader-provider.h>
 #include <data/casereader.h>
@@ -98,9 +99,11 @@ static struct variable *lookup_var_by_value_idx (struct sfm_reader *,
                                                  struct variable **,
                                                  int value_idx);
 
+static void sys_msg (struct sfm_reader *r, int class,
+                     const char *format, va_list args)
+     PRINTF_FORMAT (3, 0);
 static void sys_warn (struct sfm_reader *, const char *, ...)
      PRINTF_FORMAT (2, 3);
-
 static void sys_error (struct sfm_reader *, const char *, ...)
      PRINTF_FORMAT (2, 3)
      NO_RETURN;
@@ -112,15 +115,23 @@ static double read_float (struct sfm_reader *);
 static void read_string (struct sfm_reader *, char *, size_t);
 static void skip_bytes (struct sfm_reader *, size_t);
 
-static struct variable_to_value_map *open_variable_to_value_map (
-  struct sfm_reader *, size_t size);
-static void close_variable_to_value_map (struct sfm_reader *r,
-                                         struct variable_to_value_map *);
-static bool read_variable_to_value_map (struct sfm_reader *,
-                                        struct dictionary *,
-                                        struct variable_to_value_map *,
-                                        struct variable **var, char **value,
-                                        int *warning_cnt);
+static struct text_record *open_text_record (struct sfm_reader *, size_t size);
+static void close_text_record (struct sfm_reader *r,
+                               struct text_record *);
+static bool read_variable_to_value_pair (struct sfm_reader *,
+                                         struct dictionary *,
+                                         struct text_record *,
+                                         struct variable **var, char **value);
+static void text_warn (struct sfm_reader *r, struct text_record *text,
+                       const char *format, ...)
+  PRINTF_FORMAT (3, 4);
+static char *text_get_token (struct text_record *,
+                             struct substring delimiters);
+static bool text_match (struct text_record *, char c);
+static bool text_read_short_name (struct sfm_reader *, struct dictionary *,
+                                  struct text_record *,
+                                  struct substring delimiters,
+                                  struct variable **);
 
 static bool close_reader (struct sfm_reader *r);
 \f
@@ -163,7 +174,12 @@ static void read_long_var_name_map (struct sfm_reader *,
 static void read_long_string_map (struct sfm_reader *,
                                   size_t size, size_t count,
                                   struct dictionary *);
-
+static void read_data_file_attributes (struct sfm_reader *,
+                                       size_t size, size_t count,
+                                       struct dictionary *);
+static void read_variable_attributes (struct sfm_reader *,
+                                      size_t size, size_t count,
+                                      struct dictionary *);
 
 /* Opens the system file designated by file handle FH for
    reading.  Reads the system file's dictionary into *DICT.
@@ -752,9 +768,12 @@ read_extension_record (struct sfm_reader *r, struct dictionary *dict,
       break;
 
     case 17:
-      /* Text field that defines variable attributes.  New in
-         SPSS 14. */
-      break;
+      read_data_file_attributes (r, size, count, dict);
+      return;
+
+    case 18:
+      read_variable_attributes (r, size, count, dict);
+      return;
 
     case 20:
       /* New in SPSS 16.  Contains a single string that describes
@@ -932,14 +951,12 @@ static void
 read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count,
                         struct dictionary *dict)
 {
-  struct variable_to_value_map *map;
+  struct text_record *text;
   struct variable *var;
   char *long_name;
-  int warning_cnt = 0;
 
-  map = open_variable_to_value_map (r, size * count);
-  while (read_variable_to_value_map (r, dict, map, &var, &long_name,
-                                     &warning_cnt))
+  text = open_text_record (r, size * count);
+  while (read_variable_to_value_pair (r, dict, text, &var, &long_name))
     {
       char **short_names;
       size_t short_name_cnt;
@@ -985,7 +1002,7 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count,
         }
       free (short_names);
     }
-  close_variable_to_value_map (r, map);
+  close_text_record (r, text);
   r->has_long_var_names = true;
 }
 
@@ -995,14 +1012,12 @@ static void
 read_long_string_map (struct sfm_reader *r, size_t size, size_t count,
                       struct dictionary *dict)
 {
-  struct variable_to_value_map *map;
+  struct text_record *text;
   struct variable *var;
   char *length_s;
-  int warning_cnt = 0;
 
-  map = open_variable_to_value_map (r, size * count);
-  while (read_variable_to_value_map (r, dict, map, &var, &length_s,
-                                     &warning_cnt))
+  text = open_text_record (r, size * count);
+  while (read_variable_to_value_pair (r, dict, text, &var, &length_s))
     {
       size_t idx = var_get_dict_index (var);
       long int length;
@@ -1050,7 +1065,7 @@ read_long_string_map (struct sfm_reader *r, size_t size, size_t count,
       dict_delete_consecutive_vars (dict, idx + 1, segment_cnt - 1);
       var_set_width (var, length);
     }
-  close_variable_to_value_map (r, map);
+  close_text_record (r, text);
   dict_compact_values (dict);
 }
 
@@ -1188,6 +1203,96 @@ read_value_labels (struct sfm_reader *r,
 
   pool_destroy (subpool);
 }
+
+/* Reads a set of custom attributes from TEXT into ATTRS.
+   ATTRS may be a null pointer, in which case the attributes are
+   read but discarded. */
+static void
+read_attributes (struct sfm_reader *r, struct text_record *text,
+                 struct attrset *attrs)
+{
+  do
+    {
+      struct attribute *attr;
+      char *key;
+      int index;
+
+      /* Parse the key. */
+      key = text_get_token (text, ss_cstr ("("));
+      if (key == NULL)
+        return;
+
+      attr = attribute_create (key);
+      for (index = 1; ; index++)
+        {
+          /* Parse the value. */
+          char *value;
+          size_t length;
+
+          value = text_get_token (text, ss_cstr ("\n"));
+          if (value == NULL)
+            {
+              text_warn (r, text, _("Error parsing attribute value %s[%d]"),
+                         key, index);
+              break;
+            }              
+
+          length = strlen (value);
+          if (length >= 2 && value[0] == '\'' && value[length - 1] == '\'') 
+            {
+              value[length - 1] = '\0';
+              attribute_add_value (attr, value + 1); 
+            }
+          else 
+            {
+              text_warn (r, text,
+                         _("Attribute value %s[%d] is not quoted: %s"),
+                         key, index, value);
+              attribute_add_value (attr, value); 
+            }
+
+          /* Was this the last value for this attribute? */
+          if (text_match (text, ')'))
+            break;
+        }
+      if (attrs != NULL)
+        attrset_add (attrs, attr);
+      else
+        attribute_destroy (attr);
+    }
+  while (!text_match (text, '/'));
+}
+
+/* Reads record type 7, subtype 17, which lists custom
+   attributes on the data file.  */
+static void
+read_data_file_attributes (struct sfm_reader *r,
+                           size_t size, size_t count,
+                           struct dictionary *dict)
+{
+  struct text_record *text = open_text_record (r, size * count);
+  read_attributes (r, text, dict_get_attributes (dict));
+  close_text_record (r, text);
+}
+
+/* Reads record type 7, subtype 18, which lists custom
+   attributes on individual variables.  */
+static void
+read_variable_attributes (struct sfm_reader *r,
+                          size_t size, size_t count,
+                          struct dictionary *dict)
+{
+  struct text_record *text = open_text_record (r, size * count);
+  for (;;) 
+    {
+      struct variable *var;
+      if (!text_read_short_name (r, dict, text, ss_cstr (":"), &var))
+        break;
+      read_attributes (r, text, var != NULL ? var_get_attributes (var) : NULL);
+    }
+  close_text_record (r, text);
+}
+
 \f
 /* Case reader. */
 
@@ -1204,24 +1309,24 @@ static bool read_compressed_string (struct sfm_reader *, char *);
 static bool read_whole_strings (struct sfm_reader *, char *, size_t);
 static bool skip_whole_strings (struct sfm_reader *, size_t);
 
-/* Reads one case from READER's file into C.  Returns true only
-   if successful. */
-static bool
-sys_file_casereader_read (struct casereader *reader, void *r_,
-                          struct ccase *c)
+/* Reads and returns one case from READER's file.  Returns a null
+   pointer if not successful. */
+static struct ccase *
+sys_file_casereader_read (struct casereader *reader, void *r_)
 {
   struct sfm_reader *r = r_;
+  struct ccase *volatile c;
   int i;
 
   if (r->error)
-    return false;
+    return NULL;
 
-  case_create (c, r->value_cnt);
+  c = case_create (r->value_cnt);
   if (setjmp (r->bail_out))
     {
       casereader_force_error (reader);
-      case_destroy (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 
   for (i = 0; i < r->sfm_var_cnt; i++)
@@ -1242,15 +1347,15 @@ sys_file_casereader_read (struct casereader *reader, void *r_,
             partial_record (r);
         }
     }
-  return true;
+  return c;
 
 eof:
-  case_destroy (c);
+  case_unref (c);
   if (i != 0)
     partial_record (r);
   if (r->case_cnt != -1)
     read_error (reader, r);
-  return false;
+  return NULL;
 }
 
 /* Issues an error that R ends in a partial record. */
@@ -1523,82 +1628,124 @@ lookup_var_by_short_name (struct dictionary *d, const char *short_name)
   return NULL;
 }
 \f
-/* Helpers for reading records that contain "variable=value"
-   pairs. */
+/* Helpers for reading records that contain structured text
+   strings. */
+
+/* Maximum number of warnings to issue for a single text
+   record. */
+#define MAX_TEXT_WARNINGS 5
 
 /* State. */
-struct variable_to_value_map
+struct text_record
   {
     struct substring buffer;    /* Record contents. */
     size_t pos;                 /* Current position in buffer. */
+    int n_warnings;             /* Number of warnings issued or suppressed. */
   };
 
-/* Reads SIZE bytes into a "variable=value" map for R,
-   and returns the map. */
-static struct variable_to_value_map *
-open_variable_to_value_map (struct sfm_reader *r, size_t size)
+/* Reads SIZE bytes into a text record for R,
+   and returns the new text record. */
+static struct text_record *
+open_text_record (struct sfm_reader *r, size_t size)
 {
-  struct variable_to_value_map *map = pool_alloc (r->pool, sizeof *map);
+  struct text_record *text = pool_alloc (r->pool, sizeof *text);
   char *buffer = pool_malloc (r->pool, size + 1);
   read_bytes (r, buffer, size);
-  map->buffer = ss_buffer (buffer, size);
-  map->pos = 0;
-  return map;
+  text->buffer = ss_buffer (buffer, size);
+  text->pos = 0;
+  text->n_warnings = 0;
+  return text;
 }
 
-/* Closes MAP and frees its storage.
-   Not really needed, because the pool will free the map anyway,
-   but can be used to free it earlier. */
+/* Closes TEXT, frees its storage, and issues a final warning
+   about suppressed warnings if necesary. */
 static void
-close_variable_to_value_map (struct sfm_reader *r,
-                             struct variable_to_value_map *map)
+close_text_record (struct sfm_reader *r, struct text_record *text)
 {
-  pool_free (r->pool, ss_data (map->buffer));
+  if (text->n_warnings > MAX_TEXT_WARNINGS)
+    sys_warn (r, _("Suppressed %d additional related warnings."),
+              text->n_warnings - MAX_TEXT_WARNINGS);
+  pool_free (r->pool, ss_data (text->buffer));
 }
 
-/* Reads the next variable=value pair from MAP.
+/* Reads a variable=value pair from TEXT.
    Looks up the variable in DICT and stores it into *VAR.
    Stores a null-terminated value into *VALUE. */
 static bool
-read_variable_to_value_map (struct sfm_reader *r, struct dictionary *dict,
-                            struct variable_to_value_map *map,
-                            struct variable **var, char **value,
-                            int *warning_cnt)
+read_variable_to_value_pair (struct sfm_reader *r, struct dictionary *dict,
+                             struct text_record *text,
+                             struct variable **var, char **value)
 {
-  int max_warnings = 5;
-
   for (;;)
     {
-      struct substring short_name_ss, value_ss;
+      if (!text_read_short_name (r, dict, text, ss_cstr ("="), var))
+        return false;
+      
+      *value = text_get_token (text, ss_buffer ("\t\0", 2));
+      if (*value == NULL)
+        return false;
 
-      if (!ss_tokenize (map->buffer, ss_cstr ("="), &map->pos, &short_name_ss)
-          || !ss_tokenize (map->buffer, ss_buffer ("\t\0", 2), &map->pos,
-                           &value_ss))
-        {
-          if (*warning_cnt > max_warnings)
-            sys_warn (r, _("Suppressed %d additional variable map warnings."),
-                      *warning_cnt - max_warnings);
-          return false;
-        }
+      text->pos += ss_span (ss_substr (text->buffer, text->pos, SIZE_MAX),
+                            ss_buffer ("\t\0", 2));
 
-      map->pos += ss_span (ss_substr (map->buffer, map->pos, SIZE_MAX),
-                           ss_buffer ("\t\0", 2));
+      if (*var != NULL)
+        return true;
+    }
+}
 
-      ss_data (short_name_ss)[ss_length (short_name_ss)] = '\0';
-      *var = lookup_var_by_short_name (dict, ss_data (short_name_ss));
-      if (*var == NULL)
-        {
-          if (++*warning_cnt <= max_warnings)
-            sys_warn (r, _("Variable map refers to unknown variable %s."),
-                      ss_data (short_name_ss));
-          continue;
-        }
+static bool
+text_read_short_name (struct sfm_reader *r, struct dictionary *dict,
+                      struct text_record *text, struct substring delimiters,
+                      struct variable **var)
+{
+  char *short_name = text_get_token (text, delimiters);
+  if (short_name == NULL)
+    return false;
 
-      ss_data (value_ss)[ss_length (value_ss)] = '\0';
-      *value = ss_data (value_ss);
+  *var = lookup_var_by_short_name (dict, short_name);
+  if (*var == NULL)
+    text_warn (r, text, _("Variable map refers to unknown variable %s."),
+               short_name);
+  return true;
+}
+
+/* Displays a warning for the current file position, limiting the
+   number to MAX_TEXT_WARNINGS for TEXT. */
+static void
+text_warn (struct sfm_reader *r, struct text_record *text,
+           const char *format, ...)
+{
+  if (text->n_warnings++ < MAX_TEXT_WARNINGS) 
+    {
+      va_list args;
+
+      va_start (args, format);
+      sys_msg (r, MW, format, args);
+      va_end (args);
+    }
+}
 
+static char *
+text_get_token (struct text_record *text, struct substring delimiters)
+{
+  struct substring token;
+
+  if (!ss_tokenize (text->buffer, delimiters, &text->pos, &token))
+    return NULL;
+  ss_data (token)[ss_length (token)] = '\0';
+  return ss_data (token);
+}
+
+static bool
+text_match (struct text_record *text, char c)
+{
+  if (text->buffer.string[text->pos] == c) 
+    {
+      text->pos++;
       return true;
     }
+  else
+    return false;
 }
 \f
 /* Messages. */
index eaf051fed7a6cc73c7e5a82628100d38554ea55d..242857494ee365af5238936bdd8c7e2027c30c0a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -47,7 +47,6 @@ struct sfm_read_info
 
 struct dictionary;
 struct file_handle;
-struct ccase;
 struct casereader *sfm_open_reader (struct file_handle *,
                                     struct dictionary **,
                                     struct sfm_read_info *);
index 7804e5914f72d7ac49f06a1f8ccb1bd71aec48ce..393be4e1265292b58fc37daa64bfaf4a4ff40a15 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@
 #include <libpspp/str.h>
 #include <libpspp/version.h>
 
+#include <data/attributes.h>
 #include <data/case.h>
 #include <data/casewriter-provider.h>
 #include <data/casewriter.h>
@@ -111,6 +112,11 @@ static void write_variable_display_parameters (struct sfm_writer *w,
 
 static void write_documents (struct sfm_writer *, const struct dictionary *);
 
+static void write_data_file_attributes (struct sfm_writer *,
+                                        const struct dictionary *);
+static void write_variable_attributes (struct sfm_writer *,
+                                       const struct dictionary *);
+
 static void write_int (struct sfm_writer *, int32_t);
 static inline void convert_double_to_output_format (double, uint8_t[8]);
 static void write_float (struct sfm_writer *, double);
@@ -120,8 +126,9 @@ static void write_zeros (struct sfm_writer *, size_t);
 static void write_spaces (struct sfm_writer *, size_t);
 static void write_value (struct sfm_writer *, const union value *, int width);
 
-static void write_case_uncompressed (struct sfm_writer *, struct ccase *);
-static void write_case_compressed (struct sfm_writer *, struct ccase *);
+static void write_case_uncompressed (struct sfm_writer *,
+                                     const struct ccase *);
+static void write_case_compressed (struct sfm_writer *, const struct ccase *);
 static void flush_compressed (struct sfm_writer *);
 static void put_cmp_opcode (struct sfm_writer *, uint8_t);
 static void put_cmp_number (struct sfm_writer *, double);
@@ -235,6 +242,10 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
 
   write_vls_length_table (w, d);
 
+  if (attrset_count (dict_get_attributes (d)))
+    write_data_file_attributes (w, d);
+  write_variable_attributes (w, d);
+
   /* Write end-of-headers record. */
   write_int (w, 999);
   write_int (w, 0);
@@ -520,6 +531,72 @@ write_documents (struct sfm_writer *w, const struct dictionary *d)
   write_bytes (w, dict_get_documents (d), line_cnt * DOC_LINE_LENGTH);
 }
 
+static void
+put_attrset (struct string *string, const struct attrset *attrs)
+{
+  const struct attribute *attr;
+  struct attrset_iterator i;
+
+  for (attr = attrset_first (attrs, &i); attr != NULL;
+       attr = attrset_next (attrs, &i)) 
+    {
+      size_t n_values = attribute_get_n_values (attr);
+      size_t j;
+
+      ds_put_cstr (string, attribute_get_name (attr));
+      ds_put_char (string, '(');
+      for (j = 0; j < n_values; j++) 
+        ds_put_format (string, "'%s'\n", attribute_get_value (attr, j));
+      ds_put_char (string, ')');
+    }
+}
+
+static void
+write_attribute_record (struct sfm_writer *w, const struct string *content,
+                        int subtype) 
+{
+  write_int (w, 7);
+  write_int (w, subtype);
+  write_int (w, 1);
+  write_int (w, ds_length (content));
+  write_bytes (w, ds_data (content), ds_length (content));
+}
+
+static void
+write_data_file_attributes (struct sfm_writer *w,
+                            const struct dictionary *d)
+{
+  struct string s = DS_EMPTY_INITIALIZER;
+  put_attrset (&s, dict_get_attributes (d));
+  write_attribute_record (w, &s, 17);
+  ds_destroy (&s);
+}
+
+static void
+write_variable_attributes (struct sfm_writer *w, const struct dictionary *d)
+{
+  struct string s = DS_EMPTY_INITIALIZER;
+  size_t n_vars = dict_get_var_cnt (d);
+  size_t n_attrsets = 0;
+  size_t i;
+
+  for (i = 0; i < n_vars; i++)
+    { 
+      struct variable *v = dict_get_var (d, i);
+      struct attrset *attrs = var_get_attributes (v);
+      if (attrset_count (attrs)) 
+        {
+          if (n_attrsets++)
+            ds_put_char (&s, '/');
+          ds_put_format (&s, "%s:", var_get_short_name (v, 0));
+          put_attrset (&s, attrs);
+        }
+    }
+  if (n_attrsets) 
+    write_attribute_record (w, &s, 18);
+  ds_destroy (&s);
+}
+
 /* Write the alignment, width and scale values. */
 static void
 write_variable_display_parameters (struct sfm_writer *w,
@@ -671,7 +748,7 @@ sys_file_casewriter_write (struct casewriter *writer, void *w_,
   if (ferror (w->file))
     {
       casewriter_force_error (writer);
-      case_destroy (c);
+      case_unref (c);
       return;
     }
 
@@ -682,7 +759,7 @@ sys_file_casewriter_write (struct casewriter *writer, void *w_,
   else
     write_case_compressed (w, c);
 
-  case_destroy (c);
+  case_unref (c);
 }
 
 /* Destroys system file writer W. */
@@ -760,7 +837,7 @@ static const struct casewriter_class sys_file_casewriter_class =
 \f
 /* Writes case C to system file W, without compressing it. */
 static void
-write_case_uncompressed (struct sfm_writer *w, struct ccase *c)
+write_case_uncompressed (struct sfm_writer *w, const struct ccase *c)
 {
   size_t i;
 
@@ -781,7 +858,7 @@ write_case_uncompressed (struct sfm_writer *w, struct ccase *c)
 
 /* Writes case C to system file W, with compression. */
 static void
-write_case_compressed (struct sfm_writer *w, struct ccase *c)
+write_case_compressed (struct sfm_writer *w, const struct ccase *c)
 {
   size_t i;
 
index c6128d45677009a5cff5cff45e249a5cd53c658f..fdff49fe52a165d02488e51d67c0efd758e3047b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -31,7 +31,6 @@ struct sfm_write_options
 
 struct file_handle;
 struct dictionary;
-struct ccase;
 struct casewriter *sfm_open_writer (struct file_handle *, struct dictionary *,
                                     struct sfm_write_options);
 struct sfm_write_options sfm_writer_default_options (void);
index 08b43139e9ff118de981f61808f4bea52ea52d12..2a73cade41bbcf6e4c55622c5d00b9d8b18505b6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -178,14 +178,15 @@ trns_chain_next (struct trns_chain *chain)
   return chain->trns_cnt;
 }
 
-/* Executes the given CHAIN of transformations on C,
+/* Executes the given CHAIN of transformations on *C,
    passing CASE_NR as the case number.
+   *C may be replaced by a new case.
    Returns the result code that caused the transformations to
    terminate, or TRNS_CONTINUE if the transformations finished
    due to "falling off the end" of the set of transformations. */
 enum trns_result
 trns_chain_execute (const struct trns_chain *chain, enum trns_result start,
-                    struct ccase *c, casenumber case_nr)
+                    struct ccase **c, casenumber case_nr)
 {
   size_t i;
 
index 0a8c53e2c8bc33c571d7e0cffef3fac7d31099a3..59001f1139da18a544775337b9921baa435b7e6c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -33,7 +33,7 @@ enum trns_result
 
 struct ccase;
 typedef void trns_finalize_func (void *);
-typedef int trns_proc_func (void *, struct ccase *, casenumber);
+typedef int trns_proc_func (void *, struct ccase **, casenumber);
 typedef bool trns_free_func (void *);
 \f
 /* Transformation chains. */
@@ -47,8 +47,9 @@ bool trns_chain_is_empty (const struct trns_chain *);
 void trns_chain_append (struct trns_chain *, trns_finalize_func *,
                         trns_proc_func *, trns_free_func *, void *);
 size_t trns_chain_next (struct trns_chain *);
-enum trns_result trns_chain_execute (const struct trns_chain *, enum trns_result,
-                                     struct ccase *, casenumber case_nr);
+enum trns_result trns_chain_execute (const struct trns_chain *,
+                                     enum trns_result, struct ccase **,
+                                     casenumber case_nr);
 
 void trns_chain_splice (struct trns_chain *, struct trns_chain *);
 
index 49555d9a4ea3b11ce15149ebdca388722af6dcb4..baaaed48d4c396a94312917a1becc7b7bee9f7c2 100644 (file)
@@ -20,6 +20,7 @@
 #include <data/val-type.h>
 #include <libpspp/hash.h>
 #include <libpspp/str.h>
+#include "variable.h"
 
 #include "xalloc.h"
 
@@ -46,34 +47,27 @@ value_create (int width)
    Only the short string portion of longer strings are
    compared. */
 int
-compare_values (const union value *a, const union value *b, int width)
+compare_values_short (const void *a_, const void *b_, const void *var_) 
 {
-  return (width == 0
-          ? (a->f < b->f ? -1 : a->f > b->f)
-          : memcmp (a->s, b->s, MIN (MAX_SHORT_STRING, width)));
+  const union value *a = a_;
+  const union value *b = b_;
+  const struct variable *var = var_;
+  int width = var_get_width (var);
+  return value_compare_3way (a, b, MIN (width, MAX_SHORT_STRING));
 }
 
+
 /* Create a hash of V, which has the given WIDTH.
    Only the short string portion of a longer string is hashed. */
 unsigned
-hash_value (const union value *v, int width)
+hash_value_short (const void *v_, const void *var_)
 {
+  const union value *v = v_;
+  const struct variable *var = var_;
+  int width = var_get_width (var);
   return (width == 0
           ? hsh_hash_double (v->f)
-          : hsh_hash_bytes (v->s, MIN (MAX_SHORT_STRING, width)));
-}
-
-
-int
-compare_ptr_values (const union value **v1, const union value **v2, int width)
-{
-  return compare_values (*v1, *v2, width);
-}
-
-unsigned
-hash_ptr_value (const union value **v, int width)
-{
-  return hash_value (*v, width);
+         : hsh_hash_bytes (v->s, width));
 }
 
 
@@ -127,3 +121,13 @@ value_resize (union value *value, int old_width, int new_width)
   if (new_width > old_width)
     memset (&value->s[old_width], ' ', new_width - old_width);
 }
+
+/* Compares A and B, which both have the given WIDTH, and returns
+   a strcmp()-type result. */
+int
+value_compare_3way (const union value *a, const union value *b, int width)
+{
+  return (width == 0
+          ? (a->f < b->f ? -1 : a->f > b->f)
+          : memcmp (a->s, b->s, width));
+}
index 4554a36617eb589153dc7244f19b3edf74c0bbd7..97c24050ef6264712e05c2dd8b98ea069a0ccd89 100644 (file)
@@ -39,18 +39,18 @@ union value
 union value *value_dup (const union value *, int width);
 union value *value_create (int width);
 
-int compare_values (const union value *, const union value *, int width);
-unsigned hash_value (const union value *, int width);
-
-int compare_ptr_values (const union value **, const union value **, int width);
-unsigned hash_ptr_value (const union value **, int width);
+int compare_values (const void *, const void *, const void *var);
+unsigned hash_value (const void *, const void *var);
 
+int compare_values_short (const void *, const void *, const void *var);
+unsigned hash_value_short (const void *, const void *var);
 
 static inline size_t value_cnt_from_width (int width);
 void value_copy (union value *, const union value *, int width);
 void value_set_missing (union value *, int width);
 bool value_is_resizable (const union value *, int old_width, int new_width);
 void value_resize (union value *, int old_width, int new_width);
+int value_compare_3way (const union value *, const union value *, int width);
 
 /* Number of "union value"s required for a variable of the given
    WIDTH. */
index 35440a0de644af2fbeb3a407d7da837fe97aca1d..dfde1bffc0b3802d553c7717da47b82a4300908b 100644 (file)
@@ -41,5 +41,6 @@ void var_clear_vardict (struct variable *);
 /* Called by variable.c, defined in dictionary.c. */
 void dict_var_changed (const struct variable *v);
 void dict_var_resized (const struct variable *v, int delta);
+void dict_var_display_width_changed (const struct variable *v);
 
 #endif /* data/vardict.h */
index a455e40a1f5bd72d025d30d941460d91ded5dc2a..00c3f07f05fad273d79935a895242fb62799d0f6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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 <stdlib.h>
 
-#include "category.h"
-#include "data-out.h"
-#include "format.h"
-#include "dictionary.h"
-#include "identifier.h"
-#include "missing-values.h"
-#include "value-labels.h"
-#include "vardict.h"
+#include <data/attributes.h>
+#include <data/category.h>
+#include <data/data-out.h>
+#include <data/format.h>
+#include <data/dictionary.h>
+#include <data/identifier.h>
+#include <data/missing-values.h>
+#include <data/value-labels.h>
+#include <data/vardict.h>
 
 #include <libpspp/misc.h>
 #include <libpspp/assertion.h>
@@ -76,6 +77,9 @@ struct variable
        vectors with binary entries, so any variable of type ALPHA will
        have its values stored here. */
     struct cat_vals *obs_vals;
+
+    /* Custom attributes. */
+    struct attrset attributes;
   };
 \f
 /* Creates and returns a new variable with the given NAME and
@@ -108,6 +112,7 @@ var_create (const char *name, int width)
   v->aux = NULL;
   v->aux_dtor = NULL;
   v->obs_vals = NULL;
+  attrset_init (&v->attributes);
 
   return v;
 }
@@ -138,10 +143,32 @@ var_clone (const struct variable *old_var)
   var_set_display_width (new_var, var_get_display_width (old_var));
   var_set_alignment (new_var, var_get_alignment (old_var));
   var_set_leave (new_var, var_get_leave (old_var));
+  var_set_attributes (new_var, var_get_attributes (old_var));
 
   return new_var;
 }
 
+/* Create a variable to be used for internal calculations only.
+   The variable is assigned a unique dictionary index and a case
+   index of CASE_IDX. */
+struct variable *
+var_create_internal (int case_idx)
+{
+  struct variable *v = var_create ("$internal", 0);
+  struct vardict_info vdi;
+  static int counter = INT_MAX / 2;
+
+  vdi.dict = NULL;
+  vdi.case_index = case_idx;
+  vdi.dict_index = counter++;
+  if (counter == INT_MAX)
+    counter = INT_MAX / 2;
+
+  var_set_vardict (v, &vdi);
+
+  return v;
+}
+
 /* Destroys variable V.
    V must not belong to a dictionary.  If it does, use
    dict_delete_var instead. */
@@ -150,7 +177,11 @@ var_destroy (struct variable *v)
 {
   if (v != NULL)
     {
-      assert (!var_has_vardict (v));
+      if (var_has_vardict (v))
+       {
+         const struct vardict_info *vdi = var_get_vardict (v);
+         assert (vdi->dict == NULL);
+       }
       cat_stored_values_destroy (v->obs_vals);
       var_clear_short_names (v);
       var_clear_aux (v);
@@ -307,6 +338,20 @@ compare_var_ptrs_by_name (const void *a_, const void *b_,
   return strcasecmp (var_get_name (*a), var_get_name (*b));
 }
 
+/* A hsh_compare_func that orders pointers to variables A and B
+   by their dictionary indexes. */
+int
+compare_var_ptrs_by_dict_index (const void *a_, const void *b_,
+                                const void *aux UNUSED)
+{
+  struct variable *const *a = a_;
+  struct variable *const *b = b_;
+  size_t a_index = var_get_dict_index (*a);
+  size_t b_index = var_get_dict_index (*b);
+
+  return a_index < b_index ? -1 : a_index > b_index;
+}
+
 /* A hsh_hash_func that hashes pointer to variable V based on its
    name. */
 unsigned
@@ -578,7 +623,6 @@ var_append_value_name (const struct variable *v, const union value *value,
     ds_put_cstr (str, name);
 }
 \f
-\f
 /* Print and write formats. */
 
 /* Returns V's print format specification. */
@@ -676,8 +720,8 @@ var_set_label (struct variable *v, const char *label)
       ss_truncate (&s, 255);
       if (!ss_is_empty (s))
         v->label = ss_xstrdup (s);
-      dict_var_changed (v);
     }
+  dict_var_changed (v);
 }
 
 /* Removes any variable label from V. */
@@ -738,9 +782,15 @@ var_get_display_width (const struct variable *v)
 
 /* Sets V's display width to DISPLAY_WIDTH. */
 void
-var_set_display_width (struct variable *v, int display_width)
+var_set_display_width (struct variable *v, int new_width)
 {
-  v->display_width = display_width;
+  int old_width = v->display_width;
+
+  v->display_width = new_width;
+
+  if ( old_width != new_width)
+    dict_var_display_width_changed (v);
+
   dict_var_changed (v);
 }
 
@@ -1000,6 +1050,31 @@ var_has_obs_vals (const struct variable *v)
   return v->obs_vals != NULL;
 }
 \f
+/* Returns variable V's attribute set.  The caller may examine or
+   modify the attribute set, but must not destroy it.  Destroying
+   V, or calling var_set_attributes() on V, will also destroy its
+   attribute set. */
+struct attrset *
+var_get_attributes (const struct variable *v) 
+{
+  return (struct attrset *) &v->attributes;
+}
+
+/* Replaces variable V's attributes set by a copy of ATTRS. */
+void
+var_set_attributes (struct variable *v, const struct attrset *attrs) 
+{
+  attrset_destroy (&v->attributes);
+  attrset_clone (&v->attributes, attrs);
+}
+
+/* Returns true if V has any custom attributes, false if it has none. */
+bool
+var_has_attributes (const struct variable *v)
+{
+  return attrset_count (&v->attributes) > 0;
+}
+\f
 /* Returns V's vardict structure. */
 const struct vardict_info *
 var_get_vardict (const struct variable *v)
index c7f86aaf716896541de0909829e8039527627e47..ecfa6b765ec8447da8ce58f53506d77035cf2bec 100644 (file)
@@ -32,6 +32,8 @@ union value;
 struct variable *var_create (const char *name, int width);
 struct variable *var_clone (const struct variable *);
 void var_destroy (struct variable *);
+struct variable *var_create_internal (int case_idx);
+
 
 /* Variable names. */
 #define VAR_NAME_LEN 64 /* Maximum length of variable name, in bytes. */
@@ -48,6 +50,8 @@ unsigned hash_var_by_name (const void *, const void *);
 int compare_var_ptrs_by_name (const void *, const void *, const void *);
 unsigned hash_var_ptr_by_name (const void *, const void *);
 
+int compare_var_ptrs_by_dict_index (const void *, const void *, const void *);
+
 /* Types and widths of values associated with a variable. */
 enum val_type var_get_type (const struct variable *);
 int var_get_width (const struct variable *);
@@ -173,6 +177,11 @@ struct cat_vals *var_get_obs_vals (const struct variable *);
 void var_set_obs_vals (const struct variable *, struct cat_vals *);
 bool var_has_obs_vals (const struct variable *);
 
+/* Custom attributes. */
+struct attrset *var_get_attributes (const struct variable *);
+void var_set_attributes (struct variable *, const struct attrset *);
+bool var_has_attributes (const struct variable *);
+
 /* Function types. */
 typedef bool var_predicate_func (const struct variable *);
 
index d5ebb10e15fdefdfe85e3a2b71535ba8099cf16d..4210b7fce45f8f05153f9fd4bd2cd28881255b01 100644 (file)
@@ -10,9 +10,13 @@ include $(top_srcdir)/src/language/stats/automake.mk
 include $(top_srcdir)/src/language/data-io/automake.mk
 include $(top_srcdir)/src/language/expressions/automake.mk
 
-noinst_LIBRARIES += src/language/liblanguage.a
+noinst_LTLIBRARIES +=  src/language/liblanguage.la
 
-src_language_liblanguage_a_SOURCES = \
+
+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 \
        src/language/syntax-string-source.c \
@@ -33,8 +37,7 @@ src_language_liblanguage_a_SOURCES = \
        $(language_expressions_sources)
 
 
-
-nodist_src_language_liblanguage_a_SOURCES = \
+nodist_src_language_liblanguage_la_SOURCES = \
        $(src_language_data_io_built_sources) \
        $(src_language_utilities_built_sources) \
        $(src_language_stats_built_sources)  \
index ccf4c5560c1450a3109f66ec51083c335a2454ec..f87747d892e8d9566da798936f243ad0fdd46ec1 100644 (file)
@@ -40,11 +40,14 @@ DEF_CMD (S_ANY, 0, "SYSFILE INFO", cmd_sysfile_info)
 DEF_CMD (S_ANY, F_KEEP_FINAL_TOKEN, "TITLE", cmd_title)
 
 /* Commands that define (or replace) the active file. */
+DEF_CMD (S_INITIAL | S_DATA, 0, "ADD FILES", cmd_add_files)
 DEF_CMD (S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE, 0, "DATA LIST", cmd_data_list)
 DEF_CMD (S_INITIAL | S_DATA, 0, "GET", cmd_get)
 DEF_CMD (S_INITIAL | S_DATA, 0, "GET DATA", cmd_get_data)
 DEF_CMD (S_INITIAL | S_DATA, 0, "IMPORT", cmd_import)
 DEF_CMD (S_INITIAL | S_DATA, 0, "INPUT PROGRAM", cmd_input_program)
+DEF_CMD (S_INITIAL | S_DATA, 0, "MATCH FILES", cmd_match_files)
+DEF_CMD (S_INITIAL | S_DATA, 0, "UPDATE", cmd_update)
 
 /* Transformations and utilities that may appear after active
    file definition or within INPUT PROGRAM. */
@@ -53,6 +56,7 @@ DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "ADD DOCUMENT", cmd_add_documents)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "APPLY DICTIONARY", cmd_apply_dictionary)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "BREAK", cmd_break)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "COMPUTE", cmd_compute)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DATAFILE ATTRIBUTE", cmd_datafile_attribute)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DISPLAY", cmd_display)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, F_KEEP_FINAL_TOKEN, "DOCUMENT", cmd_document)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DO IF", cmd_do_if)
@@ -79,6 +83,7 @@ DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "SPLIT FILE", cmd_split_file)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "STRING", cmd_string)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VALUE LABELS", cmd_value_labels)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE ALIGNMENT", cmd_variable_alignment)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE ATTRIBUTE", cmd_variable_attribute)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE LABELS", cmd_variable_labels)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE LEVEL", cmd_variable_level)
 DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE WIDTH", cmd_variable_width)
@@ -104,13 +109,13 @@ DEF_CMD (S_DATA, 0, "FILTER", cmd_filter)
 DEF_CMD (S_DATA, 0, "FLIP", cmd_flip)
 DEF_CMD (S_DATA, 0, "FREQUENCIES", cmd_frequencies)
 DEF_CMD (S_DATA, 0, "LIST", cmd_list)
-DEF_CMD (S_DATA, 0, "MATCH FILES", cmd_match_files)
 DEF_CMD (S_DATA, 0, "MEANS", cmd_means)
 DEF_CMD (S_DATA, 0, "MODIFY VARS", cmd_modify_vars)
 DEF_CMD (S_DATA, 0, "NPAR TESTS", cmd_npar_tests)
 DEF_CMD (S_DATA, 0, "ONEWAY", cmd_oneway)
 DEF_CMD (S_DATA, 0, "RANK", cmd_rank)
 DEF_CMD (S_DATA, 0, "REGRESSION", cmd_regression)
+DEF_CMD (S_DATA, 0, "RELIABILITY", cmd_reliability)
 DEF_CMD (S_DATA, 0, "RENAME VARIABLES", cmd_rename_variables)
 DEF_CMD (S_DATA, 0, "SAMPLE", cmd_sample)
 DEF_CMD (S_DATA, 0, "SAVE", cmd_save)
@@ -139,7 +144,6 @@ DEF_CMD (S_ANY, F_TESTING, "DEBUG XFORM FAIL", cmd_debug_xform_fail)
 /* Unimplemented commands. */
 UNIMPL_CMD ("2SLS", "Two stage least squares regression")
 UNIMPL_CMD ("ACF", "Autocorrelation function")
-UNIMPL_CMD ("ADD FILES", "Add files to dictionary")
 UNIMPL_CMD ("ALSCAL", "Multidimensional scaling")
 UNIMPL_CMD ("ANACOR", "Correspondence analysis")
 UNIMPL_CMD ("ANOVA", "Factorial analysis of variance")
@@ -163,7 +167,6 @@ UNIMPL_CMD ("CSSELECT", "Select complex samples")
 UNIMPL_CMD ("CSTABULATE", "Tabulate complex samples")
 UNIMPL_CMD ("CTABLES", "Display complex samples")
 UNIMPL_CMD ("CURVEFIT", "Fit curve to line plot")
-UNIMPL_CMD ("DATAFILE ATTRIBUTE", "User defined datafile attributes")
 UNIMPL_CMD ("DATASET", "Alternate data set")
 UNIMPL_CMD ("DATE", "Create time series data")
 UNIMPL_CMD ("DEFINE", "Syntax macros")
@@ -231,7 +234,6 @@ UNIMPL_CMD ("RATIO STATISTICS", "Descriptives of ratios")
 UNIMPL_CMD ("READ MODEL", "Read new model")
 UNIMPL_CMD ("RECORD TYPE", "Defines a type of record within FILE TYPE")
 UNIMPL_CMD ("REFORMAT", "Read obsolete files")
-UNIMPL_CMD ("RELIABILITY", "Reliability estimates")
 UNIMPL_CMD ("REPEATING DATA", "Specify multiple cases per input record")
 UNIMPL_CMD ("REPORT", "Pretty print working file")
 UNIMPL_CMD ("RESTORE", "Restore settings")
@@ -255,7 +257,6 @@ UNIMPL_CMD ("TSPLOT", "Plot time sequence variables")
 UNIMPL_CMD ("TWOSTEP CLUSTER", "Cluster observations")
 UNIMPL_CMD ("UNIANOVA", "Univariate analysis")
 UNIMPL_CMD ("UNNUMBERED", "obsolete")
-UNIMPL_CMD ("UPDATE", "Update working file")
 UNIMPL_CMD ("VALIDATEDATA", "Identify suspicious cases")
 UNIMPL_CMD ("VARCOMP", "Estimate variance")
 UNIMPL_CMD ("VARSTOCASES", "Restructure complex data")
index 61c450a52359b76683a5c7f9c6ca926801c8c032..b3b6135f6ec4336e26a30e71da855d25c41d1e4f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -240,7 +240,7 @@ do_if_finalize_func (void *do_if_ UNUSED)
    Checks each clause and jumps to the appropriate
    transformation. */
 static int
-do_if_trns_proc (void *do_if_, struct ccase *c, casenumber case_num UNUSED)
+do_if_trns_proc (void *do_if_, struct ccase **c, casenumber case_num UNUSED)
 {
   struct do_if_trns *do_if = do_if_;
   struct clause *clause;
@@ -250,7 +250,7 @@ do_if_trns_proc (void *do_if_, struct ccase *c, casenumber case_num UNUSED)
     {
       if (clause->condition != NULL)
         {
-          double boolean = expr_evaluate_num (clause->condition, c, case_num);
+          double boolean = expr_evaluate_num (clause->condition, *c, case_num);
           if (boolean == 1.0)
             return clause->target_index;
           else if (boolean == SYSMIS)
@@ -279,7 +279,8 @@ do_if_trns_free (void *do_if_)
 
 /* Breaks out of a DO IF construct. */
 static int
-break_trns_proc (void *do_if_, struct ccase *c UNUSED, casenumber case_num UNUSED)
+break_trns_proc (void *do_if_, struct ccase **c UNUSED,
+                 casenumber case_num UNUSED)
 {
   struct do_if_trns *do_if = do_if_;
 
index 40a33c11bbafc451adddb40f9529356ed12c6776..f5d205d4303e864bf2a6203c3f6bc5792bed1657 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -304,21 +304,22 @@ loop_trns_finalize (void *do_if_ UNUSED)
 
 /* Sets up LOOP for the first pass. */
 static int
-loop_trns_proc (void *loop_, struct ccase *c, casenumber case_num)
+loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
 {
   struct loop_trns *loop = loop_;
 
   if (loop->index_var != NULL)
     {
       /* Evaluate loop index expressions. */
-      loop->cur = expr_evaluate_num (loop->first_expr, c, case_num);
+      loop->cur = expr_evaluate_num (loop->first_expr, *c, case_num);
       if (loop->by_expr != NULL)
-       loop->by = expr_evaluate_num (loop->by_expr, c, case_num);
-      loop->last = expr_evaluate_num (loop->last_expr, c, case_num);
+       loop->by = expr_evaluate_num (loop->by_expr, *c, case_num);
+      loop->last = expr_evaluate_num (loop->last_expr, *c, case_num);
 
       /* Even if the loop is never entered, set the index
          variable to the initial value. */
-      case_data_rw (c, loop->index_var)->f = loop->cur;
+      *c = case_unshare (*c);
+      case_data_rw (*c, loop->index_var)->f = loop->cur;
 
       /* Throw out pathological cases. */
       if (!isfinite (loop->cur) || !isfinite (loop->by)
@@ -336,7 +337,7 @@ loop_trns_proc (void *loop_, struct ccase *c, casenumber case_num)
 
   /* Check condition. */
   if (loop->loop_condition != NULL
-      && expr_evaluate_num (loop->loop_condition, c, case_num) != 1.0)
+      && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
     goto zero_pass;
 
   return loop->past_LOOP_index;
@@ -357,12 +358,12 @@ loop_trns_free (void *loop_)
 
 /* Finishes a pass through the loop and starts the next. */
 static int
-end_loop_trns_proc (void *loop_, struct ccase *c, casenumber case_num UNUSED)
+end_loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num UNUSED)
 {
   struct loop_trns *loop = loop_;
 
   if (loop->end_loop_condition != NULL
-      && expr_evaluate_num (loop->end_loop_condition, c, case_num) != 0.0)
+      && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
     goto break_out;
 
   /* MXLOOPS limiter. */
@@ -380,11 +381,12 @@ end_loop_trns_proc (void *loop_, struct ccase *c, casenumber case_num UNUSED)
       if ((loop->by > 0.0 && loop->cur > loop->last)
           || (loop->by < 0.0 && loop->cur < loop->last))
         goto break_out;
-      case_data_rw (c, loop->index_var)->f = loop->cur;
+      *c = case_unshare (*c);
+      case_data_rw (*c, loop->index_var)->f = loop->cur;
     }
 
   if (loop->loop_condition != NULL
-      && expr_evaluate_num (loop->loop_condition, c, case_num) != 1.0)
+      && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
     goto break_out;
 
   return loop->past_LOOP_index;
@@ -395,7 +397,8 @@ end_loop_trns_proc (void *loop_, struct ccase *c, casenumber case_num UNUSED)
 
 /* Executes BREAK. */
 static int
-break_trns_proc (void *loop_, struct ccase *c UNUSED, casenumber case_num UNUSED)
+break_trns_proc (void *loop_, struct ccase **c UNUSED,
+                 casenumber case_num UNUSED)
 {
   struct loop_trns *loop = loop_;
 
index db209be836078494018396feaf61c164a3b8dc39..ca003d1d765f73196c590de045b22a44c2a7fe61 100644 (file)
@@ -5,22 +5,26 @@ src_language_data_io_built_sources = \
        src/language/data-io/list.c
 
 language_data_io_sources = \
+       src/language/data-io/combine-files.c \
        src/language/data-io/data-list.c \
        src/language/data-io/data-parser.c \
        src/language/data-io/data-parser.h \
-       src/language/data-io/get.c \
-       src/language/data-io/get-data.c \
-       src/language/data-io/inpt-pgm.c \
-       src/language/data-io/inpt-pgm.h \
-       src/language/data-io/print.c \
-       src/language/data-io/print-space.c \
        src/language/data-io/data-reader.c \
        src/language/data-io/data-reader.h \
        src/language/data-io/data-writer.c \
        src/language/data-io/data-writer.h \
        src/language/data-io/file-handle.h \
+       src/language/data-io/get-data.c \
+       src/language/data-io/get.c \
+       src/language/data-io/inpt-pgm.c \
+       src/language/data-io/inpt-pgm.h \
        src/language/data-io/placement-parser.c \
-       src/language/data-io/placement-parser.h 
+       src/language/data-io/placement-parser.h \
+       src/language/data-io/print-space.c \
+       src/language/data-io/print.c \
+       src/language/data-io/save.c \
+       src/language/data-io/trim.c \
+       src/language/data-io/trim.h
 
 all_q_sources += $(src_language_data_io_built_sources:.c=.q)
 EXTRA_DIST += $(src_language_data_io_built_sources:.c=.q)
diff --git a/src/language/data-io/combine-files.c b/src/language/data-io/combine-files.c
new file mode 100644 (file)
index 0000000..ccbe767
--- /dev/null
@@ -0,0 +1,865 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 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 <stdlib.h>
+
+#include <data/any-reader.h>
+#include <data/case-matcher.h>
+#include <data/case.h>
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <data/dictionary.h>
+#include <data/format.h>
+#include <data/procedure.h>
+#include <data/subcase.h>
+#include <data/variable.h>
+#include <language/command.h>
+#include <language/data-io/file-handle.h>
+#include <language/data-io/trim.h>
+#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <language/stats/sort-criteria.h>
+#include <libpspp/assertion.h>
+#include <libpspp/message.h>
+#include <libpspp/taint.h>
+#include <math/sort.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+enum comb_command_type
+  {
+    COMB_ADD,
+    COMB_MATCH,
+    COMB_UPDATE
+  };
+
+/* File types. */
+enum comb_file_type
+  {
+    COMB_FILE,                 /* Specified on FILE= subcommand. */
+    COMB_TABLE                 /* Specified on TABLE= subcommand. */
+  };
+
+/* One FILE or TABLE subcommand. */
+struct comb_file
+  {
+    /* Basics. */
+    enum comb_file_type type;   /* COMB_FILE or COMB_TABLE. */
+
+    /* Variables. */
+    struct subcase by_vars;     /* BY variables in this input file. */
+    struct subcase src, dst;    /* Data to copy to output; where to put it. */
+
+    /* Input files. */
+    struct file_handle *handle; /* Input file handle. */
+    struct dictionary *dict;   /* Input file dictionary. */
+    struct casereader *reader;  /* Input data source. */
+    struct ccase *data;         /* The current input case. */
+    bool is_minimal;            /* Does 'data' have minimum BY values across
+                                   all input files? */
+    bool is_sorted;             /* Is file presorted on the BY variables? */
+
+    /* IN subcommand. */
+    char in_name[VAR_NAME_LEN + 1];
+    struct variable *in_var;
+  };
+
+struct comb_proc
+  {
+    struct comb_file *files;    /* All the files being merged. */
+    size_t n_files;             /* Number of files. */
+
+    struct dictionary *dict;    /* Dictionary of output file. */
+    struct subcase by_vars;     /* BY variables in the output. */
+    struct casewriter *output;  /* Destination for output. */
+
+    struct case_matcher *matcher;
+
+    /* FIRST, LAST.
+       Only if "first" or "last" is nonnull are the remaining
+       members used. */
+    struct variable *first;     /* Variable specified on FIRST (if any). */
+    struct variable *last;      /* Variable specified on LAST (if any). */
+    struct ccase *buffered_case; /* Case ready for output except that we don't
+                                    know the value for the LAST var yet. */
+    union value *prev_BY;       /* Values of BY vars in buffered_case. */
+  };
+
+static int combine_files (enum comb_command_type, struct lexer *,
+                          struct dataset *);
+static void free_comb_proc (struct comb_proc *);
+
+static void close_all_comb_files (struct comb_proc *);
+static bool merge_dictionary (struct dictionary *const, struct comb_file *);
+
+static void execute_update (struct comb_proc *);
+static void execute_match_files (struct comb_proc *);
+static void execute_add_files (struct comb_proc *);
+
+static bool create_flag_var (const char *subcommand_name, const char *var_name,
+                             struct dictionary *, struct variable **);
+static void output_case (struct comb_proc *, struct ccase *, union value *by);
+static void output_buffered_case (struct comb_proc *);
+
+int
+cmd_add_files (struct lexer *lexer, struct dataset *ds)
+{
+  return combine_files (COMB_ADD, lexer, ds);
+}
+
+int
+cmd_match_files (struct lexer *lexer, struct dataset *ds)
+{
+  return combine_files (COMB_MATCH, lexer, ds);
+}
+
+int
+cmd_update (struct lexer *lexer, struct dataset *ds)
+{
+  return combine_files (COMB_UPDATE, lexer, ds);
+}
+
+static int
+combine_files (enum comb_command_type command,
+               struct lexer *lexer, struct dataset *ds)
+{
+  struct comb_proc proc;
+
+  bool saw_by = false;
+  bool saw_sort = false;
+  struct casereader *active_file = NULL;
+
+  char first_name[VAR_NAME_LEN + 1] = "";
+  char last_name[VAR_NAME_LEN + 1] = "";
+
+  struct taint *taint = NULL;
+
+  size_t n_tables = 0;
+  size_t allocated_files = 0;
+
+  size_t i;
+
+  proc.files = NULL;
+  proc.n_files = 0;
+  proc.dict = dict_create ();
+  proc.output = NULL;
+  proc.matcher = NULL;
+  subcase_init_empty (&proc.by_vars);
+  proc.first = NULL;
+  proc.last = NULL;
+  proc.buffered_case = NULL;
+  proc.prev_BY = NULL;
+
+  dict_set_case_limit (proc.dict, dict_get_case_limit (dataset_dict (ds)));
+
+  lex_match (lexer, '/');
+  for (;;)
+    {
+      struct comb_file *file;
+      enum comb_file_type type;
+
+      if (lex_match_id (lexer, "FILE"))
+        type = COMB_FILE;
+      else if (command == COMB_MATCH && lex_match_id (lexer, "TABLE"))
+        {
+          type = COMB_TABLE;
+          n_tables++;
+        }
+      else
+        break;
+      lex_match (lexer, '=');
+
+      if (proc.n_files >= allocated_files)
+        proc.files = x2nrealloc (proc.files, &allocated_files,
+                                sizeof *proc.files);
+      file = &proc.files[proc.n_files++];
+      file->type = type;
+      subcase_init_empty (&file->by_vars);
+      subcase_init_empty (&file->src);
+      subcase_init_empty (&file->dst);
+      file->handle = NULL;
+      file->dict = NULL;
+      file->reader = NULL;
+      file->data = NULL;
+      file->is_sorted = true;
+      file->in_name[0] = '\0';
+      file->in_var = NULL;
+
+      if (lex_match (lexer, '*'))
+        {
+          if (!proc_has_active_file (ds))
+            {
+              msg (SE, _("Cannot specify the active file since no active "
+                         "file has been defined."));
+              goto error;
+            }
+
+          if (proc_make_temporary_transformations_permanent (ds))
+            msg (SE, _("This command may not be used after TEMPORARY when "
+                       "the active file is an input source.  "
+                       "Temporary transformations will be made permanent."));
+
+          file->dict = dict_clone (dataset_dict (ds));
+        }
+      else
+        {
+          file->handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+          if (file->handle == NULL)
+            goto error;
+
+          file->reader = any_reader_open (file->handle, &file->dict);
+          if (file->reader == NULL)
+            goto error;
+        }
+
+      while (lex_match (lexer, '/'))
+        if (lex_match_id (lexer, "RENAME"))
+          {
+            if (!parse_dict_rename (lexer, file->dict))
+              goto error;
+          }
+        else if (lex_match_id (lexer, "IN"))
+          {
+            lex_match (lexer, '=');
+            if (lex_token (lexer) != T_ID)
+              {
+                lex_error (lexer, NULL);
+                goto error;
+              }
+
+            if (file->in_name[0])
+              {
+                msg (SE, _("Multiple IN subcommands for a single FILE or "
+                           "TABLE."));
+                goto error;
+              }
+            strcpy (file->in_name, lex_tokid (lexer));
+            lex_get (lexer);
+          }
+        else if (lex_match_id (lexer, "SORT"))
+          {
+            file->is_sorted = false;
+            saw_sort = true;
+          }
+
+      merge_dictionary (proc.dict, file);
+    }
+
+  while (lex_token (lexer) != '.')
+    {
+      if (lex_match (lexer, T_BY))
+       {
+          const struct variable **by_vars;
+          size_t i;
+          bool ok;
+
+         if (saw_by)
+           {
+              lex_sbc_only_once ("BY");
+             goto error;
+           }
+          saw_by = true;
+
+         lex_match (lexer, '=');
+          if (!parse_sort_criteria (lexer, proc.dict, &proc.by_vars,
+                                    &by_vars, NULL))
+           goto error;
+
+          ok = true;
+          for (i = 0; i < proc.n_files; i++)
+            {
+              struct comb_file *file = &proc.files[i];
+              size_t j;
+
+              for (j = 0; j < subcase_get_n_values (&proc.by_vars); j++)
+                {
+                  const char *name = var_get_name (by_vars[j]);
+                  struct variable *var = dict_lookup_var (file->dict, name);
+                  if (var != NULL)
+                    subcase_add_var (&file->by_vars, var,
+                                     subcase_get_direction (&proc.by_vars, j));
+                  else
+                    {
+                      if (file->handle != NULL)
+                        msg (SE, _("File %s lacks BY variable %s."),
+                             fh_get_name (file->handle), name);
+                      else
+                        msg (SE, _("Active file lacks BY variable %s."), name);
+                      ok = false;
+                    }
+                }
+              assert (!ok || subcase_conformable (&file->by_vars,
+                                                  &proc.files[0].by_vars));
+            }
+          free (by_vars);
+
+          if (!ok)
+            goto error;
+       }
+      else if (command != COMB_UPDATE && lex_match_id (lexer, "FIRST"))
+        {
+          if (first_name[0] != '\0')
+            {
+              lex_sbc_only_once ("FIRST");
+              goto error;
+            }
+
+         lex_match (lexer, '=');
+          if (!lex_force_id (lexer))
+            goto error;
+          strcpy (first_name, lex_tokid (lexer));
+          lex_get (lexer);
+        }
+      else if (command != COMB_UPDATE && lex_match_id (lexer, "LAST"))
+        {
+          if (last_name[0] != '\0')
+            {
+              lex_sbc_only_once ("LAST");
+              goto error;
+            }
+
+         lex_match (lexer, '=');
+          if (!lex_force_id (lexer))
+            goto error;
+          strcpy (last_name, lex_tokid (lexer));
+          lex_get (lexer);
+        }
+      else if (lex_match_id (lexer, "MAP"))
+       {
+         /* FIXME. */
+       }
+      else if (lex_match_id (lexer, "DROP"))
+        {
+          if (!parse_dict_drop (lexer, proc.dict))
+            goto error;
+        }
+      else if (lex_match_id (lexer, "KEEP"))
+        {
+          if (!parse_dict_keep (lexer, proc.dict))
+            goto error;
+        }
+      else
+       {
+         lex_error (lexer, NULL);
+         goto error;
+       }
+
+      if (!lex_match (lexer, '/') && lex_token (lexer) != '.')
+        {
+          lex_end_of_command (lexer);
+          goto error;
+        }
+    }
+
+  if (!saw_by)
+    {
+      if (command == COMB_UPDATE)
+        {
+          msg (SE, _("The BY subcommand is required."));
+          goto error;
+        }
+      if (n_tables)
+        {
+          msg (SE, _("BY is required when TABLE is specified."));
+          goto error;
+        }
+      if (saw_sort)
+        {
+          msg (SE, _("BY is required when SORT is specified."));
+          goto error;
+        }
+    }
+
+  /* Add IN, FIRST, and LAST variables to master dictionary. */
+  for (i = 0; i < proc.n_files; i++)
+    {
+      struct comb_file *file = &proc.files[i];
+      if (!create_flag_var ("IN", file->in_name, proc.dict, &file->in_var))
+        goto error;
+    }
+  if (!create_flag_var ("FIRST", first_name, proc.dict, &proc.first)
+      || !create_flag_var ("LAST", last_name, proc.dict, &proc.last))
+    goto error;
+
+  dict_delete_scratch_vars (proc.dict);
+  dict_compact_values (proc.dict);
+
+  /* Set up mapping from each file's variables to master
+     variables. */
+  for (i = 0; i < proc.n_files; i++)
+    {
+      struct comb_file *file = &proc.files[i];
+      size_t src_var_cnt = dict_get_var_cnt (file->dict);
+      size_t j;
+
+      for (j = 0; j < src_var_cnt; j++)
+        {
+          struct variable *src_var = dict_get_var (file->dict, j);
+          struct variable *dst_var = dict_lookup_var (proc.dict,
+                                                      var_get_name (src_var));
+          if (dst_var != NULL)
+            {
+              subcase_add_var (&file->src, src_var, SC_ASCEND);
+              subcase_add_var (&file->dst, dst_var, SC_ASCEND);
+            }
+        }
+    }
+
+  proc.output = autopaging_writer_create (dict_get_next_value_idx (proc.dict));
+  taint = taint_clone (casewriter_get_taint (proc.output));
+
+  /* Set up case matcher. */
+  proc.matcher = case_matcher_create ();
+  for (i = 0; i < proc.n_files; i++)
+    {
+      struct comb_file *file = &proc.files[i];
+      if (file->reader == NULL)
+        {
+          if (active_file == NULL)
+            {
+              proc_discard_output (ds);
+              file->reader = active_file = proc_open (ds);
+            }
+          else
+            file->reader = casereader_clone (active_file);
+        }
+      if (!file->is_sorted)
+        file->reader = sort_execute (file->reader, &file->by_vars);
+      taint_propagate (casereader_get_taint (file->reader), taint);
+      file->data = casereader_read (file->reader);
+      if (file->type == COMB_FILE)
+        case_matcher_add_input (proc.matcher, &file->by_vars,
+                                &file->data, &file->is_minimal);
+    }
+
+  if (command == COMB_ADD)
+    execute_add_files (&proc);
+  else if (command == COMB_MATCH)
+    execute_match_files (&proc);
+  else if (command == COMB_UPDATE)
+    execute_update (&proc);
+  else
+    NOT_REACHED ();
+
+  case_matcher_destroy (proc.matcher);
+  proc.matcher = NULL;
+  close_all_comb_files (&proc);
+  if (active_file != NULL)
+    proc_commit (ds);
+
+  proc_set_active_file (ds, casewriter_make_reader (proc.output), proc.dict);
+  proc.dict = NULL;
+  proc.output = NULL;
+
+  free_comb_proc (&proc);
+
+  return taint_destroy (taint) ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+
+ error:
+  if (active_file != NULL)
+    proc_commit (ds);
+  free_comb_proc (&proc);
+  taint_destroy (taint);
+  return CMD_CASCADING_FAILURE;
+}
+
+/* Merge the dictionary for file F into master dictionary M. */
+static bool
+merge_dictionary (struct dictionary *const m, struct comb_file *f)
+{
+  struct dictionary *d = f->dict;
+  const char *d_docs, *m_docs;
+  int i;
+
+  if (dict_get_label (m) == NULL)
+    dict_set_label (m, dict_get_label (d));
+
+  d_docs = dict_get_documents (d);
+  m_docs = dict_get_documents (m);
+  if (d_docs != NULL)
+    {
+      if (m_docs == NULL)
+        dict_set_documents (m, d_docs);
+      else
+        {
+          char *new_docs = xasprintf ("%s%s", m_docs, d_docs);
+          dict_set_documents (m, new_docs);
+          free (new_docs);
+        }
+    }
+
+  for (i = 0; i < dict_get_var_cnt (d); i++)
+    {
+      struct variable *dv = dict_get_var (d, i);
+      struct variable *mv = dict_lookup_var (m, var_get_name (dv));
+
+      if (dict_class_from_id (var_get_name (dv)) == DC_SCRATCH)
+        continue;
+
+      if (mv != NULL)
+        {
+          if (var_get_width (mv) != var_get_width (dv))
+            {
+              const char *var_name = var_get_name (dv);
+              const char *file_name = fh_get_name (f->handle);
+              struct string s = DS_EMPTY_INITIALIZER;
+              ds_put_format (&s,
+                             _("Variable %s in file %s has different "
+                               "type or width from the same variable in "
+                               "earlier file."),
+                             var_name, file_name);
+              ds_put_cstr (&s, "  ");
+              if (var_is_numeric (dv))
+                ds_put_format (&s, _("In file %s, %s is numeric."),
+                               file_name, var_name);
+              else
+                ds_put_format (&s, _("In file %s, %s is a string variable "
+                                     "with width %d."),
+                               file_name, var_name, var_get_width (dv));
+              ds_put_cstr (&s, "  ");
+              if (var_is_numeric (mv))
+                ds_put_format (&s, _("In an earlier file, %s was numeric."),
+                               var_name);
+              else
+                ds_put_format (&s, _("In an earlier file, %s was a string "
+                                     "variable with width %d."),
+                               var_name, var_get_width (mv));
+              msg (SE, ds_cstr (&s));
+              ds_destroy (&s);
+              return false;
+            }
+
+          if (var_has_value_labels (dv) && !var_has_value_labels (mv))
+            var_set_value_labels (mv, var_get_value_labels (dv));
+          if (var_has_missing_values (dv) && !var_has_missing_values (mv))
+            var_set_missing_values (mv, var_get_missing_values (dv));
+          if (var_get_label (dv) && !var_get_label (mv))
+            var_set_label (mv, var_get_label (dv));
+        }
+      else
+        mv = dict_clone_var_assert (m, dv, var_get_name (dv));
+    }
+
+  return true;
+}
+
+/* If VAR_NAME is a non-empty string, attempts to create a
+   variable named VAR_NAME, with format F1.0, in DICT, and stores
+   a pointer to the variable in *VAR.  Returns true if
+   successful, false if the variable name is a duplicate (in
+   which case a message saying that the variable specified on the
+   given SUBCOMMAND is a duplicate is emitted).  Also returns
+   true, without doing anything, if VAR_NAME is null or empty. */
+static bool
+create_flag_var (const char *subcommand, const char *var_name,
+                 struct dictionary *dict, struct variable **var)
+{
+  if (var_name[0] != '\0')
+    {
+      struct fmt_spec format = fmt_for_output (FMT_F, 1, 0);
+      *var = dict_create_var (dict, var_name, 0);
+      if (*var == NULL)
+        {
+          msg (SE, _("Variable name %s specified on %s subcommand "
+                     "duplicates an existing variable name."),
+               subcommand, var_name);
+          return false;
+        }
+      var_set_both_formats (*var, &format);
+    }
+  else
+    *var = NULL;
+  return true;
+}
+
+/* Closes all the files in PROC and frees their associated data. */
+static void
+close_all_comb_files (struct comb_proc *proc)
+{
+  size_t i;
+
+  for (i = 0; i < proc->n_files; i++)
+    {
+      struct comb_file *file = &proc->files[i];
+      subcase_destroy (&file->by_vars);
+      subcase_destroy (&file->src);
+      subcase_destroy (&file->dst);
+      fh_unref (file->handle);
+      dict_destroy (file->dict);
+      casereader_destroy (file->reader);
+      case_unref (file->data);
+    }
+  free (proc->files);
+  proc->files = NULL;
+  proc->n_files = 0;
+}
+
+/* Frees all the data for the procedure. */
+static void
+free_comb_proc (struct comb_proc *proc)
+{
+  close_all_comb_files (proc);
+  dict_destroy (proc->dict);
+  casewriter_destroy (proc->output);
+  case_matcher_destroy (proc->matcher);
+  subcase_destroy (&proc->by_vars);
+  case_unref (proc->buffered_case);
+  free (proc->prev_BY);
+}
+\f
+static bool scan_table (struct comb_file *, union value by[]);
+static struct ccase *create_output_case (const struct comb_proc *);
+static void apply_case (const struct comb_file *, struct ccase *);
+static void apply_file_case_and_advance (struct comb_file *, struct ccase *,
+                                         union value by[]);
+static void output_case (struct comb_proc *, struct ccase *, union value by[]);
+static void output_buffered_case (struct comb_proc *);
+
+/* Executes the ADD FILES command. */
+static void
+execute_add_files (struct comb_proc *proc)
+{
+  union value *by;
+
+  while (case_matcher_match (proc->matcher, &by))
+    {
+      size_t i;
+
+      for (i = 0; i < proc->n_files; i++)
+        {
+          struct comb_file *file = &proc->files[i];
+          while (file->is_minimal)
+            {
+              struct ccase *output = create_output_case (proc);
+              apply_file_case_and_advance (file, output, by);
+              output_case (proc, output, by);
+            }
+        }
+    }
+  output_buffered_case (proc);
+}
+
+/* Executes the MATCH FILES command. */
+static void
+execute_match_files (struct comb_proc *proc)
+{
+  union value *by;
+
+  while (case_matcher_match (proc->matcher, &by))
+    {
+      struct ccase *output;
+      size_t i;
+
+      output = create_output_case (proc);
+      for (i = proc->n_files; i-- > 0; )
+        {
+          struct comb_file *file = &proc->files[i];
+          if (file->type == COMB_FILE)
+            {
+              if (file->is_minimal)
+                apply_file_case_and_advance (file, output, NULL);
+            }
+          else
+            {
+              if (scan_table (file, by))
+                apply_case (file, output);
+            }
+        }
+      output_case (proc, output, by);
+    }
+  output_buffered_case (proc);
+}
+
+/* Executes the UPDATE command. */
+static void
+execute_update (struct comb_proc *proc)
+{
+  union value *by;
+  size_t n_duplicates = 0;
+
+  while (case_matcher_match (proc->matcher, &by))
+    {
+      struct comb_file *first, *file;
+      struct ccase *output;
+
+      /* Find first nonnull case in array and make an output case
+         from it. */
+      output = create_output_case (proc);
+      for (first = &proc->files[0]; ; first++)
+        if (first->is_minimal)
+          break;
+      apply_file_case_and_advance (first, output, by);
+
+      /* Read additional cases and update the output case from
+         them.  (Don't update the output case from any duplicate
+         cases in the master file.) */
+      for (file = first + (first == proc->files);
+           file < &proc->files[proc->n_files]; file++)
+        {
+          while (file->is_minimal)
+            apply_file_case_and_advance (file, output, by);
+        }
+      casewriter_write (proc->output, output);
+
+      /* Write duplicate cases in the master file directly to the
+         output.  */
+      if (first == proc->files && first->is_minimal)
+        {
+          n_duplicates++;
+          while (first->is_minimal)
+            {
+              output = create_output_case (proc);
+              apply_file_case_and_advance (first, output, by);
+              casewriter_write (proc->output, output);
+            }
+        }
+    }
+
+  if (n_duplicates)
+    msg (SW, _("Encountered %zu sets of duplicate cases in the master file."),
+         n_duplicates);
+}
+
+/* Reads FILE, which must be of type COMB_TABLE, until it
+   encounters a case with BY or greater for its BY variables.
+   Returns true if a case with exactly BY for its BY variables
+   was found, otherwise false. */
+static bool
+scan_table (struct comb_file *file, union value by[])
+{
+  while (file->data != NULL)
+    {
+      int cmp = subcase_compare_3way_xc (&file->by_vars, by, file->data);
+      if (cmp > 0)
+        {
+          case_unref (file->data);
+          file->data = casereader_read (file->reader);
+        }
+      else
+        return cmp == 0;
+    }
+  return false;
+}
+
+/* Creates and returns an output case for PROC, initializing each
+   of its values to system-missing or blanks, except that the
+   values of IN variables are set to 0. */
+static struct ccase *
+create_output_case (const struct comb_proc *proc)
+{
+  size_t n_vars = dict_get_var_cnt (proc->dict);
+  struct ccase *output;
+  size_t i;
+
+  output = case_create (dict_get_next_value_idx (proc->dict));
+  for (i = 0; i < n_vars; i++)
+    {
+      struct variable *v = dict_get_var (proc->dict, i);
+      value_set_missing (case_data_rw (output, v), var_get_width (v));
+    }
+  for (i = 0; i < proc->n_files; i++)
+    {
+      struct comb_file *file = &proc->files[i];
+      if (file->in_var != NULL)
+        case_data_rw (output, file->in_var)->f = false;
+    }
+  return output;
+}
+
+/* Copies the data from FILE's case into output case OUTPUT.
+   If FILE has an IN variable, then it is set to 1 in OUTPUT. */
+static void
+apply_case (const struct comb_file *file, struct ccase *output)
+{
+  subcase_copy (&file->src, file->data, &file->dst, output);
+  if (file->in_var != NULL)
+    case_data_rw (output, file->in_var)->f = true;
+}
+
+/* Like apply_case() above, but also advances FILE to its next
+   case.  Also, if BY is nonnull, then FILE's is_minimal member
+   is updated based on whether the new case's BY values still
+   match those in BY. */
+static void
+apply_file_case_and_advance (struct comb_file *file, struct ccase *output,
+                             union value by[])
+{
+  apply_case (file, output);
+  case_unref (file->data);
+  file->data = casereader_read (file->reader);
+  if (by)
+    file->is_minimal = (file->data != NULL
+                        && subcase_equal_cx (&file->by_vars, file->data, by));
+}
+
+/* Writes OUTPUT, whose BY values has been extracted into BY, to
+   PROC's output file, first initializing any FIRST or LAST
+   variables in OUTPUT to the correct values. */
+static void
+output_case (struct comb_proc *proc, struct ccase *output, union value by[])
+{
+  if (proc->first == NULL && proc->last == NULL)
+    casewriter_write (proc->output, output);
+  else
+    {
+      /* It's harder with LAST, because we can't know whether
+         this case is the last in a group until we've prepared
+         the *next* case also.  Thus, we buffer the previous
+         output case until the next one is ready. */
+      bool new_BY;
+      if (proc->prev_BY != NULL)
+        {
+          new_BY = !subcase_equal_xx (&proc->by_vars, proc->prev_BY, by);
+          if (proc->last != NULL)
+            case_data_rw (proc->buffered_case, proc->last)->f = new_BY;
+          casewriter_write (proc->output, proc->buffered_case);
+        }
+      else
+        new_BY = true;
+
+      proc->buffered_case = output;
+      if (proc->first != NULL)
+        case_data_rw (proc->buffered_case, proc->first)->f = new_BY;
+
+      if (new_BY)
+        {
+          size_t n = (subcase_get_n_values (&proc->by_vars)
+                      * sizeof (union value));
+          if (proc->prev_BY == NULL)
+            proc->prev_BY = xmalloc (n);
+          memcpy (proc->prev_BY, by, n);
+        }
+    }
+}
+
+/* Writes a trailing buffered case to the output, if FIRST or
+   LAST is in use. */
+static void
+output_buffered_case (struct comb_proc *proc)
+{
+  if (proc->prev_BY != NULL)
+    {
+      if (proc->last != NULL)
+        case_data_rw (proc->buffered_case, proc->last)->f = 1.0;
+      casewriter_write (proc->output, proc->buffered_case);
+      proc->buffered_case = NULL;
+    }
+}
index 7a2a074b59e763eebe322780c96e86f057991ed6..d07eae5c554f7cb19b665175e2a45f9146e08102 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -463,14 +463,15 @@ data_list_trns_free (void *trns_)
   return true;
 }
 
-/* Handle DATA LIST transformation TRNS, parsing data into C. */
+/* Handle DATA LIST transformation TRNS, parsing data into *C. */
 static int
-data_list_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
+data_list_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
 {
   struct data_list_trns *trns = trns_;
   int retval;
 
-  if (data_parser_parse (trns->parser, trns->reader, c))
+  *c = case_unshare (*c);
+  if (data_parser_parse (trns->parser, trns->reader, *c))
     retval = TRNS_CONTINUE;
   else if (dfm_reader_error (trns->reader) || dfm_eof (trns->reader) > 1)
     {
@@ -484,7 +485,7 @@ data_list_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
   /* If there was an END subcommand handle it. */
   if (trns->end != NULL)
     {
-      double *end = &case_data_rw (c, trns->end)->f;
+      double *end = &case_data_rw (*c, trns->end)->f;
       if (retval == TRNS_END_FILE)
         {
           *end = 1.0;
index dfc04be44c52b2cfc5f09a2b2dfc9432018ca10d..87fd1b7919f6de9de5ec076dd1c1dc794840f47f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -364,14 +364,17 @@ static bool parse_delimited_no_span (const struct data_parser *,
 static bool parse_fixed (const struct data_parser *,
                          struct dfm_reader *, struct ccase *);
 
-/* Reads a case from DFM into C, parsing it with PARSER.
-   Returns true if successful, false at end of file or on I/O error. */
+/* Reads a case from DFM into C, parsing it with PARSER.  Returns
+   true if successful, false at end of file or on I/O error.
+
+   Case C must not be shared. */
 bool
 data_parser_parse (struct data_parser *parser, struct dfm_reader *reader,
                    struct ccase *c)
 {
   bool retval;
 
+  assert (!case_is_shared (c));
   assert (data_parser_any_fields (parser));
 
   /* Skip the requested number of records before reading the
@@ -746,18 +749,18 @@ data_parser_make_active_file (struct data_parser *parser, struct dataset *ds,
   proc_set_active_file (ds, casereader, dict);
 }
 
-static bool
-data_parser_casereader_read (struct casereader *reader UNUSED, void *r_,
-                           struct ccase *c)
+static struct ccase *
+data_parser_casereader_read (struct casereader *reader UNUSED, void *r_)
 {
   struct data_parser_casereader *r = r_;
-  bool ok;
-
-  case_create (c, r->value_cnt);
-  ok = data_parser_parse (r->parser, r->reader, c);
-  if (!ok)
-    case_destroy (c);
-  return ok;
+  struct ccase *c = case_create (r->value_cnt);
+  if (data_parser_parse (r->parser, r->reader, c))
+    return c;
+  else
+    {
+      case_unref (c);
+      return NULL;
+    }
 }
 
 static void
index 4976d3f42e69712ff52bc82f38d6432b5f5f3359..b250e91bb53d2d7cf7b628fd6f41cb8113f7f30b 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <stdbool.h>
 #include <data/case.h>
+#include <libpspp/str.h>
 
 struct dataset;
 struct dfm_reader;
index f8a84b142919d59d43f31222bf42c45fdb747b8d..32aea2476cb58cd4c3b56674b0dec35b335874e8 100644 (file)
 #include <stdlib.h>
 
 #include <data/any-reader.h>
-#include <data/any-writer.h>
 #include <data/case.h>
 #include <data/case-map.h>
 #include <data/casereader.h>
-#include <data/casewriter.h>
-#include <data/format.h>
 #include <data/dictionary.h>
 #include <data/por-file-writer.h>
 #include <data/procedure.h>
-#include <data/settings.h>
-#include <data/sys-file-writer.h>
-#include <data/transformations.h>
-#include <data/value-labels.h>
-#include <data/variable.h>
 #include <language/command.h>
 #include <language/data-io/file-handle.h>
+#include <language/data-io/trim.h>
 #include <language/lexer/lexer.h>
-#include <language/lexer/variable-parser.h>
-#include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
-#include <libpspp/hash.h>
-#include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
-#include <libpspp/taint.h>
 
 #include "xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
-
-static bool parse_dict_trim (struct lexer *, struct dictionary *);
 \f
 /* Reading system and portable files. */
 
@@ -61,8 +47,22 @@ enum reader_command
     IMPORT_CMD
   };
 
-static void get_translate_case (struct ccase *, struct ccase *, void *map_);
-static bool get_destroy_case_map (void *map_);
+static int parse_read_command (struct lexer *, struct dataset *,
+                               enum reader_command);
+
+/* GET. */
+int
+cmd_get (struct lexer *lexer, struct dataset *ds)
+{
+  return parse_read_command (lexer, ds, GET_CMD);
+}
+
+/* IMPORT. */
+int
+cmd_import (struct lexer *lexer, struct dataset *ds)
+{
+  return parse_read_command (lexer, ds, IMPORT_CMD);
+}
 
 /* Parses a GET or IMPORT command. */
 static int
@@ -126,11 +126,7 @@ parse_read_command (struct lexer *lexer, struct dataset *ds, enum reader_command
 
   map = case_map_from_dict (dict);
   if (map != NULL)
-    reader = casereader_create_translator (reader,
-                                           dict_get_next_value_idx (dict),
-                                           get_translate_case,
-                                           get_destroy_case_map,
-                                           map);
+    reader = case_map_create_input_translator (map, reader);
 
   proc_set_active_file (ds, reader, dict);
 
@@ -144,1186 +140,3 @@ parse_read_command (struct lexer *lexer, struct dataset *ds, enum reader_command
     dict_destroy (dict);
   return CMD_CASCADING_FAILURE;
 }
-
-static void
-get_translate_case (struct ccase *input, struct ccase *output,
-                    void *map_)
-{
-  struct case_map *map = map_;
-  case_map_execute (map, input, output);
-  case_destroy (input);
-}
-
-static bool
-get_destroy_case_map (void *map_)
-{
-  struct case_map *map = map_;
-  case_map_destroy (map);
-  return true;
-}
-\f
-/* GET. */
-int
-cmd_get (struct lexer *lexer, struct dataset *ds)
-{
-  return parse_read_command (lexer, ds, GET_CMD);
-}
-
-/* IMPORT. */
-int
-cmd_import (struct lexer *lexer, struct dataset *ds)
-{
-  return parse_read_command (lexer, ds, IMPORT_CMD);
-}
-\f
-/* Writing system and portable files. */
-
-/* Type of output file. */
-enum writer_type
-  {
-    SYSFILE_WRITER,     /* System file. */
-    PORFILE_WRITER      /* Portable file. */
-  };
-
-/* Type of a command. */
-enum command_type
-  {
-    XFORM_CMD,          /* Transformation. */
-    PROC_CMD            /* Procedure. */
-  };
-
-/* Parses SAVE or XSAVE or EXPORT or XEXPORT command.
-   WRITER_TYPE identifies the type of file to write,
-   and COMMAND_TYPE identifies the type of command.
-
-   On success, returns a writer.
-   For procedures only, sets *RETAIN_UNSELECTED to true if cases
-   that would otherwise be excluded by FILTER or USE should be
-   included.
-
-   On failure, returns a null pointer. */
-static struct casewriter *
-parse_write_command (struct lexer *lexer, struct dataset *ds,
-                    enum writer_type writer_type,
-                     enum command_type command_type,
-                     bool *retain_unselected)
-{
-  /* Common data. */
-  struct file_handle *handle; /* Output file. */
-  struct dictionary *dict;    /* Dictionary for output file. */
-  struct casewriter *writer;  /* Writer. */
-  struct case_map *map;       /* Map from input data to data for writer. */
-
-  /* Common options. */
-  bool print_map;             /* Print map?  TODO. */
-  bool print_short_names;     /* Print long-to-short name map.  TODO. */
-  struct sfm_write_options sysfile_opts;
-  struct pfm_write_options porfile_opts;
-
-  assert (writer_type == SYSFILE_WRITER || writer_type == PORFILE_WRITER);
-  assert (command_type == XFORM_CMD || command_type == PROC_CMD);
-  assert ((retain_unselected != NULL) == (command_type == PROC_CMD));
-
-  if (command_type == PROC_CMD)
-    *retain_unselected = true;
-
-  handle = NULL;
-  dict = dict_clone (dataset_dict (ds));
-  writer = NULL;
-  map = NULL;
-  print_map = false;
-  print_short_names = false;
-  sysfile_opts = sfm_writer_default_options ();
-  porfile_opts = pfm_writer_default_options ();
-
-  case_map_prepare_dict (dict);
-  dict_delete_scratch_vars (dict);
-
-  lex_match (lexer, '/');
-  for (;;)
-    {
-      if (lex_match_id (lexer, "OUTFILE"))
-       {
-          if (handle != NULL)
-            {
-              lex_sbc_only_once ("OUTFILE");
-              goto error;
-            }
-
-         lex_match (lexer, '=');
-
-         handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
-         if (handle == NULL)
-           goto error;
-       }
-      else if (lex_match_id (lexer, "NAMES"))
-        print_short_names = true;
-      else if (lex_match_id (lexer, "PERMISSIONS"))
-        {
-          bool cw;
-
-          lex_match (lexer, '=');
-          if (lex_match_id (lexer, "READONLY"))
-            cw = false;
-          else if (lex_match_id (lexer, "WRITEABLE"))
-            cw = true;
-          else
-            {
-              lex_error (lexer, _("expecting %s or %s"), "READONLY", "WRITEABLE");
-              goto error;
-            }
-          sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
-        }
-      else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED"))
-        {
-          lex_match (lexer, '=');
-          if (lex_match_id (lexer, "RETAIN"))
-            *retain_unselected = true;
-          else if (lex_match_id (lexer, "DELETE"))
-            *retain_unselected = false;
-          else
-            {
-              lex_error (lexer, _("expecting %s or %s"), "RETAIN", "DELETE");
-              goto error;
-            }
-        }
-      else if (writer_type == SYSFILE_WRITER && lex_match_id (lexer, "COMPRESSED"))
-       sysfile_opts.compress = true;
-      else if (writer_type == SYSFILE_WRITER && lex_match_id (lexer, "UNCOMPRESSED"))
-       sysfile_opts.compress = false;
-      else if (writer_type == SYSFILE_WRITER && lex_match_id (lexer, "VERSION"))
-       {
-         lex_match (lexer, '=');
-         if (!lex_force_int (lexer))
-            goto error;
-          sysfile_opts.version = lex_integer (lexer);
-          lex_get (lexer);
-       }
-      else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
-        {
-          lex_match (lexer, '=');
-          if (lex_match_id (lexer, "COMMUNICATIONS"))
-            porfile_opts.type = PFM_COMM;
-          else if (lex_match_id (lexer, "TAPE"))
-            porfile_opts.type = PFM_TAPE;
-          else
-            {
-              lex_error (lexer, _("expecting %s or %s"), "COMM", "TAPE");
-              goto error;
-            }
-        }
-      else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
-        {
-          lex_match (lexer, '=');
-          if (!lex_force_int (lexer))
-            goto error;
-          porfile_opts.digits = lex_integer (lexer);
-          lex_get (lexer);
-        }
-      else if (!parse_dict_trim (lexer, dict))
-        goto error;
-
-      if (!lex_match (lexer, '/'))
-       break;
-    }
-  if (lex_end_of_command (lexer) != CMD_SUCCESS)
-    goto error;
-
-  if (handle == NULL)
-    {
-      lex_sbc_missing (lexer, "OUTFILE");
-      goto error;
-    }
-
-  dict_delete_scratch_vars (dict);
-  dict_compact_values (dict);
-
-  if (fh_get_referent (handle) == FH_REF_FILE)
-    {
-      switch (writer_type)
-        {
-        case SYSFILE_WRITER:
-          writer = sfm_open_writer (handle, dict, sysfile_opts);
-          break;
-        case PORFILE_WRITER:
-          writer = pfm_open_writer (handle, dict, porfile_opts);
-          break;
-        }
-    }
-  else
-    writer = any_writer_open (handle, dict);
-  if (writer == NULL)
-    goto error;
-
-  map = case_map_from_dict (dict);
-  if (map != NULL)
-    writer = casewriter_create_translator (writer,
-                                           case_map_get_value_cnt (map),
-                                           get_translate_case,
-                                           get_destroy_case_map,
-                                           map);
-  dict_destroy (dict);
-
-  fh_unref (handle);
-  return writer;
-
- error:
-  fh_unref (handle);
-  casewriter_destroy (writer);
-  dict_destroy (dict);
-  case_map_destroy (map);
-  return NULL;
-}
-\f
-/* SAVE and EXPORT. */
-
-/* Parses and performs the SAVE or EXPORT procedure. */
-static int
-parse_output_proc (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type)
-{
-  bool retain_unselected;
-  struct variable *saved_filter_variable;
-  struct casewriter *output;
-  bool ok;
-
-  output = parse_write_command (lexer, ds, writer_type, PROC_CMD,
-                                &retain_unselected);
-  if (output == NULL)
-    return CMD_CASCADING_FAILURE;
-
-  saved_filter_variable = dict_get_filter (dataset_dict (ds));
-  if (retain_unselected)
-    dict_set_filter (dataset_dict (ds), NULL);
-
-  casereader_transfer (proc_open (ds), output);
-  ok = casewriter_destroy (output);
-  ok = proc_commit (ds) && ok;
-
-  dict_set_filter (dataset_dict (ds), saved_filter_variable);
-
-  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
-}
-
-int
-cmd_save (struct lexer *lexer, struct dataset *ds)
-{
-  return parse_output_proc (lexer, ds, SYSFILE_WRITER);
-}
-
-int
-cmd_export (struct lexer *lexer, struct dataset *ds)
-{
-  return parse_output_proc (lexer, ds, PORFILE_WRITER);
-}
-\f
-/* XSAVE and XEXPORT. */
-
-/* Transformation. */
-struct output_trns
-  {
-    struct casewriter *writer;          /* Writer. */
-  };
-
-static trns_proc_func output_trns_proc;
-static trns_free_func output_trns_free;
-
-/* Parses the XSAVE or XEXPORT transformation command. */
-static int
-parse_output_trns (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type)
-{
-  struct output_trns *t = xmalloc (sizeof *t);
-  t->writer = parse_write_command (lexer, ds, writer_type, XFORM_CMD, NULL);
-  if (t->writer == NULL)
-    {
-      free (t);
-      return CMD_CASCADING_FAILURE;
-    }
-
-  add_transformation (ds, output_trns_proc, output_trns_free, t);
-  return CMD_SUCCESS;
-}
-
-/* Writes case C to the system file specified on XSAVE or XEXPORT. */
-static int
-output_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
-{
-  struct output_trns *t = trns_;
-  struct ccase tmp;
-  case_clone (&tmp, c);
-  casewriter_write (t->writer, &tmp);
-  return TRNS_CONTINUE;
-}
-
-/* Frees an XSAVE or XEXPORT transformation.
-   Returns true if successful, false if an I/O error occurred. */
-static bool
-output_trns_free (void *trns_)
-{
-  struct output_trns *t = trns_;
-  bool ok = casewriter_destroy (t->writer);
-  free (t);
-  return ok;
-}
-
-/* XSAVE command. */
-int
-cmd_xsave (struct lexer *lexer, struct dataset *ds)
-{
-  return parse_output_trns (lexer, ds, SYSFILE_WRITER);
-}
-
-/* XEXPORT command. */
-int
-cmd_xexport (struct lexer *lexer, struct dataset *ds)
-{
-  return parse_output_trns (lexer, ds, PORFILE_WRITER);
-}
-\f
-static bool rename_variables (struct lexer *lexer, struct dictionary *dict);
-static bool drop_variables (struct lexer *, struct dictionary *dict);
-static bool keep_variables (struct lexer *, struct dictionary *dict);
-
-/* Commands that read and write system files share a great deal
-   of common syntactic structure for rearranging and dropping
-   variables.  This function parses this syntax and modifies DICT
-   appropriately.  Returns true on success, false on failure. */
-static bool
-parse_dict_trim (struct lexer *lexer, struct dictionary *dict)
-{
-  if (lex_match_id (lexer, "MAP"))
-    {
-      /* FIXME. */
-      return true;
-    }
-  else if (lex_match_id (lexer, "DROP"))
-    return drop_variables (lexer, dict);
-  else if (lex_match_id (lexer, "KEEP"))
-    return keep_variables (lexer, dict);
-  else if (lex_match_id (lexer, "RENAME"))
-    return rename_variables (lexer, dict);
-  else
-    {
-      lex_error (lexer, _("expecting a valid subcommand"));
-      return false;
-    }
-}
-
-/* Parses and performs the RENAME subcommand of GET and SAVE. */
-static bool
-rename_variables (struct lexer *lexer, struct dictionary *dict)
-{
-  size_t i;
-
-  int success = 0;
-
-  struct variable **v;
-  char **new_names;
-  size_t nv, nn;
-  char *err_name;
-
-  int group;
-
-  lex_match (lexer, '=');
-  if (lex_token (lexer) != '(')
-    {
-      struct variable *v;
-
-      v = parse_variable (lexer, dict);
-      if (v == NULL)
-       return 0;
-      if (!lex_force_match (lexer, '=')
-         || !lex_force_id (lexer))
-       return 0;
-      if (dict_lookup_var (dict, lex_tokid (lexer)) != NULL)
-       {
-         msg (SE, _("Cannot rename %s as %s because there already exists "
-                    "a variable named %s.  To rename variables with "
-                    "overlapping names, use a single RENAME subcommand "
-                    "such as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, "
-                    "\"/RENAME (A B C=B C A)\"."),
-               var_get_name (v), lex_tokid (lexer), lex_tokid (lexer));
-         return 0;
-       }
-
-      dict_rename_var (dict, v, lex_tokid (lexer));
-      lex_get (lexer);
-      return 1;
-    }
-
-  nv = nn = 0;
-  v = NULL;
-  new_names = 0;
-  group = 1;
-  while (lex_match (lexer, '('))
-    {
-      size_t old_nv = nv;
-
-      if (!parse_variables (lexer, dict, &v, &nv, PV_NO_DUPLICATE | PV_APPEND))
-       goto done;
-      if (!lex_match (lexer, '='))
-       {
-         msg (SE, _("`=' expected after variable list."));
-         goto done;
-       }
-      if (!parse_DATA_LIST_vars (lexer, &new_names, &nn, PV_APPEND | PV_NO_SCRATCH))
-       goto done;
-      if (nn != nv)
-       {
-         msg (SE, _("Number of variables on left side of `=' (%zu) does not "
-                     "match number of variables on right side (%zu), in "
-                     "parenthesized group %d of RENAME subcommand."),
-              nv - old_nv, nn - old_nv, group);
-         goto done;
-       }
-      if (!lex_force_match (lexer, ')'))
-       goto done;
-      group++;
-    }
-
-  if (!dict_rename_vars (dict, v, new_names, nv, &err_name))
-    {
-      msg (SE, _("Requested renaming duplicates variable name %s."), err_name);
-      goto done;
-    }
-  success = 1;
-
- done:
-  for (i = 0; i < nn; i++)
-    free (new_names[i]);
-  free (new_names);
-  free (v);
-
-  return success;
-}
-
-/* Parses and performs the DROP subcommand of GET and SAVE.
-   Returns true if successful, false on failure.*/
-static bool
-drop_variables (struct lexer *lexer, struct dictionary *dict)
-{
-  struct variable **v;
-  size_t nv;
-
-  lex_match (lexer, '=');
-  if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
-    return false;
-  dict_delete_vars (dict, v, nv);
-  free (v);
-
-  if (dict_get_var_cnt (dict) == 0)
-    {
-      msg (SE, _("Cannot DROP all variables from dictionary."));
-      return false;
-    }
-  return true;
-}
-
-/* Parses and performs the KEEP subcommand of GET and SAVE.
-   Returns true if successful, false on failure.*/
-static bool
-keep_variables (struct lexer *lexer, struct dictionary *dict)
-{
-  struct variable **v;
-  size_t nv;
-  size_t i;
-
-  lex_match (lexer, '=');
-  if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
-    return false;
-
-  /* Move the specified variables to the beginning. */
-  dict_reorder_vars (dict, v, nv);
-
-  /* Delete the remaining variables. */
-  v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v);
-  for (i = nv; i < dict_get_var_cnt (dict); i++)
-    v[i - nv] = dict_get_var (dict, i);
-  dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv);
-  free (v);
-
-  return true;
-}
-\f
-/* MATCH FILES. */
-
-/* File types. */
-enum mtf_type
-  {
-    MTF_FILE,                  /* Specified on FILE= subcommand. */
-    MTF_TABLE                  /* Specified on TABLE= subcommand. */
-  };
-
-/* One of the FILEs or TABLEs on MATCH FILES. */
-struct mtf_file
-  {
-    struct ll ll;               /* In list of all files and tables. */
-
-    enum mtf_type type;
-    int sequence;
-
-    const struct variable **by; /* List of BY variables for this file. */
-    struct mtf_variable *vars;  /* Variables to copy to output. */
-    size_t var_cnt;             /* Number of other variables. */
-
-    struct file_handle *handle; /* Input file handle. */
-    struct dictionary *dict;   /* Input file dictionary. */
-    struct casereader *reader;  /* Input reader. */
-    struct ccase input;         /* Input record (null at end of file). */
-
-    /* IN subcommand. */
-    char *in_name;              /* Variable name. */
-    struct variable *in_var;    /* Variable (in master dictionary). */
-  };
-
-struct mtf_variable
-  {
-    struct variable *in_var;
-    struct variable *out_var;
-  };
-
-/* MATCH FILES procedure. */
-struct mtf_proc
-  {
-    struct ll_list files;       /* List of "struct mtf_file"s. */
-    int nonempty_files;         /* FILEs that are not at end-of-file. */
-
-    bool ok;                    /* False if I/O error occurs. */
-
-    struct dictionary *dict;    /* Dictionary of output file. */
-    struct casewriter *output;  /* MATCH FILES output. */
-
-    size_t by_cnt;              /* Number of variables on BY subcommand. */
-
-    /* FIRST, LAST.
-       Only if "first" or "last" is nonnull are the remaining
-       members used. */
-    struct variable *first;     /* Variable specified on FIRST (if any). */
-    struct variable *last;      /* Variable specified on LAST (if any). */
-    struct ccase buffered_case; /* Case ready for output except that we don't
-                                   know the value for the LAST variable yet. */
-    struct ccase prev_BY_case;  /* Case with values of last set of BY vars. */
-    const struct variable **prev_BY;  /* Last set of BY variables. */
-  };
-
-static void mtf_free (struct mtf_proc *);
-
-static bool mtf_close_all_files (struct mtf_proc *);
-static bool mtf_merge_dictionary (struct dictionary *const, struct mtf_file *);
-static bool mtf_read_record (struct mtf_proc *mtf, struct mtf_file *);
-
-static void mtf_process_case (struct mtf_proc *);
-
-static bool create_flag_var (const char *subcommand_name, const char *var_name,
-                             struct dictionary *, struct variable **);
-static char *var_type_description (struct variable *);
-
-/* Parse and execute the MATCH FILES command. */
-int
-cmd_match_files (struct lexer *lexer, struct dataset *ds)
-{
-  struct mtf_proc mtf;
-  struct ll *first_table;
-  struct mtf_file *file, *next;
-
-  bool saw_in = false;
-  struct casereader *active_file = NULL;
-
-  char first_name[VAR_NAME_LEN + 1] = "";
-  char last_name[VAR_NAME_LEN + 1] = "";
-
-  struct taint *taint = NULL;
-
-  size_t i;
-
-  ll_init (&mtf.files);
-  mtf.nonempty_files = 0;
-  first_table = ll_null (&mtf.files);
-  mtf.dict = dict_create ();
-  mtf.output = NULL;
-  mtf.by_cnt = 0;
-  mtf.first = mtf.last = NULL;
-  case_nullify (&mtf.buffered_case);
-  case_nullify (&mtf.prev_BY_case);
-  mtf.prev_BY = NULL;
-
-  dict_set_case_limit (mtf.dict, dict_get_case_limit (dataset_dict (ds)));
-
-  lex_match (lexer, '/');
-  while (lex_token (lexer) == T_ID
-         && (lex_id_match (ss_cstr ("FILE"), ss_cstr (lex_tokid (lexer)))
-             || lex_id_match (ss_cstr ("TABLE"), ss_cstr (lex_tokid (lexer)))))
-    {
-      struct mtf_file *file = xmalloc (sizeof *file);
-      file->by = NULL;
-      file->handle = NULL;
-      file->reader = NULL;
-      file->dict = NULL;
-      file->in_name = NULL;
-      file->in_var = NULL;
-      file->var_cnt = 0;
-      file->vars = NULL;
-      case_nullify (&file->input);
-
-      if (lex_match_id (lexer, "FILE"))
-        {
-          file->type = MTF_FILE;
-          ll_insert (first_table, &file->ll);
-          mtf.nonempty_files++;
-        }
-      else if (lex_match_id (lexer, "TABLE"))
-        {
-          file->type = MTF_TABLE;
-          ll_push_tail (&mtf.files, &file->ll);
-          if (first_table == ll_null (&mtf.files))
-            first_table = &file->ll;
-        }
-      else
-        NOT_REACHED ();
-      lex_match (lexer, '=');
-
-      if (lex_match (lexer, '*'))
-        {
-          if (!proc_has_active_file (ds))
-            {
-              msg (SE, _("Cannot specify the active file since no active "
-                         "file has been defined."));
-              goto error;
-            }
-
-          if (proc_make_temporary_transformations_permanent (ds))
-            msg (SE,
-                 _("MATCH FILES may not be used after TEMPORARY when "
-                   "the active file is an input source.  "
-                   "Temporary transformations will be made permanent."));
-
-          file->dict = dict_clone (dataset_dict (ds));
-        }
-      else
-        {
-          file->handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
-          if (file->handle == NULL)
-            goto error;
-
-          file->reader = any_reader_open (file->handle, &file->dict);
-          if (file->reader == NULL)
-            goto error;
-        }
-
-      while (lex_match (lexer, '/'))
-        if (lex_match_id (lexer, "RENAME"))
-          {
-            if (!rename_variables (lexer, file->dict))
-              goto error;
-          }
-        else if (lex_match_id (lexer, "IN"))
-          {
-            lex_match (lexer, '=');
-            if (lex_token (lexer) != T_ID)
-              {
-                lex_error (lexer, NULL);
-                goto error;
-              }
-
-            if (file->in_name != NULL)
-              {
-                msg (SE, _("Multiple IN subcommands for a single FILE or "
-                           "TABLE."));
-                goto error;
-              }
-            file->in_name = xstrdup (lex_tokid (lexer));
-            lex_get (lexer);
-            saw_in = true;
-          }
-
-      mtf_merge_dictionary (mtf.dict, file);
-    }
-
-  while (lex_token (lexer) != '.')
-    {
-      if (lex_match (lexer, T_BY))
-       {
-          struct mtf_file *file;
-          struct variable **by;
-          bool ok;
-
-         if (mtf.by_cnt)
-           {
-              lex_sbc_only_once ("BY");
-             goto error;
-           }
-
-         lex_match (lexer, '=');
-         if (!parse_variables (lexer, mtf.dict, &by, &mtf.by_cnt,
-                               PV_NO_DUPLICATE | PV_NO_SCRATCH))
-           goto error;
-
-          ok = true;
-          ll_for_each (file, struct mtf_file, ll, &mtf.files)
-            {
-              size_t i;
-
-              file->by = xnmalloc (mtf.by_cnt, sizeof *file->by);
-              for (i = 0; i < mtf.by_cnt; i++)
-                {
-                  const char *var_name = var_get_name (by[i]);
-                  file->by[i] = dict_lookup_var (file->dict, var_name);
-                  if (file->by[i] == NULL)
-                    {
-                      if (file->handle != NULL)
-                        msg (SE, _("File %s lacks BY variable %s."),
-                             fh_get_name (file->handle), var_name);
-                      else
-                        msg (SE, _("Active file lacks BY variable %s."),
-                             var_name);
-                      ok = false;
-                    }
-                }
-            }
-          free (by);
-
-          if (!ok)
-            goto error;
-       }
-      else if (lex_match_id (lexer, "FIRST"))
-        {
-          if (first_name[0] != '\0')
-            {
-              lex_sbc_only_once ("FIRST");
-              goto error;
-            }
-
-         lex_match (lexer, '=');
-          if (!lex_force_id (lexer))
-            goto error;
-          strcpy (first_name, lex_tokid (lexer));
-          lex_get (lexer);
-        }
-      else if (lex_match_id (lexer, "LAST"))
-        {
-          if (last_name[0] != '\0')
-            {
-              lex_sbc_only_once ("LAST");
-              goto error;
-            }
-
-         lex_match (lexer, '=');
-          if (!lex_force_id (lexer))
-            goto error;
-          strcpy (last_name, lex_tokid (lexer));
-          lex_get (lexer);
-        }
-      else if (lex_match_id (lexer, "MAP"))
-       {
-         /* FIXME. */
-       }
-      else if (lex_match_id (lexer, "DROP"))
-        {
-          if (!drop_variables (lexer, mtf.dict))
-            goto error;
-        }
-      else if (lex_match_id (lexer, "KEEP"))
-        {
-          if (!keep_variables (lexer, mtf.dict))
-            goto error;
-        }
-      else
-       {
-         lex_error (lexer, NULL);
-         goto error;
-       }
-
-      if (!lex_match (lexer, '/') && lex_token (lexer) != '.')
-        {
-          lex_end_of_command (lexer);
-          goto error;
-        }
-    }
-
-  if (mtf.by_cnt == 0)
-    {
-      if (first_table != ll_null (&mtf.files))
-        {
-          msg (SE, _("BY is required when TABLE is specified."));
-          goto error;
-        }
-      if (saw_in)
-        {
-          msg (SE, _("BY is required when IN is specified."));
-          goto error;
-        }
-    }
-
-  /* Set up mapping from each file's variables to master
-     variables. */
-  ll_for_each (file, struct mtf_file, ll, &mtf.files)
-    {
-      size_t in_var_cnt = dict_get_var_cnt (file->dict);
-
-      file->vars = xnmalloc (in_var_cnt, sizeof *file->vars);
-      file->var_cnt = 0;
-      for (i = 0; i < in_var_cnt; i++)
-        {
-          struct variable *in_var = dict_get_var (file->dict, i);
-          struct variable *out_var = dict_lookup_var (mtf.dict,
-                                                      var_get_name (in_var));
-
-          if (out_var != NULL)
-            {
-              struct mtf_variable *mv = &file->vars[file->var_cnt++];
-              mv->in_var = in_var;
-              mv->out_var = out_var;
-            }
-        }
-    }
-
-  /* Add IN, FIRST, and LAST variables to master dictionary. */
-  ll_for_each (file, struct mtf_file, ll, &mtf.files)
-    if (!create_flag_var ("IN", file->in_name, mtf.dict, &file->in_var))
-      goto error;
-  if (!create_flag_var ("FIRST", first_name, mtf.dict, &mtf.first)
-      || !create_flag_var ("LAST", last_name, mtf.dict, &mtf.last))
-    goto error;
-
-  dict_delete_scratch_vars (mtf.dict);
-  dict_compact_values (mtf.dict);
-  mtf.output = autopaging_writer_create (dict_get_next_value_idx (mtf.dict));
-  taint = taint_clone (casewriter_get_taint (mtf.output));
-
-  ll_for_each (file, struct mtf_file, ll, &mtf.files)
-    {
-      if (file->reader == NULL)
-        {
-          if (active_file == NULL)
-            {
-              proc_discard_output (ds);
-              file->reader = active_file = proc_open (ds);
-            }
-          else
-            file->reader = casereader_clone (active_file);
-        }
-      taint_propagate (casereader_get_taint (file->reader), taint);
-    }
-
-  ll_for_each_safe (file, next, struct mtf_file, ll, &mtf.files)
-    mtf_read_record (&mtf, file);
-  while (mtf.nonempty_files > 0)
-    mtf_process_case (&mtf);
-  if ((mtf.first != NULL || mtf.last != NULL) && mtf.prev_BY != NULL)
-    {
-      if (mtf.last != NULL)
-        case_data_rw (&mtf.buffered_case, mtf.last)->f = 1.0;
-      casewriter_write (mtf.output, &mtf.buffered_case);
-      case_nullify (&mtf.buffered_case);
-    }
-  mtf_close_all_files (&mtf);
-  if (active_file != NULL)
-    proc_commit (ds);
-
-  proc_set_active_file (ds, casewriter_make_reader (mtf.output), mtf.dict);
-  mtf.dict = NULL;
-  mtf.output = NULL;
-
-  mtf_free (&mtf);
-
-  return taint_destroy (taint) ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
-
- error:
-  if (active_file != NULL)
-    proc_commit (ds);
-  mtf_free (&mtf);
-  taint_destroy (taint);
-  return CMD_CASCADING_FAILURE;
-}
-
-/* If VAR_NAME is a nonnull pointer to a non-empty string,
-   attempts to create a variable named VAR_NAME, with format
-   F1.0, in DICT, and stores a pointer to the variable in *VAR.
-   Returns true if successful, false if the variable name is a
-   duplicate (in which case a message saying that the variable
-   specified on the given SUBCOMMAND is a duplicate is emitted).
-   Also returns true, without doing anything, if VAR_NAME is null
-   or empty. */
-static bool
-create_flag_var (const char *subcommand, const char *var_name,
-                 struct dictionary *dict, struct variable **var)
-{
-  if (var_name != NULL && var_name[0] != '\0')
-    {
-      struct fmt_spec format = fmt_for_output (FMT_F, 1, 0);
-      *var = dict_create_var (dict, var_name, 0);
-      if (*var == NULL)
-        {
-          msg (SE, _("Variable name %s specified on %s subcommand "
-                     "duplicates an existing variable name."),
-               subcommand, var_name);
-          return false;
-        }
-      var_set_both_formats (*var, &format);
-    }
-  else
-    *var = NULL;
-  return true;
-}
-
-/* Return a string in an allocated buffer describing V's variable
-   type and width. */
-static char *
-var_type_description (struct variable *v)
-{
-  if (var_is_numeric (v))
-    return xstrdup ("numeric");
-  else
-    return xasprintf ("string with width %d", var_get_width (v));
-}
-
-/* Closes all the files in MTF and frees their associated data.
-   Returns true if successful, false if an I/O error occurred on
-   any of the files. */
-static bool
-mtf_close_all_files (struct mtf_proc *mtf)
-{
-  struct mtf_file *file;
-  bool ok = true;
-
-  ll_for_each_preremove (file, struct mtf_file, ll, &mtf->files)
-    {
-      fh_unref (file->handle);
-      casereader_destroy (file->reader);
-      free (file->by);
-      dict_destroy (file->dict);
-      free (file->in_name);
-      case_destroy (&file->input);
-      free (file->vars);
-      free (file);
-    }
-
-  return ok;
-}
-
-/* Frees all the data for the MATCH FILES procedure. */
-static void
-mtf_free (struct mtf_proc *mtf)
-{
-  mtf_close_all_files (mtf);
-  dict_destroy (mtf->dict);
-  casewriter_destroy (mtf->output);
-  case_destroy (&mtf->buffered_case);
-  case_destroy (&mtf->prev_BY_case);
-}
-
-/* Reads the next record into FILE, if possible, and update MTF's
-   nonempty_files count if not. */
-static bool
-mtf_read_record (struct mtf_proc *mtf, struct mtf_file *file)
-{
-  case_destroy (&file->input);
-  if (!casereader_read (file->reader, &file->input))
-    {
-      mtf->nonempty_files--;
-      return false;
-    }
-  else
-    return true;
-}
-
-/* Compare the BY variables for files A and B; return -1 if A <
-   B, 0 if A == B, 1 if A > B.  (If there are no BY variables,
-   then all records are equal.) */
-static inline int
-mtf_compare_BY_values (struct mtf_proc *mtf,
-                       struct mtf_file *a, struct mtf_file *b)
-{
-  return case_compare_2dict (&a->input, &b->input, a->by, b->by, mtf->by_cnt);
-}
-
-/* Processes input files and write one case to the output file. */
-static void
-mtf_process_case (struct mtf_proc *mtf)
-{
-  struct ccase c;
-  struct mtf_file *min;
-  struct mtf_file *file;
-  int min_sequence;
-  size_t i;
-
-  /* Find the set of one or more FILEs whose BY values are
-     minimal, as well as the set of zero or more TABLEs whose BY
-     values equal those of the minimum FILEs.
-
-     After each iteration of the loop, this invariant holds: the
-     FILEs with minimum BY values thus far have "sequence"
-     members equal to min_sequence, and "min" points to one of
-     the mtf_files whose case has those minimum BY values, and
-     similarly for TABLEs. */
-  min_sequence = 0;
-  min = NULL;
-  ll_for_each (file, struct mtf_file, ll, &mtf->files)
-    if (case_is_null (&file->input))
-      file->sequence = -1;
-    else if (file->type == MTF_FILE)
-      {
-        int cmp = min != NULL ? mtf_compare_BY_values (mtf, min, file) : 1;
-        if (cmp <= 0)
-          file->sequence = cmp < 0 ? -1 : min_sequence;
-        else
-          {
-            file->sequence = ++min_sequence;
-            min = file;
-          }
-      }
-    else
-      {
-        int cmp;
-        assert (min != NULL);
-        do
-          {
-            cmp = mtf_compare_BY_values (mtf, min, file);
-          }
-        while (cmp > 0 && mtf_read_record (mtf, file));
-        file->sequence = cmp == 0 ? min_sequence : -1;
-      }
-
-  /* Form the output case from the input cases. */
-  case_create (&c, dict_get_next_value_idx (mtf->dict));
-  for (i = 0; i < dict_get_var_cnt (mtf->dict); i++)
-    {
-      struct variable *v = dict_get_var (mtf->dict, i);
-      value_set_missing (case_data_rw (&c, v), var_get_width (v));
-    }
-  ll_for_each_reverse (file, struct mtf_file, ll, &mtf->files)
-    {
-      bool include_file = file->sequence == min_sequence;
-      if (include_file)
-        for (i = 0; i < file->var_cnt; i++)
-          {
-            const struct mtf_variable *mv = &file->vars[i];
-            const union value *in = case_data (&file->input, mv->in_var);
-            union value *out = case_data_rw (&c, mv->out_var);
-            value_copy (out, in, var_get_width (mv->in_var));
-          }
-      if (file->in_var != NULL)
-        case_data_rw (&c, file->in_var)->f = include_file;
-    }
-
-  /* Write the output case. */
-  if (mtf->first == NULL && mtf->last == NULL)
-    {
-      /* With no FIRST or LAST variables, it's trivial. */
-      casewriter_write (mtf->output, &c);
-    }
-  else
-    {
-      /* It's harder with LAST, because we can't know whether
-         this case is the last in a group until we've prepared
-         the *next* case also.  Thus, we buffer the previous
-         output case until the next one is ready.
-
-         We also have to save a copy of one of the previous input
-         cases, so that we can compare the BY variables.  We
-         can't compare the BY variables between the current
-         output case and the saved one because the BY variables
-         might not be in the output (the user is allowed to drop
-         them). */
-      bool new_BY;
-      if (mtf->prev_BY != NULL)
-        {
-          new_BY = case_compare_2dict (&min->input, &mtf->prev_BY_case,
-                                       min->by, mtf->prev_BY,
-                                       mtf->by_cnt);
-          if (mtf->last != NULL)
-            case_data_rw (&mtf->buffered_case, mtf->last)->f = new_BY;
-          casewriter_write (mtf->output, &mtf->buffered_case);
-        }
-      else
-        new_BY = true;
-
-      case_move (&mtf->buffered_case, &c);
-      if (mtf->first != NULL)
-        case_data_rw (&mtf->buffered_case, mtf->first)->f = new_BY;
-
-      if (new_BY)
-        {
-          mtf->prev_BY = min->by;
-          case_destroy (&mtf->prev_BY_case);
-          case_clone (&mtf->prev_BY_case, &min->input);
-        }
-    }
-
-  /* Read another record from each input file FILE with minimum
-     values. */
-  ll_for_each (file, struct mtf_file, ll, &mtf->files)
-    if (file->type == MTF_FILE)
-      {
-        if (file->sequence == min_sequence)
-          mtf_read_record (mtf, file);
-      }
-    else
-      break;
-}
-
-/* Merge the dictionary for file F into master dictionary M. */
-static bool
-mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
-{
-  struct dictionary *d = f->dict;
-  const char *d_docs, *m_docs;
-  int i;
-
-  if (dict_get_label (m) == NULL)
-    dict_set_label (m, dict_get_label (d));
-
-  d_docs = dict_get_documents (d);
-  m_docs = dict_get_documents (m);
-  if (d_docs != NULL)
-    {
-      if (m_docs == NULL)
-        dict_set_documents (m, d_docs);
-      else
-        {
-          char *new_docs = xasprintf ("%s%s", m_docs, d_docs);
-          dict_set_documents (m, new_docs);
-          free (new_docs);
-        }
-    }
-
-  for (i = 0; i < dict_get_var_cnt (d); i++)
-    {
-      struct variable *dv = dict_get_var (d, i);
-      struct variable *mv = dict_lookup_var (m, var_get_name (dv));
-
-      if (dict_class_from_id (var_get_name (dv)) == DC_SCRATCH)
-        continue;
-
-      if (mv != NULL)
-        {
-          if (var_get_width (mv) != var_get_width (dv))
-            {
-              char *dv_description = var_type_description (dv);
-              char *mv_description = var_type_description (mv);
-              msg (SE, _("Variable %s in file %s (%s) has different "
-                         "type or width from the same variable in "
-                         "earlier file (%s)."),
-                   var_get_name (dv), fh_get_name (f->handle),
-                   dv_description, mv_description);
-              free (dv_description);
-              free (mv_description);
-              return false;
-            }
-
-          if (var_get_width (dv) == var_get_width (mv))
-            {
-              if (var_has_value_labels (dv) && !var_has_value_labels (mv))
-                var_set_value_labels (mv, var_get_value_labels (dv));
-              if (var_has_missing_values (dv) && !var_has_missing_values (mv))
-                var_set_missing_values (mv, var_get_missing_values (dv));
-            }
-
-          if (var_get_label (dv) && !var_get_label (mv))
-            var_set_label (mv, var_get_label (dv));
-        }
-      else
-        mv = dict_clone_var_assert (m, dv, var_get_name (dv));
-    }
-
-  return true;
-}
index 609c9b2c92bec4b85894b6ab26daca928b0031d9..c2e5dc0f721438a7af412fc936e19cb20a99b1d1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -180,35 +180,34 @@ is_valid_state (enum trns_result state)
           || state >= 0);
 }
 
-/* Reads one case into C.
-   Returns true if successful, false at end of file or if an
+/* Reads and returns one case.
+   Returns the case if successful, null at end of file or if an
    I/O error occurred. */
-static bool
-input_program_casereader_read (struct casereader *reader UNUSED, void *inp_,
-                               struct ccase *c)
+static struct ccase *
+input_program_casereader_read (struct casereader *reader UNUSED, void *inp_)
 {
   struct input_program_pgm *inp = inp_;
-
-  case_create (c, inp->value_cnt);
+  struct ccase *c = case_create (inp->value_cnt);
 
   do
     {
       assert (is_valid_state (inp->restart));
       if (inp->restart == TRNS_ERROR || inp->restart == TRNS_END_FILE)
         {
-          case_destroy (c);
-          return false;
+          case_unref (c);
+          return NULL;
         }
 
+      c = case_unshare (c);
       caseinit_init_vars (inp->init, c);
       inp->restart = trns_chain_execute (inp->trns_chain, inp->restart,
-                                         c, inp->case_nr);
+                                         &c, inp->case_nr);
       assert (is_valid_state (inp->restart));
       caseinit_update_left_vars (inp->init, c);
     }
   while (inp->restart < 0);
 
-  return true;
+  return c;
 }
 
 static void
@@ -251,7 +250,7 @@ cmd_end_case (struct lexer *lexer, struct dataset *ds UNUSED)
 
 /* Outputs the current case */
 int
-end_case_trns_proc (void *inp_, struct ccase *c UNUSED,
+end_case_trns_proc (void *inp_, struct ccase **c UNUSED,
                     casenumber case_nr UNUSED)
 {
   struct input_program_pgm *inp = inp_;
@@ -323,7 +322,7 @@ cmd_reread (struct lexer *lexer, struct dataset *ds)
 
 /* Executes a REREAD transformation. */
 static int
-reread_trns_proc (void *t_, struct ccase *c, casenumber case_num)
+reread_trns_proc (void *t_, struct ccase **c, casenumber case_num)
 {
   struct reread_trns *t = t_;
 
@@ -331,7 +330,7 @@ reread_trns_proc (void *t_, struct ccase *c, casenumber case_num)
     dfm_reread_record (t->reader, 1);
   else
     {
-      double column = expr_evaluate_num (t->column, c, case_num);
+      double column = expr_evaluate_num (t->column, *c, case_num);
       if (!isfinite (column) || column < 1)
        {
          msg (SE, _("REREAD: Column numbers must be positive finite "
@@ -368,7 +367,7 @@ cmd_end_file (struct lexer *lexer, struct dataset *ds)
 
 /* Executes an END FILE transformation. */
 static int
-end_file_trns_proc (void *trns_ UNUSED, struct ccase *c UNUSED,
+end_file_trns_proc (void *trns_ UNUSED, struct ccase **c UNUSED,
                     casenumber case_num UNUSED)
 {
   return TRNS_END_FILE;
index 8e8bba9b69a281ae707cd021d2bd777e0176bf6f..28820a8504b5fdfc15c8ede3338439ab0f1b4d7a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -83,7 +83,7 @@ static unsigned n_chars_width (struct outp_driver *d);
 static void write_line (struct outp_driver *d, const char *s);
 
 /* Other functions. */
-static void list_case (struct ccase *, casenumber case_idx,
+static void list_case (const struct ccase *, casenumber case_idx,
                        const struct dataset *);
 static void determine_layout (void);
 static void clean_up (void);
@@ -236,15 +236,15 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
        casegrouper_get_next_group (grouper, &group);
        casereader_destroy (group))
     {
-      struct ccase c;
+      struct ccase *c;
 
       write_all_headers (group, ds);
-      for (; casereader_read (group, &c); case_destroy (&c))
+      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);
         }
     }
   ok = casegrouper_destroy (grouper);
@@ -265,12 +265,13 @@ static void
 write_all_headers (struct casereader *input, const struct dataset *ds)
 {
   struct outp_driver *d;
-  struct ccase c;
+  struct ccase *c;
 
-  if (!casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c == NULL)
     return;
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
+  output_split_file_values (ds, c);
+  case_unref (c);
 
   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
     {
@@ -649,7 +650,8 @@ determine_layout (void)
 
 /* Writes case C to output. */
 static void
-list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
+list_case (const struct ccase *c, casenumber case_idx,
+           const struct dataset *ds)
 {
   struct dictionary *dict = dataset_dict (ds);
   struct outp_driver *d;
index c3a2e09a3d93f6f56de75da1a12346ee2329a8b7..3f73ee25a8de16fb1f1883608e2c6fa8cf25fdba 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -102,7 +102,7 @@ cmd_print_space (struct lexer *lexer, struct dataset *ds)
 
 /* Executes a PRINT SPACE transformation. */
 static int
-print_space_trns_proc (void *t_, struct ccase *c,
+print_space_trns_proc (void *t_, struct ccase **c,
                        casenumber case_num UNUSED)
 {
   struct print_space_trns *trns = t_;
@@ -111,7 +111,7 @@ print_space_trns_proc (void *t_, struct ccase *c,
   n = 1;
   if (trns->expr)
     {
-      double f = expr_evaluate_num (trns->expr, c, case_num);
+      double f = expr_evaluate_num (trns->expr, *c, case_num);
       if (f == SYSMIS)
         msg (SW, _("The expression on PRINT SPACE evaluated to the "
                    "system-missing value."));
index 408224afb921acd9900d53b44a092d1a28302725..ab25ec758d784128a09e15c1ebc336f8d7e24ce8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -450,7 +450,7 @@ static void flush_records (struct print_trns *, int target_record,
 
 /* Performs the transformation inside print_trns T on case C. */
 static int
-print_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
+print_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
 {
   struct print_trns *trns = trns_;
   bool eject = trns->eject;
@@ -467,7 +467,7 @@ print_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
       ds_set_length (&trns->line, spec->first_column, encoded_space);
       if (spec->type == PRT_VAR)
         {
-          const union value *input = case_data (c, spec->var);
+          const union value *input = case_data (*c, spec->var);
           char *output = ds_put_uninit (&trns->line, spec->format.w);
           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
             data_out_legacy (input, trns->encoding, &spec->format, output);
diff --git a/src/language/data-io/save.c b/src/language/data-io/save.c
new file mode 100644 (file)
index 0000000..c34d446
--- /dev/null
@@ -0,0 +1,347 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 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 <stdlib.h>
+
+#include <data/any-writer.h>
+#include <data/case-map.h>
+#include <data/case.h>
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <data/dictionary.h>
+#include <data/por-file-writer.h>
+#include <data/procedure.h>
+#include <data/sys-file-writer.h>
+#include <data/transformations.h>
+#include <data/variable.h>
+#include <language/command.h>
+#include <language/data-io/file-handle.h>
+#include <language/data-io/trim.h>
+#include <language/lexer/lexer.h>
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Writing system and portable files. */
+
+/* Type of output file. */
+enum writer_type
+  {
+    SYSFILE_WRITER,     /* System file. */
+    PORFILE_WRITER      /* Portable file. */
+  };
+
+/* Type of a command. */
+enum command_type
+  {
+    XFORM_CMD,          /* Transformation. */
+    PROC_CMD            /* Procedure. */
+  };
+
+static int parse_output_proc (struct lexer *, struct dataset *,
+                              enum writer_type);
+static int parse_output_trns (struct lexer *, struct dataset *,
+                              enum writer_type);
+
+int
+cmd_save (struct lexer *lexer, struct dataset *ds)
+{
+  return parse_output_proc (lexer, ds, SYSFILE_WRITER);
+}
+
+int
+cmd_export (struct lexer *lexer, struct dataset *ds)
+{
+  return parse_output_proc (lexer, ds, PORFILE_WRITER);
+}
+
+int
+cmd_xsave (struct lexer *lexer, struct dataset *ds)
+{
+  return parse_output_trns (lexer, ds, SYSFILE_WRITER);
+}
+
+int
+cmd_xexport (struct lexer *lexer, struct dataset *ds)
+{
+  return parse_output_trns (lexer, ds, PORFILE_WRITER);
+}
+\f
+struct output_trns
+  {
+    struct casewriter *writer;          /* Writer. */
+  };
+
+static trns_proc_func output_trns_proc;
+static trns_free_func output_trns_free;
+static struct casewriter *parse_write_command (struct lexer *,
+                                               struct dataset *,
+                                               enum writer_type,
+                                               enum command_type,
+                                               bool *retain_unselected);
+
+/* Parses and performs the SAVE or EXPORT procedure. */
+static int
+parse_output_proc (struct lexer *lexer, struct dataset *ds,
+                   enum writer_type writer_type)
+{
+  bool retain_unselected;
+  struct variable *saved_filter_variable;
+  struct casewriter *output;
+  bool ok;
+
+  output = parse_write_command (lexer, ds, writer_type, PROC_CMD,
+                                &retain_unselected);
+  if (output == NULL)
+    return CMD_CASCADING_FAILURE;
+
+  saved_filter_variable = dict_get_filter (dataset_dict (ds));
+  if (retain_unselected)
+    dict_set_filter (dataset_dict (ds), NULL);
+
+  casereader_transfer (proc_open (ds), output);
+  ok = casewriter_destroy (output);
+  ok = proc_commit (ds) && ok;
+
+  dict_set_filter (dataset_dict (ds), saved_filter_variable);
+
+  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+}
+
+/* Parses the XSAVE or XEXPORT transformation command. */
+static int
+parse_output_trns (struct lexer *lexer, struct dataset *ds, enum writer_type writer_type)
+{
+  struct output_trns *t = xmalloc (sizeof *t);
+  t->writer = parse_write_command (lexer, ds, writer_type, XFORM_CMD, NULL);
+  if (t->writer == NULL)
+    {
+      free (t);
+      return CMD_CASCADING_FAILURE;
+    }
+
+  add_transformation (ds, output_trns_proc, output_trns_free, t);
+  return CMD_SUCCESS;
+}
+
+/* Parses SAVE or XSAVE or EXPORT or XEXPORT command.
+   WRITER_TYPE identifies the type of file to write,
+   and COMMAND_TYPE identifies the type of command.
+
+   On success, returns a writer.
+   For procedures only, sets *RETAIN_UNSELECTED to true if cases
+   that would otherwise be excluded by FILTER or USE should be
+   included.
+
+   On failure, returns a null pointer. */
+static struct casewriter *
+parse_write_command (struct lexer *lexer, struct dataset *ds,
+                    enum writer_type writer_type,
+                     enum command_type command_type,
+                     bool *retain_unselected)
+{
+  /* Common data. */
+  struct file_handle *handle; /* Output file. */
+  struct dictionary *dict;    /* Dictionary for output file. */
+  struct casewriter *writer;  /* Writer. */
+  struct case_map *map;       /* Map from input data to data for writer. */
+
+  /* Common options. */
+  bool print_map;             /* Print map?  TODO. */
+  bool print_short_names;     /* Print long-to-short name map.  TODO. */
+  struct sfm_write_options sysfile_opts;
+  struct pfm_write_options porfile_opts;
+
+  assert (writer_type == SYSFILE_WRITER || writer_type == PORFILE_WRITER);
+  assert (command_type == XFORM_CMD || command_type == PROC_CMD);
+  assert ((retain_unselected != NULL) == (command_type == PROC_CMD));
+
+  if (command_type == PROC_CMD)
+    *retain_unselected = true;
+
+  handle = NULL;
+  dict = dict_clone (dataset_dict (ds));
+  writer = NULL;
+  map = NULL;
+  print_map = false;
+  print_short_names = false;
+  sysfile_opts = sfm_writer_default_options ();
+  porfile_opts = pfm_writer_default_options ();
+
+  case_map_prepare_dict (dict);
+  dict_delete_scratch_vars (dict);
+
+  lex_match (lexer, '/');
+  for (;;)
+    {
+      if (lex_match_id (lexer, "OUTFILE"))
+       {
+          if (handle != NULL)
+            {
+              lex_sbc_only_once ("OUTFILE");
+              goto error;
+            }
+
+         lex_match (lexer, '=');
+
+         handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+         if (handle == NULL)
+           goto error;
+       }
+      else if (lex_match_id (lexer, "NAMES"))
+        print_short_names = true;
+      else if (lex_match_id (lexer, "PERMISSIONS"))
+        {
+          bool cw;
+
+          lex_match (lexer, '=');
+          if (lex_match_id (lexer, "READONLY"))
+            cw = false;
+          else if (lex_match_id (lexer, "WRITEABLE"))
+            cw = true;
+          else
+            {
+              lex_error (lexer, _("expecting %s or %s"),
+                         "READONLY", "WRITEABLE");
+              goto error;
+            }
+          sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
+        }
+      else if (command_type == PROC_CMD && lex_match_id (lexer, "UNSELECTED"))
+        {
+          lex_match (lexer, '=');
+          if (lex_match_id (lexer, "RETAIN"))
+            *retain_unselected = true;
+          else if (lex_match_id (lexer, "DELETE"))
+            *retain_unselected = false;
+          else
+            {
+              lex_error (lexer, _("expecting %s or %s"), "RETAIN", "DELETE");
+              goto error;
+            }
+        }
+      else if (writer_type == SYSFILE_WRITER
+               && lex_match_id (lexer, "COMPRESSED"))
+       sysfile_opts.compress = true;
+      else if (writer_type == SYSFILE_WRITER
+               && lex_match_id (lexer, "UNCOMPRESSED"))
+       sysfile_opts.compress = false;
+      else if (writer_type == SYSFILE_WRITER
+               && lex_match_id (lexer, "VERSION"))
+       {
+         lex_match (lexer, '=');
+         if (!lex_force_int (lexer))
+            goto error;
+          sysfile_opts.version = lex_integer (lexer);
+          lex_get (lexer);
+       }
+      else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "TYPE"))
+        {
+          lex_match (lexer, '=');
+          if (lex_match_id (lexer, "COMMUNICATIONS"))
+            porfile_opts.type = PFM_COMM;
+          else if (lex_match_id (lexer, "TAPE"))
+            porfile_opts.type = PFM_TAPE;
+          else
+            {
+              lex_error (lexer, _("expecting %s or %s"), "COMM", "TAPE");
+              goto error;
+            }
+        }
+      else if (writer_type == PORFILE_WRITER && lex_match_id (lexer, "DIGITS"))
+        {
+          lex_match (lexer, '=');
+          if (!lex_force_int (lexer))
+            goto error;
+          porfile_opts.digits = lex_integer (lexer);
+          lex_get (lexer);
+        }
+      else if (!parse_dict_trim (lexer, dict))
+        goto error;
+
+      if (!lex_match (lexer, '/'))
+       break;
+    }
+  if (lex_end_of_command (lexer) != CMD_SUCCESS)
+    goto error;
+
+  if (handle == NULL)
+    {
+      lex_sbc_missing (lexer, "OUTFILE");
+      goto error;
+    }
+
+  dict_delete_scratch_vars (dict);
+  dict_compact_values (dict);
+
+  if (fh_get_referent (handle) == FH_REF_FILE)
+    {
+      switch (writer_type)
+        {
+        case SYSFILE_WRITER:
+          writer = sfm_open_writer (handle, dict, sysfile_opts);
+          break;
+        case PORFILE_WRITER:
+          writer = pfm_open_writer (handle, dict, porfile_opts);
+          break;
+        }
+    }
+  else
+    writer = any_writer_open (handle, dict);
+  if (writer == NULL)
+    goto error;
+
+  map = case_map_from_dict (dict);
+  if (map != NULL)
+    writer = case_map_create_output_translator (map, writer);
+  dict_destroy (dict);
+
+  fh_unref (handle);
+  return writer;
+
+ error:
+  fh_unref (handle);
+  casewriter_destroy (writer);
+  dict_destroy (dict);
+  case_map_destroy (map);
+  return NULL;
+}
+
+/* Writes case *C to the system file specified on XSAVE or XEXPORT. */
+static int
+output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
+{
+  struct output_trns *t = trns_;
+  casewriter_write (t->writer, case_ref (*c));
+  return TRNS_CONTINUE;
+}
+
+/* Frees an XSAVE or XEXPORT transformation.
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+output_trns_free (void *trns_)
+{
+  struct output_trns *t = trns_;
+  bool ok = casewriter_destroy (t->writer);
+  free (t);
+  return ok;
+}
diff --git a/src/language/data-io/trim.c b/src/language/data-io/trim.c
new file mode 100644 (file)
index 0000000..7a14c99
--- /dev/null
@@ -0,0 +1,196 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2008 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 <language/data-io/trim.h>
+
+#include <stdlib.h>
+
+#include <data/dictionary.h>
+#include <data/variable.h>
+#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <libpspp/message.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Commands that read and write system files share a great deal
+   of common syntactic structure for rearranging and dropping
+   variables.  This function parses this syntax and modifies DICT
+   appropriately.  Returns true on success, false on failure. */
+bool
+parse_dict_trim (struct lexer *lexer, struct dictionary *dict)
+{
+  if (lex_match_id (lexer, "MAP"))
+    {
+      /* FIXME. */
+      return true;
+    }
+  else if (lex_match_id (lexer, "DROP"))
+    return parse_dict_drop (lexer, dict);
+  else if (lex_match_id (lexer, "KEEP"))
+    return parse_dict_keep (lexer, dict);
+  else if (lex_match_id (lexer, "RENAME"))
+    return parse_dict_rename (lexer, dict);
+  else
+    {
+      lex_error (lexer, _("expecting a valid subcommand"));
+      return false;
+    }
+}
+
+/* Parses and performs the RENAME subcommand of GET, SAVE, and
+   related commands. */
+bool
+parse_dict_rename (struct lexer *lexer, struct dictionary *dict)
+{
+  size_t i;
+
+  int success = 0;
+
+  struct variable **v;
+  char **new_names;
+  size_t nv, nn;
+  char *err_name;
+
+  int group;
+
+  lex_match (lexer, '=');
+  if (lex_token (lexer) != '(')
+    {
+      struct variable *v;
+
+      v = parse_variable (lexer, dict);
+      if (v == NULL)
+       return 0;
+      if (!lex_force_match (lexer, '=')
+         || !lex_force_id (lexer))
+       return 0;
+      if (dict_lookup_var (dict, lex_tokid (lexer)) != NULL)
+       {
+         msg (SE, _("Cannot rename %s as %s because there already exists "
+                    "a variable named %s.  To rename variables with "
+                    "overlapping names, use a single RENAME subcommand "
+                    "such as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, "
+                    "\"/RENAME (A B C=B C A)\"."),
+               var_get_name (v), lex_tokid (lexer), lex_tokid (lexer));
+         return 0;
+       }
+
+      dict_rename_var (dict, v, lex_tokid (lexer));
+      lex_get (lexer);
+      return 1;
+    }
+
+  nv = nn = 0;
+  v = NULL;
+  new_names = 0;
+  group = 1;
+  while (lex_match (lexer, '('))
+    {
+      size_t old_nv = nv;
+
+      if (!parse_variables (lexer, dict, &v, &nv, PV_NO_DUPLICATE | PV_APPEND))
+       goto done;
+      if (!lex_match (lexer, '='))
+       {
+         msg (SE, _("`=' expected after variable list."));
+         goto done;
+       }
+      if (!parse_DATA_LIST_vars (lexer, &new_names, &nn, PV_APPEND | PV_NO_SCRATCH))
+       goto done;
+      if (nn != nv)
+       {
+         msg (SE, _("Number of variables on left side of `=' (%zu) does not "
+                     "match number of variables on right side (%zu), in "
+                     "parenthesized group %d of RENAME subcommand."),
+              nv - old_nv, nn - old_nv, group);
+         goto done;
+       }
+      if (!lex_force_match (lexer, ')'))
+       goto done;
+      group++;
+    }
+
+  if (!dict_rename_vars (dict, v, new_names, nv, &err_name))
+    {
+      msg (SE, _("Requested renaming duplicates variable name %s."), err_name);
+      goto done;
+    }
+  success = 1;
+
+ done:
+  for (i = 0; i < nn; i++)
+    free (new_names[i]);
+  free (new_names);
+  free (v);
+
+  return success;
+}
+
+/* Parses and performs the DROP subcommand of GET, SAVE, and
+   related commands.
+   Returns true if successful, false on failure.*/
+bool
+parse_dict_drop (struct lexer *lexer, struct dictionary *dict)
+{
+  struct variable **v;
+  size_t nv;
+
+  lex_match (lexer, '=');
+  if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
+    return false;
+  dict_delete_vars (dict, v, nv);
+  free (v);
+
+  if (dict_get_var_cnt (dict) == 0)
+    {
+      msg (SE, _("Cannot DROP all variables from dictionary."));
+      return false;
+    }
+  return true;
+}
+
+/* Parses and performs the KEEP subcommand of GET, SAVE, and
+   related commands.
+   Returns true if successful, false on failure.*/
+bool
+parse_dict_keep (struct lexer *lexer, struct dictionary *dict)
+{
+  struct variable **v;
+  size_t nv;
+  size_t i;
+
+  lex_match (lexer, '=');
+  if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
+    return false;
+
+  /* Move the specified variables to the beginning. */
+  dict_reorder_vars (dict, v, nv);
+
+  /* Delete the remaining variables. */
+  v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v);
+  for (i = nv; i < dict_get_var_cnt (dict); i++)
+    v[i - nv] = dict_get_var (dict, i);
+  dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv);
+  free (v);
+
+  return true;
+}
diff --git a/src/language/data-io/trim.h b/src/language/data-io/trim.h
new file mode 100644 (file)
index 0000000..106d1a8
--- /dev/null
@@ -0,0 +1,29 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2008 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_DATA_IO_TRIM_H
+#define LANGUAGE_DATA_IO_TRIM_H
+
+#include <stdbool.h>
+
+struct lexer;
+struct dictionary;
+bool parse_dict_trim (struct lexer *, struct dictionary *);
+bool parse_dict_rename (struct lexer *, struct dictionary *);
+bool parse_dict_drop (struct lexer *, struct dictionary *);
+bool parse_dict_keep (struct lexer *, struct dictionary *);
+
+#endif /* trim.c */
index ff4d7853c3a5e8b6487fafb40e111e8dd624d17e..a1b3310176e1687385dd2dbf3bc124e711e08da1 100644 (file)
@@ -120,12 +120,19 @@ cmd_apply_dictionary (struct lexer *lexer, struct dataset *ds)
           var_set_print_format (t, var_get_print_format (s));
           var_set_write_format (t, var_get_write_format (s));
        }
+
+      if (var_has_attributes (s)) 
+        var_set_attributes (t, var_get_attributes (s));
     }
 
   if (!n_matched)
     msg (SW, _("No matching variables found between the source "
               "and target files."));
 
+  /* Data file attributes. */
+  if (dict_has_attributes (dict))
+    dict_set_attributes (dataset_dict (ds), dict_get_attributes (dict));
+
   /* Weighting. */
   if (dict_get_weight (dict) != NULL)
     {
diff --git a/src/language/dictionary/attributes.c b/src/language/dictionary/attributes.c
new file mode 100644 (file)
index 0000000..9e9b808
--- /dev/null
@@ -0,0 +1,200 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 <stdlib.h>
+
+#include <data/attributes.h>
+#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/variable.h>
+#include <language/command.h>
+#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <libpspp/message.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static enum cmd_result parse_attributes (struct lexer *, struct attrset **,
+                                         size_t n);
+
+/* Parses the DATAFILE ATTRIBUTE command. */
+int
+cmd_datafile_attribute (struct lexer *lexer, struct dataset *ds)
+{
+  struct attrset *set = dict_get_attributes (dataset_dict (ds));
+  return parse_attributes (lexer, &set, 1);
+}
+
+/* Parses the VARIABLE ATTRIBUTE command. */
+int
+cmd_variable_attribute (struct lexer *lexer, struct dataset *ds)
+{
+  do 
+    {
+      struct variable **vars;
+      struct attrset **sets;
+      size_t n_vars, i;
+      bool ok;
+
+      if (!lex_force_match_id (lexer, "VARIABLES")
+          || !lex_force_match (lexer, '=')
+          || !parse_variables (lexer, dataset_dict (ds), &vars, &n_vars,
+                               PV_NONE))
+        return CMD_FAILURE;
+
+      sets = xmalloc (n_vars * sizeof *sets);
+      for (i = 0; i < n_vars; i++)
+        sets[i] = var_get_attributes (vars[i]);
+
+      ok = parse_attributes (lexer, sets, n_vars);
+      free (vars);
+      free (sets);
+      if (!ok)
+        return CMD_FAILURE;
+    }
+  while (lex_match (lexer, '/'));
+
+  return lex_end_of_command (lexer);
+}
+
+static bool
+match_subcommand (struct lexer *lexer, const char *keyword) 
+{
+  if (lex_token (lexer) == T_ID
+      && lex_id_match (ss_cstr (lex_tokid (lexer)), ss_cstr (keyword))
+      && lex_look_ahead (lexer) == '=') 
+    {
+      lex_get (lexer);          /* Skip keyword. */
+      lex_get (lexer);          /* Skip '='. */
+      return true;
+    }
+  else
+    return false;
+}
+
+static bool
+parse_attribute_name (struct lexer *lexer, char name[VAR_NAME_LEN + 1],
+                      size_t *index) 
+{
+  if (!lex_force_id (lexer))
+    return false;
+  strcpy (name, lex_tokid (lexer));
+  lex_get (lexer);
+
+  if (lex_match (lexer, '[')) 
+    {
+      if (!lex_force_int (lexer))
+        return false;
+      if (lex_integer (lexer) < 1 || lex_integer (lexer) > 65535)
+        {
+          msg (SE, _("Attribute array index must be between 1 and 65535."));
+          return false;
+        }
+      *index = lex_integer (lexer);
+      lex_get (lexer);
+      if (!lex_force_match (lexer, ']'))
+        return false;
+    }
+  else
+    *index = 0;
+  return true;
+}
+
+static bool
+add_attribute (struct lexer *lexer, struct attrset **sets, size_t n) 
+{
+  char name[VAR_NAME_LEN + 1];
+  size_t index, i;
+  char *value;
+
+  if (!parse_attribute_name (lexer, name, &index)
+      || !lex_force_match (lexer, '(')
+      || !lex_force_string (lexer))
+    return false;
+  value = ds_cstr (lex_tokstr (lexer));
+
+  for (i = 0; i < n; i++)
+    {
+      struct attribute *attr = attrset_lookup (sets[i], name);
+      if (attr == NULL) 
+        {
+          attr = attribute_create (name);
+          attrset_add (sets[i], attr); 
+        }
+      attribute_set_value (attr, index ? index - 1 : 0, value);
+    }
+
+  lex_get (lexer);
+  return lex_force_match (lexer, ')');
+}
+
+static bool
+delete_attribute (struct lexer *lexer, struct attrset **sets, size_t n) 
+{
+  char name[VAR_NAME_LEN + 1];
+  size_t index, i;
+
+  if (!parse_attribute_name (lexer, name, &index))
+    return false;
+
+  for (i = 0; i < n; i++) 
+    {
+      struct attrset *set = sets[i];
+      if (index == 0)
+        attrset_delete (set, name);
+      else
+        {
+          struct attribute *attr = attrset_lookup (set, name);
+          if (attr != NULL) 
+            {
+              attribute_del_value (attr, index - 1);
+              if (attribute_get_n_values (attr) == 0)
+                attrset_delete (set, name); 
+            }
+        }
+    }
+  return true;
+}
+
+static enum cmd_result
+parse_attributes (struct lexer *lexer, struct attrset **sets, size_t n) 
+{
+  enum { UNKNOWN, ADD, DELETE } command = UNKNOWN;
+  do 
+    {
+      if (match_subcommand (lexer, "ATTRIBUTE"))
+        command = ADD;
+      else if (match_subcommand (lexer, "DELETE"))
+        command = DELETE;
+      else if (command == UNKNOWN)
+        {
+          lex_error (lexer, _("expecting ATTRIBUTE= or DELETE="));
+          return CMD_FAILURE;
+        }
+
+      if (!(command == ADD
+            ? add_attribute (lexer, sets, n)
+            : delete_attribute (lexer, sets, n)))
+        return CMD_FAILURE;
+    }
+  while (lex_token (lexer) != '/' && lex_token (lexer) != '.');
+  return CMD_SUCCESS;
+}
index 825832a26cd966556da4426b276f6055b1b61678..2aa91842c5db8bdc42848624e049b15bcd9187c8 100644 (file)
@@ -1,6 +1,7 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 language_dictionary_sources = \
+ src/language/dictionary/attributes.c \
  src/language/dictionary/apply-dictionary.c \
  src/language/dictionary/delete-variables.c \
  src/language/dictionary/formats.c \
index b98854db6ea290a053a43fa377b6d0e4d246b305..d6279159a6587e8cf2d8c82524e52581c50ba979 100644 (file)
@@ -19,6 +19,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 
+#include <data/attributes.h>
 #include <data/casereader.h>
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* Constants for DISPLAY utility. */
-enum
+/* Information to include in displaying a dictionary. */
+enum 
   {
-    AS_NAMES = 0,
-    AS_INDEX,
-    AS_VARIABLES,
-    AS_LABELS,
-    AS_DICTIONARY,
-    AS_SCRATCH,
-    AS_VECTOR
+    DF_DICT_INDEX       = 1 << 0,
+    DF_FORMATS          = 1 << 1,
+    DF_VALUE_LABELS     = 1 << 2,
+    DF_VARIABLE_LABELS  = 1 << 3,
+    DF_MISSING_VALUES   = 1 << 4,
+    DF_AT_ATTRIBUTES    = 1 << 5, /* Attributes whose names begin with @. */
+    DF_ATTRIBUTES       = 1 << 6, /* All other attributes. */
+    DF_MISC             = 1 << 7,
+    DF_ALL              = (1 << 8) - 1
   };
 
-static int describe_variable (const struct variable *v, struct tab_table *t, int r, int as);
+static int describe_variable (const struct variable *v, struct tab_table *t,
+                              int r, int pc, int flags);
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
@@ -87,8 +91,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
   struct tab_table *t;
   struct casereader *reader;
   struct sfm_read_info info;
-  int r, nr;
-  int i;
+  int r, i;
 
   lex_match_id (lexer, "FILE");
   lex_match (lexer, '=');
@@ -153,9 +156,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
   tab_dim (t, tab_natural_dimensions);
   tab_submit (t);
 
-  nr = 1 + 2 * dict_get_var_cnt (d);
-
-  t = tab_create (4, nr, 1);
+  t = tab_create (4, 1 + 2 * dict_get_var_cnt (d), 1);
   tab_dim (t, sysfile_info_dim);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
@@ -163,19 +164,8 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
   tab_text (t, 3, 0, TAB_LEFT | TAT_TITLE, _("Position"));
   tab_hline (t, TAL_2, 0, 3, 1);
   for (r = 1, i = 0; i < dict_get_var_cnt (d); i++)
-    {
-      struct variable *v = dict_get_var (d, i);
-      const int nvl = val_labs_count (var_get_value_labels (v));
-
-      if (r + 13 + nvl > nr)
-       {
-         nr = MAX (nr * dict_get_var_cnt (d) / (i + 1), nr);
-         nr += 10 + nvl;
-         tab_realloc (t, 4, nr);
-       }
-
-      r = describe_variable (v, t, r, AS_DICTIONARY);
-    }
+    r = describe_variable (dict_get_var (d, i), t, r, 3,
+                           DF_ALL & ~DF_AT_ATTRIBUTES);
 
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, r);
   tab_vline (t, TAL_1, 1, 0, r);
@@ -197,6 +187,7 @@ static void display_macros (void);
 static void display_documents (const struct dictionary *dict);
 static void display_variables (const struct variable **, size_t, int);
 static void display_vectors (const struct dictionary *dict, int sorted);
+static void display_data_file_attributes (struct attrset *, int flags);
 
 int
 cmd_display (struct lexer *lexer, struct dataset *ds)
@@ -228,73 +219,81 @@ cmd_display (struct lexer *lexer, struct dataset *ds)
     }
   else
     {
-      static const char *sbc[] =
-       {"NAMES", "INDEX", "VARIABLES", "LABELS",
-        "DICTIONARY", "SCRATCH", "VECTORS", NULL};
-      const char **cp;
-      int as;
+      int flags;
 
       sorted = lex_match_id (lexer, "SORTED");
 
-      for (cp = sbc; *cp; cp++)
-       if (lex_token (lexer) == T_ID
-            && lex_id_match (ss_cstr (*cp), ss_cstr (lex_tokid (lexer))))
-         {
-           lex_get (lexer);
-           break;
-         }
-      as = cp - sbc;
-
-      if (*cp == NULL)
-       as = AS_NAMES;
-
-      if (as == AS_VECTOR)
+      if (lex_match_id (lexer, "VECTORS"))
        {
          display_vectors (dataset_dict(ds), sorted);
-         return CMD_SUCCESS;
+         return lex_end_of_command (lexer);
        }
+      else if (lex_match_id (lexer, "SCRATCH")) 
+        {
+          dict_get_vars (dataset_dict (ds), &vl, &n, DC_ORDINARY);
+          flags = 0;
+        }
+      else 
+        {
+          struct subcommand 
+            {
+              const char *name;
+              int flags;
+            };
+          static const struct subcommand subcommands[] = 
+            {
+              {"@ATTRIBUTES", DF_ATTRIBUTES | DF_AT_ATTRIBUTES},
+              {"ATTRIBUTES", DF_ATTRIBUTES},
+              {"DICTIONARY", DF_ALL & ~DF_AT_ATTRIBUTES},
+              {"INDEX", DF_DICT_INDEX},
+              {"LABELS", DF_DICT_INDEX | DF_VARIABLE_LABELS},
+              {"NAMES", 0},
+              {"VARIABLES",
+               DF_DICT_INDEX | DF_FORMATS | DF_MISSING_VALUES | DF_MISC},
+              {NULL, 0},
+            };
+          const struct subcommand *sbc;
+
+          flags = 0;
+          for (sbc = subcommands; sbc->name != NULL; sbc++)
+            if (lex_match_id (lexer, sbc->name))
+              {
+                flags = sbc->flags;
+                break;
+              }
+
+          lex_match (lexer, '/');
+          lex_match_id (lexer, "VARIABLES");
+          lex_match (lexer, '=');
+
+          if (lex_token (lexer) != '.')
+            {
+              if (!parse_variables_const (lexer, dataset_dict (ds), &vl, &n,
+                                          PV_NONE))
+                {
+                  free (vl);
+                  return CMD_FAILURE;
+                }
+            }
+          else
+            dict_get_vars (dataset_dict (ds), &vl, &n, 0);
+        }
 
-      lex_match (lexer, '/');
-      lex_match_id (lexer, "VARIABLES");
-      lex_match (lexer, '=');
-
-      if (lex_token (lexer) != '.')
-       {
-         if (!parse_variables_const (lexer, dataset_dict (ds), &vl, &n, PV_NONE))
-           {
-             free (vl);
-             return CMD_FAILURE;
-           }
-         as = AS_DICTIONARY;
-       }
+      if (n > 0) 
+        {
+          sort (vl, n, sizeof *vl,
+                (sorted
+                 ? compare_var_ptrs_by_name
+                 : compare_var_ptrs_by_dict_index), NULL);
+          display_variables (vl, n, flags);
+        }
       else
-       dict_get_vars (dataset_dict (ds), &vl, &n, 0);
-
-      if (as == AS_SCRATCH)
-       {
-         size_t i, m;
-         for (i = 0, m = n; i < n; i++)
-           if (dict_class_from_id (var_get_name (vl[i])) != DC_SCRATCH)
-             {
-               vl[i] = NULL;
-               m--;
-             }
-         as = AS_NAMES;
-         n = m;
-       }
-
-      if (n == 0)
-       {
-         msg (SW, _("No variables to display."));
-         return CMD_FAILURE;
-       }
-
-      if (sorted)
-       sort (vl, n, sizeof *vl, compare_var_ptrs_by_name, NULL);
-
-      display_variables (vl, n, as);
-
+        msg (SW, _("No variables to display."));
       free (vl);
+
+      if (flags & (DF_ATTRIBUTES | DF_AT_ATTRIBUTES))
+        display_data_file_attributes (dict_get_attributes (dataset_dict (ds)),
+                                      flags);
     }
 
   return lex_end_of_command (lexer);
@@ -333,7 +332,7 @@ display_documents (const struct dictionary *dict)
     }
 }
 
-static int _as;
+static int _flags;
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
@@ -344,14 +343,16 @@ variables_dim (struct tab_table *t, struct outp_driver *d)
   int i;
 
   t->w[0] = tab_natural_width (t, d, 0);
-  if (_as == AS_DICTIONARY || _as == AS_VARIABLES || _as == AS_LABELS)
+  if (_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;
     }
-  else pc = 1;
-  if (_as != AS_NAMES)
+  else
+    pc = 1;
+  if (_flags & DF_DICT_INDEX)
     t->w[pc] = tab_natural_width (t, d, pc);
 
   for (i = 0; i < t->nr; i++)
@@ -359,155 +360,220 @@ variables_dim (struct tab_table *t, struct outp_driver *d)
 }
 
 static void
-display_variables (const struct variable **vl, size_t n, int as)
+display_variables (const struct variable **vl, size_t n, int flags)
 {
-  const struct variable **vp = vl;     /* Variable pointer. */
   struct tab_table *t;
   int nc;                      /* Number of columns. */
-  int nr;                      /* Number of rows. */
   int pc;                      /* `Position column' */
   int r;                       /* Current row. */
   size_t i;
 
-  _as = as;
-  switch (as)
-    {
-    case AS_INDEX:
-      nc = 2;
-      break;
-    case AS_NAMES:
-      nc = 1;
-      break;
-    default:
-      nc = 4;
-      break;
-    }
+  _flags = flags;
+
+  /* One column for the name,
+     two columns for general description,
+     one column for dictionary index. */
+  nc = 1;
+  if (flags & ~DF_DICT_INDEX)
+    nc += 2;
+  pc = nc;
+  if (flags & DF_DICT_INDEX)
+    nc++;
 
   t = tab_create (nc, n + 5, 1);
   tab_headers (t, 0, 0, 1, 0);
-  nr = n + 5;
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
-  pc = (as == AS_INDEX ? 1 : 3);
-  if (as != AS_NAMES)
+  if (flags & ~DF_DICT_INDEX) 
+    tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE,
+                    (flags & ~(DF_DICT_INDEX | DF_VARIABLE_LABELS)
+                     ? _("Description") : _("Label")));
+  if (flags & DF_DICT_INDEX)
     tab_text (t, pc, 0, TAB_LEFT | TAT_TITLE, _("Position"));
-  if (as == AS_DICTIONARY || as == AS_VARIABLES)
-    tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
-  else if (as == AS_LABELS)
-    tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Label"));
   tab_dim (t, variables_dim);
 
-  for (i = r = 1; i <= n; i++)
-    {
-      const struct variable *v;
-
-      while (*vp == NULL)
-       vp++;
-      v = *vp++;
-
-      if (as == AS_DICTIONARY || as == AS_VARIABLES)
-       {
-         int nvl = val_labs_count (var_get_value_labels (v));
-
-         if (r + 13 + nvl > nr)
-           {
-             nr = MAX (nr * n / (i + 1), nr);
-             nr += 10 + nvl;
-             tab_realloc (t, nc, nr);
-           }
-
-         r = describe_variable (v, t, r, as);
-       } else {
-         tab_text (t, 0, r, TAB_LEFT, var_get_name (v));
-         if (as == AS_LABELS)
-            {
-              const char *label = var_get_label (v);
-              tab_joint_text (t, 1, r, 2, r, TAB_LEFT,
-                              label == NULL ? "(no label)" : label);
-            }
-         if (as != AS_NAMES)
-           {
-             tab_text (t, pc, r, TAT_PRINTF, "%zu",
-                        var_get_dict_index (v) + 1);
-             tab_hline (t, TAL_1, 0, nc - 1, r);
-           }
-         r++;
-       }
-    }
-  tab_hline (t, as == AS_NAMES ? TAL_1 : TAL_2, 0, nc - 1, 1);
-  if (as != AS_NAMES)
+  r = 1;
+  for (i = 0; i < n; i++)
+    r = describe_variable (vl[i], t, r, pc, flags);
+  tab_hline (t, flags & ~DF_DICT_INDEX ? TAL_2 : TAL_1, 0, nc - 1, 1);
+  if (flags)
     {
       tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, r - 1);
       tab_vline (t, TAL_1, 1, 0, r - 1);
     }
   else
     tab_flags (t, SOMF_NO_TITLE);
-  if (as == AS_DICTIONARY || as == AS_VARIABLES || as == AS_LABELS)
-    tab_vline (t, TAL_1, 3, 0, r - 1);
+  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_submit (t);
 }
 \f
-/* Puts a description of variable V into table T starting at row R.
-   The variable will be described in the format AS.  Returns the next
-   row available for use in the table. */
+static bool
+is_at_name (const char *name) 
+{
+  return name[0] == '@' || (name[0] == '$' && name[1] == '@');
+}
+
+static size_t
+count_attributes (const struct attrset *set, int flags) 
+{
+  struct attrset_iterator i;
+  struct attribute *attr;
+  size_t n_attrs;
+  
+  n_attrs = 0;
+  for (attr = attrset_first (set, &i); attr != NULL;
+       attr = attrset_next (set, &i)) 
+    if (flags & DF_AT_ATTRIBUTES || !is_at_name (attribute_get_name (attr)))
+      n_attrs += attribute_get_n_values (attr);
+  return n_attrs;
+}
+
+static void
+display_attributes (struct tab_table *t, const struct attrset *set, int flags,
+                    int c, int r)
+{
+  struct attrset_iterator i;
+  struct attribute *attr;
+
+  for (attr = attrset_first (set, &i); attr != NULL;
+       attr = attrset_next (set, &i)) 
+    {
+      const char *name = attribute_get_name (attr);
+      size_t n_values;
+      size_t i;
+
+      if (!(flags & DF_AT_ATTRIBUTES) && is_at_name (name))
+        continue;
+
+      n_values = attribute_get_n_values (attr);
+      for (i = 0; i < n_values; i++)
+        {
+          if (n_values > 1)
+            tab_text (t, c, r, TAB_LEFT | TAT_PRINTF, "%s[%d]",
+                      name, i + 1);
+          else
+            tab_text (t, c, r, TAB_LEFT, name);
+          tab_text (t, c + 1, r, TAB_LEFT, attribute_get_value (attr, i));
+          r++;
+        }
+    }
+}
+
+static void
+display_data_file_attributes (struct attrset *set, int flags) 
+{
+  struct tab_table *t;
+  size_t n_attrs;
+
+  n_attrs = count_attributes (set, flags);
+  if (!n_attrs)
+    return;
+
+  t = tab_create (2, n_attrs + 1, 0);
+  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);
+  tab_title (t, "Custom data file attributes.");
+  tab_submit (t);
+}
+
+/* Puts a description of variable V into table T starting at row
+   R.  The variable will be described in the format given by
+   FLAGS.  Returns the next row available for use in the
+   table. */
 static int
-describe_variable (const struct variable *v, struct tab_table *t, int r, int as)
+describe_variable (const struct variable *v, struct tab_table *t, int r,
+                   int pc, int flags)
 {
-  const struct fmt_spec *print = var_get_print_format (v);
-  const struct fmt_spec *write = var_get_write_format (v);
-  enum measure m = var_get_measure (v);
-  enum alignment a = var_get_alignment (v);
+  size_t n_attrs = 0;
+  int need_rows;
+
+  /* Make sure that enough rows are allocated. */
+  need_rows = 1;
+  if (flags & ~(DF_DICT_INDEX | DF_VARIABLE_LABELS))
+    need_rows += 15;
+  if (flags & DF_VALUE_LABELS)
+    need_rows += val_labs_count (var_get_value_labels (v));
+  if (flags & (DF_ATTRIBUTES | DF_AT_ATTRIBUTES))
+    {
+      n_attrs = count_attributes (var_get_attributes (v), flags);
+      need_rows += n_attrs; 
+    }
+  if (r + need_rows > tab_nr (t))
+    {
+      int nr = MAX (r + need_rows, tab_nr (t) * 2);
+      tab_realloc (t, -1, nr);
+    }
 
   /* Put the name, var label, and position into the first row. */
   tab_text (t, 0, r, TAB_LEFT, var_get_name (v));
-  tab_text (t, 3, r, TAT_PRINTF, "%zu", var_get_dict_index (v) + 1);
+  if (flags & DF_DICT_INDEX)
+    tab_text (t, pc, r, TAT_PRINTF, "%zu", var_get_dict_index (v) + 1);
 
-  if (as == AS_DICTIONARY && var_has_label (v))
+  if (flags & DF_VARIABLE_LABELS && var_has_label (v))
     {
       tab_joint_text (t, 1, r, 2, r, TAB_LEFT, var_get_label (v));
       r++;
     }
 
   /* Print/write format, or print and write formats. */
-  if (fmt_equal (print, write))
+  if (flags & DF_FORMATS) 
     {
-      char str[FMT_STRING_LEN_MAX + 1];
-      tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF, _("Format: %s"),
-                     fmt_to_string (print, str));
-      r++;
+      const struct fmt_spec *print = var_get_print_format (v);
+      const struct fmt_spec *write = var_get_write_format (v);
+
+      if (fmt_equal (print, write))
+        {
+          char str[FMT_STRING_LEN_MAX + 1];
+          tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
+                          _("Format: %s"), fmt_to_string (print, str));
+          r++;
+        }
+      else
+        {
+          char str[FMT_STRING_LEN_MAX + 1];
+          tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
+                          _("Print Format: %s"), fmt_to_string (print, str));
+          r++;
+          tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
+                          _("Write Format: %s"), fmt_to_string (write, str));
+          r++;
+        }
     }
-  else
+  
+  /* Measurement level, display width, alignment. */
+  if (flags & DF_MISC) 
     {
-      char str[FMT_STRING_LEN_MAX + 1];
+      enum measure m = var_get_measure (v);
+      enum alignment a = var_get_alignment (v);
+
       tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
-                     _("Print Format: %s"), fmt_to_string (print, str));
+                      _("Measure: %s"),
+                      m == MEASURE_NOMINAL ? _("Nominal")
+                      : m == MEASURE_ORDINAL ? _("Ordinal")
+                      : _("Scale"));
       r++;
       tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
-                     _("Write Format: %s"), fmt_to_string (write, str));
+                      _("Display Alignment: %s"),
+                      a == ALIGN_LEFT ? _("Left")
+                      : a == ALIGN_CENTRE ? _("Center")
+                      : _("Right"));
+      r++;
+      tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
+                      _("Display Width: %d"), var_get_display_width (v));
       r++;
     }
-
-  /* Measurement level, display width, alignment. */
-  tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
-                  _("Measure: %s"),
-                  m == MEASURE_NOMINAL ? _("Nominal")
-                  : m == MEASURE_ORDINAL ? _("Ordinal")
-                  : _("Scale"));
-  r++;
-  tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
-                  _("Display Alignment: %s"),
-                  a == ALIGN_LEFT ? _("Left")
-                  : a == ALIGN_CENTRE ? _("Center")
-                  : _("Right"));
-  r++;
-  tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
-                  _("Display Width: %d"), var_get_display_width (v));
-  r++;
-
+  
   /* Missing values if any. */
-  if (var_has_missing_values (v))
+  if (flags & DF_MISSING_VALUES && var_has_missing_values (v))
     {
       char buf[128];
       char *cp;
@@ -552,7 +618,7 @@ describe_variable (const struct variable *v, struct tab_table *t, int r, int as)
     }
 
   /* Value labels. */
-  if (as == AS_DICTIONARY && var_has_value_labels (v))
+  if (flags & DF_VALUE_LABELS && var_has_value_labels (v))
     {
       const struct val_labs *val_labs = var_get_value_labels (v);
       struct val_labs_iterator *i;
@@ -587,8 +653,17 @@ describe_variable (const struct variable *v, struct tab_table *t, int r, int as)
       tab_vline (t, TAL_1, 2, orig_r, r - 1);
     }
 
+  if (flags & (DF_ATTRIBUTES | DF_AT_ATTRIBUTES) && n_attrs)
+    {
+      tab_joint_text (t, 1, r, 2, r, TAB_LEFT, "Custom attributes:");
+      r++;
+
+      display_attributes (t, var_get_attributes (v), flags, 1, r);
+      r += n_attrs;
+    }
+
   /* Draw a line below the last row of information on this variable. */
-  tab_hline (t, TAL_1, 0, 3, r);
+  tab_hline (t, TAL_1, 0, tab_nc (t) - 1, r);
 
   return r;
 }
index 4b7c03961783c0109b30c8e5a32bcb13b43658b3..39f544ec4b917fa2b517129a06ad6c252d5cc171 100644 (file)
@@ -163,11 +163,9 @@ get_label (struct lexer *lexer, struct variable **vars, size_t var_cnt)
        {
          if (!lex_is_number (lexer))
            {
-             lex_error (lexer, _("expecting integer"));
+             lex_error (lexer, _("expecting number"));
              return 0;
            }
-         if (!lex_is_integer (lexer))
-           msg (SW, _("Value label `%g' is not integer."), lex_tokval (lexer));
          value.f = lex_tokval (lexer);
        }
       lex_get (lexer);
index fc5b74a3ab57b7727e96226c785640e5a3e8ee43..91b9c84249b76c0e7d799ad7dc089f66874c314a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -170,12 +170,9 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
             }
 
           if (c == NULL)
-            {
-              c = xmalloc (sizeof *c);
-              case_create (c, dict_get_next_value_idx (d));
-            }
+            c = case_create (dict_get_next_value_idx (d));
           else
-            case_resize (c, dict_get_next_value_idx (d));
+            c = case_resize (c, dict_get_next_value_idx (d));
 
           if (lex_is_number (lexer))
             case_data_rw (c, v)->f = lex_tokval (lexer);
@@ -255,11 +252,7 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
   if (ds)
     destroy_dataset (ds);
 
-  if (c != NULL)
-    {
-      case_destroy (c);
-      free (c);
-    }
+  case_unref (c);
 
   return retval;
 }
index 695c2335370cb19a46043f5e7d272ea04b21998d..85705098a2df44e2ad0e39da0764f75684bc9407 100644 (file)
@@ -589,7 +589,7 @@ ncdf_beta (double x, double a, double b, double lambda)
 double
 cdf_bvnor (double x0, double x1, double r)
 {
-  double z = x0 * x0 - 2. * r * x0 * x1 + x1 * x1;
+  double z = pow2 (x0) - 2. * r * x0 * x1 + pow2 (x1);
   return exp (-z / (2. * (1 - r * r))) * (2. * M_PI * sqrt (1 - r * r));
 }
 
index ea640e18e194a27aa1d99cd6676eb495417186e7..2d31bd47182dcb158d9f9c50e644fb03bb7b07bf 100644 (file)
@@ -1,7 +1,7 @@
 // -*- c -*-
 //
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+// Copyright (C) 2005, 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
@@ -988,7 +988,7 @@ no_opt string operator STR_VAR ()
 no_opt perm_only function LAG (num_var v, pos_int n_before)
     dataset ds;
 {
-  struct ccase *c = lagged_case (ds, n_before);
+  const struct ccase *c = lagged_case (ds, n_before);
   if (c != NULL)
     {
       double x = case_num (c, v);
@@ -1001,7 +1001,7 @@ no_opt perm_only function LAG (num_var v, pos_int n_before)
 no_opt perm_only function LAG (num_var v)
     dataset ds;
 {
-  struct ccase *c = lagged_case (ds, 1);
+  const struct ccase *c = lagged_case (ds, 1);
   if (c != NULL)
     {
       double x = case_num (c, v);
@@ -1015,7 +1015,7 @@ no_opt perm_only string function LAG (str_var v, pos_int n_before)
      expression e;
      dataset ds;
 {
-  struct ccase *c = lagged_case (ds, n_before);
+  const struct ccase *c = lagged_case (ds, n_before);
   if (c != NULL)
     return copy_string (e, case_str (c, v), var_get_width (v));
   else
@@ -1026,7 +1026,7 @@ no_opt perm_only string function LAG (str_var v)
      expression e;
      dataset ds;
 {
-  struct ccase *c = lagged_case (ds, 1);
+  const struct ccase *c = lagged_case (ds, 1);
   if (c != NULL)
     return copy_string (e, case_str (c, v), var_get_width (v));
   else
index 1c9542d78ed54ccf5271811b5cc27be54337aeae..265116141eb36f0d8ffa685a326431cd79f7b7ad 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -17,7 +17,8 @@
 #include <config.h>
 #include "lexer.h"
 #include <libpspp/message.h>
-#include <ctype.h>
+#include <c-ctype.h>
+#include <c-strtod.h>
 #include <errno.h>
 #include <limits.h>
 #include <math.h>
@@ -187,7 +188,7 @@ lex_get (struct lexer *lexer)
   for (;;)
     {
       /* Skip whitespace. */
-         while (isspace ((unsigned char) *lexer->prog))
+         while (c_isspace ((unsigned char) *lexer->prog))
            lexer->prog++;
 
          if (*lexer->prog)
@@ -244,10 +245,10 @@ lex_get (struct lexer *lexer)
            if (*lexer->prog == '-')
              {
                ds_put_char (&lexer->tokstr, *lexer->prog++);
-               while (isspace ((unsigned char) *lexer->prog))
+               while (c_isspace ((unsigned char) *lexer->prog))
                  lexer->prog++;
 
-               if (!isdigit ((unsigned char) *lexer->prog) && *lexer->prog != '.')
+               if (!c_isdigit ((unsigned char) *lexer->prog) && *lexer->prog != '.')
                  {
                    lexer->token = '-';
                    break;
@@ -258,12 +259,12 @@ lex_get (struct lexer *lexer)
               lexer->token = T_POS_NUM;
 
            /* Parse the number, copying it into tokstr. */
-           while (isdigit ((unsigned char) *lexer->prog))
+           while (c_isdigit ((unsigned char) *lexer->prog))
              ds_put_char (&lexer->tokstr, *lexer->prog++);
            if (*lexer->prog == '.')
              {
                ds_put_char (&lexer->tokstr, *lexer->prog++);
-               while (isdigit ((unsigned char) *lexer->prog))
+               while (c_isdigit ((unsigned char) *lexer->prog))
                  ds_put_char (&lexer->tokstr, *lexer->prog++);
              }
            if (*lexer->prog == 'e' || *lexer->prog == 'E')
@@ -271,12 +272,12 @@ lex_get (struct lexer *lexer)
                ds_put_char (&lexer->tokstr, *lexer->prog++);
                if (*lexer->prog == '+' || *lexer->prog == '-')
                  ds_put_char (&lexer->tokstr, *lexer->prog++);
-               while (isdigit ((unsigned char) *lexer->prog))
+               while (c_isdigit ((unsigned char) *lexer->prog))
                  ds_put_char (&lexer->tokstr, *lexer->prog++);
              }
 
            /* Parse as floating point. */
-           lexer->tokval = strtod (ds_cstr (&lexer->tokstr), &tail);
+           lexer->tokval = c_strtod (ds_cstr (&lexer->tokstr), &tail);
            if (*tail)
              {
                msg (SE, _("%s does not form a valid number."),
@@ -295,6 +296,7 @@ lex_get (struct lexer *lexer)
          break;
 
        case '(': case ')': case ',': case '=': case '+': case '/':
+        case '[': case ']':
          lexer->token = *lexer->prog++;
          break;
 
@@ -382,10 +384,11 @@ lex_get (struct lexer *lexer)
             }
           else
             {
-              if (isgraph ((unsigned char) *lexer->prog))
-                msg (SE, _("Bad character in input: `%c'."), *lexer->prog++);
+              unsigned char c = *lexer->prog++;
+              if (c_isgraph (c))
+                msg (SE, _("Bad character in input: `%c'."), c);
               else
-                msg (SE, _("Bad character in input: `\\%o'."), *lexer->prog++);
+                msg (SE, _("Bad character in input: `\\%o'."), c);
               continue;
             }
         }
@@ -690,7 +693,7 @@ lex_look_ahead (struct lexer *lexer)
 
       for (;;)
        {
-         while (isspace ((unsigned char) *lexer->prog))
+         while (c_isspace ((unsigned char) *lexer->prog))
            lexer->prog++;
          if (*lexer->prog)
            break;
@@ -865,7 +868,7 @@ lex_preprocess_line (struct string *line,
   if (syntax == GETL_BATCH)
     {
       int first = ds_first (line);
-      *line_starts_command = !isspace (first);
+      *line_starts_command = !c_isspace (first);
       if (first == '+' || first == '-')
         *ds_data (line) = ' ';
     }
@@ -950,7 +953,7 @@ lex_token_representation (struct lexer *lexer)
        char *sp, *dp;
 
        for (sp = ds_cstr (&lexer->tokstr); sp < ds_end (&lexer->tokstr); sp++)
-         if (!isprint ((unsigned char) *sp))
+         if (!c_isprint ((unsigned char) *sp))
            {
              hexstring = 1;
              break;
@@ -1164,7 +1167,7 @@ parse_string (struct lexer *lexer, enum string_type type)
        break;
       for (;;)
        {
-         while (isspace ((unsigned char) *lexer->prog))
+         while (c_isspace ((unsigned char) *lexer->prog))
            lexer->prog++;
          if (*lexer->prog)
            break;
@@ -1186,7 +1189,7 @@ parse_string (struct lexer *lexer, enum string_type type)
        break;
       for (;;)
        {
-         while (isspace ((unsigned char) *lexer->prog))
+         while (c_isspace ((unsigned char) *lexer->prog))
            lexer->prog++;
          if (*lexer->prog)
            break;
@@ -1304,3 +1307,28 @@ lex_tokstr (const struct lexer *lexer)
 {
   return &lexer->tokstr;
 }
+
+/* If the lexer is positioned at the (pseudo)identifier S, which
+   may contain a hyphen ('-'), skips it and returns true.  Each
+   half of the identifier may be abbreviated to its first three
+   letters.
+   Otherwise, returns false. */
+bool
+lex_match_hyphenated_word (struct lexer *lexer, const char *s)
+{
+  const char *hyphen = strchr (s, '-');
+  if (hyphen == NULL)
+    return lex_match_id (lexer, s);
+  else if (lexer->token != T_ID
+          || !lex_id_match (ss_buffer (s, hyphen - s), ss_cstr (lexer->tokid))
+          || lex_look_ahead (lexer) != '-')
+    return false;
+  else
+    {
+      lex_get (lexer);
+      lex_force_match (lexer, '-');
+      lex_force_match_id (lexer, hyphen + 1);
+      return true;
+    }
+}
+
index 53732b49995f5fb60429e96f65e89a61ca108f4c..9e5d09aec03f8dee7f96dcd73e7067f650ca9a22 100644 (file)
@@ -55,6 +55,8 @@ bool lex_match (struct lexer *, int);
 bool lex_match_id (struct lexer *, const char *);
 bool lex_match_id_n (struct lexer *, const char *, size_t n);
 bool lex_match_int (struct lexer *, int);
+bool lex_match_hyphenated_word (struct lexer *lexer, const char *s);
+
 
 /* Forcible matching functions. */
 bool lex_force_match (struct lexer *, int);
index d14c69d0febec3d7d14b36f87bc94477fd0ecb7e..ea4e348148b29febf66718075ca8aabfef77eb29 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2008 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
@@ -357,6 +357,44 @@ dump_token (void)
 }
 #endif /* DUMP_TOKENS */
 
+
+const char hyphen_proxy = '_';
+
+static void
+id_cpy (char **cp)
+{
+  char *dest = tokstr;
+  char *src = *cp;
+
+  while (*src == '_' || *src == '-' || isalnum ((unsigned char) *src))
+    {
+      *dest++ = *src == '-' ? hyphen_proxy :toupper ((unsigned char) (*src));
+      src++;
+    }
+
+  *cp = src;
+  *dest++ = '\0';
+}
+
+static char *
+unmunge (const char *s)
+{
+  char *dest = xmalloc (strlen (s));
+  char *d = dest;
+
+  while (*s)
+    {
+      if (*s == hyphen_proxy)
+       *d = '-';
+      else
+       *d = *s;
+      s++;
+      d++;
+    }
+
+  return dest;
+}
+
 /* Reads a token from the input file. */
 static int
 lex_get (void)
@@ -398,9 +436,8 @@ lex_get (void)
     {
       char *dest = tokstr;
       token = T_ID;
-      while (*cp == '_' || isalnum ((unsigned char) *cp))
-       *dest++ = toupper ((unsigned char) (*cp++));
-      *dest++ = '\0';
+
+      id_cpy (&cp);
     }
   else
     token = *cp++;
@@ -1374,7 +1411,11 @@ make_match (const char *t)
   else if (isdigit ((unsigned char) t[0]))
     sprintf (s, "lex_match_int (lexer, %s)", t);
   else
-    sprintf (s, "lex_match_id (lexer, \"%s\")", t);
+    {
+      char *c = unmunge (t);
+      sprintf (s, "lex_match_hyphenated_word (lexer, \"%s\")", c);
+      free (c);
+    }
 
   return s;
 }
index d3d5a40f9683deff33be24a8a60a457906aea0b2..6df3f96b69a980626f1fcffee0d6d91f038345ba 100644 (file)
@@ -2,9 +2,11 @@ correlations.c
 crosstabs.c
 examine.c
 frequencies.c
+glm.c
 means.c
 npar.c
 oneway.c
 rank.c
 regression.c
+reliability.c
 t-test.c
index f58b97cf8994c4eebcef4509c5cc1b8267f7f871..c217310910271f2639830eb09d44b2fcdffd69db 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
 #include <stdlib.h>
 
 #include <data/any-writer.h>
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casegrouper.h>
 #include <data/casereader.h>
@@ -29,6 +28,7 @@
 #include <data/format.h>
 #include <data/procedure.h>
 #include <data/settings.h>
+#include <data/subcase.h>
 #include <data/sys-file-writer.h>
 #include <data/variable.h>
 #include <language/command.h>
@@ -43,6 +43,8 @@
 #include <libpspp/str.h>
 #include <math/moments.h>
 #include <math/sort.h>
+#include <math/statistic.h>
+#include <math/percentiles.h>
 
 #include "minmax.h"
 #include "xalloc.h"
@@ -75,12 +77,17 @@ struct agr_var
     char *string;
     bool saw_missing;
     struct moments1 *moments;
+    double cc;
+
+    struct variable *subject;
+    struct variable *weight;
+    struct casewriter *writer;
   };
 
 /* Aggregation functions. */
 enum
   {
-    NONE, SUM, MEAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
+    NONE, SUM, MEAN, MEDIAN, SD, MAX, MIN, PGT, PLT, PIN, POUT, FGT, FLT, FIN,
     FOUT, N, NU, NMISS, NUMISS, FIRST, LAST,
     N_AGR_FUNCS, N_NO_VARS, NU_NO_VARS,
     FUNC = 0x1f, /* Function mask. */
@@ -102,6 +109,7 @@ static const struct agr_func agr_func_tab[] =
     {"<NONE>",  0, -1,          {0, 0, 0}},
     {"SUM",     0, -1,          {FMT_F, 8, 2}},
     {"MEAN",   0, -1,          {FMT_F, 8, 2}},
+    {"MEDIAN", 0, -1,          {FMT_F, 8, 2}},
     {"SD",      0, -1,          {FMT_F, 8, 2}},
     {"MAX",     0, VAL_STRING,  {-1, -1, -1}},
     {"MIN",     0, VAL_STRING,  {-1, -1, -1}},
@@ -135,10 +143,10 @@ enum missing_treatment
 struct agr_proc
   {
     /* Break variables. */
-    struct case_ordering *sort;         /* Sort criteria. */
+    struct subcase sort;                /* Sort criteria (break variables). */
     const struct variable **break_vars;       /* Break variables. */
     size_t break_var_cnt;               /* Number of break variables. */
-    struct ccase break_case;            /* Last values of break variables. */
+    struct ccase *break_case;           /* Last values of break variables. */
 
     enum missing_treatment missing;     /* How to treat missing values. */
     struct agr_var *agr_vars;           /* First aggregate variable. */
@@ -149,6 +157,7 @@ struct agr_proc
 
 static void initialize_aggregate_info (struct agr_proc *,
                                        const struct ccase *);
+
 static void accumulate_aggregate_info (struct agr_proc *,
                                        const struct ccase *);
 /* Prototypes. */
@@ -178,10 +187,11 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
 
   memset(&agr, 0 , sizeof (agr));
   agr.missing = ITEMWISE;
-  case_nullify (&agr.break_case);
+  agr.break_case = NULL;
 
   agr.dict = dict_create ();
   agr.src_dict = dict;
+  subcase_init_empty (&agr.sort);
   dict_set_label (agr.dict, dict_get_label (dict));
   dict_set_documents (agr.dict, dict_get_documents (dict));
 
@@ -220,13 +230,10 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
           int i;
 
          lex_match (lexer, '=');
-          agr.sort = parse_case_ordering (lexer, dict,
-
-                                          &saw_direction);
-          if (agr.sort == NULL)
+          if (!parse_sort_criteria (lexer, dict, &agr.sort, &agr.break_vars,
+                                    &saw_direction))
             goto error;
-          case_ordering_get_vars (agr.sort,
-                                  &agr.break_vars, &agr.break_var_cnt);
+          agr.break_var_cnt = subcase_get_n_fields (&agr.sort);
 
           for (i = 0; i < agr.break_var_cnt; i++)
             dict_clone_var_assert (agr.dict, agr.break_vars[i],
@@ -277,10 +284,10 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
     }
 
   input = proc_open (ds);
-  if (agr.sort != NULL && !presorted)
+  if (!subcase_is_empty (&agr.sort) && !presorted)
     {
-      input = sort_execute (input, agr.sort);
-      agr.sort = NULL;
+      input = sort_execute (input, &agr.sort);
+      subcase_clear (&agr.sort);
     }
 
   for (grouper = casegrouper_create_vars (input, agr.break_vars,
@@ -288,18 +295,17 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
        casegrouper_get_next_group (grouper, &group);
        casereader_destroy (group))
     {
-      struct ccase c;
-
-      if (!casereader_peek (group, 0, &c))
+      struct ccase *c = casereader_peek (group, 0);
+      if (c == NULL)
         {
           casereader_destroy (group);
           continue;
         }
-      initialize_aggregate_info (&agr, &c);
-      case_destroy (&c);
+      initialize_aggregate_info (&agr, c);
+      case_unref (c);
 
-      for (; casereader_read (group, &c); case_destroy (&c))
-        accumulate_aggregate_info (&agr, &c);
+      for (; (c = casereader_read (group)) != NULL; case_unref (c))
+        accumulate_aggregate_info (&agr, c);
       dump_aggregate_info (&agr, output);
     }
   if (!casegrouper_destroy (grouper))
@@ -344,7 +350,8 @@ error:
 
 /* Parse all the aggregate functions. */
 static bool
-parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict, struct agr_proc *agr)
+parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
+                          struct agr_proc *agr)
 {
   struct agr_var *tail; /* Tail of linked list starting at agr->vars. */
 
@@ -545,7 +552,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict, s
          variables. */
       for (i = 0; i < n_dest; i++)
        {
-         struct agr_var *v = xmalloc (sizeof *v);
+         struct agr_var *v = xzalloc (sizeof *v);
 
          /* Add variable to chain. */
          if (agr->agr_vars != NULL)
@@ -684,9 +691,9 @@ agr_destroy (struct agr_proc *agr)
 {
   struct agr_var *iter, *next;
 
-  case_ordering_destroy (agr->sort);
+  subcase_destroy (&agr->sort);
   free (agr->break_vars);
-  case_destroy (&agr->break_case);
+  case_unref (agr->break_case);
   for (iter = agr->agr_vars; iter; iter = next)
     {
       next = iter->next;
@@ -703,6 +710,10 @@ agr_destroy (struct agr_proc *agr)
        }
       else if (iter->function == SD)
         moments1_destroy (iter->moments);
+
+      var_destroy (iter->subject);
+      var_destroy (iter->weight);
+
       free (iter);
     }
   if (agr->dict != NULL)
@@ -755,6 +766,23 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             iter->dbl[0] += v->f * weight;
             iter->dbl[1] += weight;
             break;
+         case MEDIAN:
+           {
+             double wv ;
+             struct ccase *cout = case_create (2);
+
+             case_data_rw (cout, iter->subject)->f
+                = case_data (input, iter->src)->f;
+
+             wv = dict_get_case_weight (agr->src_dict, input, NULL);
+
+             case_data_rw (cout, iter->weight)->f = wv;
+
+             iter->cc += wv;
+
+             casewriter_write (iter->writer, cout);
+           }
+           break;
          case SD:
             moments1_add (iter->moments, v->f, weight);
             break;
@@ -885,9 +913,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
 static void
 dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
 {
-  struct ccase c;
-
-  case_create (&c, dict_get_next_value_idx (agr->dict));
+  struct ccase *c = case_create (dict_get_next_value_idx (agr->dict));
 
   {
     int value_idx = 0;
@@ -897,8 +923,8 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
       {
         const struct variable *v = agr->break_vars[i];
         size_t value_cnt = var_get_value_cnt (v);
-        memcpy (case_data_rw_idx (&c, value_idx),
-                case_data (&agr->break_case, v),
+        memcpy (case_data_rw_idx (c, value_idx),
+                case_data (agr->break_case, v),
                 sizeof (union value) * value_cnt);
         value_idx += value_cnt;
       }
@@ -909,7 +935,8 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
 
     for (i = agr->agr_vars; i; i = i->next)
       {
-       union value *v = case_data_rw (&c, i->dest);
+       union value *v = case_data_rw (c, i->dest);
+
 
        if (agr->missing == COLUMNWISE && i->saw_missing
            && (i->function & FUNC) != N && (i->function & FUNC) != NU
@@ -919,6 +946,9 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
              memset (v->s, ' ', var_get_width (i->dest));
            else
              v->f = SYSMIS;
+
+           casewriter_destroy (i->writer);
+
            continue;
          }
 
@@ -930,6 +960,25 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case MEAN:
            v->f = i->dbl[1] != 0.0 ? i->dbl[0] / i->dbl[1] : SYSMIS;
            break;
+         case MEDIAN:
+           {
+             struct casereader *sorted_reader;
+             struct order_stats *median = percentile_create (0.5, i->cc);
+
+             sorted_reader = casewriter_make_reader (i->writer);
+
+             order_stats_accumulate (&median, 1,
+                                     sorted_reader,
+                                     i->weight,
+                                     i->subject,
+                                     i->exclude);
+
+             v->f = percentile_calculate ((struct percentile *) median,
+                                          PC_HAVERAGE);
+
+             statistic_destroy ((struct statistic *) median);
+           }
+           break;
          case SD:
             {
               double variance;
@@ -1013,7 +1062,7 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
       }
   }
 
-  casewriter_write (output, &c);
+  casewriter_write (output, c);
 }
 
 /* Resets the state for all the aggregate functions. */
@@ -1022,8 +1071,8 @@ initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
 {
   struct agr_var *iter;
 
-  case_destroy (&agr->break_case);
-  case_clone (&agr->break_case, input);
+  case_unref (agr->break_case);
+  agr->break_case = case_ref (input);
 
   for (iter = agr->agr_vars; iter; iter = iter->next)
     {
@@ -1044,6 +1093,23 @@ initialize_aggregate_info (struct agr_proc *agr, const struct ccase *input)
        case MAX | FSTRING:
          memset (iter->string, 0, var_get_width (iter->src));
          break;
+       case MEDIAN:
+         {
+            struct subcase ordering;
+
+           if ( ! iter->subject)
+             iter->subject = var_create_internal (0);
+
+           if ( ! iter->weight)
+             iter->weight = var_create_internal (1);
+
+            subcase_init_var (&ordering, iter->subject, SC_ASCEND);
+           iter->writer = sort_create_writer (&ordering, 2);
+            subcase_destroy (&ordering);
+
+           iter->cc = 0;
+         }
+         break;
         case SD:
           if (iter->moments == NULL)
             iter->moments = moments1_create (MOMENT_VARIANCE);
index d60cb0dbdb6a15a59fcd31312a147f6b4da15d9e..3e9ab232a29a66368f6995ccf18340a2e9d5eeb8 100644 (file)
@@ -13,6 +13,7 @@ src_language_stats_built_sources = \
        src/language/stats/oneway.c \
        src/language/stats/rank.c \
        src/language/stats/regression.c \
+       src/language/stats/reliability.c \
        src/language/stats/t-test.c
 
 language_stats_sources = \
@@ -31,7 +32,9 @@ language_stats_sources = \
        src/language/stats/freq.c \
        src/language/stats/freq.h \
        src/language/stats/npar-summary.c \
-       src/language/stats/npar-summary.h 
+       src/language/stats/npar-summary.h \
+       src/language/stats/wilcoxon.c \
+       src/language/stats/wilcoxon.h
 
 all_q_sources += $(src_language_stats_built_sources:.c=.q)
 EXTRA_DIST += $(src_language_stats_built_sources:.c=.q)
index af9280b557c5669e4ec0033de09b8c17ca4c2e9d..de9416821c15bf5fa8c37d5e9a241d7a647fd804 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -103,7 +103,7 @@ cmd_autorecode (struct lexer *lexer, struct dataset *ds)
 {
   struct autorecode_pgm arc;
   struct casereader *input;
-  struct ccase c;
+  struct ccase *c;
   size_t dst_cnt;
   size_t i;
   bool ok;
@@ -189,15 +189,15 @@ cmd_autorecode (struct lexer *lexer, struct dataset *ds)
    }
 
   input = proc_open (ds);
-  for (; casereader_read (input, &c); case_destroy (&c))
+  for (; (c = casereader_read (input)) != NULL; case_unref (c))
     for (i = 0; i < arc.var_cnt; i++)
       {
         union arc_value v, *vp, **vpp;
 
         if (var_is_numeric (arc.src_vars[i]))
-          v.f = case_num (&c, arc.src_vars[i]);
+          v.f = case_num (c, arc.src_vars[i]);
         else
-          v.c = (char *) case_str (&c, arc.src_vars[i]);
+          v.c = (char *) case_str (c, arc.src_vars[i]);
 
         vpp = (union arc_value **) hsh_probe (arc.src_values[i], &v);
         if (*vpp == NULL)
@@ -300,11 +300,13 @@ recode (struct dataset *ds, const struct autorecode_pgm *arc)
 
 /* Executes an AUTORECODE transformation. */
 static int
-autorecode_trns_proc (void *trns_, struct ccase *c, casenumber case_idx UNUSED)
+autorecode_trns_proc (void *trns_, struct ccase **c,
+                      casenumber case_idx UNUSED)
 {
   struct autorecode_trns *trns = trns_;
   size_t i;
 
+  *c = case_unshare (*c);
   for (i = 0; i < trns->spec_cnt; i++)
     {
       struct arc_spec *spec = &trns->specs[i];
@@ -312,12 +314,12 @@ autorecode_trns_proc (void *trns_, struct ccase *c, casenumber case_idx UNUSED)
       union arc_value v;
 
       if (var_is_numeric (spec->src))
-        v.f = case_num (c, spec->src);
+        v.f = case_num (*c, spec->src);
       else
-        v.c = (char *) case_str (c, spec->src);
+        v.c = (char *) case_str (*c, spec->src);
       item = hsh_force_find (spec->items, &v);
 
-      case_data_rw (c, spec->dest)->f = item->to;
+      case_data_rw (*c, spec->dest)->f = item->to;
     }
   return TRNS_CONTINUE;
 }
index 7a0ac72292e802587312f43cfe8eac18894c21cc..f4344b76e86010603e10e0af33a12e8f717632af 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -99,41 +99,51 @@ do_binomial (const struct dictionary *dict,
   bool warn = true;
 
   const struct one_sample_test *ost = (const struct one_sample_test *) bst;
-  struct ccase c;
+  struct ccase *c;
 
-  while (casereader_read(input, &c))
+  while ((c = casereader_read(input)) != NULL)
     {
       int v;
-      double w = dict_get_case_weight (dict, &c, &warn);
+      double w = dict_get_case_weight (dict, c, &warn);
 
       for (v = 0 ; v < ost->n_vars ; ++v )
        {
          const struct variable *var = ost->vars[v];
-         const union value *value = case_data (&c, var);
+         const union value *value = case_data (c, var);
           int width = var_get_width (var);
 
          if (var_is_value_missing (var, value, exclude))
-           break;
+           continue;
 
-         if ( NULL == cat1[v].value )
+         if (bst->cutpoint != SYSMIS)
            {
-             cat1[v].value = value_dup (value, width);
-             cat1[v].count = w;
+             if ( compare_values_short (cat1[v].value, value, var) >= 0 )
+                 cat1[v].count  += w;
+             else
+                 cat2[v].count += w;
            }
-         else if ( 0 == compare_values (cat1[v].value, value, width))
-           cat1[v].count += w;
-         else if ( NULL == cat2[v].value )
+         else
            {
-             cat2[v].value = value_dup (value, width);
-             cat2[v].count = w;
+             if ( NULL == cat1[v].value )
+               {
+                 cat1[v].value = value_dup (value, width);
+                 cat1[v].count = w;
+               }
+             else if ( 0 == compare_values_short (cat1[v].value, value, var))
+               cat1[v].count += w;
+             else if ( NULL == cat2[v].value )
+               {
+                 cat2[v].value = value_dup (value, width);
+                 cat2[v].count = w;
+               }
+             else if ( 0 == compare_values_short (cat2[v].value, value, var))
+               cat2[v].count += w;
+             else if ( bst->category1 == SYSMIS)
+               msg (ME, _("Variable %s is not dichotomous"), var_get_name (var));
            }
-         else if ( 0 == compare_values (cat2[v].value, value, width))
-           cat2[v].count += w;
-         else if ( bst->category1 == SYSMIS)
-           msg (ME, _("Variable %s is not dichotomous"), var_get_name (var));
        }
 
-      case_destroy (&c);
+      case_unref (c);
     }
   return casereader_destroy (input);
 }
@@ -144,7 +154,9 @@ void
 binomial_execute (const struct dataset *ds,
                  struct casereader *input,
                   enum mv_class exclude,
-                 const struct npar_test *test)
+                 const struct npar_test *test,
+                 bool exact UNUSED,
+                 double timer UNUSED)
 {
   int v;
   const struct dictionary *dict = dataset_dict (ds);
@@ -154,20 +166,32 @@ binomial_execute (const struct dataset *ds,
   struct freq_mutable *cat1 = xzalloc (sizeof (*cat1) * ost->n_vars);
   struct freq_mutable *cat2 = xzalloc (sizeof (*cat1) * ost->n_vars);
 
-  assert ((bst->category1 == SYSMIS) == (bst->category2 == SYSMIS) );
+  assert ((bst->category1 == SYSMIS) == (bst->category2 == SYSMIS) || bst->cutpoint != SYSMIS);
 
-  if ( bst->category1 != SYSMIS )
+  if ( bst->cutpoint != SYSMIS )
     {
+      int i;
+      union value v;
+      v.f = bst->cutpoint;
+      for (i = 0; i < ost->n_vars; i++)
+       cat1[i].value = value_dup (&v, 0);
+    }
+  else  if ( bst->category1 != SYSMIS )
+    {
+      int i;
       union value v;
       v.f = bst->category1;
-      cat1->value = value_dup (&v, 0);
+      for (i = 0; i < ost->n_vars; i++)
+       cat1[i].value = value_dup (&v, 0);
     }
 
   if ( bst->category2 != SYSMIS )
     {
+      int i;
       union value v;
       v.f = bst->category2;
-      cat2->value = value_dup (&v, 0);
+      for (i = 0; i < ost->n_vars; i++)
+       cat2[i].value = value_dup (&v, 0);
     }
 
   if (do_binomial (dict, input, bst, cat1, cat2, exclude))
@@ -197,8 +221,15 @@ binomial_execute (const struct dataset *ds,
          ds_init_empty (&catstr1);
          ds_init_empty (&catstr2);
 
-         var_append_value_name (var, cat1[v].value, &catstr1);
-         var_append_value_name (var, cat2[v].value, &catstr2);
+         if ( bst->cutpoint != SYSMIS)
+           {
+             ds_put_format (&catstr1, "<= %g", bst->cutpoint);
+           }
+         else
+           {
+             var_append_value_name (var, cat1[v].value, &catstr1);
+             var_append_value_name (var, cat2[v].value, &catstr2);
+           }
 
           tab_hline (table, TAL_1, 0, tab_nc (table) -1, 1 + v * 3);
 
@@ -229,7 +260,7 @@ binomial_execute (const struct dataset *ds,
                      cat2[v].count / n_total, NULL);
 
           tab_double (table, 4, 3 + v * 3, TAB_NONE,
-                     (cat1[v].count + cat2[v].count) / n_total, wfmt);
+                     (cat1[v].count + cat2[v].count) / n_total, NULL);
 
           /* Significance */
           sig = calculate_binomial (cat1[v].count, cat2[v].count, bst->p);
index a8360330aa43d4a49c53f93fbd1f4798bf35d77e..df01a13bc56dbeb221b532267fae53b5ecb301ed 100644 (file)
@@ -40,6 +40,7 @@ struct dataset;
 void binomial_execute (const struct dataset *,
                       struct casereader *,
                        enum mv_class,
-                      const struct npar_test *);
+                      const struct npar_test *,
+                      bool, double);
 
 #endif
index 6287977b5abc1fb21272823e6ec5c0f4bd2fa101..6cb7fc0b192ee2bfce5e50a620a26bd466a3ee6e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -58,7 +58,7 @@ create_freq_hash_with_range (const struct dictionary *dict,
 {
   bool warn = true;
   float i_d;
-  struct ccase c;
+  struct ccase *c;
 
   struct hsh_table *freq_hash =
     hsh_create (4, compare_freq, hash_freq,
@@ -79,21 +79,21 @@ create_freq_hash_with_range (const struct dictionary *dict,
       hsh_insert (freq_hash, fr);
     }
 
-  while (casereader_read (input, &c))
+  while ((c = casereader_read (input)) != NULL)
     {
       union value obs_value;
       struct freq **existing_fr;
       struct freq *fr = xmalloc(sizeof  (*fr));
-      fr->value = case_data (&c, var);
+      fr->value = case_data (c, var);
 
-      fr->count = dict_get_case_weight (dict, &c, &warn);
+      fr->count = dict_get_case_weight (dict, c, &warn);
 
       obs_value.f = trunc (fr->value->f);
 
       if ( obs_value.f < lo || obs_value.f > hi)
        {
          free (fr);
-         case_destroy (&c);
+         case_unref (c);
          continue;
        }
 
@@ -108,7 +108,7 @@ create_freq_hash_with_range (const struct dictionary *dict,
       (*existing_fr)->count += fr->count;
       free (fr);
 
-      case_destroy (&c);
+      case_unref (c);
     }
   if (casereader_destroy (input))
     return freq_hash;
@@ -131,20 +131,20 @@ create_freq_hash (const struct dictionary *dict,
                  const struct variable *var)
 {
   bool warn = true;
-  struct ccase c;
+  struct ccase *c;
 
   struct hsh_table *freq_hash =
     hsh_create (4, compare_freq, hash_freq,
                free_freq_mutable_hash,
                (void *) var);
 
-  for (; casereader_read (input, &c); case_destroy (&c))
+  for (; (c = casereader_read (input)) != NULL; case_unref (c))
     {
       struct freq **existing_fr;
       struct freq *fr = xmalloc(sizeof  (*fr));
-      fr->value = case_data (&c, var);
+      fr->value = case_data (c, var);
 
-      fr->count = dict_get_case_weight (dict, &c, &warn);
+      fr->count = dict_get_case_weight (dict, c, &warn);
 
       existing_fr = (struct freq **) hsh_probe (freq_hash, fr);
       if ( *existing_fr)
@@ -321,7 +321,9 @@ void
 chisquare_execute (const struct dataset *ds,
                   struct casereader *input,
                    enum mv_class exclude,
-                  const struct npar_test *test)
+                  const struct npar_test *test,
+                  bool exact UNUSED,
+                  double timer UNUSED)
 {
   const struct dictionary *dict = dataset_dict (ds);
   int v, i;
@@ -348,7 +350,8 @@ chisquare_execute (const struct dataset *ds,
          struct hsh_table *freq_hash = NULL;
           struct casereader *reader =
             casereader_create_filter_missing (casereader_clone (input),
-                                              &ost->vars[v], 1, exclude, NULL);
+                                              &ost->vars[v], 1, exclude,
+                                             NULL, NULL);
          struct tab_table *freq_table =
             create_variable_frequency_table(dict, reader, cst, v, &freq_hash);
 
@@ -418,7 +421,8 @@ chisquare_execute (const struct dataset *ds,
          double total_obs = 0.0;
           struct casereader *reader =
             casereader_create_filter_missing (casereader_clone (input),
-                                              &ost->vars[v], 1, exclude, NULL);
+                                              &ost->vars[v], 1, exclude,
+                                             NULL, NULL);
          struct hsh_table *freq_hash =
            create_freq_hash_with_range (dict, reader,
                                          ost->vars[v], cst->lo, cst->hi);
index 916a26392e9e1c6ebea43ad7eef47c423e3726fc..91a17d1a14fc23e89e5d0cd2984b8888d6eba51b 100644 (file)
@@ -46,7 +46,9 @@ void chisquare_insert_variables (const struct npar_test *test,
 void chisquare_execute (const struct dataset *ds,
                        struct casereader *input,
                         enum mv_class exclude,
-                       const struct npar_test *test);
+                       const struct npar_test *test,
+                       bool,
+                       double);
 
 
 
index 186ee12b995551b51b66851b502445ec008fe0b1..309b27fc3c51cb136ae5f9d012e5d538625ade5d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -177,9 +177,10 @@ static struct pool *pl_col;        /* For column data. */
 
 static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds);
 static void precalc (struct casereader *, const struct dataset *);
-static void calc_general (struct ccase *, const struct dataset *);
-static void calc_integer (struct ccase *, const struct dataset *);
+static void calc_general (const struct ccase *, const struct dataset *);
+static void calc_integer (const struct ccase *, const struct dataset *);
 static void postcalc (const struct dataset *);
+
 static void submit (struct tab_table *);
 
 static void format_short (char *s, const struct fmt_spec *fp,
@@ -305,16 +306,16 @@ internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
   grouper = casegrouper_create_splits (input, dataset_dict (ds));
   while (casegrouper_get_next_group (grouper, &group))
     {
-      struct ccase c;
+      struct ccase *c;
 
       precalc (group, ds);
 
-      for (; casereader_read (group, &c); case_destroy (&c))
+      for (; (c = casereader_read (group)) != NULL; case_unref (c))
         {
           if (mode == GENERAL)
-            calc_general (&c, ds);
+            calc_general (c, ds);
           else
-            calc_integer (&c, ds);
+            calc_integer (c, ds);
         }
       casereader_destroy (group);
 
@@ -518,12 +519,13 @@ static unsigned hash_table_entry (const void *, const void *);
 static void
 precalc (struct casereader *input, const struct dataset *ds)
 {
-  struct ccase c;
+  struct ccase *c;
 
-  if (casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c != NULL)
     {
-      output_split_file_values (ds, &c);
-      case_destroy (&c);
+      output_split_file_values (ds, c);
+      case_unref (c);
     }
 
   if (mode == GENERAL)
@@ -598,7 +600,7 @@ precalc (struct casereader *input, const struct dataset *ds)
 
 /* Form crosstabulations for general mode. */
 static void
-calc_general (struct ccase *c, const struct dataset *ds)
+calc_general (const struct ccase *c, const struct dataset *ds)
 {
   /* Missing values to exclude. */
   enum mv_class exclude = (cmd.miss == CRS_TABLE ? MV_ANY
@@ -672,7 +674,7 @@ calc_general (struct ccase *c, const struct dataset *ds)
 }
 
 static void
-calc_integer (struct ccase *c, const struct dataset *ds)
+calc_integer (const struct ccase *c, const struct dataset *ds)
 {
   bool bad_warn = true;
 
@@ -2474,7 +2476,7 @@ calc_r (double *X, double *Y, double *r, double *ase_0, double *ase_1)
   for (sum_Xr = sum_X2r = 0., i = 0; i < n_rows; i++)
     {
       sum_Xr += X[i] * row_tot[i];
-      sum_X2r += X[i] * X[i] * row_tot[i];
+      sum_X2r += pow2 (X[i]) * row_tot[i];
     }
   Xbar = sum_Xr / W;
 
@@ -2486,11 +2488,11 @@ calc_r (double *X, double *Y, double *r, double *ase_0, double *ase_1)
   Ybar = sum_Yc / W;
 
   S = sum_XYf - sum_Xr * sum_Yc / W;
-  SX = sum_X2r - sum_Xr * sum_Xr / W;
-  SY = sum_Y2c - sum_Yc * sum_Yc / W;
+  SX = sum_X2r - pow2 (sum_Xr) / W;
+  SY = sum_Y2c - pow2 (sum_Yc) / W;
   T = sqrt (SX * SY);
   *r = S / T;
-  *ase_0 = sqrt ((sum_X2Y2f - (sum_XYf * sum_XYf) / W) / (sum_X2r * sum_Y2c));
+  *ase_0 = sqrt ((sum_X2Y2f - pow2 (sum_XYf) / W) / (sum_X2r * sum_Y2c));
 
   {
     double s, c, y, t;
@@ -2580,9 +2582,9 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
 
        Dr = Dc = W * W;
        for (r = 0; r < n_rows; r++)
-         Dr -= row_tot[r] * row_tot[r];
+         Dr -= pow2 (row_tot[r]);
        for (c = 0; c < n_cols; c++)
-         Dc -= col_tot[c] * col_tot[c];
+         Dc -= pow2 (col_tot[c]);
       }
 
       {
@@ -3091,10 +3093,10 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
            }
 
        for (sum_ri2 = 0., i = 0; i < n_rows; i++)
-         sum_ri2 += row_tot[i] * row_tot[i];
+         sum_ri2 += pow2 (row_tot[i]);
 
        for (sum_cj2 = 0., j = 0; j < n_cols; j++)
-         sum_cj2 += col_tot[j] * col_tot[j];
+         sum_cj2 += pow2 (col_tot[j]);
 
        v[3] = (W * sum_fij2_ci - sum_ri2) / (W * W - sum_ri2);
        v[4] = (W * sum_fij2_ri - sum_cj2) / (W * W - sum_cj2);
@@ -3184,9 +3186,9 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        for (sum_Xr = sum_X2r = 0., i = 0; i < n_rows; i++)
          {
            sum_Xr += rows[i].f * row_tot[i];
-           sum_X2r += rows[i].f * rows[i].f * row_tot[i];
+           sum_X2r += pow2 (rows[i].f) * row_tot[i];
          }
-       SX = sum_X2r - sum_Xr * sum_Xr / W;
+       SX = sum_X2r - pow2 (sum_Xr) / W;
 
        for (SXW = 0., j = 0; j < n_cols; j++)
          {
@@ -3194,7 +3196,7 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
 
            for (cum = 0., i = 0; i < n_rows; i++)
              {
-               SXW += rows[i].f * rows[i].f * mat[j + i * n_cols];
+               SXW += pow2 (rows[i].f) * mat[j + i * n_cols];
                cum += rows[i].f * mat[j + i * n_cols];
              }
 
@@ -3211,7 +3213,7 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        for (sum_Yc = sum_Y2c = 0., i = 0; i < n_cols; i++)
          {
            sum_Yc += cols[i].f * col_tot[i];
-           sum_Y2c += cols[i].f * cols[i].f * col_tot[i];
+           sum_Y2c += pow2 (cols[i].f) * col_tot[i];
          }
        SY = sum_Y2c - sum_Yc * sum_Yc / W;
 
@@ -3221,7 +3223,7 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
 
            for (cum = 0., j = 0; j < n_cols; j++)
              {
-               SYW += cols[j].f * cols[j].f * mat[j + i * n_cols];
+               SYW += pow2 (cols[j].f) * mat[j + i * n_cols];
                cum += cols[j].f * mat[j + i * n_cols];
              }
 
index e26eadf93e064f5b48a3654ab95b24a9552bf617..23bb1aa8e0fcc9ce329bccde237da98c178af1df 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -583,7 +583,7 @@ dump_z_table (struct dsc_proc *dsc)
    (either system or user-missing values that weren't included).
 */
 static int
-descriptives_trns_proc (void *trns_, struct ccase * c,
+descriptives_trns_proc (void *trns_, struct ccase **c,
                         casenumber case_idx UNUSED)
 {
   struct dsc_trns *t = trns_;
@@ -596,7 +596,7 @@ descriptives_trns_proc (void *trns_, struct ccase * c,
       assert(t->vars);
       for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
        {
-         double score = case_num (c, *vars);
+         double score = case_num (*c, *vars);
          if (var_is_num_missing (*vars, score, t->exclude))
            {
              all_sysmis = 1;
@@ -605,10 +605,11 @@ descriptives_trns_proc (void *trns_, struct ccase * c,
        }
     }
 
+  *c = case_unshare (*c);
   for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
     {
-      double input = case_num (c, z->src_var);
-      double *output = &case_data_rw (c, z->z_var)->f;
+      double input = case_num (*c, z->src_var);
+      double *output = &case_data_rw (*c, z->z_var)->f;
 
       if (z->mean == SYSMIS || z->std_dev == SYSMIS || all_sysmis
           || var_is_num_missing (z->src_var, input, t->exclude))
@@ -695,16 +696,17 @@ calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
                    struct dataset *ds)
 {
   struct casereader *pass1, *pass2;
-  struct ccase c;
+  struct ccase *c;
   size_t i;
 
-  if (!casereader_peek (group, 0, &c))
+  c = casereader_peek (group, 0);
+  if (c == NULL)
     {
       casereader_destroy (group);
       return;
     }
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
+  output_split_file_values (ds, c);
+  case_unref (c);
 
   group = casereader_create_filter_weight (group, dataset_dict (ds),
                                            NULL, NULL);
@@ -726,12 +728,12 @@ calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
   dsc->valid = 0.;
 
   /* First pass to handle most of the work. */
-  for (; casereader_read (pass1, &c); case_destroy (&c))
+  for (; (c = casereader_read (pass1)) != NULL; case_unref (c))
     {
-      double weight = dict_get_case_weight (dataset_dict (ds), &c, NULL);
+      double weight = dict_get_case_weight (dataset_dict (ds), c, NULL);
 
       /* Check for missing values. */
-      if (listwise_missing (dsc, &c))
+      if (listwise_missing (dsc, c))
         {
           dsc->missing_listwise += weight;
           if (dsc->missing_type == DSC_LISTWISE)
@@ -742,7 +744,7 @@ calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
       for (i = 0; i < dsc->var_cnt; i++)
         {
           struct dsc_var *dv = &dsc->vars[i];
-          double x = case_num (&c, dv->v);
+          double x = case_num (c, dv->v);
 
           if (var_is_num_missing (dv->v, x, dsc->exclude))
             {
@@ -768,18 +770,18 @@ calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
   /* Second pass for higher-order moments. */
   if (dsc->max_moment > MOMENT_MEAN)
     {
-      for (; casereader_read (pass2, &c); case_destroy (&c))
+      for (; (c = casereader_read (pass2)) != NULL; case_unref (c))
         {
-          double weight = dict_get_case_weight (dataset_dict (ds), &c, NULL);
+          double weight = dict_get_case_weight (dataset_dict (ds), c, NULL);
 
           /* Check for missing values. */
-          if (dsc->missing_type == DSC_LISTWISE && listwise_missing (dsc, &c))
+          if (dsc->missing_type == DSC_LISTWISE && listwise_missing (dsc, c))
             continue;
 
           for (i = 0; i < dsc->var_cnt; i++)
             {
               struct dsc_var *dv = &dsc->vars[i];
-              double x = case_num (&c, dv->v);
+              double x = case_num (c, dv->v);
 
               if (var_is_num_missing (dv->v, x, dsc->exclude))
                 continue;
index febb60fedeeb5d5146253c5b3c434bece64ff79e..51f2832052e57461e628b96353c4d16b3101f6d4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 <stdio.h>
 #include <stdlib.h>
 
+#include <math/sort.h>
+#include <math/order-stats.h>
+#include <math/percentiles.h>
+#include <math/tukey-hinges.h>
+#include <math/box-whisker.h>
+#include <math/trimmed-mean.h>
+#include <math/extrema.h>
+#include <math/np.h>
 #include <data/case.h>
 #include <data/casegrouper.h>
 #include <data/casereader.h>
+#include <data/casewriter.h>
 #include <data/dictionary.h>
 #include <data/procedure.h>
+#include <data/subcase.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
-#include <data/format.h>
 #include <language/command.h>
 #include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
@@ -38,9 +47,7 @@
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
-#include <math/factor-stats.h>
 #include <math/moments.h>
-#include <math/percentiles.h>
 #include <output/charts/box-whisker.h>
 #include <output/charts/cartesian.h>
 #include <output/manager.h>
@@ -57,6 +64,7 @@
 #include <output/chart.h>
 #include <output/charts/plot-hist.h>
 #include <output/charts/plot-chart.h>
+#include <math/histogram.h>
 
 /* (specification)
    "EXAMINE" (xmn_):
@@ -64,8 +72,8 @@
    +total=custom;
    +nototal=custom;
    missing=miss:pairwise/!listwise,
-           rep:report/!noreport,
-           incl:include/!exclude;
+   rep:report/!noreport,
+   incl:include/!exclude;
    +compare=cmp:variables/!groups;
    +percentiles=custom;
    +id=var;
 /* (functions) */
 
 
-
 static struct cmd_examine cmd;
 
 static const struct variable **dependent_vars;
-
 static size_t n_dependent_vars;
 
+/* PERCENTILES */
+
+static subc_list_double percentile_list;
+static enum pc_alg percentile_algorithm;
 
-struct factor
+struct factor_metrics
 {
-  /* The independent variable */
-  struct variable *indep_var[2];
+  struct moments1 *moments;
+
+  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;
+
+  /* Three quartiles indexing into PTL */
+  struct percentile **quartiles;
+
+  /* A reader sorted in ASCENDING order */
+  struct casereader *up_reader;
+
+  /* The minimum value of all the weights */
+  double cmin;
+
+  /* Sum of all weights, including those for missing values */
+  double n;
+
+  /* Sum of weights of non_missing values */
+  double n_valid;
 
+  double mean;
 
-  /* Hash table of factor stats indexed by 2 values */
-  struct hsh_table *fstats;
+  double variance;
 
-  /* The hash table after it has been crunched */
-  struct factor_statistics **fs;
+  double skewness;
 
-  struct factor *next;
+  double kurtosis;
 
+  double se_mean;
+
+  struct extrema *minima;
+  struct extrema *maxima;
 };
 
-/* Linked list of factors */
-static struct factor *factors = 0;
+struct factor_result
+{
+  struct ll ll;
 
-static struct metrics *totals = 0;
+  union value *value[2];
 
-/* Parse the clause specifying the factors */
-static int examine_parse_independent_vars (struct lexer *lexer, const struct dictionary *dict, struct cmd_examine *cmd);
+  /* An array of factor metrics, one for each variable */
+  struct factor_metrics *metrics;
+};
 
+struct xfactor
+{
+  /* We need to make a list of this structure */
+  struct ll ll;
 
+  /* The independent variable */
+  const struct variable const* indep_var[2];
 
-/* Output functions */
-static void show_summary (const struct variable **dependent_var, int n_dep_var,
-                         const struct dictionary *dict,
-                         const struct factor *f);
+  /* A list of results for this factor */
+  struct ll_list result_list ;
+};
 
-static void show_extremes (const struct variable **dependent_var,
-                          int n_dep_var,
-                          const struct factor *factor,
-                          int n_extremities);
 
-static void show_descriptives (const struct variable **dependent_var,
-                             int n_dep_var,
-                             struct factor *factor);
+static void
+factor_destroy (struct xfactor *fctr)
+{
+  struct ll *ll = ll_head (&fctr->result_list);
+  while (ll != ll_null (&fctr->result_list))
+    {
+      int v;
+      struct factor_result *result =
+       ll_data (ll, struct factor_result, ll);
 
-static void show_percentiles (const struct variable **dependent_var,
-                            int n_dep_var,
-                            struct factor *factor);
+      for (v = 0; v < n_dependent_vars; ++v)
+       {
+         int i;
+         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);
+         for (i = 0 ; i < result->metrics[v].n_ptiles; ++i)
+           statistic_destroy ((struct statistic *) result->metrics[v].ptl[i]);
+         free (result->metrics[v].ptl);
+         free (result->metrics[v].quartiles);
+         casereader_destroy (result->metrics[v].up_reader);
+       }
+
+      free (result->value[0]);
+      free (result->value[1]);
+      free (result->metrics);
+      ll = ll_next (ll);
+      free (result);
+    }
+}
 
+static struct xfactor level0_factor;
+static struct ll_list factor_list;
+
+/* Parse the clause specifying the factors */
+static int examine_parse_independent_vars (struct lexer *lexer,
+                                          const struct dictionary *dict,
+                                          struct cmd_examine *cmd);
 
+/* Output functions */
+static void show_summary (const struct variable **dependent_var, int n_dep_var,
+                         const struct dictionary *dict,
+                         const struct xfactor *f);
 
 
-void np_plot (const struct metrics *m, const char *factorname);
+static void show_descriptives (const struct variable **dependent_var,
+                              int n_dep_var,
+                              const struct xfactor *f);
 
 
-void box_plot_group (const struct factor *fctr,
-                   const struct variable **vars, int n_vars,
-                   const struct variable *id
-                   ) ;
+static void show_percentiles (const struct variable **dependent_var,
+                              int n_dep_var,
+                              const struct xfactor *f);
 
 
-void box_plot_variables (const struct factor *fctr,
-                       const struct variable **vars, int n_vars,
-                       const struct variable *id
-                       );
+static void show_extremes (const struct variable **dependent_var,
+                          int n_dep_var,
+                          const struct xfactor *f);
+
 
 
 
@@ -163,34 +241,24 @@ void factor_calc (const struct ccase *c, int case_no,
 
 /* Represent a factor as a string, so it can be
    printed in a human readable fashion */
-static void factor_to_string (const struct factor *fctr,
-                              const struct factor_statistics *fs,
-                             const struct variable *var,
-                             struct string *str
-                             );
+static void factor_to_string (const struct xfactor *fctr,
+                             const struct factor_result *result,
+                             struct string *str);
 
 /* Represent a factor as a string, so it can be
    printed in a human readable fashion,
    but sacrificing some readablility for the sake of brevity */
-static void factor_to_string_concise (const struct factor *fctr,
-                                     const struct factor_statistics *fs,
-                                     struct string *);
-
+static void
+factor_to_string_concise (const struct xfactor *fctr,
+                         const struct factor_result *result,
+                         struct string *str
+                         );
 
 
 
 /* Categories of missing values to exclude. */
 static enum mv_class exclude_values;
 
-/* PERCENTILES */
-
-static subc_list_double percentile_list;
-
-static enum pc_alg percentile_algorithm;
-
-static short sbc_percentile;
-
-
 int
 cmd_examine (struct lexer *lexer, struct dataset *ds)
 {
@@ -201,6 +269,8 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
   subc_list_double_create (&percentile_list);
   percentile_algorithm = PC_HAVERAGE;
 
+  ll_init (&factor_list);
+
   if ( !parse_examine (lexer, ds, &cmd, NULL) )
     {
       subc_list_double_destroy (&percentile_list);
@@ -226,225 +296,404 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
     }
 
   grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
+
   while (casegrouper_get_next_group (grouper, &group))
-    run_examine (&cmd, group, ds);
+    {
+      struct casereader *reader =
+       casereader_create_arithmetic_sequence (group, 1, 1);
+
+      run_examine (&cmd, reader, ds);
+    }
+
   ok = casegrouper_destroy (grouper);
   ok = proc_commit (ds) && ok;
 
-  if ( totals )
+  if ( dependent_vars )
+    free (dependent_vars);
+
+  subc_list_double_destroy (&percentile_list);
+
+  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+};
+
+
+/* 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 )
     {
-      free ( totals );
+      msg (MW, _("Not creating plot because data set is empty."));
+      return ;
     }
 
-  if ( dependent_vars )
-    free (dependent_vars);
+  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 factor *f = factors ;
-    while ( f )
+    struct casereader *reader = casewriter_make_reader (np->writer);
+    struct ccase *c;
+    while ((c = casereader_read (reader)) != NULL)
       {
-       struct factor *ff = f;
+       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);
 
-       f = f->next;
-       free ( ff->fs );
-       hsh_destroy ( ff->fstats ) ;
-       free ( ff ) ;
+       case_unref (c);
       }
-    factors = 0;
+    casereader_destroy (reader);
   }
 
-  subc_list_double_destroy (&percentile_list);
-
-  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
-};
+  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);
+}
 
 
-/* Show all the appropriate tables */
 static void
-output_examine (const struct dictionary *dict)
+show_npplot (const struct variable **dependent_var,
+            int n_dep_var,
+            const struct xfactor *fctr)
 {
-  struct factor *fctr;
+  int v;
 
-  /* Show totals if appropriate */
-  if ( ! cmd.sbc_nototal || factors == 0 )
+  for (v = 0; v < n_dep_var; ++v)
     {
-      show_summary (dependent_vars, n_dependent_vars, dict, 0);
+      struct ll *ll;
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list);
+          ll = ll_next (ll))
+       {
+         struct string str;
+         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);
+       }
+    }
+}
+
+
+static void
+show_histogram (const struct variable **dependent_var,
+               int n_dep_var,
+               const struct xfactor *fctr)
+{
+  int v;
 
-      if ( cmd.sbc_statistics )
+  for (v = 0; v < n_dep_var; ++v)
+    {
+      struct ll *ll;
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list);
+          ll = ll_next (ll))
        {
-         if ( cmd.a_statistics[XMN_ST_EXTREME])
-           show_extremes (dependent_vars, n_dependent_vars, 0, cmd.st_n);
+         struct string str;
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
-         if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES])
-           show_descriptives (dependent_vars, n_dependent_vars, 0);
+         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);
+
+         ds_destroy (&str);
        }
-      if ( sbc_percentile )
-       show_percentiles (dependent_vars, n_dependent_vars, 0);
+    }
+}
+
+
+
+static void
+show_boxplot_groups (const struct variable **dependent_var,
+                    int n_dep_var,
+                    const struct xfactor *fctr)
+{
+  int v;
+
+  for (v = 0; v < n_dep_var; ++v)
+    {
+      struct ll *ll;
+      int f = 0;
+      struct chart *ch = chart_create ();
+      double y_min = DBL_MAX;
+      double y_max = -DBL_MAX;
 
-      if ( cmd.sbc_plot)
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list);
+          ll = ll_next (ll))
        {
-         int v;
-         if ( cmd.a_plot[XMN_PLT_STEMLEAF] )
-           msg (SW, _ ("%s is not currently supported."), "STEMLEAF");
+         const struct extremum  *max, *min;
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
-         if ( cmd.a_plot[XMN_PLT_SPREADLEVEL] )
-           msg (SW, _ ("%s is not currently supported."), "SPREADLEVEL");
+         const struct ll_list *max_list =
+           extrema_list (result->metrics[v].maxima);
 
-         if ( cmd.a_plot[XMN_PLT_NPPLOT] )
-           {
-             for ( v = 0 ; v < n_dependent_vars; ++v )
-               np_plot (&totals[v], var_to_string (dependent_vars[v]));
-           }
+         const struct ll_list *min_list =
+           extrema_list (result->metrics[v].minima);
 
-         if ( cmd.a_plot[XMN_PLT_BOXPLOT] )
+         if ( ll_is_empty (max_list))
            {
-             if ( cmd.cmp == XMN_GROUPS )
-               {
-                 box_plot_group (0, (const struct variable **) dependent_vars,
-                                  n_dependent_vars, cmd.v_id);
-               }
-             else
-               box_plot_variables (0,
-                                    (const struct variable **) dependent_vars,
-                                    n_dependent_vars, cmd.v_id);
+             msg (MW, _("Not creating plot because data set is empty."));
+             continue;
            }
 
-         if ( cmd.a_plot[XMN_PLT_HISTOGRAM] )
-           {
-             for ( v = 0 ; v < n_dependent_vars; ++v )
-               {
-                 struct normal_curve normal;
+         max = (const struct extremum *)
+           ll_data (ll_head(max_list), struct extremum, ll);
 
-                 normal.N      = totals[v].n;
-                 normal.mean   = totals[v].mean;
-                 normal.stddev = totals[v].stddev;
+          min = (const struct extremum *)
+           ll_data (ll_head (min_list), struct extremum, ll);
 
-                 histogram_plot (totals[v].histogram,
-                                var_to_string (dependent_vars[v]),
-                                &normal, 0);
-               }
-           }
+         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"),
+                          var_to_string (dependent_var[v]),
+                          var_to_string (fctr->indep_var[0]) );
+      else
+       chart_write_title (ch, _("Boxplot of %s"),
+                          var_to_string (dependent_var[v]));
+
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list);
+          ll = ll_next (ll))
+       {
+         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);
+         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));
+
+         ds_destroy (&str);
        }
 
+      chart_submit (ch);
     }
+}
 
 
-  /* Show grouped statistics  as appropriate */
-  fctr = factors;
-  while ( fctr )
-    {
-      show_summary (dependent_vars, n_dependent_vars, dict, fctr);
 
-      if ( cmd.sbc_statistics )
-       {
-         if ( cmd.a_statistics[XMN_ST_EXTREME])
-           show_extremes (dependent_vars, n_dependent_vars, fctr, cmd.st_n);
+static void
+show_boxplot_variables (const struct variable **dependent_var,
+                       int n_dep_var,
+                       const struct xfactor *fctr
+                       )
 
-         if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES])
-           show_descriptives (dependent_vars, n_dependent_vars, fctr);
-       }
+{
+  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))
+
+    {
+      struct string title;
+      struct chart *ch = chart_create ();
+      double y_min = DBL_MAX;
+      double y_max = -DBL_MAX;
 
-      if ( sbc_percentile )
-       show_percentiles (dependent_vars, n_dependent_vars, fctr);
+      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 ) ;
 
-      if ( cmd.sbc_plot)
+      for (v = 0; v < n_dep_var; ++v)
        {
-         size_t 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));
 
-         struct factor_statistics **fs = fctr->fs ;
+         const struct extremum  *max =
+           (const struct extremum *) ll_data (max_ll, struct extremum, ll);
 
-         if ( cmd.a_plot[XMN_PLT_BOXPLOT] )
-           {
-             if ( cmd.cmp == XMN_VARIABLES )
-               box_plot_variables (fctr,
-                                    (const struct variable **) dependent_vars,
-                                    n_dependent_vars, cmd.v_id);
-             else
-               box_plot_group (fctr,
-                                (const struct variable **) dependent_vars,
-                                n_dependent_vars, cmd.v_id);
-           }
+          const struct extremum  *min =
+           (const struct extremum *) ll_data (min_ll, struct extremum, ll);
 
-         for ( v = 0 ; v < n_dependent_vars; ++v )
-           {
+         y_max = MAX (y_max, max->value);
+         y_min = MIN (y_min, min->value);
+       }
 
-             for ( fs = fctr->fs ; *fs ; ++fs )
-               {
-                 struct string str;
-                 ds_init_empty (&str);
-                 factor_to_string (fctr, *fs, dependent_vars[v], &str);
 
-                 if ( cmd.a_plot[XMN_PLT_NPPLOT] )
-                   np_plot (& (*fs)->m[v], ds_cstr (&str));
+      boxplot_draw_yscale (ch, y_max, y_min);
 
-                 if ( cmd.a_plot[XMN_PLT_HISTOGRAM] )
-                   {
-                     struct normal_curve normal;
+      ds_init_empty (&title);
+      factor_to_string (fctr, result, &title);
 
-                     normal.N      = (*fs)->m[v].n;
-                     normal.mean   = (*fs)->m[v].mean;
-                     normal.stddev = (*fs)->m[v].stddev;
+#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
 
-                     histogram_plot ((*fs)->m[v].histogram,
-                                    ds_cstr (&str) ,  &normal, 0);
-                   }
+      chart_write_title (ch, ds_cstr (&title));
+      ds_destroy (&title);
 
-                 ds_destroy (&str);
+      for (v = 0; v < n_dep_var; ++v)
+       {
+         struct string str;
+         const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
 
-               } /* for ( fs .... */
+         ds_init_empty (&str);
+         ds_init_cstr (&str, var_get_name (dependent_var[v]));
 
-           } /* for ( v = 0 ..... */
+         boxplot_draw_boxplot (ch,
+                               box_centre, box_width,
+                               (const struct box_whisker *) result->metrics[v].box_whisker,
+                               ds_cstr (&str));
 
+         ds_destroy (&str);
        }
 
-      fctr = fctr->next;
+      chart_submit (ch);
     }
-
 }
 
 
-/* Create a hash table of percentiles and their values from the list of
-   percentiles */
-static struct hsh_table *
-list_to_ptile_hash (const subc_list_double *l)
+/* Show all the appropriate tables */
+static void
+output_examine (const struct dictionary *dict)
 {
-  int i;
+  struct ll *ll;
+
+  show_summary (dependent_vars, n_dependent_vars, dict, &level0_factor);
 
-  struct hsh_table *h ;
+  if ( cmd.a_statistics[XMN_ST_EXTREME] )
+    show_extremes (dependent_vars, n_dependent_vars, &level0_factor);
 
-  h = hsh_create (subc_list_double_count (l),
-                (hsh_compare_func *) ptile_compare,
-                (hsh_hash_func *) ptile_hash,
-                (hsh_free_func *) free,
-                0);
+  if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
+    show_descriptives (dependent_vars, n_dependent_vars, &level0_factor);
 
+  if ( cmd.sbc_percentiles)
+    show_percentiles (dependent_vars, n_dependent_vars, &level0_factor);
 
-  for ( i = 0 ; i < subc_list_double_count (l) ; ++i )
+  if ( cmd.sbc_plot)
     {
-      struct percentile *p = xmalloc (sizeof *p);
-
-      p->p = subc_list_double_at (l,i);
-      p->v = SYSMIS;
+      if (cmd.a_plot[XMN_PLT_BOXPLOT])
+       show_boxplot_groups (dependent_vars, n_dependent_vars, &level0_factor);
 
-      hsh_insert (h, p);
+      if (cmd.a_plot[XMN_PLT_HISTOGRAM])
+       show_histogram (dependent_vars, n_dependent_vars, &level0_factor);
 
+      if (cmd.a_plot[XMN_PLT_NPPLOT])
+       show_npplot (dependent_vars, n_dependent_vars, &level0_factor);
     }
 
-  return h;
+  for (ll = ll_head (&factor_list);
+       ll != ll_null (&factor_list); ll = ll_next (ll))
+    {
+      struct xfactor *factor = ll_data (ll, struct xfactor, ll);
+      show_summary (dependent_vars, n_dependent_vars, dict, factor);
+
+      if ( cmd.a_statistics[XMN_ST_EXTREME] )
+       show_extremes (dependent_vars, n_dependent_vars, factor);
 
+      if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
+       show_descriptives (dependent_vars, n_dependent_vars, factor);
+
+      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_HISTOGRAM])
+       show_histogram (dependent_vars, n_dependent_vars, factor);
+
+      if (cmd.a_plot[XMN_PLT_NPPLOT])
+       show_npplot (dependent_vars, n_dependent_vars, factor);
+    }
 }
 
 /* Parse the PERCENTILES subcommand */
 static int
 xmn_custom_percentiles (struct lexer *lexer, struct dataset *ds UNUSED,
-                      struct cmd_examine *p UNUSED, void *aux UNUSED)
+                       struct cmd_examine *p UNUSED, void *aux UNUSED)
 {
-  sbc_percentile = 1;
-
   lex_match (lexer, '=');
 
   lex_match (lexer, '(');
@@ -496,11 +745,12 @@ xmn_custom_percentiles (struct lexer *lexer, struct dataset *ds UNUSED,
 
 /* TOTAL and NOTOTAL are simple, mutually exclusive flags */
 static int
-xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED, struct cmd_examine *p, void *aux UNUSED)
+xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
+                 struct cmd_examine *p, void *aux UNUSED)
 {
   if ( p->sbc_nototal )
     {
-      msg (SE, _ ("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
+      msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
       return 0;
     }
 
@@ -513,7 +763,7 @@ xmn_custom_nototal (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
 {
   if ( p->sbc_total )
     {
-      msg (SE, _ ("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
+      msg (SE, _("%s and %s are mutually exclusive"), "TOTAL", "NOTOTAL");
       return 0;
     }
 
@@ -525,19 +775,21 @@ xmn_custom_nototal (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
 /* Parser for the variables sub command
    Returns 1 on success */
 static int
-xmn_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_examine *cmd, void *aux UNUSED)
+xmn_custom_variables (struct lexer *lexer, struct dataset *ds,
+                     struct cmd_examine *cmd,
+                     void *aux UNUSED)
 {
   const struct dictionary *dict = dataset_dict (ds);
   lex_match (lexer, '=');
 
   if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
-      && lex_token (lexer) != T_ALL)
+       && lex_token (lexer) != T_ALL)
     {
       return 2;
     }
 
   if (!parse_variables_const (lexer, dict, &dependent_vars, &n_dependent_vars,
-                       PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
+                             PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
     {
       free (dependent_vars);
       return 0;
@@ -545,16 +797,15 @@ xmn_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_examin
 
   assert (n_dependent_vars);
 
-  totals = xnmalloc (n_dependent_vars, sizeof *totals);
 
   if ( lex_match (lexer, T_BY))
     {
       int success ;
       success =  examine_parse_independent_vars (lexer, dict, cmd);
-      if ( success != 1 ) {
-        free (dependent_vars);
-       free (totals) ;
-      }
+      if ( success != 1 )
+       {
+         free (dependent_vars);
+       }
       return success;
     }
 
@@ -565,47 +816,44 @@ xmn_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_examin
 
 /* Parse the clause specifying the factors */
 static int
-examine_parse_independent_vars (struct lexer *lexer, const struct dictionary *dict, struct cmd_examine *cmd)
+examine_parse_independent_vars (struct lexer *lexer,
+                               const struct dictionary *dict,
+                               struct cmd_examine *cmd)
 {
   int success;
-  struct factor *sf = xmalloc (sizeof *sf);
+  struct xfactor *sf = xmalloc (sizeof *sf);
 
-  if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
-      && lex_token (lexer) != T_ALL)
+  ll_init (&sf->result_list);
+
+  if ( (lex_token (lexer) != T_ID ||
+       dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
+       && lex_token (lexer) != T_ALL)
     {
       free ( sf ) ;
       return 2;
     }
 
-
   sf->indep_var[0] = parse_variable (lexer, dict);
-  sf->indep_var[1] = 0;
+  sf->indep_var[1] = NULL;
 
   if ( lex_token (lexer) == T_BY )
     {
-
       lex_match (lexer, T_BY);
 
-      if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
-         && lex_token (lexer) != T_ALL)
+      if ( (lex_token (lexer) != T_ID ||
+           dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
+          && lex_token (lexer) != T_ALL)
        {
-         free ( sf ) ;
+         free (sf);
          return 2;
        }
 
       sf->indep_var[1] = parse_variable (lexer, dict);
 
+      ll_push_tail (&factor_list, &sf->ll);
     }
-
-
-  sf->fstats = hsh_create (4,
-                         (hsh_compare_func *) factor_statistics_compare,
-                         (hsh_hash_func *) factor_statistics_hash,
-                         (hsh_free_func *) factor_statistics_free,
-                         0);
-
-  sf->next = factors;
-  factors = sf;
+  else
+    ll_push_tail (&factor_list, &sf->ll);
 
   lex_match (lexer, ',');
 
@@ -620,339 +868,379 @@ examine_parse_independent_vars (struct lexer *lexer, const struct dictionary *di
   return success;
 }
 
+static void
+examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
+              const struct dictionary *dict, struct xfactor *factor)
+{
+  struct ccase *c;
+  const struct variable *wv = dict_get_weight (dict);
+  int v;
+  int n_extrema = 1;
+  struct factor_result *result = xzalloc (sizeof (*result));
+
+  result->metrics = xcalloc (n_dependent_vars, sizeof (*result->metrics));
 
+  if ( cmd->a_statistics[XMN_ST_EXTREME] )
+    n_extrema = cmd->st_n;
 
 
-static void populate_percentiles (struct tab_table *tbl, int col, int row,
-                                 const struct metrics *m);
+  c = casereader_peek (reader, 0);
+  if (c != NULL)
+    {
+      if ( level > 0)
+       {
+         result->value[0] =
+           value_dup (case_data (c, factor->indep_var[0]),
+                      var_get_width (factor->indep_var[0]));
+
+         if ( level > 1)
+           result->value[1] =
+             value_dup (case_data (c, factor->indep_var[1]),
+                        var_get_width (factor->indep_var[1]));
+       }
+      case_unref (c);
+    }
 
-static void populate_descriptives (struct tab_table *t, int col, int row,
-                                  const struct variable *,
-                                  const struct metrics *fs);
+  for (v = 0; v < n_dependent_vars; ++v)
+    {
+      struct casewriter *writer;
+      struct casereader *input = casereader_clone (reader);
+
+      result->metrics[v].moments = moments1_create (MOMENT_KURTOSIS);
+      result->metrics[v].minima = extrema_create (n_extrema, EXTREME_MINIMA);
+      result->metrics[v].maxima = extrema_create (n_extrema, EXTREME_MAXIMA);
+      result->metrics[v].cmin = DBL_MAX;
+
+      if (cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
+         cmd->a_plot[XMN_PLT_BOXPLOT] ||
+         cmd->a_plot[XMN_PLT_NPPLOT] ||
+         cmd->sbc_percentiles)
+       {
+         /* In this case, we need to sort the data, so we create a sorting
+            casewriter */
+         struct subcase up_ordering;
+          subcase_init_var (&up_ordering, dependent_vars[v], SC_ASCEND);
+         writer = sort_create_writer (&up_ordering,
+                                      casereader_get_value_cnt (reader));
+          subcase_destroy (&up_ordering);
+       }
+      else
+       {
+         /* but in this case, sorting is unnecessary, so an ordinary
+            casewriter is sufficient */
+         writer =
+           autopaging_writer_create (casereader_get_value_cnt (reader));
+       }
 
-static void populate_extremes (struct tab_table *t, int col, int row, int n,
-                              const struct variable *var,
-                              const struct metrics *m);
 
-static void populate_summary (struct tab_table *t, int col, int row,
-                             const struct dictionary *dict,
-                             const struct metrics *m);
+      /* Sort or just iterate, whilst calculating moments etc */
+      while ((c = casereader_read (input)) != NULL)
+       {
+         const casenumber loc =
+             case_data_idx (c, casereader_get_value_cnt (reader) - 1)->f;
 
+         const double weight = wv ? case_data (c, wv)->f : 1.0;
+         const union value *value = case_data (c, dependent_vars[v]);
 
+         if (weight != SYSMIS)
+           minimize (&result->metrics[v].cmin, weight);
 
+         moments1_add (result->metrics[v].moments,
+                       value->f,
+                       weight);
 
-/* Perform calculations for the sub factors */
-void
-factor_calc (const struct ccase *c, int case_no, double weight,
-            bool case_missing)
-{
-  size_t v;
-  struct factor *fctr = factors;
+         result->metrics[v].n += weight;
 
-  while ( fctr)
-    {
-      struct factor_statistics **foo ;
-      union value *indep_vals[2] ;
+         if ( ! var_is_value_missing (dependent_vars[v], value, MV_ANY) )
+           result->metrics[v].n_valid += weight;
 
-      indep_vals[0] = value_dup (
-                                case_data (c, fctr->indep_var[0]),
-                                var_get_width (fctr->indep_var[0])
-                                );
+         extrema_add (result->metrics[v].maxima,
+                      value->f,
+                      weight,
+                      loc);
 
-      if ( fctr->indep_var[1] )
-       indep_vals[1] = value_dup (
-                                  case_data (c, fctr->indep_var[1]),
-                                  var_get_width (fctr->indep_var[1])
-                                  );
-      else
-       {
-         const union value sm = {SYSMIS};
-         indep_vals[1] = value_dup (&sm, 0);
+         extrema_add (result->metrics[v].minima,
+                      value->f,
+                      weight,
+                      loc);
+
+         casewriter_write (writer, c);
        }
+      casereader_destroy (input);
+      result->metrics[v].up_reader = casewriter_make_reader (writer);
+    }
 
-      assert (fctr->fstats);
+  /* If percentiles or descriptives have been requested, then a
+     second pass through the data (which has now been sorted)
+     is necessary */
+  if ( cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
+       cmd->a_plot[XMN_PLT_BOXPLOT] ||
+       cmd->a_plot[XMN_PLT_NPPLOT] ||
+       cmd->sbc_percentiles)
+    {
+      for (v = 0; v < n_dependent_vars; ++v)
+       {
+         int i;
+         int n_os;
+         struct order_stats **os ;
+         struct factor_metrics *metric = &result->metrics[v];
 
-      foo = ( struct factor_statistics ** )
-       hsh_probe (fctr->fstats, (void *) &indep_vals);
+         metric->n_ptiles = percentile_list.n_data;
 
-      if ( !*foo )
-       {
+         metric->ptl = xcalloc (metric->n_ptiles,
+                                sizeof (struct percentile *));
 
-         *foo = create_factor_statistics (n_dependent_vars,
-                                         indep_vals[0],
-                                         indep_vals[1]);
+         metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
 
-         for ( v =  0 ; v  < n_dependent_vars ; ++v )
+         for (i = 0 ; i < metric->n_ptiles; ++i)
            {
-             metrics_precalc ( & (*foo)->m[v] );
+             metric->ptl[i] = (struct percentile *)
+               percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
+
+             if ( percentile_list.data[i] == 25)
+               metric->quartiles[0] = metric->ptl[i];
+             else if ( percentile_list.data[i] == 50)
+               metric->quartiles[1] = metric->ptl[i];
+             else if ( percentile_list.data[i] == 75)
+               metric->quartiles[2] = metric->ptl[i];
            }
 
-       }
-      else
-       {
-         free (indep_vals[0]);
-         free (indep_vals[1]);
-       }
+         metric->tukey_hinges = tukey_hinges_create (metric->n, metric->cmin);
+         metric->trimmed_mean = trimmed_mean_create (metric->n, 0.05);
 
-      for ( v =  0 ; v  < n_dependent_vars ; ++v )
-       {
-         const struct variable *var = dependent_vars[v];
-         union value *val = value_dup (
-                                       case_data (c, var),
-                                       var_get_width (var)
-                                       );
+         n_os = metric->n_ptiles + 2;
 
-         if (case_missing || var_is_value_missing (var, val, exclude_values))
+        if ( cmd->a_plot[XMN_PLT_NPPLOT] )
            {
-             free (val);
-             val = NULL;
+             metric->np = np_create (metric->moments);
+             n_os ++;
            }
 
-         metrics_calc ( & (*foo)->m[v], val, weight, case_no);
+         os = xcalloc (sizeof (struct order_stats *), n_os);
 
-         free (val);
-       }
+         for (i = 0 ; i < metric->n_ptiles ; ++i )
+           {
+             os[i] = (struct order_stats *) metric->ptl[i];
+           }
 
-      fctr = fctr->next;
-    }
-}
+         os[i] = (struct order_stats *) metric->tukey_hinges;
+         os[i+1] = (struct order_stats *) metric->trimmed_mean;
 
-static void
-run_examine (struct cmd_examine *cmd, struct casereader *input,
-             struct dataset *ds)
-{
-  struct dictionary *dict = dataset_dict (ds);
-  casenumber case_no;
-  struct ccase c;
-  int v;
-  bool ok;
+         if (cmd->a_plot[XMN_PLT_NPPLOT])
+           os[i+2] = metric->np;
 
-  struct factor *fctr;
-
-  if (!casereader_peek (input, 0, &c))
-    {
-      casereader_destroy (input);
-      return;
+         order_stats_accumulate (os, n_os,
+                                 casereader_clone (metric->up_reader),
+                                 wv, dependent_vars[v], MV_ANY);
+         free (os);
+       }
     }
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
-
-  input = casereader_create_filter_weight (input, dict, NULL, NULL);
-  input = casereader_create_counter (input, &case_no, 0);
 
-  /* Make sure we haven't got rubbish left over from a
-     previous split. */
-  fctr = factors;
-  while (fctr)
+  /* FIXME: Do this in the above loop */
+  if ( cmd->a_plot[XMN_PLT_HISTOGRAM] )
     {
-      struct factor *next = fctr->next;
+      struct ccase *c;
+      struct casereader *input = casereader_clone (reader);
 
-      hsh_clear (fctr->fstats);
+      for (v = 0; v < n_dependent_vars; ++v)
+       {
+         const struct extremum  *max, *min;
+         struct factor_metrics *metric = &result->metrics[v];
 
-      fctr->fs = 0;
+         const struct ll_list *max_list =
+           extrema_list (result->metrics[v].maxima);
 
-      fctr = next;
-    }
+         const struct ll_list *min_list =
+           extrema_list (result->metrics[v].minima);
 
-  for ( v = 0 ; v < n_dependent_vars ; ++v )
-    metrics_precalc (&totals[v]);
+         if ( ll_is_empty (max_list))
+           {
+             msg (MW, _("Not creating plot because data set is empty."));
+             continue;
+           }
 
-  for (; casereader_read (input, &c); case_destroy (&c))
-    {
-      bool case_missing = false;
-      const double weight = dict_get_case_weight (dict, &c, NULL);
+         assert (! ll_is_empty (min_list));
 
-      if ( cmd->miss == XMN_LISTWISE )
-       {
-         for ( v = 0 ; v < n_dependent_vars ; ++v )
-           {
-             const struct variable *var = dependent_vars[v];
-             union value *val = value_dup (
-                                                 case_data (&c, var),
-                                                 var_get_width (var)
-                                                 );
+         max = (const struct extremum *)
+           ll_data (ll_head(max_list), struct extremum, ll);
 
-             if ( var_is_value_missing (var, val, exclude_values))
-               case_missing = true;
+          min = (const struct extremum *)
+           ll_data (ll_head (min_list), struct extremum, ll);
 
-             free (val);
-           }
+         metric->histogram = histogram_create (10, min->value, max->value);
        }
 
-      for ( v = 0 ; v < n_dependent_vars ; ++v )
+      while ((c = casereader_read (input)) != NULL)
        {
-         const struct variable *var = dependent_vars[v];
-         union value *val = value_dup (
-                                       case_data (&c, var),
-                                       var_get_width (var)
-                                       );
-
-         if ( var_is_value_missing (var, val, exclude_values)
-               || case_missing )
+         const double weight = wv ? case_data (c, wv)->f : 1.0;
+
+         for (v = 0; v < n_dependent_vars; ++v)
            {
-             free (val) ;
-             val = NULL;
+             struct factor_metrics *metric = &result->metrics[v];
+             if ( metric->histogram)
+               histogram_add ((struct histogram *) metric->histogram,
+                              case_data (c, dependent_vars[v])->f, weight);
            }
-
-         metrics_calc (&totals[v], val, weight, case_no);
-
-         free (val);
+         case_unref (c);
        }
-
-      factor_calc (&c, case_no, weight, case_missing);
+      casereader_destroy (input);
     }
-  ok = casereader_destroy (input);
 
-  for ( v = 0 ; v < n_dependent_vars ; ++v)
+  /* In this case, a third iteration is required */
+  if (cmd->a_plot[XMN_PLT_BOXPLOT])
     {
-      fctr = factors;
-      while ( fctr )
+      for (v = 0; v < n_dependent_vars; ++v)
        {
-         struct hsh_iterator hi;
-         struct factor_statistics *fs;
+         struct factor_metrics *metric = &result->metrics[v];
+
+         metric->box_whisker =
+           box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
+                               cmd->v_id,
+                               casereader_get_value_cnt (metric->up_reader)
+                               - 1);
+
+         order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
+                                 1,
+                                 casereader_clone (metric->up_reader),
+                                 wv, dependent_vars[v], MV_ANY);
+       }
+    }
 
-         for ( fs = hsh_first (fctr->fstats, &hi);
-               fs != 0 ;
-               fs = hsh_next (fctr->fstats, &hi))
-           {
+  ll_push_tail (&factor->result_list, &result->ll);
+  casereader_destroy (reader);
+}
 
-             fs->m[v].ptile_hash = list_to_ptile_hash (&percentile_list);
-             fs->m[v].ptile_alg = percentile_algorithm;
-             metrics_postcalc (&fs->m[v]);
-           }
 
-         fctr = fctr->next;
-       }
+static void
+run_examine (struct cmd_examine *cmd, struct casereader *input,
+             struct dataset *ds)
+{
+  struct ll *ll;
+  const struct dictionary *dict = dataset_dict (ds);
+  struct ccase *c;
+  struct casereader *level0 = casereader_clone (input);
 
-      totals[v].ptile_hash = list_to_ptile_hash (&percentile_list);
-      totals[v].ptile_alg = percentile_algorithm;
-      metrics_postcalc (&totals[v]);
+  c = casereader_peek (input, 0);
+  if (c == NULL)
+    {
+      casereader_destroy (input);
+      return;
     }
 
+  output_split_file_values (ds, c);
+  case_unref (c);
 
-  /* Make sure that the combination of factors are complete */
-
-  fctr = factors;
-  while ( fctr )
-    {
-      struct hsh_iterator hi;
-      struct hsh_iterator hi0;
-      struct hsh_iterator hi1;
-      struct factor_statistics *fs;
+  ll_init (&level0_factor.result_list);
 
-      struct hsh_table *idh0 = NULL;
-      struct hsh_table *idh1 = NULL;
-      union value **val0;
-      union value **val1;
+  examine_group (cmd, level0, 0, dict, &level0_factor);
 
-      idh0 = hsh_create (4, (hsh_compare_func *) compare_ptr_values,
-                        (hsh_hash_func *) hash_ptr_value,
-                       0,0);
+  for (ll = ll_head (&factor_list);
+       ll != ll_null (&factor_list);
+       ll = ll_next (ll))
+    {
+      struct xfactor *factor = ll_data (ll, struct xfactor, ll);
 
-      idh1 = hsh_create (4, (hsh_compare_func *) compare_ptr_values,
-                        (hsh_hash_func *) hash_ptr_value,
-                       0,0);
+      struct casereader *group = NULL;
+      struct casereader *level1;
+      struct casegrouper *grouper1 = NULL;
 
+      level1 = casereader_clone (input);
+      level1 = sort_execute_1var (level1, factor->indep_var[0]);
+      grouper1 = casegrouper_create_vars (level1, &factor->indep_var[0], 1);
 
-      for ( fs = hsh_first (fctr->fstats, &hi);
-           fs != 0 ;
-           fs = hsh_next (fctr->fstats, &hi))
+      while (casegrouper_get_next_group (grouper1, &group))
        {
-         hsh_insert (idh0, &fs->id[0]);
-         hsh_insert (idh1, &fs->id[1]);
-       }
+         struct casereader *group_copy = casereader_clone (group);
 
-      /* Ensure that the factors combination is complete */
-      for ( val0 = hsh_first (idh0, &hi0);
-           val0 != 0 ;
-           val0 = hsh_next (idh0, &hi0))
-       {
-         for ( val1 = hsh_first (idh1, &hi1);
-               val1 != 0 ;
-               val1 = hsh_next (idh1, &hi1))
+         if ( !factor->indep_var[1])
+           examine_group (cmd, group_copy, 1, dict, factor);
+         else
            {
-             struct factor_statistics **ffs;
-             union value *key[2];
-             key[0] = *val0;
-             key[1] = *val1;
-
-             ffs = (struct factor_statistics **)
-               hsh_probe (fctr->fstats, &key );
-
-             if ( !*ffs ) {
-               size_t i;
-                (*ffs) = create_factor_statistics (n_dependent_vars,
-                                                  key[0], key[1]);
-               for ( i = 0 ; i < n_dependent_vars ; ++i )
-                 metrics_precalc ( & (*ffs)->m[i]);
-             }
-           }
-       }
+             int n_groups = 0;
+             struct casereader *group2 = NULL;
+             struct casegrouper *grouper2 = NULL;
 
-      hsh_destroy (idh0);
-      hsh_destroy (idh1);
+             group_copy = sort_execute_1var (group_copy,
+                                              factor->indep_var[1]);
 
-      fctr->fs = (struct factor_statistics **) hsh_sort_copy (fctr->fstats);
+             grouper2 = casegrouper_create_vars (group_copy,
+                                                  &factor->indep_var[1], 1);
+
+             while (casegrouper_get_next_group (grouper2, &group2))
+               {
+                 examine_group (cmd, group2, 2, dict, factor);
+                 n_groups++;
+               }
+             casegrouper_destroy (grouper2);
+           }
 
-      fctr = fctr->next;
+         casereader_destroy (group);
+       }
+      casegrouper_destroy (grouper1);
     }
 
-  if (ok)
-    output_examine (dict);
+  casereader_destroy (input);
 
+  output_examine (dict);
+
+  factor_destroy (&level0_factor);
+
+  {
+    struct ll *ll;
+    for (ll = ll_head (&factor_list);
+        ll != ll_null (&factor_list);
+        ll = ll_next (ll))
+      {
+       struct xfactor *f = ll_data (ll, struct xfactor, ll);
+       factor_destroy (f);
+      }
+  }
 
-  if ( totals )
-    {
-      size_t i;
-      for ( i = 0 ; i < n_dependent_vars ; ++i )
-       {
-         metrics_destroy (&totals[i]);
-       }
-    }
 }
 
 
 static void
 show_summary (const struct variable **dependent_var, int n_dep_var,
              const struct dictionary *dict,
-             const struct factor *fctr)
+             const struct xfactor *fctr)
 {
+  const struct variable *wv = dict_get_weight (dict);
+  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
+
   static const char *subtitle[]=
     {
-      N_ ("Valid"),
-      N_ ("Missing"),
-      N_ ("Total")
+      N_("Valid"),
+      N_("Missing"),
+      N_("Total")
     };
 
-  int i;
-  int heading_columns ;
+  int v, j;
+  int heading_columns = 1;
   int n_cols;
   const int heading_rows = 3;
   struct tab_table *tbl;
 
   int n_rows ;
-  int n_factors = 1;
+  n_rows = n_dep_var;
+
+  assert (fctr);
 
-  if ( fctr )
+  if ( fctr->indep_var[0] )
     {
       heading_columns = 2;
-      n_factors = hsh_count (fctr->fstats);
-      n_rows = n_dep_var * n_factors ;
 
       if ( fctr->indep_var[1] )
-       heading_columns = 3;
-    }
-  else
-    {
-      heading_columns = 1;
-      n_rows = n_dep_var;
+       {
+         heading_columns = 3;
+       }
     }
 
+  n_rows *= ll_count (&fctr->result_list);
   n_rows += heading_rows;
 
   n_cols = heading_columns + 6;
 
-  tbl = tab_create (n_cols,n_rows,0);
+  tbl = tab_create (n_cols, n_rows, 0);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
   tab_dim (tbl, tab_natural_dimensions);
@@ -979,12 +1267,12 @@ show_summary (const struct variable **dependent_var, int n_dep_var,
   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
 
 
-  tab_title (tbl, _ ("Case Processing Summary"));
+  tab_title (tbl, _("Case Processing Summary"));
 
   tab_joint_text (tbl, heading_columns, 0,
-                n_cols -1, 0,
-                TAB_CENTER | TAT_TITLE,
-                ("Cases"));
+                 n_cols -1, 0,
+                 TAB_CENTER | TAT_TITLE,
+                 _("Cases"));
 
   /* Remove lines ... */
   tab_box (tbl,
@@ -993,28 +1281,28 @@ show_summary (const struct variable **dependent_var, int n_dep_var,
           heading_columns, 0,
           n_cols - 1, 0);
 
-  for ( i = 0 ; i < 3 ; ++i )
+  for (j = 0 ; j < 3 ; ++j)
     {
-      tab_text (tbl, heading_columns + i * 2 , 2, TAB_CENTER | TAT_TITLE,
-               _ ("N"));
+      tab_text (tbl, heading_columns + j * 2 , 2, TAB_CENTER | TAT_TITLE,
+               _("N"));
 
-      tab_text (tbl, heading_columns + i * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
-               _ ("Percent"));
+      tab_text (tbl, heading_columns + j * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
+               _("Percent"));
 
-      tab_joint_text (tbl, heading_columns + i*2 , 1,
-                    heading_columns + i * 2 + 1, 1,
-                    TAB_CENTER | TAT_TITLE,
-                    subtitle[i]);
+      tab_joint_text (tbl, heading_columns + j * 2 , 1,
+                     heading_columns + j * 2 + 1, 1,
+                     TAB_CENTER | TAT_TITLE,
+                     subtitle[j]);
 
       tab_box (tbl, -1, -1,
               TAL_0, TAL_0,
-              heading_columns + i * 2, 1,
-              heading_columns + i * 2 + 1, 1);
+              heading_columns + j * 2, 1,
+              heading_columns + j * 2 + 1, 1);
     }
 
 
   /* Titles for the independent variables */
-  if ( fctr )
+  if ( fctr->indep_var[0] )
     {
       tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
                var_to_string (fctr->indep_var[0]));
@@ -1026,1283 +1314,886 @@ show_summary (const struct variable **dependent_var, int n_dep_var,
        }
     }
 
-
-  for ( i = 0 ; i < n_dep_var ; ++i )
+  for (v = 0 ; v < n_dep_var ; ++v)
     {
-      int n_factors = 1;
-      if ( fctr )
-       n_factors = hsh_count (fctr->fstats);
+      int j = 0;
+      struct ll *ll;
+      union value *last_value = NULL;
 
-      if ( i > 0 )
-       tab_hline (tbl, TAL_1, 0, n_cols -1 , i * n_factors + heading_rows);
+      if ( v > 0 )
+       tab_hline (tbl, TAL_1, 0, n_cols -1 ,
+                  v * ll_count (&fctr->result_list)
+                  + heading_rows);
 
       tab_text (tbl,
-               0, i * n_factors + heading_rows,
+               0,
+               v * ll_count (&fctr->result_list) + heading_rows,
                TAB_LEFT | TAT_TITLE,
-               var_to_string (dependent_var[i])
+               var_to_string (dependent_var[v])
                );
 
-      if ( !fctr )
-       populate_summary (tbl, heading_columns,
-                        (i * n_factors) + heading_rows,
-                         dict,
-                        &totals[i]);
-      else
+
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list); ll = ll_next (ll))
        {
-         struct factor_statistics **fs = fctr->fs;
-         int count = 0 ;
-         const union value *prev = NULL;
+         double n;
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
-         while (*fs)
+         if ( fctr->indep_var[0] )
            {
-             if ( !prev ||
-                  0 != compare_values (prev, (*fs)->id[0],
-                                  var_get_width (fctr->indep_var[0])))
+
+             if ( last_value == NULL ||
+                  compare_values_short (last_value, result->value[0],
+                                         fctr->indep_var[0]))
                {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[0],
-                                     (*fs)->id[0], &vstr);
-
-                 tab_text (tbl,
-                           1,
-                           (i * n_factors ) + count +
-                           heading_rows,
+                 struct string str;
+
+                 last_value = result->value[0];
+                 ds_init_empty (&str);
+
+                 var_append_value_name (fctr->indep_var[0], result->value[0],
+                                        &str);
+
+                 tab_text (tbl, 1,
+                           heading_rows + j +
+                           v * ll_count (&fctr->result_list),
                            TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                           );
+                           ds_cstr (&str));
 
-                 ds_destroy (&vstr);
+                 ds_destroy (&str);
 
-                 if (fctr->indep_var[1] && count > 0 )
+                 if ( fctr->indep_var[1] && j > 0)
                    tab_hline (tbl, TAL_1, 1, n_cols - 1,
-                             (i * n_factors ) + count + heading_rows);
+                              heading_rows + j +
+                              v * ll_count (&fctr->result_list));
                }
 
-             prev = (*fs)->id[0];
-
              if ( fctr->indep_var[1])
                {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
+                 struct string str;
+
+                 ds_init_empty (&str);
+
                  var_append_value_name (fctr->indep_var[1],
-                                        (*fs)->id[1], &vstr);
-                 tab_text (tbl,
-                           2,
-                           (i * n_factors ) + count +
-                           heading_rows,
+                                        result->value[1], &str);
+
+                 tab_text (tbl, 2,
+                           heading_rows + j +
+                           v * ll_count (&fctr->result_list),
                            TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                           );
-                 ds_destroy (&vstr);
+                           ds_cstr (&str));
+
+                 ds_destroy (&str);
                }
+           }
 
-             populate_summary (tbl, heading_columns,
-                               (i * n_factors) + count
-                               + heading_rows,
-                               dict,
-                               & (*fs)->m[i]);
 
-             count++ ;
-             fs++;
-           }
+         moments1_calculate (result->metrics[v].moments,
+                             &n, &result->metrics[v].mean,
+                             &result->metrics[v].variance,
+                             &result->metrics[v].skewness,
+                             &result->metrics[v].kurtosis);
+
+         result->metrics[v].se_mean = sqrt (result->metrics[v].variance / n) ;
+
+         /* Total Valid */
+         tab_double (tbl, heading_columns,
+                    heading_rows + j + v * ll_count (&fctr->result_list),
+                    TAB_LEFT,
+                    n, wfmt);
+
+         tab_text (tbl, heading_columns + 1,
+                   heading_rows + j + v * ll_count (&fctr->result_list),
+                   TAB_RIGHT | TAT_PRINTF,
+                   "%g%%", n * 100.0 / result->metrics[v].n);
+
+         /* Total Missing */
+         tab_double (tbl, heading_columns + 2,
+                    heading_rows + j + v * ll_count (&fctr->result_list),
+                    TAB_LEFT,
+                    result->metrics[v].n - n,
+                    wfmt);
+
+         tab_text (tbl, heading_columns + 3,
+                   heading_rows + j + v * ll_count (&fctr->result_list),
+                   TAB_RIGHT | TAT_PRINTF,
+                   "%g%%",
+                   (result->metrics[v].n - n) * 100.0 / result->metrics[v].n
+                   );
+
+         /* Total Valid + Missing */
+         tab_double (tbl, heading_columns + 4,
+                    heading_rows + j + v * ll_count (&fctr->result_list),
+                    TAB_LEFT,
+                    result->metrics[v].n,
+                    wfmt);
+
+         tab_text (tbl, heading_columns + 5,
+                   heading_rows + j + v * ll_count (&fctr->result_list),
+                   TAB_RIGHT | TAT_PRINTF,
+                   "%g%%",
+                   (result->metrics[v].n) * 100.0 / result->metrics[v].n
+                   );
+
+         ++j;
        }
     }
 
-  tab_submit (tbl);
-}
-
 
-static void
-populate_summary (struct tab_table *t, int col, int row,
-                 const struct dictionary *dict,
-                 const struct metrics *m)
-
-{
-  const double total = m->n + m->n_missing ;
-
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
-
-  tab_double (t, col + 0, row + 0, TAB_RIGHT, m->n, wfmt);
-
-  tab_double (t, col + 2, row + 0, TAB_RIGHT, m->n_missing, wfmt);
-
-  tab_double (t, col + 4, row + 0, TAB_RIGHT, total, wfmt);
-
-
-  if ( total > 0 ) {
-    tab_text (t, col + 1, row + 0, TAB_RIGHT | TAT_PRINTF, "%2.0f%%",
-             100.0 * m->n / total );
-
-    tab_text (t, col + 3, row + 0, TAB_RIGHT | TAT_PRINTF, "%2.0f%%",
-             100.0 * m->n_missing / total );
-
-    /* This seems a bit pointless !!! */
-    tab_text (t, col + 5, row + 0, TAB_RIGHT | TAT_PRINTF, "%2.0f%%",
-             100.0 * total / total );
-  }
+  tab_submit (tbl);
 }
 
-
+#define DESCRIPTIVE_ROWS 13
 
 static void
-show_extremes (const struct variable **dependent_var, int n_dep_var,
-              const struct factor *fctr,
-              int n_extremities)
+show_descriptives (const struct variable **dependent_var,
+                  int n_dep_var,
+                  const struct xfactor *fctr)
 {
-  int i;
-  int heading_columns ;
+  int v;
+  int heading_columns = 3;
   int n_cols;
   const int heading_rows = 1;
   struct tab_table *tbl;
 
-
-
-  int n_factors = 1;
   int n_rows ;
+  n_rows = n_dep_var;
 
-  if ( fctr )
-    {
-      heading_columns = 2;
-      n_factors = hsh_count (fctr->fstats);
+  assert (fctr);
 
-      n_rows = n_dep_var * 2 * n_extremities * n_factors;
+  if ( fctr->indep_var[0] )
+    {
+      heading_columns = 4;
 
       if ( fctr->indep_var[1] )
-       heading_columns = 3;
-    }
-  else
-    {
-      heading_columns = 1;
-      n_rows = n_dep_var * 2 * n_extremities;
+       {
+         heading_columns = 5;
+       }
     }
 
+  n_rows *= ll_count (&fctr->result_list) * DESCRIPTIVE_ROWS;
   n_rows += heading_rows;
 
-  heading_columns += 2;
   n_cols = heading_columns + 2;
 
-  tbl = tab_create (n_cols,n_rows,0);
+  tbl = tab_create (n_cols, n_rows, 0);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
   tab_dim (tbl, tab_natural_dimensions);
 
-  /* Outline the box, No internal lines*/
+  /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
           -1, -1,
           0, 0,
           n_cols - 1, n_rows - 1);
 
-  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
 
-  tab_title (tbl, _ ("Extreme Values"));
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
+  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
 
-  tab_vline (tbl, TAL_2, n_cols - 2, 0, n_rows -1);
-  tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows -1);
+  tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
 
-  if ( fctr )
-    {
-      tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
-               var_to_string (fctr->indep_var[0]));
 
-      if ( fctr->indep_var[1] )
-       tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
-                 var_to_string (fctr->indep_var[1]));
-    }
+  if ( fctr->indep_var[0])
+    tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
 
-  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _ ("Value"));
-  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _ ("Case Number"));
+  if ( fctr->indep_var[1])
+    tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
 
-  for ( i = 0 ; i < n_dep_var ; ++i )
+  for (v = 0 ; v < n_dep_var ; ++v )
     {
+      struct ll *ll;
+      int i = 0;
 
-      if ( i > 0 )
-       tab_hline (tbl, TAL_1, 0, n_cols -1 ,
-                 i * 2 * n_extremities * n_factors + heading_rows);
+      const int row_var_start =
+       v * DESCRIPTIVE_ROWS * ll_count(&fctr->result_list);
 
-      tab_text (tbl, 0,
-               i * 2 * n_extremities * n_factors  + heading_rows,
+      tab_text (tbl,
+               0,
+               heading_rows + row_var_start,
                TAB_LEFT | TAT_TITLE,
-               var_to_string (dependent_var[i])
+               var_to_string (dependent_var[v])
                );
 
-
-      if ( !fctr )
-       populate_extremes (tbl, heading_columns - 2,
-                          i * 2 * n_extremities * n_factors  + heading_rows,
-                          n_extremities,
-                          dependent_var[i],
-                          &totals[i]);
-      else
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
        {
-         struct factor_statistics **fs = fctr->fs;
-         int count = 0 ;
-         const union value *prev  = NULL;
-
-         while (*fs)
-           {
-             const int row = heading_rows + ( 2 * n_extremities )  *
-                ( ( i  * n_factors  ) +  count );
-
-
-             if ( !prev || 0 != compare_values (prev, (*fs)->id[0],
-                                        var_get_width (fctr->indep_var[0])))
-               {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[0],
-                                     (*fs)->id[0], &vstr);
-
-                 if ( count > 0 )
-                   tab_hline (tbl, TAL_1, 1, n_cols - 1, row);
-
-                 tab_text (tbl,
-                           1, row,
-                           TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                           );
-
-                 ds_destroy (&vstr);
-               }
-
-             prev = (*fs)->id[0];
-
-             if (fctr->indep_var[1] && count > 0 )
-               tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
-
-             if ( fctr->indep_var[1])
-               {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[1], (*fs)->id[1], &vstr);
-
-               tab_text (tbl, 2, row,
-                         TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                         );
-
-                 ds_destroy (&vstr);
-               }
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
-             populate_extremes (tbl, heading_columns - 2,
-                                row, n_extremities,
-                                dependent_var[i],
-                                & (*fs)->m[i]);
+         const double t =
+           gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0) / 2.0,
+                                      result->metrics[v].n - 1);
 
-             count++ ;
-             fs++;
+         if ( i > 0 || v > 0 )
+           {
+             const int left_col = (i == 0) ? 0 : 1;
+             tab_hline (tbl, TAL_1, left_col, n_cols - 1,
+                        heading_rows + row_var_start + i * DESCRIPTIVE_ROWS);
            }
-       }
-    }
-
-  tab_submit (tbl);
-}
-
-
 
-/* Fill in the extremities table */
-static void
-populate_extremes (struct tab_table *t,
-                  int col, int row, int n,
-                  const struct variable *var,
-                  const struct metrics *m)
-{
-  int extremity;
-  int idx=0;
-
-  tab_text (t, col, row,
-          TAB_RIGHT | TAT_TITLE ,
-          _ ("Highest")
-          );
-
-  tab_text (t, col, row + n ,
-          TAB_RIGHT | TAT_TITLE ,
-          _ ("Lowest")
-          );
-
-
-  tab_hline (t, TAL_1, col, col + 3, row + n );
-
-  for (extremity = 0; extremity < n ; ++extremity )
-    {
-      /* Highest */
-      tab_fixed (t, col + 1, row + extremity,
-               TAB_RIGHT,
-               extremity + 1, 8, 0);
-
-
-      /* Lowest */
-      tab_fixed (t, col + 1, row + extremity + n,
-               TAB_RIGHT,
-               extremity + 1, 8, 0);
-
-    }
-
-
-  /* Lowest */
-  for (idx = 0, extremity = 0; extremity < n && idx < m->n_data ; ++idx )
-    {
-      int j;
-      const struct weighted_value *wv = m->wvp[idx];
-      struct case_node *cn = wv->case_nos;
-
-
-      for (j = 0 ; j < wv->w ; ++j  )
-       {
-         if ( extremity + j >= n )
-           break ;
-
-         tab_value (t, col + 3, row + extremity + j  + n,
-                    TAB_RIGHT,
-                    &wv->v, var_get_print_format (var));
+         if ( fctr->indep_var[0])
+           {
+             struct string vstr;
+             ds_init_empty (&vstr);
+             var_append_value_name (fctr->indep_var[0],
+                                    result->value[0], &vstr);
+
+             tab_text (tbl, 1,
+                       heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
+                       TAB_LEFT,
+                       ds_cstr (&vstr)
+                       );
 
-         tab_fixed (t, col + 2, row + extremity + j  + n,
-                     TAB_RIGHT,
-                     cn->num, 10, 0);
+             ds_destroy (&vstr);
+           }
 
-         if ( cn->next )
-           cn = cn->next;
 
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Mean"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT | TAT_PRINTF,
+                   _("%g%% Confidence Interval for Mean"),
+                   cmd.n_cinterval[0]);
+
+         tab_text (tbl, n_cols - 3,
+                   heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Lower Bound"));
+
+         tab_text (tbl, n_cols - 3,
+                   heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Upper Bound"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT | TAT_PRINTF,
+                   _("5%% Trimmed Mean"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Median"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Variance"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Std. Deviation"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Minimum"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Maximum"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Range"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Interquartile Range"));
+
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Skewness"));
+
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
+                   TAB_LEFT,
+                   _("Kurtosis"));
+
+
+         /* Now the statistics ... */
+
+         tab_double (tbl, n_cols - 2,
+                   heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].mean,
+                    NULL);
+
+         tab_double (tbl, n_cols - 1,
+                   heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].se_mean,
+                    NULL);
+
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].mean - t *
+                     result->metrics[v].se_mean,
+                    NULL);
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].mean + t *
+                     result->metrics[v].se_mean,
+                    NULL);
+
+
+         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),
+                    NULL);
+
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    percentile_calculate (result->metrics[v].quartiles[1], percentile_algorithm),
+                    NULL);
+
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].variance,
+                    NULL);
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    sqrt (result->metrics[v].variance),
+                    NULL);
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    percentile_calculate (result->metrics[v].quartiles[2],
+                                          percentile_algorithm) -
+                    percentile_calculate (result->metrics[v].quartiles[0],
+                                          percentile_algorithm),
+                    NULL);
+
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].skewness,
+                    NULL);
+
+         tab_double (tbl, n_cols - 2,
+                    heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    result->metrics[v].kurtosis,
+                    NULL);
+
+         tab_double (tbl, n_cols - 1,
+                    heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    calc_seskew (result->metrics[v].n),
+                    NULL);
+
+         tab_double (tbl, n_cols - 1,
+                    heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
+                    TAB_CENTER,
+                    calc_sekurt (result->metrics[v].n),
+                    NULL);
+
+         {
+           struct extremum *minimum, *maximum ;
+
+           struct ll *max_ll = ll_head (extrema_list (result->metrics[v].maxima));
+           struct ll *min_ll = ll_head (extrema_list (result->metrics[v].minima));
+
+           maximum = ll_data (max_ll, struct extremum, ll);
+           minimum = ll_data (min_ll, struct extremum, ll);
+
+           tab_double (tbl, n_cols - 2,
+                      heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
+                      TAB_CENTER,
+                      minimum->value,
+                      NULL);
+
+           tab_double (tbl, n_cols - 2,
+                      heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
+                      TAB_CENTER,
+                      maximum->value,
+                      NULL);
+
+           tab_double (tbl, n_cols - 2,
+                      heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
+                      TAB_CENTER,
+                      maximum->value - minimum->value,
+                      NULL);
+         }
        }
-
-      extremity +=  wv->w ;
     }
 
+  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
 
-  /* Highest */
-  for (idx = m->n_data - 1, extremity = 0; extremity < n && idx >= 0; --idx )
-    {
-      int j;
-      const struct weighted_value *wv = m->wvp[idx];
-      struct case_node *cn = wv->case_nos;
-
-      for (j = 0 ; j < wv->w ; ++j  )
-       {
-         if ( extremity + j >= n )
-           break ;
-
-         tab_value (t, col + 3, row + extremity + j,
-                    TAB_RIGHT,
-                    &wv->v, var_get_print_format (var));
-
-         tab_fixed (t, col + 2, row + extremity + j,
-                   TAB_RIGHT,
-                     cn->num, 10, 0);
+  tab_title (tbl, _("Descriptives"));
 
-         if ( cn->next )
-           cn = cn->next;
+  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
+           _("Statistic"));
 
-       }
+  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
+           _("Std. Error"));
 
-      extremity +=  wv->w ;
-    }
+  tab_submit (tbl);
 }
 
 
-/* Show the descriptives table */
-void
-show_descriptives (const struct variable **dependent_var,
-                 int n_dep_var,
-                 struct factor *fctr)
+
+static void
+show_extremes (const struct variable **dependent_var,
+              int n_dep_var,
+              const struct xfactor *fctr)
 {
-  int i;
-  int heading_columns ;
+  int v;
+  int heading_columns = 3;
   int n_cols;
-  const int n_stat_rows = 13;
-
   const int heading_rows = 1;
-
   struct tab_table *tbl;
 
-  int n_factors = 1;
   int n_rows ;
+  n_rows = n_dep_var;
 
-  if ( fctr )
+  assert (fctr);
+
+  if ( fctr->indep_var[0] )
     {
       heading_columns = 4;
-      n_factors = hsh_count (fctr->fstats);
-
-      n_rows = n_dep_var * n_stat_rows * n_factors;
 
       if ( fctr->indep_var[1] )
-       heading_columns = 5;
-    }
-  else
-    {
-      heading_columns = 3;
-      n_rows = n_dep_var * n_stat_rows;
+       {
+         heading_columns = 5;
+       }
     }
 
+  n_rows *= ll_count (&fctr->result_list) * cmd.st_n * 2;
   n_rows += heading_rows;
 
   n_cols = heading_columns + 2;
 
-
   tbl = tab_create (n_cols, n_rows, 0);
-
-  tab_headers (tbl, heading_columns + 1, 0, heading_rows, 0);
+  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
   tab_dim (tbl, tab_natural_dimensions);
 
-  /* Outline the box and have no internal lines*/
+  /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
           -1, -1,
           0, 0,
           n_cols - 1, n_rows - 1);
 
-  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
 
-  tab_vline (tbl, TAL_1, heading_columns, 0, n_rows - 1);
-  tab_vline (tbl, TAL_2, n_cols - 2, 0, n_rows - 1);
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
+  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
 
-  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE, _ ("Statistic"));
-  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Error"));
+  if ( fctr->indep_var[0])
+    tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
 
-  tab_title (tbl, _ ("Descriptives"));
+  if ( fctr->indep_var[1])
+    tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
 
-
-  for ( i = 0 ; i < n_dep_var ; ++i )
+  for (v = 0 ; v < n_dep_var ; ++v )
     {
-      const int row = heading_rows + i * n_stat_rows * n_factors ;
-
-      if ( i > 0 )
-       tab_hline (tbl, TAL_1, 0, n_cols - 1, row );
+      struct ll *ll;
+      int i = 0;
+      const int row_var_start = v * cmd.st_n * 2 * ll_count(&fctr->result_list);
 
-      tab_text (tbl, 0,
-               i * n_stat_rows * n_factors  + heading_rows,
+      tab_text (tbl,
+               0,
+               heading_rows + row_var_start,
                TAB_LEFT | TAT_TITLE,
-               var_to_string (dependent_var[i])
+               var_to_string (dependent_var[v])
                );
 
-
-      if ( fctr  )
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
        {
-         const union value *prev = NULL;
+         int e ;
+         struct ll *min_ll;
+         struct ll *max_ll;
+         const int row_result_start = i * cmd.st_n * 2;
 
-         struct factor_statistics **fs = fctr->fs;
-         int count = 0;
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
-         tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
-                   var_to_string (fctr->indep_var[0]));
+         if (i > 0 || v > 0)
+           tab_hline (tbl, TAL_1, 1, n_cols - 1,
+                      heading_rows + row_var_start + row_result_start);
 
+         tab_hline (tbl, TAL_1, heading_columns - 2, n_cols - 1,
+                    heading_rows + row_var_start + row_result_start + cmd.st_n);
 
-         if ( fctr->indep_var[1])
-           tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
-                     var_to_string (fctr->indep_var[1]));
-
-         while ( *fs )
+         for ( e = 1; e <= cmd.st_n; ++e )
            {
-             const int row = heading_rows + n_stat_rows  *
-                ( ( i  * n_factors  ) +  count );
-
-
-             if ( !prev || 0 != compare_values (prev, (*fs)->id[0],
-                                        var_get_width (fctr->indep_var[0])))
-               {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[0],
-                                     (*fs)->id[0], &vstr);
-
-                 if ( count > 0 )
-                   tab_hline (tbl, TAL_1, 1, n_cols - 1, row);
-
-                 tab_text (tbl,
-                           1, row,
-                           TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                           );
-
-                 ds_destroy (&vstr);
-               }
+             tab_text (tbl, n_cols - 3,
+                       heading_rows + row_var_start + row_result_start + e - 1,
+                       TAB_RIGHT | TAT_PRINTF,
+                       _("%d"), e);
+
+             tab_text (tbl, n_cols - 3,
+                       heading_rows + row_var_start + row_result_start + cmd.st_n + e - 1,
+                       TAB_RIGHT | TAT_PRINTF,
+                       _("%d"), e);
+           }
 
-             prev = (*fs)->id[0];
 
-             if (fctr->indep_var[1] && count > 0 )
-               tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
+         min_ll = ll_head (extrema_list (result->metrics[v].minima));
+         for (e = 0; e < cmd.st_n;)
+           {
+             struct extremum *minimum = ll_data (min_ll, struct extremum, ll);
+             double weight = minimum->weight;
 
-             if ( fctr->indep_var[1])
+             while (weight-- > 0 && e < cmd.st_n)
                {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[1], (*fs)->id[1], &vstr);
-
-               tab_text (tbl, 2, row,
-                         TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                         );
-
-                 ds_destroy (&vstr);
+                 tab_double (tbl, n_cols - 1,
+                            heading_rows + row_var_start + row_result_start + cmd.st_n + e,
+                            TAB_RIGHT,
+                            minimum->value,
+                            NULL);
+
+
+                 tab_fixed (tbl, n_cols - 2,
+                            heading_rows + row_var_start +
+                            row_result_start + cmd.st_n + e,
+                            TAB_RIGHT,
+                            minimum->location,
+                            10, 0);
+                 ++e;
                }
 
-             populate_descriptives (tbl, heading_columns - 2,
-                                    row,
-                                    dependent_var[i],
-                                    & (*fs)->m[i]);
-
-             count++ ;
-             fs++;
+             min_ll = ll_next (min_ll);
            }
 
-       }
-
-      else
-       {
-
-         populate_descriptives (tbl, heading_columns - 2,
-                                i * n_stat_rows * n_factors  + heading_rows,
-                                dependent_var[i],
-                                &totals[i]);
-       }
-    }
-
-  tab_submit (tbl);
-}
-
 
-/* Fill in the descriptives data */
-static void
-populate_descriptives (struct tab_table *tbl, int col, int row,
-                      const struct variable *var,
-                      const struct metrics *m)
-{
-  const double t = gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0)/2.0,
-                                      m->n -1);
-
-  tab_text (tbl, col,
-           row,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Mean"));
-
-  tab_double (tbl, col + 2,
-             row,
-             TAB_CENTER,
-             m->mean,
-             NULL);
-
-  tab_double (tbl, col + 3,
-             row,
-             TAB_CENTER,
-             m->se_mean,
-             NULL);
-
-
-  tab_text (tbl, col,
-           row + 1,
-           TAB_LEFT | TAT_TITLE | TAT_PRINTF,
-           _ ("%g%% Confidence Interval for Mean"), cmd.n_cinterval[0]);
-
-
-  tab_text (tbl, col + 1,
-           row  + 1,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Lower Bound"));
-
-  tab_double (tbl, col + 2,
-             row + 1,
-             TAB_CENTER,
-             m->mean - t * m->se_mean,
-             NULL);
-
-  tab_text (tbl, col + 1,
-           row + 2,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Upper Bound"));
-
-
-  tab_double (tbl, col + 2,
-             row + 2,
-             TAB_CENTER,
-             m->mean + t * m->se_mean,
-             NULL);
-
-  tab_text (tbl, col,
-           row + 3,
-           TAB_LEFT | TAT_TITLE | TAT_PRINTF,
-           _ ("5%% Trimmed Mean"));
-
-  tab_double (tbl, col + 2,
-             row + 3,
-             TAB_CENTER,
-             m->trimmed_mean,
-             NULL);
-
-  tab_text (tbl, col,
-           row + 4,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Median"));
-
-  {
-    struct percentile *p;
-    double d = 50;
-
-    p = hsh_find (m->ptile_hash, &d);
-
-    assert (p);
-
-
-    tab_double (tbl, col + 2,
-               row + 4,
-               TAB_CENTER,
-               p->v,
-               NULL);
-  }
-
-
-  tab_text (tbl, col,
-           row + 5,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Variance"));
-
-  tab_double (tbl, col + 2,
-             row + 5,
-             TAB_CENTER,
-             m->var,
-             NULL);
-
-
-  tab_text (tbl, col,
-           row + 6,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Std. Deviation"));
-
-
-  tab_double (tbl, col + 2,
-             row + 6,
-             TAB_CENTER,
-             m->stddev,
-             NULL);
-
-
-  tab_text (tbl, col,
-           row + 7,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Minimum"));
-
-  tab_double (tbl, col + 2,
-             row + 7,
-             TAB_CENTER,
-             m->min, var_get_print_format (var));
-
-  tab_text (tbl, col,
-           row + 8,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Maximum"));
-
-  tab_double (tbl, col + 2,
-             row + 8,
-             TAB_CENTER,
-             m->max, var_get_print_format (var));
-
-  tab_text (tbl, col,
-           row + 9,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Range"));
-
-
-  tab_double (tbl, col + 2,
-             row + 9,
-             TAB_CENTER,
-             m->max - m->min,
-             NULL);
-
-  tab_text (tbl, col,
-           row + 10,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Interquartile Range"));
-
-  {
-    struct percentile *p1;
-    struct percentile *p2;
-
-    double d = 75;
-    p1 = hsh_find (m->ptile_hash, &d);
-
-    d = 25;
-    p2 = hsh_find (m->ptile_hash, &d);
-
-    assert (p1);
-    assert (p2);
-
-    tab_double (tbl, col + 2,
-               row + 10,
-               TAB_CENTER,
-               p1->v - p2->v,
-               NULL);
-  }
-
-  tab_text (tbl, col,
-           row + 11,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Skewness"));
-
-
-  tab_double (tbl, col + 2,
-             row + 11,
-             TAB_CENTER,
-             m->skewness,
-             NULL);
-
-  /* stderr of skewness */
-  tab_double (tbl, col + 3,
-             row + 11,
-             TAB_CENTER,
-             calc_seskew (m->n),
-             NULL);
-
-  tab_text (tbl, col,
-           row + 12,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Kurtosis"));
-
-
-  tab_double (tbl, col + 2,
-             row + 12,
-             TAB_CENTER,
-             m->kurtosis,
-             NULL);
-
-  /* stderr of kurtosis */
-  tab_double (tbl, col + 3,
-             row + 12,
-             TAB_CENTER,
-             calc_sekurt (m->n),
-             NULL);
-}
-
-
-
-void
-box_plot_variables (const struct factor *fctr,
-                  const struct variable **vars, int n_vars,
-                  const struct variable *id)
-{
-
-  int i;
-  struct factor_statistics **fs ;
-
-  if ( ! fctr )
-    {
-      box_plot_group (fctr, vars, n_vars, id);
-      return;
-    }
-
-  for ( fs = fctr->fs ; *fs ; ++fs )
-    {
-      struct string str;
-      double y_min = DBL_MAX;
-      double y_max = -DBL_MAX;
-      struct chart *ch = chart_create ();
-      ds_init_empty (&str);
-      factor_to_string (fctr, *fs, 0, &str );
-
-      chart_write_title (ch, ds_cstr (&str));
-
-      for ( i = 0 ; i < n_vars ; ++i )
-       {
-         y_max = MAX (y_max, (*fs)->m[i].max);
-         y_min = MIN (y_min, (*fs)->m[i].min);
-       }
-
-      boxplot_draw_yscale (ch, y_max, y_min);
-
-      for ( i = 0 ; i < n_vars ; ++i )
-       {
-
-         const double box_width = (ch->data_right - ch->data_left)
-           / (n_vars * 2.0 ) ;
-
-         const double box_centre = ( i * 2 + 1) * box_width
-           + ch->data_left;
-
-         boxplot_draw_boxplot (ch,
-                              box_centre, box_width,
-                              & (*fs)->m[i],
-                              var_to_string (vars[i]));
-
-
-       }
-
-      chart_submit (ch);
-      ds_destroy (&str);
-    }
-}
-
-
-
-/* Do a box plot, grouping all factors into one plot ;
-   each dependent variable has its own plot.
-*/
-void
-box_plot_group (const struct factor *fctr,
-              const struct variable **vars,
-              int n_vars,
-              const struct variable *id UNUSED)
-{
-
-  int i;
-
-  for ( i = 0 ; i < n_vars ; ++i )
-    {
-      struct factor_statistics **fs ;
-      struct chart *ch;
-
-      ch = chart_create ();
+         max_ll = ll_head (extrema_list (result->metrics[v].maxima));
+         for (e = 0; e < cmd.st_n;)
+           {
+             struct extremum *maximum = ll_data (max_ll, struct extremum, ll);
+             double weight = maximum->weight;
 
-      boxplot_draw_yscale (ch, totals[i].max, totals[i].min);
+             while (weight-- > 0 && e < cmd.st_n)
+               {
+                 tab_double (tbl, n_cols - 1,
+                            heading_rows + row_var_start +
+                             row_result_start + e,
+                            TAB_RIGHT,
+                            maximum->value,
+                            NULL);
+
+
+                 tab_fixed (tbl, n_cols - 2,
+                            heading_rows + row_var_start +
+                            row_result_start + e,
+                            TAB_RIGHT,
+                            maximum->location,
+                            10, 0);
+                 ++e;
+               }
 
-      if ( fctr )
-       {
-         int n_factors = 0;
-         int f=0;
-         for ( fs = fctr->fs ; *fs ; ++fs )
-           ++n_factors;
+             max_ll = ll_next (max_ll);
+           }
 
-         chart_write_title (ch, _ ("Boxplot of %s vs. %s"),
-                           var_to_string (vars[i]), var_to_string (fctr->indep_var[0]) );
 
-         for ( fs = fctr->fs ; *fs ; ++fs )
+         if ( fctr->indep_var[0])
            {
-             struct string str;
-             const double box_width = (ch->data_right - ch->data_left)
-               / (n_factors * 2.0 ) ;
-
-             const double box_centre = ( f++ * 2 + 1) * box_width
-               + ch->data_left;
-
-             ds_init_empty (&str);
-             factor_to_string_concise (fctr, *fs, &str);
+             struct string vstr;
+             ds_init_empty (&vstr);
+             var_append_value_name (fctr->indep_var[0],
+                                    result->value[0], &vstr);
+
+             tab_text (tbl, 1,
+                       heading_rows + row_var_start + row_result_start,
+                       TAB_LEFT,
+                       ds_cstr (&vstr)
+                       );
 
-             boxplot_draw_boxplot (ch,
-                                  box_centre, box_width,
-                                  & (*fs)->m[i],
-                                  ds_cstr (&str));
-              ds_destroy (&str);
+             ds_destroy (&vstr);
            }
-       }
-      else if ( ch )
-       {
-         const double box_width = (ch->data_right - ch->data_left) / 3.0;
-         const double box_centre = (ch->data_right + ch->data_left) / 2.0;
 
-         chart_write_title (ch, _ ("Boxplot"));
 
-         boxplot_draw_boxplot (ch,
-                              box_centre,    box_width,
-                              &totals[i],
-                              var_to_string (vars[i]) );
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + row_result_start,
+                   TAB_RIGHT,
+                   _("Highest"));
 
+         tab_text (tbl, n_cols - 4,
+                   heading_rows + row_var_start + row_result_start + cmd.st_n,
+                   TAB_RIGHT,
+                   _("Lowest"));
        }
-
-      chart_submit (ch);
     }
-}
-
 
-/* Plot the normal and detrended normal plots for m
-   Label the plots with factorname */
-void
-np_plot (const struct metrics *m, const char *factorname)
-{
-  int i;
-  double yfirst=0, ylast=0;
-
-  /* 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 / m->stddev;
-  const double intercept = - m->mean / m->stddev;
-
-  /* Cowardly refuse to plot an empty data set */
-  if ( m->n_data == 0 )
-    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"), factorname);
-  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"),
-                   factorname);
-  chart_write_xlabel (dnp_chart, _ ("Observed Value"));
-  chart_write_ylabel (dnp_chart, _ ("Dev from Normal"));
-
-  yfirst = gsl_cdf_ugaussian_Pinv (m->wvp[0]->rank / ( m->n + 1));
-  ylast =  gsl_cdf_ugaussian_Pinv (m->wvp[m->n_data-1]->rank / ( m->n + 1));
-
-
-  {
-    /* Need to make sure that both the scatter plot and the ideal fit into the
-       plot */
-    double x_lower = MIN (m->min, (yfirst - intercept) / slope) ;
-    double x_upper = MAX (m->max, (ylast  - intercept) / slope) ;
-    double slack = (x_upper - x_lower)  * 0.05 ;
-
-    chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
-
-    chart_write_xscale (dnp_chart, m->min, m->max, 5);
-
-  }
-
-  chart_write_yscale (np_chart, yfirst, ylast, 5);
-
-  {
-    /* We have to cache the detrended data, beacause we need to
-       find its limits before we can plot it */
-    double *d_data = xnmalloc (m->n_data, sizeof *d_data);
-    double d_max = -DBL_MAX;
-    double d_min = DBL_MAX;
-    for ( i = 0 ; i < m->n_data; ++i )
-      {
-       const double ns = gsl_cdf_ugaussian_Pinv (m->wvp[i]->rank / ( m->n + 1));
+  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
 
-       chart_datum (np_chart, 0, m->wvp[i]->v.f, ns);
 
-       d_data[i] = (m->wvp[i]->v.f - m->mean) / m->stddev  - ns;
+  tab_title (tbl, _("Extreme Values"));
 
-       if ( d_data[i] < d_min ) d_min = d_data[i];
-       if ( d_data[i] > d_max ) d_max = d_data[i];
-      }
-    chart_write_yscale (dnp_chart, d_min, d_max, 5);
 
-    for ( i = 0 ; i < m->n_data; ++i )
-      chart_datum (dnp_chart, 0, m->wvp[i]->v.f, d_data[i]);
+  tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
+           _("Case Number"));
 
-    free (d_data);
-  }
 
-  chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
-  chart_line (dnp_chart, 0, 0, m->min, m->max , CHART_DIM_X);
+  tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
+           _("Value"));
 
-  chart_submit (np_chart);
-  chart_submit (dnp_chart);
+  tab_submit (tbl);
 }
 
+#define PERCENTILE_ROWS 2
 
-
-
-/* Show the percentiles */
-void
+static void
 show_percentiles (const struct variable **dependent_var,
-                int n_dep_var,
-                struct factor *fctr)
+                 int n_dep_var,
+                 const struct xfactor *fctr)
 {
-  struct tab_table *tbl;
   int i;
+  int v;
+  int heading_columns = 2;
+  int n_cols;
+  const int n_percentiles = subc_list_double_count (&percentile_list);
+  const int heading_rows = 2;
+  struct tab_table *tbl;
 
-  int n_cols, n_rows;
-  int n_factors;
-
-  struct hsh_table *ptiles ;
-
-  int n_heading_columns;
-  const int n_heading_rows = 2;
-  const int n_stat_rows = 2;
+  int n_rows ;
+  n_rows = n_dep_var;
 
-  int n_ptiles ;
+  assert (fctr);
 
-  if ( fctr )
+  if ( fctr->indep_var[0] )
     {
-      struct factor_statistics **fs = fctr->fs ;
-      n_heading_columns = 3;
-      n_factors = hsh_count (fctr->fstats);
-
-      ptiles = (*fs)->m[0].ptile_hash;
+      heading_columns = 3;
 
       if ( fctr->indep_var[1] )
-       n_heading_columns = 4;
-    }
-  else
-    {
-      n_factors = 1;
-      n_heading_columns = 2;
-
-      ptiles = totals[0].ptile_hash;
+       {
+         heading_columns = 4;
+       }
     }
 
-  n_ptiles = hsh_count (ptiles);
-
-  n_rows = n_heading_rows + n_dep_var * n_stat_rows * n_factors;
+  n_rows *= ll_count (&fctr->result_list) * PERCENTILE_ROWS;
+  n_rows += heading_rows;
 
-  n_cols = n_heading_columns + n_ptiles ;
+  n_cols = heading_columns + n_percentiles;
 
   tbl = tab_create (n_cols, n_rows, 0);
-
-  tab_headers (tbl, n_heading_columns + 1, 0, n_heading_rows, 0);
+  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
   tab_dim (tbl, tab_natural_dimensions);
 
-  /* Outline the box and have no internal lines*/
+  /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
           -1, -1,
           0, 0,
           n_cols - 1, n_rows - 1);
 
-  tab_hline (tbl, TAL_2, 0, n_cols - 1, n_heading_rows );
-
-  tab_vline (tbl, TAL_2, n_heading_columns, 0, n_rows - 1);
-
-
-  tab_title (tbl, _ ("Percentiles"));
-
-
-  tab_hline (tbl, TAL_1, n_heading_columns, n_cols - 1, 1 );
-
 
-  tab_box (tbl,
-          -1, -1,
-          -1, TAL_1,
-          0, n_heading_rows,
-          n_heading_columns - 1, n_rows - 1);
-
-
-  tab_box (tbl,
-          -1, -1,
-          -1, TAL_1,
-          n_heading_columns, n_heading_rows - 1,
-          n_cols - 1, n_rows - 1);
-
-  tab_joint_text (tbl, n_heading_columns + 1, 0,
-                n_cols - 1 , 0,
-                TAB_CENTER | TAT_TITLE ,
-                _ ("Percentiles"));
-
-
-  {
-    /* Put in the percentile break points as headings */
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
+  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
 
-    struct percentile **p = (struct percentile **) hsh_sort (ptiles);
+  if ( fctr->indep_var[0])
+    tab_text (tbl, 1, 1, TAT_TITLE, var_to_string (fctr->indep_var[0]));
 
-    i = 0;
-    while ( (*p)  )
-      {
-       tab_fixed (tbl, n_heading_columns + i++ , 1,
-                   TAB_CENTER,
-                   (*p)->p,
-                   8, 0);
-       p++;
-      }
+  if ( fctr->indep_var[1])
+    tab_text (tbl, 2, 1, TAT_TITLE, var_to_string (fctr->indep_var[1]));
 
-  }
-
-  for ( i = 0 ; i < n_dep_var ; ++i )
+  for (v = 0 ; v < n_dep_var ; ++v )
     {
-      const int n_stat_rows = 2;
-      const int row = n_heading_rows + i * n_stat_rows * n_factors ;
+      double hinges[3];
+      struct ll *ll;
+      int i = 0;
 
-      if ( i > 0 )
-       tab_hline (tbl, TAL_1, 0, n_cols - 1, row );
+      const int row_var_start =
+       v * PERCENTILE_ROWS * ll_count(&fctr->result_list);
 
-      tab_text (tbl, 0,
-               i * n_stat_rows * n_factors  + n_heading_rows,
+      tab_text (tbl,
+               0,
+               heading_rows + row_var_start,
                TAB_LEFT | TAT_TITLE,
-               var_to_string (dependent_var[i])
+               var_to_string (dependent_var[v])
                );
 
-      if ( fctr  )
+      for (ll = ll_head (&fctr->result_list);
+          ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
        {
-         const union value *prev  = NULL ;
-         struct factor_statistics **fs = fctr->fs;
-         int count = 0;
+         int j;
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
-         tab_text (tbl, 1, n_heading_rows - 1,
-                   TAB_CENTER | TAT_TITLE,
-                   var_to_string (fctr->indep_var[0]));
+         if ( i > 0 || v > 0 )
+           {
+             const int left_col = (i == 0) ? 0 : 1;
+             tab_hline (tbl, TAL_1, left_col, n_cols - 1,
+                        heading_rows + row_var_start + i * PERCENTILE_ROWS);
+           }
 
+         if ( fctr->indep_var[0])
+           {
+             struct string vstr;
+             ds_init_empty (&vstr);
+             var_append_value_name (fctr->indep_var[0],
+                                    result->value[0], &vstr);
+
+             tab_text (tbl, 1,
+                       heading_rows + row_var_start + i * PERCENTILE_ROWS,
+                       TAB_LEFT,
+                       ds_cstr (&vstr)
+                       );
 
-         if ( fctr->indep_var[1])
-           tab_text (tbl, 2, n_heading_rows - 1, TAB_CENTER | TAT_TITLE,
-                     var_to_string (fctr->indep_var[1]));
+             ds_destroy (&vstr);
+           }
 
-         while ( *fs )
-           {
-             const int row = n_heading_rows + n_stat_rows  *
-                ( ( i  * n_factors  ) +  count );
 
+         tab_text (tbl, n_cols - n_percentiles - 1,
+                   heading_rows + row_var_start + i * PERCENTILE_ROWS,
+                   TAB_LEFT,
+                   ptile_alg_desc [percentile_algorithm]);
 
-             if ( !prev || 0 != compare_values (prev, (*fs)->id[0],
-                                        var_get_width (fctr->indep_var[0])))
-               {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[0],
-                                     (*fs)->id[0], &vstr);
 
+         tab_text (tbl, n_cols - n_percentiles - 1,
+                   heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
+                   TAB_LEFT,
+                   _("Tukey's Hinges"));
 
-                 if ( count > 0 )
-                   tab_hline (tbl, TAL_1, 1, n_cols - 1, row);
 
-                 tab_text (tbl,
-                           1, row,
-                           TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                           );
+         tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
 
-                 ds_destroy (&vstr);
-               }
+         tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
+                                 hinges);
 
-             prev = (*fs)->id[0];
+         for (j = 0; j < n_percentiles; ++j)
+           {
+             double hinge = SYSMIS;
+             tab_double (tbl, n_cols - n_percentiles + j,
+                        heading_rows + row_var_start + i * PERCENTILE_ROWS,
+                        TAB_CENTER,
+                        percentile_calculate (result->metrics[v].ptl[j],
+                                              percentile_algorithm),
+                        NULL
+                        );
+
+             if ( result->metrics[v].ptl[j]->ptile == 0.5)
+               hinge = hinges[1];
+             else if ( result->metrics[v].ptl[j]->ptile == 0.25)
+               hinge = hinges[0];
+             else if ( result->metrics[v].ptl[j]->ptile == 0.75)
+               hinge = hinges[2];
+
+             if ( hinge != SYSMIS)
+               tab_double (tbl, n_cols - n_percentiles + j,
+                          heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
+                          TAB_CENTER,
+                          hinge,
+                          NULL
+                          );
 
-             if (fctr->indep_var[1] && count > 0 )
-               tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
+           }
+       }
+    }
 
-             if ( fctr->indep_var[1])
-               {
-                 struct string vstr;
-                 ds_init_empty (&vstr);
-                 var_append_value_name (fctr->indep_var[1], (*fs)->id[1], &vstr);
+  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
 
-               tab_text (tbl, 2, row,
-                         TAB_LEFT | TAT_TITLE,
-                           ds_cstr (&vstr)
-                         );
+  tab_title (tbl, _("Percentiles"));
 
-                 ds_destroy (&vstr);
-               }
 
+  for (i = 0 ; i < n_percentiles; ++i )
+    {
+      tab_text (tbl, n_cols - n_percentiles + i, 1,
+               TAB_CENTER | TAT_TITLE | TAT_PRINTF,
+               _("%g"),
+               subc_list_double_at (&percentile_list, i)
+               );
 
-             populate_percentiles (tbl, n_heading_columns - 1,
-                                   row,
-                                   & (*fs)->m[i]);
 
+    }
 
-             count++ ;
-             fs++;
-           }
+  tab_joint_text (tbl,
+                 n_cols - n_percentiles, 0,
+                 n_cols - 1, 0,
+                 TAB_CENTER | TAT_TITLE,
+                 _("Percentiles"));
 
+  /* Vertical lines for the data only */
+  tab_box (tbl,
+          -1, -1,
+          -1, TAL_1,
+          n_cols - n_percentiles, 1,
+          n_cols - 1, n_rows - 1);
 
-       }
-      else
-       {
-         populate_percentiles (tbl, n_heading_columns - 1,
-                               i * n_stat_rows * n_factors  + n_heading_rows,
-                               &totals[i]);
-       }
-    }
+  tab_hline (tbl, TAL_1, n_cols - n_percentiles, n_cols - 1, 1);
 
 
   tab_submit (tbl);
 }
 
 
-
-
 static void
-populate_percentiles (struct tab_table *tbl, int col, int row,
-                     const struct metrics *m)
+factor_to_string_concise (const struct xfactor *fctr,
+                         const struct factor_result *result,
+                         struct string *str
+                         )
 {
-  int i;
-
-  struct percentile **p = (struct percentile **) hsh_sort (m->ptile_hash);
-
-  tab_text (tbl,
-           col, row + 1,
-           TAB_LEFT | TAT_TITLE,
-           _ ("Tukey\'s Hinges")
-           );
+  if (fctr->indep_var[0])
+    {
+      var_append_value_name (fctr->indep_var[0], result->value[0], str);
 
-  tab_text (tbl,
-           col, row,
-           TAB_LEFT | TAT_TITLE,
-           ptile_alg_desc[m->ptile_alg]
-           );
+      if ( fctr->indep_var[1] )
+       {
+         ds_put_cstr (str, ",");
 
+         var_append_value_name (fctr->indep_var[1], result->value[1], str);
 
-  i = 0;
-  while ( (*p)  )
-    {
-      tab_double (tbl, col + i + 1 , row,
-                 TAB_CENTER,
-                 (*p)->v,
-                 NULL);
-
-      if ( (*p)->p == 25 )
-       tab_double (tbl, col + i + 1 , row + 1,
-                   TAB_CENTER,
-                   m->hinge[0],
-                   NULL);
-
-      if ( (*p)->p == 50 )
-       tab_double (tbl, col + i + 1 , row + 1,
-                   TAB_CENTER,
-                   m->hinge[1],
-                   NULL);
-
-
-      if ( (*p)->p == 75 )
-       tab_double (tbl, col + i + 1 , row + 1,
-                   TAB_CENTER,
-                   m->hinge[2],
-                   NULL);
-      i++;
-      p++;
+         ds_put_cstr (str, ")");
+       }
     }
 }
 
+
 static void
-factor_to_string (const struct factor *fctr,
-                 const struct factor_statistics *fs,
-                 const struct variable *var,
+factor_to_string (const struct xfactor *fctr,
+                 const struct factor_result *result,
                  struct string *str
                  )
 {
-  if (var)
-    ds_put_format (str, "%s (",var_to_string (var) );
-
-
-  ds_put_format (str,  "%s = ",
-                var_to_string (fctr->indep_var[0]));
+  if (fctr->indep_var[0])
+    {
+      ds_put_format (str, "(%s = ", var_get_name (fctr->indep_var[0]));
 
-  var_append_value_name (fctr->indep_var[0], fs->id[0], str);
+      var_append_value_name (fctr->indep_var[0], result->value[0], str);
 
-  if ( fctr->indep_var[1] )
-    {
-      ds_put_format (str, "; %s = )",
-                    var_to_string (fctr->indep_var[1]));
+      if ( fctr->indep_var[1] )
+       {
+         ds_put_cstr (str, ",");
+         ds_put_format (str, "%s = ", var_get_name (fctr->indep_var[1]));
 
-      var_append_value_name (fctr->indep_var[1], fs->id[1], str);
-    }
-  else
-    {
-      if ( var )
-       ds_put_cstr (str, ")");
+         var_append_value_name (fctr->indep_var[1], result->value[1], str);
+       }
+      ds_put_cstr (str, ")");
     }
 }
 
 
-static void
-factor_to_string_concise (const struct factor *fctr,
-                         const struct factor_statistics *fs,
-                         struct string *str
-                         )
 
-{
-  var_append_value_name (fctr->indep_var[0], fs->id[0], str);
-
-  if ( fctr->indep_var[1] )
-    {
-      ds_put_cstr (str, ",");
-
-      var_append_value_name (fctr->indep_var[1],fs->id[1], str);
-
-      ds_put_cstr (str, ")");
-    }
-}
 
 /*
   Local Variables:
index ccb84dd8a4f37abecb352a2bf04dc92f95b71602..7583374b22874c530f3047d0adafca477f391247 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -88,7 +88,7 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   struct flip_pgm *flip;
   struct casereader *input, *reader;
   union value *output_buf;
-  struct ccase c;
+  struct ccase *c;
   size_t i;
   bool ok;
 
@@ -169,10 +169,10 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   proc_discard_output (ds);
 
   input = proc_open (ds);
-  while (casereader_read (input, &c))
+  while ((c = casereader_read (input)) != NULL)
     {
-      write_flip_case (flip, &c);
-      case_destroy (&c);
+      write_flip_case (flip, c);
+      case_unref (c);
     }
   ok = casereader_destroy (input);
   ok = proc_commit (ds) && ok;
@@ -464,26 +464,25 @@ flip_file (struct flip_pgm *flip)
   return true;
 }
 
-/* Reads one case into C.
-   Returns true if successful, false at end of file or if an
-   I/O error occurred. */
-static bool
-flip_casereader_read (struct casereader *reader UNUSED, void *flip_,
-                      struct ccase *c)
+/* Reads and returns one case.
+   Returns a null pointer at end of file or if an I/O error occurred. */
+static struct ccase *
+flip_casereader_read (struct casereader *reader UNUSED, void *flip_)
 {
   struct flip_pgm *flip = flip_;
+  struct ccase *c;
   size_t i;
 
   if (flip->error || flip->cases_read >= flip->var_cnt)
-    return false;
+    return NULL;
 
-  case_create (c, flip->case_cnt);
+  c = case_create (flip->case_cnt);
   for (i = 0; i < flip->case_cnt; i++)
     {
       double in;
       if (fread (&in, sizeof in, 1, flip->file) != 1)
         {
-          case_destroy (c);
+          case_unref (c);
           if (ferror (flip->file))
             msg (SE, _("Error reading FLIP temporary file: %s."),
                  strerror (errno));
@@ -492,14 +491,14 @@ flip_casereader_read (struct casereader *reader UNUSED, void *flip_,
           else
             NOT_REACHED ();
           flip->error = true;
-          return false;
+          return NULL;
         }
       case_data_rw_idx (c, i)->f = in;
     }
 
   flip->cases_read++;
 
-  return true;
+  return c;
 }
 
 /* Destroys the source.
index f2054380b8807fff50107d3a5b8902a484a6e742..952857479aed75f9f7e27e37b919fa383b70b0fb 100644 (file)
@@ -30,16 +30,15 @@ compare_freq ( const void *_f1, const void *_f2, const void *_var)
   const struct freq *f2 = _f2;
   const struct variable *var = _var;
 
-  return  compare_values (f1->value, f2->value, var_get_width (var) );
+  return  compare_values_short (f1->value, f2->value, var );
 }
 
 unsigned int
-hash_freq (const void *_f, const void *_var)
+hash_freq (const void *_f, const void *var)
 {
   const struct freq *f = _f;
-  const struct variable *var  = _var;
 
-  return hash_value (f->value, var_get_width (var));
+  return hash_value_short (f->value, var);
 }
 
 /* Free function to be used on FR whose value parameter has been copied */
index a09ecc100759b76d2ecf4d682a050c9999be79f5..25866a417acd109d1c715dec892bdef27294f2f5 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -268,7 +268,7 @@ static hsh_compare_func compare_freq_numeric_d, compare_freq_alpha_d;
 static void do_piechart(const struct variable *var,
                        const struct freq_tab *frq_tab);
 
-gsl_histogram *
+struct histogram *
 freq_tab_to_hist(const struct freq_tab *ft, const struct variable *var);
 
 
@@ -376,11 +376,11 @@ internal_cmd_frequencies (struct lexer *lexer, struct dataset *ds)
   for (; casegrouper_get_next_group (grouper, &group);
        casereader_destroy (group))
     {
-      struct ccase c;
+      struct ccase *c;
 
       precalc (group, ds);
-      for (; casereader_read (group, &c); case_destroy (&c))
-        calc (&c, ds);
+      for (; (c = casereader_read (group)) != NULL; case_unref (c))
+        calc (c, ds);
       postcalc (ds);
     }
   ok = casegrouper_destroy (grouper);
@@ -535,13 +535,14 @@ calc (const struct ccase *c, const struct dataset *ds)
 static void
 precalc (struct casereader *input, struct dataset *ds)
 {
-  struct ccase c;
+  struct ccase *c;
   size_t i;
 
-  if (casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c != NULL)
     {
-      output_split_file_values (ds, &c);
-      case_destroy (&c);
+      output_split_file_values (ds, c);
+      case_unref (c);
     }
 
   pool_destroy (data_pool);
@@ -608,31 +609,26 @@ postcalc (const struct dataset *ds)
       if ( chart == GFT_HIST)
        {
          double d[frq_n_stats];
-         struct normal_curve norm;
-         gsl_histogram *hist ;
-
-
-         norm.N = vf->tab.valid_cases;
+         struct histogram *hist ;
 
          calc_stats (v, d);
-         norm.mean = d[frq_mean];
-         norm.stddev = d[frq_stddev];
 
-         hist = freq_tab_to_hist(ft,v);
+         hist = freq_tab_to_hist (ft,v);
 
-         histogram_plot(hist, var_to_string(v), &norm, normal);
+         histogram_plot_n (hist, var_to_string(v),
+                         vf->tab.valid_cases,
+                         d[frq_mean],
+                         d[frq_stddev],
+                         normal);
 
-         gsl_histogram_free(hist);
+         statistic_destroy ((struct statistic *)hist);
        }
 
-
       if ( chart == GFT_PIE)
        {
          do_piechart(v_variables[i], ft);
        }
 
-
-
       cleanup_freq_tab (v);
 
     }
@@ -1442,14 +1438,14 @@ dump_statistics (const struct variable *v, bool show_varname,
 
 
 /* Create a gsl_histogram from a freq_tab */
-gsl_histogram *
-freq_tab_to_hist(const struct freq_tab *ft, const struct variable *var)
+struct histogram *
+freq_tab_to_hist (const struct freq_tab *ft, const struct variable *var)
 {
   int i;
   double x_min = DBL_MAX;
   double x_max = -DBL_MAX;
 
-  gsl_histogram *hist;
+  struct statistic *hist;
   const double bins = 11;
 
   struct hsh_iterator hi;
@@ -1466,15 +1462,15 @@ freq_tab_to_hist(const struct freq_tab *ft, const struct variable *var)
       if ( frq->value[0].f > x_max ) x_max = frq->value[0].f ;
     }
 
-  hist = histogram_create(bins, x_min, x_max);
+  hist = histogram_create (bins, x_min, x_max);
 
   for( i = 0 ; i < ft->n_valid ; ++i )
     {
       frq = &ft->valid[i];
-      gsl_histogram_accumulate(hist, frq->value[0].f, frq->count);
+      histogram_add ((struct histogram *)hist, frq->value[0].f, frq->count);
     }
 
-  return hist;
+  return (struct histogram *)hist;
 }
 
 
index fd48b735e55b83a1f0c2908426ec7c4017306794..26b903651eea06771c7b90799f9700162a9fb478 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@
 #include <language/data-io/file-handle.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/compiler.h>
+#include <libpspp/hash.h>
 #include <libpspp/message.h>
 #include <math/covariance-matrix.h>
 #include <math/coefficient.h>
@@ -47,8 +48,6 @@
 #include "xalloc.h"
 #include "gettext.h"
 
-#define GLM_LARGE_DATA 1000
-
 /* (headers) */
 
 /* (specification)
@@ -95,19 +94,16 @@ int cmd_glm (struct lexer *lexer, struct dataset *ds);
 
 static bool run_glm (struct casereader *,
                     struct cmd_glm *,
-                    const struct dataset *, pspp_linreg_cache *);
+                    const struct dataset *);
 
 int
 cmd_glm (struct lexer *lexer, struct dataset *ds)
 {
   struct casegrouper *grouper;
   struct casereader *group;
-  pspp_linreg_cache *model = NULL;
 
   bool ok;
 
-  model = xmalloc (sizeof *model);
-
   if (!parse_glm (lexer, ds, &cmd, NULL))
     return CMD_FAILURE;
 
@@ -115,12 +111,11 @@ cmd_glm (struct lexer *lexer, struct dataset *ds)
   grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
   while (casegrouper_get_next_group (grouper, &group))
     {
-      run_glm (group, &cmd, ds, model);
+      run_glm (group, &cmd, ds);
     }
   ok = casegrouper_destroy (grouper);
   ok = proc_commit (ds) && ok;
 
-  free (model);
   free (v_dependent);
   return ok ? CMD_SUCCESS : CMD_FAILURE;
 }
@@ -151,131 +146,59 @@ glm_custom_dependent (struct lexer *lexer, struct dataset *ds,
   return 1;
 }
 
-static void
-coeff_init (pspp_linreg_cache * c, struct design_matrix *dm)
-{
-  c->coeff = xnmalloc (dm->m->size2 + 1, sizeof (*c->coeff));
-  c->coeff[0] = xmalloc (sizeof (*(c->coeff[0])));     /* The first coefficient is the intercept. */
-  c->coeff[0]->v_info = NULL;  /* Intercept has no associated variable. */
-  pspp_coeff_init (c->coeff + 1, dm);
-}
-
 /*
-  Put the moments in the linreg cache.
+  COV is the covariance matrix for variables included in the
+  model. That means the dependent variable is in there, too.
  */
 static void
-compute_moments (pspp_linreg_cache * c, struct moments_var *mom,
-                struct design_matrix *dm, size_t n)
+coeff_init (pspp_linreg_cache * c, const struct design_matrix *cov)
 {
-  size_t i;
-  size_t j;
-  double weight;
-  double mean;
-  double variance;
-  double skewness;
-  double kurtosis;
-  /*
-     Scan the variable names in the columns of the design matrix.
-     When we find the variable we need, insert its mean in the cache.
-   */
-  for (i = 0; i < dm->m->size2; i++)
-    {
-      for (j = 0; j < n; j++)
-       {
-         if (design_matrix_col_to_var (dm, i) == (mom + j)->v)
-           {
-             moments1_calculate ((mom + j)->m, &weight, &mean, &variance,
-                                 &skewness, &kurtosis);
-             gsl_vector_set (c->indep_means, i, mean);
-             gsl_vector_set (c->indep_std, i, sqrt (variance));
-           }
-       }
-    }
+  c->coeff = xnmalloc (cov->m->size2, sizeof (*c->coeff));
+  c->n_coeffs = cov->m->size2 - 1;
+  pspp_coeff_init (c->coeff, cov);
 }
 
-/* Encode categorical variables.
-   Returns number of valid cases. */
-static int
-data_pass_one (struct casereader *input,
-              const struct variable **vars, size_t n_vars,
-              struct moments_var **mom)
-{
-  int n_data;
-  struct ccase c;
-  size_t i;
-
-  for (i = 0; i < n_vars; i++)
-    {
-      mom[i] = xmalloc (sizeof (*mom[i]));
-      mom[i]->v = vars[i];
-      mom[i]->mean = xmalloc (sizeof (*mom[i]->mean));
-      mom[i]->variance = xmalloc (sizeof (*mom[i]->mean));
-      mom[i]->weight = xmalloc (sizeof (*mom[i]->weight));
-      mom[i]->m = moments1_create (MOMENT_VARIANCE);
-      if (var_is_alpha (vars[i]))
-       cat_stored_values_create (vars[i]);
-    }
 
-  n_data = 0;
-  for (; casereader_read (input, &c); case_destroy (&c))
-    {
-      /*
-         The second condition ensures the program will run even if
-         there is only one variable to act as both explanatory and
-         response.
-       */
-      for (i = 0; i < n_vars; i++)
-       {
-         const union value *val = case_data (&c, vars[i]);
-         if (var_is_alpha (vars[i]))
-           cat_value_update (vars[i], val);
-         else
-           moments1_add (mom[i]->m, val->f, 1.0);
-       }
-      n_data++;
-    }
-  casereader_destroy (input);
-  for (i = 0; i < n_vars; i++)
-    {
-      if (var_is_numeric (mom[i]->v))
-       {
-         moments1_calculate (mom[i]->m, mom[i]->weight, mom[i]->mean,
-                             mom[i]->variance, NULL, NULL);
-       }
-    }
-
-  return n_data;
+static pspp_linreg_cache *
+fit_model (const struct covariance_matrix *cov,
+          const struct variable *dep_var, 
+          const struct variable ** indep_vars, 
+          size_t n_data, size_t n_indep)
+{
+  pspp_linreg_cache *result = NULL;
+  result = pspp_linreg_cache_alloc (dep_var, indep_vars, n_data, n_indep);
+  coeff_init (result, covariance_to_design (cov));
+  pspp_linreg_with_cov (cov, result);  
+  
+  return result;
 }
 
 static bool
 run_glm (struct casereader *input,
         struct cmd_glm *cmd,
-        const struct dataset *ds, pspp_linreg_cache * model)
+        const struct dataset *ds)
 {
-  size_t i;
-  size_t j;
-  int n_indep = 0;
-  struct ccase c;
+  casenumber row;
   const struct variable **indep_vars;
   const struct variable **all_vars;
-  struct design_matrix *X;
-  struct moments_var **mom;
-  struct casereader *reader;
-  casenumber row;
+  int n_indep = 0;
+  pspp_linreg_cache *model = NULL; 
+  pspp_linreg_opts lopts;
+  struct ccase *c;
+  size_t i;
   size_t n_all_vars;
   size_t n_data;               /* Number of valid cases. */
+  struct casereader *reader;
+  struct covariance_matrix *cov;
 
-  pspp_linreg_opts lopts;
-
-  assert (model != NULL);
-
-  if (!casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c == NULL)
     {
       casereader_destroy (input);
       return true;
     }
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
+  output_split_file_values (ds, c);
+  case_unref (c);
 
   if (!v_dependent)
     {
@@ -283,8 +206,6 @@ run_glm (struct casereader *input,
                     1u << DC_SYSTEM);
     }
 
-
-
   lopts.get_depvar_mean_std = 1;
 
   lopts.get_indep_mean_std = xnmalloc (n_dependent, sizeof (int));
@@ -302,54 +223,39 @@ run_glm (struct casereader *input,
       all_vars[i + n_dependent] = cmd->v_by[i];
     }
   n_indep = cmd->n_by;
-  mom = xnmalloc (n_all_vars, sizeof (*mom));
-
 
   reader = casereader_clone (input);
   reader = casereader_create_filter_missing (reader, indep_vars, n_indep,
-                                            MV_ANY, NULL);
+                                            MV_ANY, NULL, NULL);
   reader = casereader_create_filter_missing (reader, v_dependent, 1,
-                                            MV_ANY, NULL);
-  n_data = data_pass_one (casereader_clone (reader),
-                         (const struct variable **) all_vars, n_all_vars,
-                         mom);
+                                            MV_ANY, NULL, NULL);
 
-  if ((n_data > 0) && (n_indep > 0))
+  if (n_indep > 0)
     {
-      X =
-       covariance_matrix_create (n_all_vars,
-                                 (const struct variable **) all_vars);
+      for (i = 0; i < n_all_vars; i++)
+       if (var_is_alpha (all_vars[i]))
+         cat_stored_values_create (all_vars[i]);
+      
+      cov = covariance_matrix_init (n_all_vars, all_vars, ONE_PASS, PAIRWISE, MV_ANY);
       reader = casereader_create_counter (reader, &row, -1);
-      for (; casereader_read (reader, &c); case_destroy (&c))
+      for (; (c = casereader_read (reader)) != NULL; case_unref (c))
        {
          /* 
             Accumulate the covariance matrix.
-          */
-         for (i = 0; i < n_all_vars; ++i)
-           {
-             const struct variable *v = all_vars[i];
-             const union value *val_v = case_data (&c, v);
-             for (j = i; j < n_all_vars; j++)
-               {
-                 const struct variable *w = all_vars[j];
-                 const union value *val_w = case_data (&c, w);
-                 covariance_pass_two (X, *mom[i]->mean, *mom[j]->mean,
-                                      (double) n_data,
-                                      v, w, val_v, val_w);
-               }
-           }
+         */
+         covariance_matrix_accumulate (cov, c, NULL, 0);
+         n_data++;
        }
-      casereader_destroy (reader);
-      for (i = 0; i < n_all_vars; i++)
+      covariance_matrix_compute (cov);
+
+      for (i = 0; i < n_dependent; i++)
        {
-         moments1_destroy (mom[i]->m);
-         free (mom[i]->mean);
-         free (mom[i]->variance);
-         free (mom[i]->weight);
-         free (mom[i]);
+         model = fit_model (cov, v_dependent[i], indep_vars, n_data, n_indep);
+         pspp_linreg_cache_free (model);
        }
-      free (mom);
-      covariance_matrix_destroy (X);
+
+      casereader_destroy (reader);
+      covariance_matrix_destroy (cov);
     }
   else
     {
index db40dfc29bd7ebd98501b1728f76d71444b9cd1b..3ad3a5bb1554194cca7160be6b9b87fd4200e7dd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -47,23 +47,23 @@ npar_summary_calc_descriptives (struct descriptives *desc,
       double maximum = -DBL_MAX;
       double var;
       struct moments1 *moments = moments1_create (MOMENT_VARIANCE);
-      struct ccase c;
+      struct ccase *c;
       const struct variable *v = *vv++;
       struct casereader *pass;
 
       pass = casereader_clone (input);
       pass = casereader_create_filter_missing (pass,
                                                &v, 1,
-                                               filter, NULL);
+                                               filter, NULL, NULL);
       pass = casereader_create_filter_weight (pass, dict, NULL, NULL);
-      while (casereader_read(pass, &c))
+      while ((c = casereader_read (pass)) != NULL)
        {
-          double val = case_num (&c, v);
-          double w = dict_get_case_weight (dict, &c, NULL);
+          double val = case_num (c, v);
+          double w = dict_get_case_weight (dict, c, NULL);
           minimum = MIN (minimum, val);
           maximum = MAX (maximum, val);
           moments1_add (moments, val, w);
-         case_destroy (&c);
+         case_unref (c);
        }
       casereader_destroy (pass);
 
index 37939fe9172b496eaeca10210c7c94fb85969c37..6aed01cf2c3509ff44e25d1a6052f9fc85924de7 100644 (file)
@@ -18,6 +18,7 @@
 #define npar_h 1
 
 #include <stddef.h>
+#include <stdbool.h>
 #include <data/missing-values.h>
 
 #include <stddef.h>
@@ -36,8 +37,9 @@ struct npar_test
   void (*execute) (const struct dataset *,
                   struct casereader *,
                    enum mv_class exclude,
-                  const struct npar_test *
-                  );
+                  const struct npar_test *,
+                  bool,
+                  double);
 
   void (*insert_variables) (const struct npar_test *,
                            struct const_hsh_table *);
index 688ce2379bc103eaed0ce6f1b230a951053f0743..4caa112aefe8895555add598c773518078f5415d 100644 (file)
@@ -1,5 +1,5 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+/* PSPP - a program for statistical analysis. -*-c-*-
+   Copyright (C) 2006, 2008 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@
 #include <language/lexer/variable-parser.h>
 #include <language/stats/binomial.h>
 #include <language/stats/chisquare.h>
+#include <language/stats/wilcoxon.h>
 #include <libpspp/hash.h>
 #include <libpspp/pool.h>
 #include <libpspp/taint.h>
@@ -53,7 +54,8 @@
    +friedman=varlist;
    +kendall=varlist;
    missing=miss:!analysis/listwise,
-           incl:include/!exclude;
+   incl:include/!exclude;
+   method=custom;
    +statistics[st_]=descriptives,quartiles,all.
 */
 /* (declarations) */
@@ -70,17 +72,25 @@ struct npar_specs
   size_t n_tests;
 
   const struct variable ** vv; /* Compendium of all variables
-                                      (those mentioned on ANY subcommand */
+                                 (those mentioned on ANY subcommand */
   int n_vars; /* Number of variables in vv */
 
   enum mv_class filter;    /* Missing values to filter. */
 
   bool descriptives;       /* Descriptive statistics should be calculated */
   bool quartiles;          /* Quartiles should be calculated */
+
+  bool exact;  /* Whether exact calculations have been requested */
+  double timer;   /* Maximum time (in minutes) to wait for exact calculations */
 };
 
-void one_sample_insert_variables (const struct npar_test *test,
-                                 struct const_hsh_table *variables);
+static void one_sample_insert_variables (const struct npar_test *test,
+                                        struct const_hsh_table *variables);
+
+static void two_sample_insert_variables (const struct npar_test *test,
+                                        struct const_hsh_table *variables);
+
+
 
 static void
 npar_execute(struct casereader *input,
@@ -98,7 +108,7 @@ npar_execute(struct casereader *input,
          msg (SW, _("NPAR subcommand not currently implemented."));
          continue;
        }
-      test->execute (ds, casereader_clone (input), specs->filter, test);
+      test->execute (ds, casereader_clone (input), specs->filter, test, specs->exact, specs->timer);
     }
 
   if ( specs->descriptives )
@@ -126,7 +136,7 @@ cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
 {
   bool ok;
   int i;
-  struct npar_specs npar_specs = {0, 0, 0, 0, 0, 0, 0, 0};
+  struct npar_specs npar_specs = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   struct const_hsh_table *var_hash;
   struct casegrouper *grouper;
   struct casereader *input, *group;
@@ -134,8 +144,8 @@ cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
   npar_specs.pool = pool_create ();
 
   var_hash = const_hsh_create_pool (npar_specs.pool, 0,
-                             compare_vars_by_name, hash_var_by_name,
-                             NULL, NULL);
+                                   compare_vars_by_name, hash_var_by_name,
+                                   NULL, NULL);
 
   if ( ! parse_npar_tests (lexer, ds, &cmd, &npar_specs) )
     {
@@ -183,10 +193,14 @@ cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
 
   input = proc_open (ds);
   if ( cmd.miss == NPAR_LISTWISE )
-    input = casereader_create_filter_missing (input,
-                                              npar_specs.vv,
-                                              npar_specs.n_vars,
-                                              npar_specs.filter, NULL);
+    {
+      input = casereader_create_filter_missing (input,
+                                               npar_specs.vv,
+                                               npar_specs.n_vars,
+                                               npar_specs.filter,
+                                               NULL, NULL);
+    }
+
 
   grouper = casegrouper_create_splits (input, dataset_dict (ds));
   while (casegrouper_get_next_group (grouper, &group))
@@ -202,7 +216,8 @@ cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
 }
 
 int
-npar_custom_chisquare(struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests *cmd UNUSED, void *aux )
+npar_custom_chisquare (struct lexer *lexer, struct dataset *ds,
+                      struct cmd_npar_tests *cmd UNUSED, void *aux )
 {
   struct npar_specs *specs = aux;
 
@@ -213,8 +228,8 @@ npar_custom_chisquare(struct lexer *lexer, struct dataset *ds, struct cmd_npar_t
   ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
 
   if (!parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
-                            &tp->vars, &tp->n_vars,
-                            PV_NO_SCRATCH | PV_NO_DUPLICATE))
+                                  &tp->vars, &tp->n_vars,
+                                  PV_NO_SCRATCH | PV_NO_DUPLICATE))
     {
       return 2;
     }
@@ -307,7 +322,8 @@ npar_custom_chisquare(struct lexer *lexer, struct dataset *ds, struct cmd_npar_t
 
 
 int
-npar_custom_binomial(struct lexer *lexer, struct dataset *ds, struct cmd_npar_tests *cmd UNUSED, void *aux)
+npar_custom_binomial (struct lexer *lexer, struct dataset *ds,
+                     struct cmd_npar_tests *cmd UNUSED, void *aux)
 {
   struct npar_specs *specs = aux;
   struct binomial_test *btp = pool_alloc(specs->pool, sizeof(*btp));
@@ -333,34 +349,31 @@ npar_custom_binomial(struct lexer *lexer, struct dataset *ds, struct cmd_npar_te
   if ( lex_match (lexer, '=') )
     {
       if (parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
-                               &tp->vars, &tp->n_vars,
-                               PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
+                                     &tp->vars, &tp->n_vars,
+                                     PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
        {
          if ( lex_match (lexer, '('))
            {
              lex_force_num (lexer);
              btp->category1 = lex_number (lexer);
-             lex_get (lexer);
-             if ( ! lex_force_match (lexer, ',')) return 2;
-             if ( ! lex_force_num (lexer) ) return 2;
-             btp->category2 = lex_number (lexer);
-             lex_get (lexer);
+             lex_get (lexer);
+             if ( lex_match (lexer, ','))
+               {
+                 if ( ! lex_force_num (lexer) ) return 2;
+                 btp->category2 = lex_number (lexer);
+                 lex_get (lexer);
+               }
+             else
+               {
+                 btp->cutpoint = btp->category1;
+               }
+
              lex_force_match (lexer, ')');
            }
        }
       else
        return 2;
     }
-  else
-    {
-      if ( lex_match (lexer, '(') )
-       {
-         lex_force_num (lexer);
-         btp->cutpoint = lex_number (lexer);
-         lex_get (lexer);
-         lex_force_match (lexer, ')');
-       }
-    }
 
   specs->n_tests++;
   specs->test = pool_realloc (specs->pool,
@@ -398,18 +411,20 @@ parse_two_sample_related_test (struct lexer *lexer,
   const struct variable **vlist2;
   size_t n_vlist2;
 
+  ((struct npar_test *)test_parameters)->insert_variables = two_sample_insert_variables;
+
   if (!parse_variables_const_pool (lexer, pool,
-                            dict,
-                            &vlist1, &n_vlist1,
-                            PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
+                                  dict,
+                                  &vlist1, &n_vlist1,
+                                  PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
     return false;
 
   if ( lex_match(lexer, T_WITH))
     {
       with = true;
       if ( !parse_variables_const_pool (lexer, pool, dict,
-                                 &vlist2, &n_vlist2,
-                                 PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
+                                       &vlist2, &n_vlist2,
+                                       PV_NUMERIC | PV_NO_SCRATCH | PV_NO_DUPLICATE) )
        return false;
 
       paired = (lex_match (lexer, '(') &&
@@ -449,8 +464,8 @@ parse_two_sample_related_test (struct lexer *lexer,
          assert (n_vlist1 == n_vlist2);
          for ( i = 0 ; i < n_vlist1; ++i )
            {
-             test_parameters->pairs[n][0] = vlist1[i];
-             test_parameters->pairs[n][1] = vlist2[i];
+             test_parameters->pairs[n][1] = vlist1[i];
+             test_parameters->pairs[n][0] = vlist2[i];
              n++;
            }
        }
@@ -461,8 +476,8 @@ parse_two_sample_related_test (struct lexer *lexer,
            {
              for ( j = 0 ; j < n_vlist2; ++j )
                {
-                 test_parameters->pairs[n][0] = vlist1[i];
-                 test_parameters->pairs[n][1] = vlist2[j];
+                 test_parameters->pairs[n][1] = vlist1[i];
+                 test_parameters->pairs[n][0] = vlist2[j];
                  n++;
                }
            }
@@ -476,8 +491,8 @@ parse_two_sample_related_test (struct lexer *lexer,
          for ( j = i + 1 ; j < n_vlist1; ++j )
            {
              assert ( n < test_parameters->n_pairs);
-             test_parameters->pairs[n][0] = vlist1[i];
-             test_parameters->pairs[n][1] = vlist1[j];
+             test_parameters->pairs[n][1] = vlist1[i];
+             test_parameters->pairs[n][0] = vlist1[j];
              n++;
            }
        }
@@ -495,8 +510,8 @@ npar_custom_wilcoxon (struct lexer *lexer,
 {
   struct npar_specs *specs = aux;
 
-  struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
-  ((struct npar_test *)tp)->execute = NULL;
+  struct two_sample_test *tp = pool_alloc (specs->pool, sizeof(*tp));
+  ((struct npar_test *)tp)->execute = wilcoxon_execute;
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd,
                                      tp, specs->pool) )
@@ -559,9 +574,9 @@ npar_custom_sign (struct lexer *lexer, struct dataset *ds,
 }
 
 /* Insert the variables for TEST into VAR_HASH */
-void
+static void
 one_sample_insert_variables (const struct npar_test *test,
-                           struct const_hsh_table *var_hash)
+                            struct const_hsh_table *var_hash)
 {
   int i;
   struct one_sample_test *ost = (struct one_sample_test *) test;
@@ -570,3 +585,50 @@ one_sample_insert_variables (const struct npar_test *test,
     const_hsh_insert (var_hash, ost->vars[i]);
 }
 
+static void
+two_sample_insert_variables (const struct npar_test *test,
+                            struct const_hsh_table *var_hash)
+{
+  int i;
+
+  const struct two_sample_test *tst = (const struct two_sample_test *) test;
+
+  for ( i = 0 ; i < tst->n_pairs ; ++i )
+    {
+      variable_pair *pair = &tst->pairs[i];
+
+      const_hsh_insert (var_hash, (*pair)[0]);
+      const_hsh_insert (var_hash, (*pair)[1]);
+    }
+
+}
+
+
+static int
+npar_custom_method (struct lexer *lexer, struct dataset *ds UNUSED,
+                    struct cmd_npar_tests *test UNUSED, void *aux)
+{
+  struct npar_specs *specs = aux;
+
+  if ( lex_match_id (lexer, "EXACT") )
+    {
+      specs->exact = true;
+      specs->timer = 0.0;
+      if (lex_match_id (lexer, "TIMER"))
+       {
+         specs->timer = 5.0;
+
+         if ( lex_match (lexer, '('))
+           {
+             if ( lex_force_num (lexer) )
+               {
+                 specs->timer = lex_number (lexer);
+                 lex_get (lexer);
+               }
+             lex_force_match (lexer, ')');
+           }
+       }
+    }
+
+  return 1;
+}
index 34f2a216a1cc26571cd72d17c8e388ee4ac564b7..40107f77c877205af42c9555ac1c329f29b39d47 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -56,7 +56,7 @@
    "ONEWAY" (oneway_):
    *^variables=custom;
    missing=miss:!analysis/listwise,
-           incl:include/!exclude;
+   incl:include/!exclude;
    +contrast= double list;
    +statistics[st_]=descriptives,homogeneity.
 */
@@ -77,7 +77,7 @@ static const struct variable **vars;
 
 /* A  hash table containing all the distinct values of the independent
    variables */
-static struct hsh_table *global_group_hash ;
+static struct hsh_table *global_group_hash;
 
 /* The number of distinct values of the independent variable, when all
    missing values are disregarded */
@@ -93,13 +93,13 @@ static void show_anova_table(void);
 static void show_descriptives (const struct dictionary *dict);
 static void show_homogeneity(void);
 
-static void show_contrast_coeffs(short *);
-static void show_contrast_tests(short *);
+static void show_contrast_coeffs (short *);
+static void show_contrast_tests (short *);
 
 
 enum stat_table_t {STAT_DESC = 1, STAT_HOMO = 2};
 
-static enum stat_table_t stat_tables ;
+static enum stat_table_t stat_tables;
 
 static void output_oneway (const struct dictionary *dict);
 
@@ -112,25 +112,26 @@ cmd_oneway (struct lexer *lexer, struct dataset *ds)
   int i;
   bool ok;
 
-  if ( !parse_oneway (lexer, ds, &cmd, NULL) )
+  if ( !parse_oneway (lexer, ds, &cmd, NULL))
     return CMD_FAILURE;
 
   /* What statistics were requested */
-  if ( cmd.sbc_statistics )
+  if ( cmd.sbc_statistics)
     {
 
-      for (i = 0 ; i < ONEWAY_ST_count ; ++i )
+      for (i = 0; i < ONEWAY_ST_count; ++i)
        {
-         if  ( ! cmd.a_statistics[i]  ) continue;
-
-         switch (i) {
-         case ONEWAY_ST_DESCRIPTIVES:
-           stat_tables |= STAT_DESC;
-           break;
-         case ONEWAY_ST_HOMOGENEITY:
-           stat_tables |= STAT_HOMO;
-           break;
-         }
+         if (! cmd.a_statistics[i]) continue;
+
+         switch (i) 
+           {
+           case ONEWAY_ST_DESCRIPTIVES:
+             stat_tables |= STAT_DESC;
+             break;
+           case ONEWAY_ST_HOMOGENEITY:
+             stat_tables |= STAT_HOMO;
+             break;
+           }
        }
     }
 
@@ -152,88 +153,85 @@ static void
 output_oneway (const struct dictionary *dict)
 {
   size_t i;
-  short *bad_contrast ;
+  short *bad_contrast;
 
   bad_contrast = xnmalloc (cmd.sbc_contrast, sizeof *bad_contrast);
 
   /* Check the sanity of the given contrast values */
-  for (i = 0 ; i < cmd.sbc_contrast ; ++i )
+  for (i = 0; i < cmd.sbc_contrast; ++i)
     {
       int j;
       double sum = 0;
 
       bad_contrast[i] = 0;
-      if ( subc_list_double_count(&cmd.dl_contrast[i]) !=
-          ostensible_number_of_groups )
+      if (subc_list_double_count (&cmd.dl_contrast[i]) !=
+         ostensible_number_of_groups)
        {
-         msg(SW,
-             _("Number of contrast coefficients must equal the number of groups"));
+         msg (SW,
+              _("Number of contrast coefficients must equal the number of groups"));
          bad_contrast[i] = 1;
          continue;
        }
 
-      for (j=0; j < ostensible_number_of_groups ; ++j )
-       sum += subc_list_double_at(&cmd.dl_contrast[i],j);
+      for (j = 0; j < ostensible_number_of_groups; ++j)
+       sum += subc_list_double_at (&cmd.dl_contrast[i], j);
 
       if ( sum != 0.0 )
-       msg(SW,_("Coefficients for contrast %zu do not total zero"), i + 1);
+       msg (SW, _("Coefficients for contrast %zu do not total zero"), i + 1);
     }
 
   if ( stat_tables & STAT_DESC )
     show_descriptives (dict);
 
   if ( stat_tables & STAT_HOMO )
-    show_homogeneity();
+    show_homogeneity ();
 
-  show_anova_table();
+  show_anova_table ();
 
   if (cmd.sbc_contrast )
     {
-      show_contrast_coeffs(bad_contrast);
-      show_contrast_tests(bad_contrast);
+      show_contrast_coeffs (bad_contrast);
+      show_contrast_tests (bad_contrast);
     }
 
-
-  free(bad_contrast);
+  free (bad_contrast);
 
   /* Clean up */
-  for (i = 0 ; i < n_vars ; ++i )
+  for (i = 0; i < n_vars; ++i )
     {
       struct hsh_table *group_hash = group_proc_get (vars[i])->group_hash;
 
-      hsh_destroy(group_hash);
+      hsh_destroy (group_hash);
     }
 
-  hsh_destroy(global_group_hash);
-
+  hsh_destroy (global_group_hash);
 }
 
 
-
-
 /* Parser for the variables sub command */
 static int
 oneway_custom_variables (struct lexer *lexer,
-                       struct dataset *ds, struct cmd_oneway *cmd UNUSED,
-                       void *aux UNUSED)
+                        struct dataset *ds, struct cmd_oneway *cmd UNUSED,
+                        void *aux UNUSED)
 {
   struct dictionary *dict = dataset_dict (ds);
 
   lex_match (lexer, '=');
 
-  if ((lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
+  if ((lex_token (lexer) != T_ID ||
+       dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
       && lex_token (lexer) != T_ALL)
     return 2;
 
   if (!parse_variables_const (lexer, dict, &vars, &n_vars,
-                       PV_DUPLICATE
-                       | PV_NUMERIC | PV_NO_SCRATCH) )
+                             PV_DUPLICATE
+                             | PV_NUMERIC | PV_NO_SCRATCH) )
     {
       free (vars);
       return 0;
     }
 
-  assert(n_vars);
+  assert (n_vars);
 
   if ( ! lex_match (lexer, T_BY))
     return 2;
@@ -242,7 +240,7 @@ oneway_custom_variables (struct lexer *lexer,
 
   if ( !indep_var )
     {
-      msg(SE,_("`%s' is not a variable name"),lex_tokid (lexer));
+      msg (SE, _("`%s' is not a variable name"), lex_tokid (lexer));
       return 0;
     }
 
@@ -252,7 +250,7 @@ oneway_custom_variables (struct lexer *lexer,
 
 /* Show the ANOVA table */
 static void
-show_anova_table(void)
+show_anova_table (void)
 {
   size_t i;
   int n_cols =7;
@@ -261,7 +259,7 @@ show_anova_table(void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 2, 0, 1, 0);
   tab_dim (t, tab_natural_dimensions);
 
@@ -283,23 +281,23 @@ show_anova_table(void)
   tab_text (t, 6, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
 
 
-  for ( i=0 ; i < n_vars ; ++i )
+  for (i = 0; i < n_vars; ++i)
     {
       struct group_statistics *totals = &group_proc_get (vars[i])->ugs;
       struct hsh_table *group_hash = group_proc_get (vars[i])->group_hash;
       struct hsh_iterator g;
       struct group_statistics *gs;
-      double ssa=0;
-      const char *s = var_to_string(vars[i]);
+      double ssa = 0;
+      const char *s = var_to_string (vars[i]);
 
-      for (gs =  hsh_first (group_hash,&g);
+      for (gs =  hsh_first (group_hash, &g);
           gs != 0;
-          gs = hsh_next(group_hash,&g))
+          gs = hsh_next (group_hash, &g))
        {
-         ssa += (gs->sum * gs->sum)/gs->n;
+         ssa += pow2 (gs->sum) / gs->n;
        }
 
-      ssa -= ( totals->sum * totals->sum ) / totals->n ;
+      ssa -= pow2 (totals->sum) / totals->n;
 
       tab_text (t, 0, i * 3 + 1, TAB_LEFT | TAT_TITLE, s);
       tab_text (t, 1, i * 3 + 1, TAB_LEFT | TAT_TITLE, _("Between Groups"));
@@ -307,13 +305,13 @@ show_anova_table(void)
       tab_text (t, 1, i * 3 + 3, TAB_LEFT | TAT_TITLE, _("Total"));
 
       if (i > 0)
-       tab_hline(t, TAL_1, 0, n_cols - 1 , i * 3 + 1);
+       tab_hline (t, TAL_1, 0, n_cols - 1, i * 3 + 1);
 
       {
         struct group_proc *gp = group_proc_get (vars[i]);
-       const double sst = totals->ssq - ( totals->sum * totals->sum) / totals->n ;
+       const double sst = totals->ssq - pow2 (totals->sum) / totals->n;
        const double df1 = gp->n_groups - 1;
-       const double df2 = totals->n - gp->n_groups ;
+       const double df2 = totals->n - gp->n_groups;
        const double msa = ssa / df1;
 
        gp->mse  = (sst - ssa) / df2;
@@ -334,7 +332,6 @@ show_anova_table(void)
        tab_double (t, 4, i * 3 + 1, TAB_RIGHT, msa, NULL);
        tab_double (t, 4, i * 3 + 2, TAB_RIGHT, gp->mse, NULL);
 
-
        {
          const double F = msa / gp->mse ;
 
@@ -342,11 +339,9 @@ show_anova_table(void)
          tab_double (t, 5, i * 3 + 1, 0,  F, NULL);
 
          /* The significance */
-         tab_double (t, 6, i * 3 + 1, 0, gsl_cdf_fdist_Q (F, df1,df2), NULL);
+         tab_double (t, 6, i * 3 + 1, 0, gsl_cdf_fdist_Q (F, df1, df2), NULL);
        }
-
       }
-
     }
 
 
@@ -370,12 +365,12 @@ show_descriptives (const struct dictionary *dict)
   const struct variable *wv = dict_get_weight (dict);
   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 
-  int n_rows = 2 ;
+  int n_rows = 2;
 
-  for ( v = 0 ; v < n_vars ; ++v )
+  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, 0);
   tab_headers (t, 2, 0, 2, 0);
   tab_dim (t, tab_natural_dimensions);
 
@@ -388,7 +383,7 @@ show_descriptives (const struct dictionary *dict)
           n_cols - 1, n_rows - 1);
 
   /* Underline headers */
-  tab_hline (t, TAL_2, 0, n_cols - 1, 2 );
+  tab_hline (t, TAL_2, 0, n_cols - 1, 2);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
 
   tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("N"));
@@ -397,9 +392,10 @@ show_descriptives (const struct dictionary *dict)
   tab_text (t, 5, 1, TAB_CENTER | TAT_TITLE, _("Std. Error"));
 
 
-  tab_vline(t, TAL_0, 7, 0, 0);
-  tab_hline(t, TAL_1, 6, 7, 1);
-  tab_joint_text (t, 6, 0, 7, 0, TAB_CENTER | TAT_TITLE | TAT_PRINTF, _("%g%% Confidence Interval for Mean"),confidence*100.0);
+  tab_vline (t, TAL_0, 7, 0, 0);
+  tab_hline (t, TAL_1, 6, 7, 1);
+  tab_joint_text (t, 6, 0, 7, 0, TAB_CENTER | TAT_TITLE | TAT_PRINTF,
+                 _("%g%% Confidence Interval for Mean"), confidence*100.0);
 
   tab_text (t, 6, 1, TAB_CENTER | TAT_TITLE, _("Lower Bound"));
   tab_text (t, 7, 1, TAB_CENTER | TAT_TITLE, _("Upper Bound"));
@@ -412,7 +408,7 @@ show_descriptives (const struct dictionary *dict)
 
 
   row = 2;
-  for ( v=0 ; v < n_vars ; ++v )
+  for (v = 0; v < n_vars; ++v)
     {
       double T;
       double std_error;
@@ -426,12 +422,12 @@ show_descriptives (const struct dictionary *dict)
       const struct fmt_spec *fmt = var_get_print_format (vars[v]);
 
       struct group_statistics *const *gs_array =
-       (struct group_statistics *const *) hsh_sort(gp->group_hash);
+       (struct group_statistics *const *) hsh_sort (gp->group_hash);
       int count = 0;
 
       tab_text (t, 0, row, TAB_LEFT | TAT_TITLE, s);
       if ( v > 0)
-       tab_hline(t, TAL_1, 0, n_cols - 1 , row);
+       tab_hline (t, TAL_1, 0, n_cols - 1, row);
 
       for (count = 0; count < hsh_count (gp->group_hash); ++count)
        {
@@ -476,7 +472,7 @@ show_descriptives (const struct dictionary *dict)
        }
 
       tab_text (t, 1, row + count,
-               TAB_LEFT | TAT_TITLE ,_("Total"));
+               TAB_LEFT | TAT_TITLE_("Total"));
 
       tab_double (t, 2, row + count, 0, totals->n, wfmt);
 
@@ -506,13 +502,12 @@ show_descriptives (const struct dictionary *dict)
       row += gp->n_groups + 1;
     }
 
-
   tab_submit (t);
 }
 
 /* Show the homogeneity table */
 static void
-show_homogeneity(void)
+show_homogeneity (void)
 {
   size_t v;
   int n_cols = 5;
@@ -521,7 +516,7 @@ show_homogeneity(void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 1, 0, 1, 0);
   tab_dim (t, tab_natural_dimensions);
 
@@ -533,28 +528,27 @@ show_homogeneity(void)
           n_cols - 1, n_rows - 1);
 
 
-  tab_hline(t, TAL_2, 0, n_cols - 1, 1);
-  tab_vline(t, TAL_2, 1, 0, n_rows - 1);
-
+  tab_hline (t, TAL_2, 0, n_cols - 1, 1);
+  tab_vline (t, TAL_2, 1, 0, n_rows - 1);
 
-  tab_text (t,  1, 0, TAB_CENTER | TAT_TITLE, _("Levene Statistic"));
-  tab_text (t,  2, 0, TAB_CENTER | TAT_TITLE, _("df1"));
-  tab_text (t,  3, 0, TAB_CENTER | TAT_TITLE, _("df2"));
-  tab_text (t,  4, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
 
+  tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Levene Statistic"));
+  tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("df1"));
+  tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df2"));
+  tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
 
   tab_title (t, _("Test of Homogeneity of Variances"));
 
-  for ( v=0 ; v < n_vars ; ++v )
+  for (v = 0; v < n_vars; ++v)
     {
       double F;
       const struct variable *var = vars[v];
       const struct group_proc *gp = group_proc_get (vars[v]);
-      const char *s = var_to_string(var);
+      const char *s = var_to_string (var);
       const struct group_statistics *totals = &gp->ugs;
 
       const double df1 = gp->n_groups - 1;
-      const double df2 = totals->n - gp->n_groups ;
+      const double df2 = totals->n - gp->n_groups;
 
       tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s);
 
@@ -578,12 +572,12 @@ show_contrast_coeffs (short *bad_contrast)
   int n_cols = 2 + ostensible_number_of_groups;
   int n_rows = 2 + cmd.sbc_contrast;
   union value *group_value;
-  int count = 0 ;
-  void *const *group_values ;
+  int count = 0;
+  void *const *group_values;
 
   struct tab_table *t;
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 2, 0, 2, 0);
   tab_dim (t, tab_natural_dimensions);
 
@@ -595,21 +589,21 @@ show_contrast_coeffs (short *bad_contrast)
           n_cols - 1, n_rows - 1);
 
   tab_box (t,
-          -1,-1,
+          -1, -1,
           TAL_0, TAL_0,
           2, 0,
           n_cols - 1, 0);
 
   tab_box (t,
-          -1,-1,
+          -1, -1,
           TAL_0, TAL_0,
-          0,0,
-          1,1);
+          0, 0,
+          1, 1);
 
-  tab_hline(t, TAL_1, 2, n_cols - 1, 1);
-  tab_hline(t, TAL_2, 0, n_cols - 1, 2);
+  tab_hline (t, TAL_1, 2, n_cols - 1, 1);
+  tab_hline (t, TAL_2, 0, n_cols - 1, 2);
 
-  tab_vline(t, TAL_2, 2, 0, n_rows - 1);
+  tab_vline (t, TAL_2, 2, 0, n_rows - 1);
 
   tab_title (t, _("Contrast Coefficients"));
 
@@ -617,11 +611,11 @@ show_contrast_coeffs (short *bad_contrast)
 
 
   tab_joint_text (t, 2, 0, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
-                 var_to_string(indep_var));
+                 var_to_string (indep_var));
 
-  group_values = hsh_sort(global_group_hash);
-  for (count = 0 ;
-       count < hsh_count(global_group_hash) ;
+  group_values = hsh_sort (global_group_hash);
+  for (count = 0;
+       count < hsh_count (global_group_hash);
        ++count)
     {
       int i;
@@ -638,16 +632,16 @@ show_contrast_coeffs (short *bad_contrast)
       ds_destroy (&vstr);
 
 
-      for (i = 0 ; i < cmd.sbc_contrast ; ++i )
+      for (i = 0; i < cmd.sbc_contrast; ++i )
        {
-         tab_text(t, 1, i + 2, TAB_CENTER | TAT_PRINTF, "%d", i + 1);
+         tab_text (t, 1, i + 2, TAB_CENTER | TAT_PRINTF, "%d", i + 1);
 
          if ( bad_contrast[i] )
-           tab_text(t, count + 2, i + 2, TAB_RIGHT, "?" );
+           tab_text (t, count + 2, i + 2, TAB_RIGHT, "?" );
          else
-           tab_text(t, count + 2, i + 2, TAB_RIGHT | TAT_PRINTF, "%g",
-                    subc_list_double_at(&cmd.dl_contrast[i], count)
-                    );
+           tab_text (t, count + 2, i + 2, TAB_RIGHT | TAT_PRINTF, "%g",
+                     subc_list_double_at (&cmd.dl_contrast[i], count)
+                     );
        }
     }
 
@@ -657,7 +651,7 @@ show_contrast_coeffs (short *bad_contrast)
 
 /* Show the results of the contrast tests */
 static void
-show_contrast_tests(short *bad_contrast)
+show_contrast_tests (short *bad_contrast)
 {
   size_t v;
   int n_cols = 8;
@@ -665,7 +659,7 @@ show_contrast_tests(short *bad_contrast)
 
   struct tab_table *t;
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 3, 0, 1, 0);
   tab_dim (t, tab_natural_dimensions);
 
@@ -677,34 +671,34 @@ show_contrast_tests(short *bad_contrast)
           n_cols - 1, n_rows - 1);
 
   tab_box (t,
-          -1,-1,
+          -1, -1,
           TAL_0, TAL_0,
           0, 0,
           2, 0);
 
-  tab_hline(t, TAL_2, 0, n_cols - 1, 1);
-  tab_vline(t, TAL_2, 3, 0, n_rows - 1);
+  tab_hline (t, TAL_2, 0, n_cols - 1, 1);
+  tab_vline (t, TAL_2, 3, 0, n_rows - 1);
 
 
   tab_title (t, _("Contrast Tests"));
 
-  tab_text (t,  2, 0, TAB_CENTER | TAT_TITLE, _("Contrast"));
-  tab_text (t,  3, 0, TAB_CENTER | TAT_TITLE, _("Value of Contrast"));
+  tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Contrast"));
+  tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Value of Contrast"));
   tab_text (t,  4, 0, TAB_CENTER | TAT_TITLE, _("Std. Error"));
   tab_text (t,  5, 0, TAB_CENTER | TAT_TITLE, _("t"));
   tab_text (t,  6, 0, TAB_CENTER | TAT_TITLE, _("df"));
   tab_text (t,  7, 0, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
 
-  for ( v = 0 ; v < n_vars ; ++v )
+  for (v = 0; v < n_vars; ++v)
     {
       int i;
       int lines_per_variable = 2 * cmd.sbc_contrast;
 
 
       tab_text (t,  0, (v * lines_per_variable) + 1, TAB_LEFT | TAT_TITLE,
-               var_to_string(vars[v]));
+               var_to_string (vars[v]));
 
-      for ( i = 0 ; i < cmd.sbc_contrast ; ++i )
+      for (i = 0; i < cmd.sbc_contrast; ++i)
        {
          int ci;
          double contrast_value = 0.0;
@@ -715,18 +709,18 @@ show_contrast_tests(short *bad_contrast)
          void *const *group_stat_array;
 
          double T;
-         double std_error_contrast ;
+         double std_error_contrast;
          double df;
-         double sec_vneq=0.0;
+         double sec_vneq = 0.0;
 
 
          /* Note: The calculation of the degrees of freedom in the
             "variances not equal" case is painfull!!
             The following formula may help to understand it:
-            \frac{\left(\sum_{i=1}^k{c_i^2\frac{s_i^2}{n_i}}\right)^2}
+            \frac{\left (\sum_{i=1}^k{c_i^2\frac{s_i^2}{n_i}}\right)^2}
             {
-            \sum_{i=1}^k\left(
-            \frac{\left(c_i^2\frac{s_i^2}{n_i}\right)^2}  {n_i-1}
+            \sum_{i=1}^k\left (
+            \frac{\left (c_i^2\frac{s_i^2}{n_i}\right)^2}  {n_i-1}
             \right)
             }
          */
@@ -745,35 +739,35 @@ show_contrast_tests(short *bad_contrast)
            }
 
          tab_text (t,  2, (v * lines_per_variable) + i + 1,
-                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1);
+                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d", i + 1);
 
 
          tab_text (t,  2, (v * lines_per_variable) + i + 1 + cmd.sbc_contrast,
-                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1);
+                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d", i + 1);
 
 
          if ( bad_contrast[i])
            continue;
 
-         group_stat_array = hsh_sort(group_hash);
+         group_stat_array = hsh_sort (group_hash);
 
-         for (ci = 0 ; ci < hsh_count(group_hash) ;  ++ci)
+         for (ci = 0; ci < hsh_count (group_hash);  ++ci)
            {
-             const double coef = subc_list_double_at(&cmd.dl_contrast[i], ci);
+             const double coef = subc_list_double_at (&cmd.dl_contrast[i], ci);
              struct group_statistics *gs = group_stat_array[ci];
 
-             const double winv = (gs->std_dev * gs->std_dev) / gs->n;
+             const double winv = pow2 (gs->std_dev) / gs->n;
 
              contrast_value += coef * gs->mean;
 
-             coef_msq += (coef * coef) / gs->n ;
+             coef_msq += (coef * coef) / gs->n;
 
-             sec_vneq += (coef * coef) * (gs->std_dev * gs->std_dev ) /gs->n ;
+             sec_vneq += (coef * coef) * pow2 (gs->std_dev) /gs->n;
 
              df_numerator += (coef * coef) * winv;
              df_denominator += pow2((coef * coef) * winv) / (gs->n - 1);
            }
-         sec_vneq = sqrt(sec_vneq);
+         sec_vneq = sqrt (sec_vneq);
 
          df_numerator = pow2 (df_numerator);
 
@@ -791,7 +785,7 @@ show_contrast_tests(short *bad_contrast)
                     TAB_RIGHT, std_error_contrast,
                     NULL);
 
-         T = fabs(contrast_value / std_error_contrast) ;
+         T = fabs (contrast_value / std_error_contrast);
 
          /* T Statistic */
 
@@ -812,7 +806,6 @@ show_contrast_tests(short *bad_contrast)
                     TAB_RIGHT,  2 * gsl_cdf_tdist_Q (T, df),
                     NULL);
 
-
          /* Now for the Variances NOT Equal case */
 
          /* Std. Error */
@@ -821,14 +814,12 @@ show_contrast_tests(short *bad_contrast)
                     TAB_RIGHT, sec_vneq,
                     NULL);
 
-
          T = contrast_value / sec_vneq;
          tab_double (t,  5,
                     (v * lines_per_variable) + i + 1 + cmd.sbc_contrast,
                     TAB_RIGHT, T,
                     NULL);
 
-
          df = df_numerator / df_denominator;
 
          tab_double (t,  6,
@@ -841,12 +832,10 @@ show_contrast_tests(short *bad_contrast)
          tab_double (t, 7, (v * lines_per_variable) + i + 1 + cmd.sbc_contrast,
                     TAB_RIGHT,  2 * gsl_cdf_tdist_Q (T,df),
                     NULL);
-
-
        }
 
       if ( v > 0 )
-       tab_hline(t, TAL_1, 0, n_cols - 1, (v * lines_per_variable) + 1);
+       tab_hline (t, TAL_1, 0, n_cols - 1, (v * lines_per_variable) + 1);
     }
 
   tab_submit (t);
@@ -855,19 +844,19 @@ show_contrast_tests(short *bad_contrast)
 
 /* ONEWAY ANOVA Calculations */
 
-static void  postcalc (  struct cmd_oneway *cmd UNUSED );
+static void  postcalc (struct cmd_oneway *cmd UNUSED);
 
-static void  precalc ( struct cmd_oneway *cmd UNUSED );
+static void  precalc (struct cmd_oneway *cmd UNUSED);
 
 
 
 /* Pre calculations */
 static void
-precalc ( struct cmd_oneway *cmd UNUSED )
+precalc (struct cmd_oneway *cmd UNUSED)
 {
-  size_t i=0;
+  size_t i = 0;
 
-  for(i=0; i< n_vars ; ++i)
+  for (i = 0; i < n_vars; ++i)
     {
       struct group_proc *gp = group_proc_get (vars[i]);
       struct group_statistics *totals = &gp->ugs;
@@ -876,19 +865,15 @@ precalc ( struct cmd_oneway *cmd UNUSED )
         The hash contains a group_statistics structure,
         and is keyed by value of the independent variable */
 
-      gp->group_hash =
-       hsh_create(4,
-                  (hsh_compare_func *) compare_group,
-                  (hsh_hash_func *) hash_group,
-                  (hsh_free_func *) free_group,
-                  (void *) var_get_width (indep_var) );
+      gp->group_hash = hsh_create (4, compare_group, hash_group,
+                                  (hsh_free_func *) free_group,
+                                  indep_var);
 
-
-      totals->sum=0;
-      totals->n=0;
-      totals->ssq=0;
-      totals->sum_diff=0;
-      totals->maximum = - DBL_MAX;
+      totals->sum = 0;
+      totals->n = 0;
+      totals->ssq = 0;
+      totals->sum_diff = 0;
+      totals->maximum = -DBL_MAX;
       totals->minimum = DBL_MAX;
     }
 }
@@ -909,80 +894,81 @@ run_oneway (struct cmd_oneway *cmd,
   struct dictionary *dict = dataset_dict (ds);
   enum mv_class exclude;
   struct casereader *reader;
-  struct ccase c;
+  struct ccase *c;
 
-  if (!casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c == NULL)
     {
       casereader_destroy (input);
       return;
     }
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
+  output_split_file_values (ds, c);
+  case_unref (c);
 
   taint = taint_clone (casereader_get_taint (input));
 
-  global_group_hash = hsh_create(4,
-                                (hsh_compare_func *) compare_values,
-                                (hsh_hash_func *) hash_value,
-                                free_value,
-                                (void *) var_get_width (indep_var) );
+  global_group_hash = hsh_create (4,
+                                 compare_values_short,
+                                 hash_value_short,
+                                 free_value,
+                                 indep_var);
 
-  precalc(cmd);
+  precalc (cmd);
 
   exclude = cmd->incl != ONEWAY_INCLUDE ? MV_ANY : MV_SYSTEM;
   input = casereader_create_filter_missing (input, &indep_var, 1,
-                                            exclude, NULL);
+                                            exclude, NULL, NULL);
   if (cmd->miss == ONEWAY_LISTWISE)
     input = casereader_create_filter_missing (input, vars, n_vars,
-                                              exclude, NULL);
+                                              exclude, NULL, NULL);
   input = casereader_create_filter_weight (input, dict, NULL, NULL);
 
   reader = casereader_clone (input);
-  for (; casereader_read (reader, &c); case_destroy (&c))
+  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
     {
       size_t i;
 
-      const double weight = dict_get_case_weight (dict, &c, NULL);
+      const double weight = dict_get_case_weight (dict, c, NULL);
 
-      const union value *indep_val = case_data (&c, indep_var);
+      const union value *indep_val = case_data (c, indep_var);
       void **p = hsh_probe (global_group_hash, indep_val);
       if (*p == NULL)
         *p = value_dup (indep_val, var_get_width (indep_var));
 
-      for ( i = 0 ; i < n_vars ; ++i )
+      for (i = 0; i < n_vars; ++i)
        {
          const struct variable *v = vars[i];
 
-         const union value *val = case_data (&c, v);
+         const union value *val = case_data (c, v);
 
           struct group_proc *gp = group_proc_get (vars[i]);
          struct hsh_table *group_hash = gp->group_hash;
 
          struct group_statistics *gs;
 
-         gs = hsh_find(group_hash, (void *) indep_val );
+         gs = hsh_find (group_hash, indep_val );
 
          if ( ! gs )
            {
              gs = xmalloc (sizeof *gs);
              gs->id = *indep_val;
-             gs->sum=0;
-             gs->n=0;
-             gs->ssq=0;
-             gs->sum_diff=0;
+             gs->sum = 0;
+             gs->n = 0;
+             gs->ssq = 0;
+             gs->sum_diff = 0;
              gs->minimum = DBL_MAX;
              gs->maximum = -DBL_MAX;
 
-             hsh_insert ( group_hash, (void *) gs );
+             hsh_insert ( group_hash, gs );
            }
 
          if (!var_is_value_missing (v, val, exclude))
            {
              struct group_statistics *totals = &gp->ugs;
 
-             totals->n+=weight;
-             totals->sum+=weight * val->f;
-             totals->ssq+=weight * val->f * val->f;
+             totals->n += weight;
+             totals->sum += weight * val->f;
+             totals->ssq += weight * pow2 (val->f);
 
              if ( val->f * weight  < totals->minimum )
                totals->minimum = val->f * weight;
@@ -990,9 +976,9 @@ run_oneway (struct cmd_oneway *cmd,
              if ( val->f * weight  > totals->maximum )
                totals->maximum = val->f * weight;
 
-             gs->n+=weight;
-             gs->sum+=weight * val->f;
-             gs->ssq+=weight * val->f * val->f;
+             gs->n += weight;
+             gs->sum += weight * val->f;
+             gs->ssq += weight * pow2 (val->f);
 
              if ( val->f * weight  < gs->minimum )
                gs->minimum = val->f * weight;
@@ -1007,7 +993,7 @@ run_oneway (struct cmd_oneway *cmd,
     }
   casereader_destroy (reader);
 
-  postcalc(cmd);
+  postcalc (cmd);
 
 
   if ( stat_tables & STAT_HOMO )
@@ -1028,10 +1014,9 @@ run_oneway (struct cmd_oneway *cmd,
 void
 postcalc (  struct cmd_oneway *cmd UNUSED )
 {
-  size_t i=0;
-
+  size_t i = 0;
 
-  for(i = 0; i < n_vars ; ++i)
+  for (i = 0; i < n_vars; ++i)
     {
       struct group_proc *gp = group_proc_get (vars[i]);
       struct hsh_table *group_hash = gp->group_hash;
@@ -1040,35 +1025,29 @@ postcalc (  struct cmd_oneway *cmd UNUSED )
       struct hsh_iterator g;
       struct group_statistics *gs;
 
-      for (gs =  hsh_first (group_hash,&g);
+      for (gs =  hsh_first (group_hash, &g);
           gs != 0;
-          gs = hsh_next(group_hash,&g))
+          gs = hsh_next (group_hash, &g))
        {
-         gs->mean=gs->sum / gs->n;
-         gs->s_std_dev= sqrt(
-                             ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
-                             ) ;
-
-         gs->std_dev= sqrt(
-                           gs->n/(gs->n-1) *
-                           ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
-                           ) ;
+         gs->mean = gs->sum / gs->n;
+         gs->s_std_dev = sqrt (gs->ssq / gs->n - pow2 (gs->mean));
 
-         gs->se_mean = gs->std_dev / sqrt(gs->n);
-         gs->mean_diff= gs->sum_diff / gs->n;
+         gs->std_dev = sqrt (
+                             gs->n / (gs->n - 1) *
+                             ( gs->ssq / gs->n - pow2 (gs->mean))
+                             );
 
+         gs->se_mean = gs->std_dev / sqrt (gs->n);
+         gs->mean_diff = gs->sum_diff / gs->n;
        }
 
-
-
       totals->mean = totals->sum / totals->n;
-      totals->std_dev= sqrt(
-                           totals->n/(totals->n-1) *
-                           ( (totals->ssq / totals->n ) - totals->mean * totals->mean )
-                           ) ;
-
-      totals->se_mean = totals->std_dev / sqrt(totals->n);
+      totals->std_dev = sqrt (
+                             totals->n / (totals->n - 1) *
+                             (totals->ssq / totals->n - pow2 (totals->mean))
+                             );
 
+      totals->se_mean = totals->std_dev / sqrt (totals->n);
     }
 }
 
index cb63949076bb4b03d50d009b0805762a3d250dae..3bbd39d1e0b0ea26ff7c1b78d67a2db35b5d8f4f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2005, 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
@@ -19,7 +19,6 @@
 #include <limits.h>
 #include <math.h>
 
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casegrouper.h>
 #include <data/casereader.h>
@@ -29,6 +28,7 @@
 #include <data/missing-values.h>
 #include <data/procedure.h>
 #include <data/short-names.h>
+#include <data/subcase.h>
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/stats/sort-criteria.h>
@@ -152,7 +152,7 @@ static enum mv_class exclude_values;
 static struct rank_spec *rank_specs;
 static size_t n_rank_specs;
 
-static struct case_ordering *sc;
+static struct subcase sc;
 
 static const struct variable **group_vars;
 static size_t n_group_vars;
@@ -233,14 +233,14 @@ create_var_label (struct variable *dest_var,
 
 
 static bool
-rank_cmd (struct dataset *ds, const struct case_ordering *sc,
+rank_cmd (struct dataset *ds, const struct subcase *sc,
          const struct rank_spec *rank_specs, int n_rank_specs)
 {
   struct dictionary *d = dataset_dict (ds);
   bool ok = true;
   int i;
 
-  for (i = 0 ; i < case_ordering_get_var_cnt (sc) ; ++i )
+  for (i = 0 ; i < subcase_get_n_fields (sc) ; ++i )
     {
       /* Rank variable at index I in SC. */
       struct casegrouper *split_grouper;
@@ -253,21 +253,18 @@ rank_cmd (struct dataset *ds, const struct case_ordering *sc,
 
       while (casegrouper_get_next_group (split_grouper, &split_group))
         {
-          struct case_ordering *ordering;
+          struct subcase ordering;
           struct casereader *ordered;
           struct casegrouper *by_grouper;
           struct casereader *by_group;
-          int j;
 
           /* Sort this split group by the BY variables as primary
              keys and the rank variable as secondary key. */
-          ordering = case_ordering_create ();
-          for (j = 0; j < n_group_vars; j++)
-            case_ordering_add_var (ordering, group_vars[j], SRT_ASCEND);
-          case_ordering_add_var (ordering,
-                                 case_ordering_get_var (sc, i),
-                                 case_ordering_get_direction (sc, i));
-          ordered = sort_execute (split_group, ordering);
+          subcase_init_vars (&ordering, group_vars, n_group_vars);
+          subcase_add_var (&ordering, src_vars[i],
+                           subcase_get_direction (sc, i));
+          ordered = sort_execute (split_group, &ordering);
+          subcase_destroy (&ordering);
 
           /* Rank the rank variable within this split group. */
           by_grouper = casegrouper_create_vars (ordered,
@@ -479,21 +476,21 @@ rank_sorted_file (struct casereader *input,
 {
   struct casereader *pass1, *pass2, *pass2_1;
   struct casegrouper *tie_grouper;
-  struct ccase c;
+  struct ccase *c;
   double w = 0.0;
   double cc = 0.0;
   int tie_group = 1;
 
 
   input = casereader_create_filter_missing (input, &rank_var, 1,
-                                            exclude_values, output);
+                                            exclude_values, NULL, output);
   input = casereader_create_filter_weight (input, dict, NULL, output);
 
   casereader_split (input, &pass1, &pass2);
 
   /* Pass 1: Get total group weight. */
-  for (; casereader_read (pass1, &c); case_destroy (&c))
-    w += dict_get_case_weight (dict, &c, NULL);
+  for (; (c = casereader_read (pass1)) != NULL; case_unref (c))
+    w += dict_get_case_weight (dict, c, NULL);
   casereader_destroy (pass1);
 
   /* Pass 2: Do ranking. */
@@ -510,21 +507,22 @@ rank_sorted_file (struct casereader *input,
                        casewriter_get_taint (output));
 
       /* Pass 2.1: Sum up weight for tied cases. */
-      for (; casereader_read (pass2_1, &c); case_destroy (&c))
-        tw += dict_get_case_weight (dict, &c, NULL);
+      for (; (c = casereader_read (pass2_1)) != NULL; case_unref (c))
+        tw += dict_get_case_weight (dict, c, NULL);
       cc += tw;
       casereader_destroy (pass2_1);
 
       /* Pass 2.2: Rank tied cases. */
-      while (casereader_read (pass2_2, &c))
+      while ((c = casereader_read (pass2_2)) != NULL)
         {
+          c = case_unshare (c);
           for (i = 0; i < n_rank_specs; ++i)
             {
               const struct variable *dst_var = rs[i].destvars[dest_idx];
-              double *dst_value = &case_data_rw (&c, dst_var)->f;
+              double *dst_value = &case_data_rw (c, dst_var)->f;
               *dst_value = rank_func[rs[i].rfunc] (tw, cc, cc_1, tie_group, w);
             }
-          casewriter_write (output, &c);
+          casewriter_write (output, c);
         }
       casereader_destroy (pass2_2);
 
@@ -535,11 +533,12 @@ rank_sorted_file (struct casereader *input,
 
 /* Transformation function to enumerate all the cases */
 static int
-create_resort_key (void *key_var_, struct ccase *cc, casenumber case_num)
+create_resort_key (void *key_var_, struct ccase **cc, casenumber case_num)
 {
   struct variable *key_var = key_var_;
 
-  case_data_rw(cc, key_var)->f = case_num;
+  *cc = case_unshare (*cc);
+  case_data_rw (*cc, key_var)->f = case_num;
 
   return TRNS_CONTINUE;
 }
@@ -625,8 +624,7 @@ rank_cleanup(void)
   rank_specs = NULL;
   n_rank_specs = 0;
 
-  case_ordering_destroy (sc);
-  sc = NULL;
+  subcase_destroy (&sc);
 
   free (src_vars);
   src_vars = NULL;
@@ -641,6 +639,7 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
   size_t i;
   n_rank_specs = 0;
 
+  subcase_init_empty (&sc);
   if ( !parse_rank (lexer, ds, &cmd, NULL) )
     {
       rank_cleanup ();
@@ -660,12 +659,12 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
       rank_specs = xmalloc (sizeof (*rank_specs));
       rank_specs[0].rfunc = RANK;
       rank_specs[0].destvars =
-       xcalloc (case_ordering_get_var_cnt (sc), sizeof (struct variable *));
+       xcalloc (subcase_get_n_fields (&sc), sizeof (struct variable *));
 
       n_rank_specs = 1;
     }
 
-  assert ( case_ordering_get_var_cnt (sc) == n_src_vars);
+  assert ( subcase_get_n_fields (&sc) == n_src_vars);
 
   /* Create variables for all rank destinations which haven't
      already been created with INTO.
@@ -773,17 +772,17 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
   add_transformation (ds, create_resort_key, 0, order);
 
   /* Do the ranking */
-  result = rank_cmd (ds, sc, rank_specs, n_rank_specs);
+  result = rank_cmd (ds, &sc, rank_specs, n_rank_specs);
 
   /* Put the active file back in its original order.  Delete
      our sort key, which we don't need anymore.  */
   {
-    struct case_ordering *ordering = case_ordering_create ();
     struct casereader *sorted;
-    case_ordering_add_var (ordering, order, SRT_ASCEND);
+
     /* FIXME: loses error conditions. */
+
     proc_discard_output (ds);
-    sorted = sort_execute (proc_open (ds), ordering);
+    sorted = sort_execute_1var (proc_open (ds), order);
     result = proc_commit (ds) && result;
 
     dict_delete_var (dataset_dict (ds), order);
@@ -808,10 +807,9 @@ rank_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_rank
       && lex_token (lexer) != T_ALL)
       return 2;
 
-  sc = parse_case_ordering (lexer, dataset_dict (ds), NULL);
-  if (sc == NULL)
+  if (!parse_sort_criteria (lexer, dataset_dict (ds), &sc, &src_vars, NULL))
     return 0;
-  case_ordering_get_vars (sc, &src_vars, &n_src_vars);
+  n_src_vars = subcase_get_n_fields (&sc);
 
   if ( lex_match (lexer, T_BY)  )
     {
@@ -845,8 +843,7 @@ parse_rank_function (struct lexer *lexer, struct dictionary *dict, struct cmd_ra
   rank_specs[n_rank_specs - 1].destvars = NULL;
 
   rank_specs[n_rank_specs - 1].destvars =
-           xcalloc (case_ordering_get_var_cnt (sc),
-                     sizeof (struct variable *));
+           xcalloc (subcase_get_n_fields (&sc), sizeof (struct variable *));
 
   if (lex_match_id (lexer, "INTO"))
     {
@@ -860,7 +857,7 @@ parse_rank_function (struct lexer *lexer, struct dictionary *dict, struct cmd_ra
              msg(SE, _("Variable %s already exists."), lex_tokid (lexer));
              return 0;
            }
-         if ( var_count >= case_ordering_get_var_cnt (sc) )
+         if ( var_count >= subcase_get_n_fields (&sc) )
            {
              msg(SE, _("Too many variables in INTO clause."));
              return 0;
index a067b3afcfa7ec609caba7a60b4de44f542184b6..13cc4f69f7224450b2c384e05f78618c5f3dd573 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2005 Free Software Foundation, Inc.
+   Copyright (C) 2005, 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
@@ -542,7 +542,7 @@ regression_trns_free (void *t_)
   Gets the predicted values.
  */
 static int
-regression_trns_pred_proc (void *t_, struct ccase *c,
+regression_trns_pred_proc (void *t_, struct ccase **c,
                           casenumber case_idx UNUSED)
 {
   size_t i;
@@ -563,12 +563,12 @@ regression_trns_pred_proc (void *t_, struct ccase *c,
   n_vals = (*model->get_vars) (model, vars);
 
   vals = xnmalloc (n_vals, sizeof (*vals));
-  output = case_data_rw (c, model->pred);
-  assert (output != NULL);
+  *c = case_unshare (*c);
+  output = case_data_rw (*c, model->pred);
 
   for (i = 0; i < n_vals; i++)
     {
-      vals[i] = case_data (c, vars[i]);
+      vals[i] = case_data (*c, vars[i]);
     }
   output->f = (*model->predict) ((const struct variable **) vars,
                                 vals, model, n_vals);
@@ -581,7 +581,7 @@ regression_trns_pred_proc (void *t_, struct ccase *c,
   Gets the residuals.
  */
 static int
-regression_trns_resid_proc (void *t_, struct ccase *c,
+regression_trns_resid_proc (void *t_, struct ccase **c,
                            casenumber case_idx UNUSED)
 {
   size_t i;
@@ -603,14 +603,15 @@ regression_trns_resid_proc (void *t_, struct ccase *c,
   n_vals = (*model->get_vars) (model, vars);
 
   vals = xnmalloc (n_vals, sizeof (*vals));
-  output = case_data_rw (c, model->resid);
+  *c = case_unshare (*c);
+  output = case_data_rw (*c, model->resid);
   assert (output != NULL);
 
   for (i = 0; i < n_vals; i++)
     {
-      vals[i] = case_data (c, vars[i]);
+      vals[i] = case_data (*c, vars[i]);
     }
-  obs = case_data (c, model->depvar);
+  obs = case_data (*c, model->depvar);
   output->f = (*model->residual) ((const struct variable **) vars,
                                  vals, obs, model, n_vals);
   free (vals);
@@ -688,17 +689,21 @@ subcommand_save (struct dataset *ds, int save, pspp_linreg_cache ** models)
 
       for (lc = models; lc < models + cmd.n_dependent; lc++)
        {
-         assert (*lc != NULL);
-         assert ((*lc)->depvar != NULL);
-         if (cmd.a_save[REGRESSION_SV_RESID])
-           {
-             reg_save_var (ds, "RES", regression_trns_resid_proc, *lc,
-                           &(*lc)->resid, n_trns);
-           }
-         if (cmd.a_save[REGRESSION_SV_PRED])
+         if (*lc != NULL)
            {
-             reg_save_var (ds, "PRED", regression_trns_pred_proc, *lc,
-                           &(*lc)->pred, n_trns);
+             if ((*lc)->depvar != NULL)
+               {
+                 if (cmd.a_save[REGRESSION_SV_RESID])
+                   {
+                     reg_save_var (ds, "RES", regression_trns_resid_proc, *lc,
+                                   &(*lc)->resid, n_trns);
+                   }
+                 if (cmd.a_save[REGRESSION_SV_PRED])
+                   {
+                     reg_save_var (ds, "PRED", regression_trns_pred_proc, *lc,
+                                   &(*lc)->pred, n_trns);
+                   }
+               }
            }
        }
     }
@@ -821,7 +826,7 @@ prepare_categories (struct casereader *input,
                    struct moments_var *mom)
 {
   int n_data;
-  struct ccase c;
+  struct ccase *c;
   size_t i;
 
   assert (vars != NULL);
@@ -832,7 +837,7 @@ prepare_categories (struct casereader *input,
       cat_stored_values_create (vars[i]);
 
   n_data = 0;
-  for (; casereader_read (input, &c); case_destroy (&c))
+  for (; (c = casereader_read (input)) != NULL; case_unref (c))
     {
       /*
          The second condition ensures the program will run even if
@@ -841,7 +846,7 @@ prepare_categories (struct casereader *input,
        */
       for (i = 0; i < n_vars; i++)
        {
-         const union value *val = case_data (&c, vars[i]);
+         const union value *val = case_data (c, vars[i]);
          if (var_is_alpha (vars[i]))
            cat_value_update (vars[i], val);
          else
@@ -861,39 +866,6 @@ coeff_init (pspp_linreg_cache * c, struct design_matrix *dm)
   pspp_coeff_init (c->coeff, dm);
 }
 
-/*
-  Put the moments in the linreg cache.
- */
-static void
-compute_moments (pspp_linreg_cache * c, struct moments_var *mom,
-                struct design_matrix *dm, size_t n)
-{
-  size_t i;
-  size_t j;
-  double weight;
-  double mean;
-  double variance;
-  double skewness;
-  double kurtosis;
-  /*
-     Scan the variable names in the columns of the design matrix.
-     When we find the variable we need, insert its mean in the cache.
-   */
-  for (i = 0; i < dm->m->size2; i++)
-    {
-      for (j = 0; j < n; j++)
-       {
-         if (design_matrix_col_to_var (dm, i) == (mom + j)->v)
-           {
-             moments1_calculate ((mom + j)->m, &weight, &mean, &variance,
-                                 &skewness, &kurtosis);
-             pspp_linreg_set_indep_variable_mean (c, (mom + j)->v, mean);
-             pspp_linreg_set_indep_variable_sd (c, (mom + j)->v, sqrt (variance));
-           }
-       }
-    }
-}
-
 static bool
 run_regression (struct casereader *input, struct cmd_regression *cmd,
                struct dataset *ds, pspp_linreg_cache **models)
@@ -901,7 +873,7 @@ run_regression (struct casereader *input, struct cmd_regression *cmd,
   size_t i;
   int n_indep = 0;
   int k;
-  struct ccase c;
+  struct ccase *c;
   const struct variable **indep_vars;
   struct design_matrix *X;
   struct moments_var *mom;
@@ -911,13 +883,14 @@ run_regression (struct casereader *input, struct cmd_regression *cmd,
 
   assert (models != NULL);
 
-  if (!casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c == NULL)
     {
       casereader_destroy (input);
       return true;
     }
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
+  output_split_file_values (ds, c);
+  case_unref (c);
 
   if (!v_variables)
     {
@@ -949,16 +922,16 @@ run_regression (struct casereader *input, struct cmd_regression *cmd,
       const struct variable *dep_var;
       struct casereader *reader;
       casenumber row;
-      struct ccase c;
+      struct ccase *c;
       size_t n_data;           /* Number of valid cases. */
 
       dep_var = cmd->v_dependent[k];
       n_indep = identify_indep_vars (indep_vars, dep_var);
       reader = casereader_clone (input);
       reader = casereader_create_filter_missing (reader, indep_vars, n_indep,
-                                                MV_ANY, NULL);
+                                                MV_ANY, NULL, NULL);
       reader = casereader_create_filter_missing (reader, &dep_var, 1,
-                                                MV_ANY, NULL);
+                                                MV_ANY, NULL, NULL);
       n_data = prepare_categories (casereader_clone (reader),
                                   indep_vars, n_indep, mom);
 
@@ -973,7 +946,8 @@ run_regression (struct casereader *input, struct cmd_regression *cmd,
            {
              lopts.get_indep_mean_std[i] = 1;
            }
-         models[k] = pspp_linreg_cache_alloc (X->m->size1, X->m->size2);
+         models[k] = pspp_linreg_cache_alloc (dep_var, (const struct variable **) indep_vars,
+                                              X->m->size1, X->m->size2);
          models[k]->depvar = dep_var;
          /*
             For large data sets, use QR decomposition.
@@ -987,18 +961,18 @@ run_regression (struct casereader *input, struct cmd_regression *cmd,
             The second pass fills the design matrix.
           */
          reader = casereader_create_counter (reader, &row, -1);
-         for (; casereader_read (reader, &c); case_destroy (&c))
+         for (; (c = casereader_read (reader)) != NULL; case_unref (c))
            {
              for (i = 0; i < n_indep; ++i)
                {
                  const struct variable *v = indep_vars[i];
-                 const union value *val = case_data (&c, v);
+                 const union value *val = case_data (c, v);
                  if (var_is_alpha (v))
                    design_matrix_set_categorical (X, row, v, val);
                  else
                    design_matrix_set_numeric (X, row, v, val);
                }
-             gsl_vector_set (Y, row, case_num (&c, dep_var));
+             gsl_vector_set (Y, row, case_num (c, dep_var));
            }
          /*
             Now that we know the number of coefficients, allocate space
diff --git a/src/language/stats/reliability.q b/src/language/stats/reliability.q
new file mode 100644 (file)
index 0000000..0e7f91a
--- /dev/null
@@ -0,0 +1,824 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 "xalloc.h"
+#include "xmalloca.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+#include <data/variable.h>
+#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/casereader.h>
+#include <data/casegrouper.h>
+#include <math/moments.h>
+#include <data/case.h>
+
+#include <language/command.h>
+
+#include <output/manager.h>
+#include <output/table.h>
+
+/* (headers) */
+
+/* (specification)
+   reliability (rel_):
+     *^variables=varlist("PV_NO_SCRATCH | PV_NUMERIC");
+     scale=custom;
+     missing=miss:!exclude/include;
+     model=custom;
+     method=covariance;
+     +summary[sum_]=total.
+*/
+/* (declarations) */
+/* (functions) */
+
+
+static int rel_custom_scale (struct lexer *lexer, struct dataset *ds,
+                     struct cmd_reliability *p, void *aux);
+
+static int rel_custom_model (struct lexer *, struct dataset *,
+                            struct cmd_reliability *, void *);
+
+int cmd_reliability (struct lexer *lexer, struct dataset *ds);
+
+struct cronbach
+{
+  const struct variable **items;
+  size_t n_items;
+  double alpha;
+  double sum_of_variances;
+  double variance_of_sums;
+  int totals_idx;          /* Casereader index into the totals */
+
+  struct moments1 **m ;    /* Moments of the items */
+  struct moments1 *total ; /* Moments of the totals */
+};
+
+#if 0
+static void
+dump_cronbach (const struct cronbach *s)
+{
+  int i;
+  printf ("N items %d\n", s->n_items);
+  for (i = 0 ; i < s->n_items; ++i)
+    {
+      printf ("%s\n", var_get_name (s->items[i]));
+    }
+
+  printf ("Totals idx %d\n", s->totals_idx);
+
+  printf ("scale variance %g\n", s->variance_of_sums);
+  printf ("alpha %g\n", s->alpha);
+  putchar ('\n');
+}
+#endif
+
+enum model
+  {
+    MODEL_ALPHA,
+    MODEL_SPLIT
+  };
+
+
+struct reliability
+{
+  const struct dictionary *dict;
+  const struct variable **variables;
+  int n_variables;
+  enum mv_class exclude;
+
+  struct cronbach *sc;
+  int n_sc;
+
+  int total_start;
+
+  struct string scale_name;
+
+  enum model model;
+  int split_point;
+};
+
+
+static double
+alpha (int k, double sum_of_variances, double variance_of_sums)
+{
+  return k / ( k - 1.0) * ( 1 - sum_of_variances / variance_of_sums);
+}
+
+static void reliability_summary_total (const struct reliability *rel);
+
+static void reliability_statistics (const struct reliability *rel);
+
+
+
+static void
+run_reliability (struct casereader *group, struct dataset *ds,
+                struct reliability *rel);
+
+
+int
+cmd_reliability (struct lexer *lexer, struct dataset *ds)
+{
+  int i;
+  bool ok = false;
+  struct casegrouper *grouper;
+  struct casereader *group;
+  struct cmd_reliability cmd;
+
+  struct reliability rel = {NULL,
+    NULL, 0, MV_ANY, NULL, 0, -1,
+    DS_EMPTY_INITIALIZER,
+    MODEL_ALPHA, 0};
+
+  cmd.v_variables = NULL;
+
+  if ( ! parse_reliability (lexer, ds, &cmd, &rel) )
+    {
+      goto done;
+    }
+
+  rel.dict = dataset_dict (ds);
+  rel.variables = cmd.v_variables;
+  rel.n_variables = cmd.n_variables;
+  rel.exclude = MV_ANY;
+
+
+  if (NULL == rel.sc)
+    {
+      struct cronbach *c;
+      /* Create a default Scale */
+
+      rel.n_sc = 1;
+      rel.sc = xzalloc (sizeof (struct cronbach) * rel.n_sc);
+
+      ds_init_cstr (&rel.scale_name, "ANY");
+
+      c = &rel.sc[0];
+      c->n_items = cmd.n_variables;
+      c->items = xzalloc (sizeof (struct variable*) * c->n_items);
+
+      for (i = 0 ; i < c->n_items ; ++i)
+       c->items[i] = cmd.v_variables[i];
+    }
+
+  if ( cmd.miss == REL_INCLUDE)
+    rel.exclude = MV_SYSTEM;
+
+  if ( rel.model == MODEL_SPLIT)
+    {
+      int i;
+      const struct cronbach *s;
+
+      rel.n_sc += 2 ;
+      rel.sc = xrealloc (rel.sc, sizeof (struct cronbach) * rel.n_sc);
+
+      s = &rel.sc[0];
+
+      rel.sc[1].n_items =
+       (rel.split_point == -1) ? s->n_items / 2 : rel.split_point;
+
+      rel.sc[2].n_items = s->n_items - rel.sc[1].n_items;
+      rel.sc[1].items = xzalloc (sizeof (struct variable *)
+                                * rel.sc[1].n_items);
+
+      rel.sc[2].items = xzalloc (sizeof (struct variable *) *
+                                rel.sc[2].n_items);
+
+      for  (i = 0; i < rel.sc[1].n_items ; ++i)
+       rel.sc[1].items[i] = s->items[i];
+
+      while (i < s->n_items)
+       {
+         rel.sc[2].items[i - rel.sc[1].n_items] = s->items[i];
+         i++;
+       }
+    }
+
+  if (cmd.a_summary[REL_SUM_TOTAL])
+    {
+      int i;
+      const int base_sc = rel.n_sc;
+
+      rel.total_start = base_sc;
+
+      rel.n_sc +=  rel.sc[0].n_items ;
+      rel.sc = xrealloc (rel.sc, sizeof (struct cronbach) * rel.n_sc);
+
+      for (i = 0 ; i < rel.sc[0].n_items; ++i )
+       {
+         int v_src;
+         int v_dest = 0;
+         struct cronbach *s = &rel.sc[i + base_sc];
+
+         s->n_items = rel.sc[0].n_items - 1;
+         s->items = xzalloc (sizeof (struct variable *) * s->n_items);
+         for (v_src = 0 ; v_src < rel.sc[0].n_items ; ++v_src)
+           {
+             if ( v_src != i)
+               s->items[v_dest++] = rel.sc[0].items[v_src];
+           }
+       }
+    }
+
+  /* Data pass. */
+  grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
+  while (casegrouper_get_next_group (grouper, &group))
+    {
+      run_reliability (group, ds, &rel);
+
+      reliability_statistics (&rel);
+
+      if (cmd.a_summary[REL_SUM_TOTAL])
+       reliability_summary_total (&rel);
+    }
+  ok = casegrouper_destroy (grouper);
+  ok = proc_commit (ds) && ok;
+
+  free_reliability (&cmd);
+
+ done:
+
+  /* Free all the stuff */
+  for (i = 0 ; i < rel.n_sc; ++i)
+    {
+      int x;
+      struct cronbach *c = &rel.sc[i];
+      free (c->items);
+
+      moments1_destroy (c->total);
+
+      if ( c->m)
+       for (x = 0 ; x < c->n_items; ++x)
+         moments1_destroy (c->m[x]);
+
+      free (c->m);
+    }
+
+  ds_destroy (&rel.scale_name);
+  free (rel.sc);
+
+  if (ok)
+    return CMD_SUCCESS;
+
+  return CMD_FAILURE;
+}
+
+/* Return the sum of all the item variables in S */
+static  double
+append_sum (const struct ccase *c, casenumber n UNUSED, void *aux)
+{
+  double sum = 0;
+  const struct cronbach *s = aux;
+
+  int v;
+  for (v = 0 ; v < s->n_items; ++v)
+    {
+      sum += case_data (c, s->items[v])->f;
+    }
+
+  return sum;
+};
+
+
+static void case_processing_summary (casenumber n_valid, casenumber n_missing, 
+                                    const struct dictionary *dict);
+
+static void
+run_reliability (struct casereader *input, struct dataset *ds,
+                struct reliability *rel)
+{
+  int i;
+  int si;
+  struct ccase *c;
+  casenumber n_missing ;
+  casenumber n_valid = 0;
+
+
+  for (si = 0 ; si < rel->n_sc; ++si)
+    {
+      struct cronbach *s = &rel->sc[si];
+
+      s->m = xzalloc (sizeof (s->m) * s->n_items);
+      s->total = moments1_create (MOMENT_VARIANCE);
+
+      for (i = 0 ; i < s->n_items ; ++i )
+       s->m[i] = moments1_create (MOMENT_VARIANCE);
+    }
+
+  input = casereader_create_filter_missing (input,
+                                           rel->variables,
+                                           rel->n_variables,
+                                           rel->exclude,
+                                           &n_missing,
+                                           NULL);
+
+  for (si = 0 ; si < rel->n_sc; ++si)
+    {
+      struct cronbach *s = &rel->sc[si];
+
+
+      s->totals_idx = casereader_get_value_cnt (input);
+      input =
+       casereader_create_append_numeric (input, append_sum,
+                                         s, NULL);
+    }
+
+  for (; (c = casereader_read (input)) != NULL; case_unref (c))
+    {
+      double weight = 1.0;
+      n_valid ++;
+
+      for (si = 0; si < rel->n_sc; ++si)
+       {
+         struct cronbach *s = &rel->sc[si];
+
+         for (i = 0 ; i < s->n_items ; ++i )
+           moments1_add (s->m[i], case_data (c, s->items[i])->f, weight);
+
+         moments1_add (s->total, case_data_idx (c, s->totals_idx)->f, weight);
+       }
+    }
+  casereader_destroy (input);
+
+  for (si = 0; si < rel->n_sc; ++si)
+    {
+      struct cronbach *s = &rel->sc[si];
+
+      s->sum_of_variances = 0;
+      for (i = 0 ; i < s->n_items ; ++i )
+       {
+         double weight, mean, variance;
+         moments1_calculate (s->m[i], &weight, &mean, &variance, NULL, NULL);
+
+         s->sum_of_variances += variance;
+       }
+
+      moments1_calculate (s->total, NULL, NULL, &s->variance_of_sums,
+                         NULL, NULL);
+
+      s->alpha =
+       alpha (s->n_items, s->sum_of_variances, s->variance_of_sums);
+    }
+
+
+  {
+    struct tab_table *tab = tab_create(1, 1, 0);
+
+    tab_dim (tab, tab_natural_dimensions);
+    tab_flags (tab, SOMF_NO_TITLE );
+
+    tab_text(tab, 0, 0, TAT_PRINTF, "Scale: %s", ds_cstr (&rel->scale_name));
+
+    tab_submit(tab);
+  }
+
+
+  case_processing_summary (n_valid, n_missing, dataset_dict (ds));
+}
+
+
+static void reliability_statistics_model_alpha (struct tab_table *tbl,
+                                               const struct reliability *rel);
+
+static void reliability_statistics_model_split (struct tab_table *tbl,
+                                               const struct reliability *rel);
+
+struct reliability_output_table
+{
+  int n_cols;
+  int n_rows;
+  int heading_cols;
+  int heading_rows;
+  void (*populate) (struct tab_table *, const struct reliability *);
+};
+
+static struct reliability_output_table rol[2] =
+  {
+    { 2, 2, 1, 1, reliability_statistics_model_alpha},
+    { 4, 9, 3, 0, reliability_statistics_model_split}
+  };
+
+static void
+reliability_statistics (const struct reliability *rel)
+{
+  int n_cols = rol[rel->model].n_cols;
+  int n_rows = rol[rel->model].n_rows;
+  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);
+  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
+
+  tab_dim (tbl, tab_natural_dimensions);
+
+  tab_title (tbl, _("Reliability Statistics"));
+
+  /* Vertical lines for the data only */
+  tab_box (tbl,
+          -1, -1,
+          -1, TAL_1,
+          heading_columns, 0,
+          n_cols - 1, n_rows - 1);
+
+  /* Box around table */
+  tab_box (tbl,
+          TAL_2, TAL_2,
+          -1, -1,
+          0, 0,
+          n_cols - 1, n_rows - 1);
+
+
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows);
+
+  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
+
+  if ( rel->model == MODEL_ALPHA )
+    reliability_statistics_model_alpha (tbl, rel);
+  else if (rel->model == MODEL_SPLIT )
+    reliability_statistics_model_split (tbl, rel);
+
+  tab_submit (tbl);
+}
+
+static void
+reliability_summary_total (const struct reliability *rel)
+{
+  int i;
+  const int n_cols = 5;
+  const int heading_columns = 1;
+  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);
+  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
+
+  tab_dim (tbl, tab_natural_dimensions);
+
+  tab_title (tbl, _("Item-Total Statistics"));
+
+  /* Vertical lines for the data only */
+  tab_box (tbl,
+          -1, -1,
+          -1, TAL_1,
+          heading_columns, 0,
+          n_cols - 1, n_rows - 1);
+
+  /* Box around table */
+  tab_box (tbl,
+          TAL_2, TAL_2,
+          -1, -1,
+          0, 0,
+          n_cols - 1, n_rows - 1);
+
+
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows);
+
+  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
+
+  tab_text (tbl, 1, 0, TAB_CENTER | TAT_TITLE,
+           _("Scale Mean if Item Deleted"));
+
+  tab_text (tbl, 2, 0, TAB_CENTER | TAT_TITLE,
+           _("Scale Variance if Item Deleted"));
+
+  tab_text (tbl, 3, 0, TAB_CENTER | TAT_TITLE,
+           _("Corrected Item-Total Correlation"));
+
+  tab_text (tbl, 4, 0, TAB_CENTER | TAT_TITLE,
+           _("Cronbach's Alpha if Item Deleted"));
+
+
+  for (i = 0 ; i < rel->sc[0].n_items; ++i)
+    {
+      double cov, item_to_total_r;
+      double mean, weight, var;
+
+      const struct cronbach *s = &rel->sc[rel->total_start + i];
+      tab_text (tbl, 0, heading_rows + i, TAB_LEFT| TAT_TITLE,
+               var_to_string (rel->sc[0].items[i]));
+
+      moments1_calculate (s->total, &weight, &mean, &var, 0, 0);
+
+      tab_double (tbl, 1, heading_rows + i, TAB_RIGHT,
+                mean, NULL);
+
+      tab_double (tbl, 2, heading_rows + i, TAB_RIGHT,
+                s->variance_of_sums, NULL);
+
+      tab_double (tbl, 4, heading_rows + i, TAB_RIGHT,
+                s->alpha, NULL);
+
+
+      moments1_calculate (rel->sc[0].m[i], &weight, &mean, &var, 0,0);
+      cov = rel->sc[0].variance_of_sums + var - s->variance_of_sums;
+      cov /= 2.0;
+
+      item_to_total_r = (cov - var) / (sqrt(var) * sqrt (s->variance_of_sums));
+
+
+      tab_double (tbl, 3, heading_rows + i, TAB_RIGHT,
+                item_to_total_r, NULL);
+    }
+
+
+  tab_submit (tbl);
+}
+
+
+static void
+reliability_statistics_model_alpha (struct tab_table *tbl,
+                                   const struct reliability *rel)
+{
+  const struct variable *wv = dict_get_weight (rel->dict);
+  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
+
+  const struct cronbach *s = &rel->sc[0];
+
+  tab_text (tbl, 0, 0, TAB_CENTER | TAT_TITLE,
+               _("Cronbach's Alpha"));
+
+  tab_text (tbl, 1, 0, TAB_CENTER | TAT_TITLE,
+               _("N of items"));
+
+  tab_double (tbl, 0, 1, TAB_RIGHT, s->alpha, NULL);
+
+  tab_double (tbl, 1, 1, TAB_RIGHT, s->n_items, wfmt);
+}
+
+
+static void
+reliability_statistics_model_split (struct tab_table *tbl,
+                                   const struct reliability *rel)
+{
+  const struct variable *wv = dict_get_weight (rel->dict);
+  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
+
+  tab_text (tbl, 0, 0, TAB_LEFT,
+           _("Cronbach's Alpha"));
+
+  tab_text (tbl, 1, 0, TAB_LEFT,
+           _("Part 1"));
+
+  tab_text (tbl, 2, 0, TAB_LEFT,
+           _("Value"));
+
+  tab_text (tbl, 2, 1, TAB_LEFT,
+           _("N of Items"));
+
+
+
+  tab_text (tbl, 1, 2, TAB_LEFT,
+           _("Part 2"));
+
+  tab_text (tbl, 2, 2, TAB_LEFT,
+           _("Value"));
+
+  tab_text (tbl, 2, 3, TAB_LEFT,
+           _("N of Items"));
+
+
+
+  tab_text (tbl, 1, 4, TAB_LEFT,
+           _("Total N of Items"));
+
+  tab_text (tbl, 0, 5, TAB_LEFT,
+           _("Correlation Between Forms"));
+
+
+  tab_text (tbl, 0, 6, TAB_LEFT,
+           _("Spearman-Brown Coefficient"));
+
+  tab_text (tbl, 1, 6, TAB_LEFT,
+           _("Equal Length"));
+
+  tab_text (tbl, 1, 7, TAB_LEFT,
+           _("Unequal Length"));
+
+
+  tab_text (tbl, 0, 8, TAB_LEFT,
+           _("Guttman Split-Half Coefficient"));
+
+
+
+  tab_double (tbl, 3, 0, TAB_RIGHT, rel->sc[1].alpha, NULL);
+  tab_double (tbl, 3, 2, TAB_RIGHT, rel->sc[2].alpha, NULL);
+
+  tab_double (tbl, 3, 1, TAB_RIGHT, rel->sc[1].n_items, wfmt);
+  tab_double (tbl, 3, 3, TAB_RIGHT, rel->sc[2].n_items, wfmt);
+
+  tab_double (tbl, 3, 4, TAB_RIGHT,
+            rel->sc[1].n_items + rel->sc[2].n_items, wfmt);
+
+  {
+    /* R is the correlation between the two parts */
+    double r = rel->sc[0].variance_of_sums -
+      rel->sc[1].variance_of_sums -
+      rel->sc[2].variance_of_sums ;
+
+    /* Guttman Split Half Coefficient */
+    double g = 2 * r / rel->sc[0].variance_of_sums;
+
+    /* Unequal Length Spearman Brown Coefficient, and
+     intermediate value used in the computation thereof */
+    double uly, tmp;
+
+    r /= sqrt (rel->sc[1].variance_of_sums);
+    r /= sqrt (rel->sc[2].variance_of_sums);
+    r /= 2.0;
+
+    tab_double (tbl, 3, 5, TAB_RIGHT, r, NULL);
+
+    /* Equal length Spearman-Brown Coefficient */
+    tab_double (tbl, 3, 6, TAB_RIGHT, 2 * r / (1.0 + r), NULL);
+
+    tab_double (tbl, 3, 8, TAB_RIGHT, g, NULL);
+
+    tmp = (1.0 - r*r) * rel->sc[1].n_items * rel->sc[2].n_items /
+      pow2 (rel->sc[0].n_items);
+
+    uly = sqrt( pow4 (r) + 4 * pow2 (r) * tmp);
+    uly -= pow2 (r);
+    uly /= 2 * tmp;
+
+    tab_double (tbl, 3, 7, TAB_RIGHT, uly, NULL);
+  }
+}
+
+
+
+static void
+case_processing_summary (casenumber n_valid, casenumber n_missing,
+                        const struct dictionary *dict)
+{
+  const struct variable *wv = dict_get_weight (dict);
+  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
+
+  casenumber total;
+  int n_cols = 4;
+  int n_rows = 4;
+  int heading_columns = 2;
+  int heading_rows = 1;
+  struct tab_table *tbl;
+  tbl = tab_create (n_cols, n_rows, 0);
+  tab_headers (tbl, heading_columns, 0, heading_rows, 0);
+
+  tab_dim (tbl, tab_natural_dimensions);
+
+  tab_title (tbl, _("Case Processing Summary"));
+
+  /* Vertical lines for the data only */
+  tab_box (tbl,
+          -1, -1,
+          -1, TAL_1,
+          heading_columns, 0,
+          n_cols - 1, n_rows - 1);
+
+  /* Box around table */
+  tab_box (tbl,
+          TAL_2, TAL_2,
+          -1, -1,
+          0, 0,
+          n_cols - 1, n_rows - 1);
+
+
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows);
+
+  tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
+
+
+  tab_text (tbl, 0, heading_rows, TAB_LEFT | TAT_TITLE,
+               _("Cases"));
+
+  tab_text (tbl, 1, heading_rows, TAB_LEFT | TAT_TITLE,
+               _("Valid"));
+
+  tab_text (tbl, 1, heading_rows + 1, TAB_LEFT | TAT_TITLE,
+               _("Excluded"));
+
+  tab_text (tbl, 1, heading_rows + 2, TAB_LEFT | TAT_TITLE,
+               _("Total"));
+
+  tab_text (tbl, heading_columns, 0, TAB_CENTER | TAT_TITLE,
+               _("N"));
+
+  tab_text (tbl, heading_columns + 1, 0, TAB_CENTER | TAT_TITLE | TAT_PRINTF,
+               _("%%"));
+
+  total = n_missing + n_valid;
+
+  tab_double (tbl, 2, heading_rows, TAB_RIGHT,
+            n_valid, wfmt);
+
+
+  tab_double (tbl, 2, heading_rows + 1, TAB_RIGHT,
+            n_missing, wfmt);
+
+
+  tab_double (tbl, 2, heading_rows + 2, TAB_RIGHT,
+            total, wfmt);
+
+
+  tab_double (tbl, 3, heading_rows, TAB_RIGHT,
+            100 * n_valid / (double) total, NULL);
+
+
+  tab_double (tbl, 3, heading_rows + 1, TAB_RIGHT,
+            100 * n_missing / (double) total, NULL);
+
+
+  tab_double (tbl, 3, heading_rows + 2, TAB_RIGHT,
+            100 * total / (double) total, NULL);
+
+
+  tab_submit (tbl);
+}
+
+static int
+rel_custom_model (struct lexer *lexer, struct dataset *ds UNUSED,
+                 struct cmd_reliability *cmd UNUSED, void *aux)
+{
+  struct reliability *rel = aux;
+
+  if (lex_match_id (lexer, "ALPHA"))
+    {
+      rel->model = MODEL_ALPHA;
+    }
+  else if (lex_match_id (lexer, "SPLIT"))
+    {
+      rel->model = MODEL_SPLIT;
+      rel->split_point = -1;
+      if ( lex_match (lexer, '('))
+       {
+         lex_force_num (lexer);
+         rel->split_point = lex_number (lexer);
+         lex_get (lexer);
+         lex_force_match (lexer, ')');
+       }
+    }
+  else
+    return 0;
+
+  return 1;
+}
+
+
+
+static int
+rel_custom_scale (struct lexer *lexer, struct dataset *ds UNUSED,
+                 struct cmd_reliability *p, void *aux)
+{
+  struct const_var_set *vs;
+  struct reliability *rel = aux;
+  struct cronbach *scale;
+
+  rel->n_sc = 1;
+  rel->sc = xzalloc (sizeof (struct cronbach) * rel->n_sc);
+  scale = &rel->sc[0];
+
+  if ( ! lex_force_match (lexer, '(')) return 0;
+
+  if ( ! lex_force_string (lexer) ) return 0;
+
+  ds_init_string (&rel->scale_name, lex_tokstr (lexer));
+
+  lex_get (lexer);
+
+  if ( ! lex_force_match (lexer, ')')) return 0;
+
+  lex_match (lexer, '=');
+
+  vs = const_var_set_create_from_array (p->v_variables, p->n_variables);
+
+  if (!parse_const_var_set_vars (lexer, vs, &scale->items, &scale->n_items, 0))
+    {
+      const_var_set_destroy (vs);
+      return 2;
+    }
+
+  const_var_set_destroy (vs);
+  return 1;
+}
+
+/*
+   Local Variables:
+   mode: c
+   End:
+*/
index 85acc6c2b76696f4175cb8f69457be8660a8b3f7..deb8b5cbc97b00f1ee4cd7da54bbff8701260ffa 100644 (file)
@@ -27,7 +27,7 @@
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/message.h>
-#include <data/case-ordering.h>
+#include <data/subcase.h>
 #include <math/sort.h>
 #include <sys/types.h>
 
 int
 cmd_sort_cases (struct lexer *lexer, struct dataset *ds)
 {
-  struct case_ordering *ordering;
+  struct subcase ordering;
   struct casereader *output;
   bool ok = false;
 
   lex_match (lexer, T_BY);
 
   proc_cancel_temporary_transformations (ds);
-  ordering = parse_case_ordering (lexer, dataset_dict (ds), NULL);
-  if (ordering == NULL)
+  subcase_init_empty (&ordering);
+  if (!parse_sort_criteria (lexer, dataset_dict (ds), &ordering, NULL, NULL))
     return CMD_CASCADING_FAILURE;
 
   if (settings_get_testing_mode () && lex_match (lexer, '/'))
@@ -69,8 +69,7 @@ cmd_sort_cases (struct lexer *lexer, struct dataset *ds)
     }
 
   proc_discard_output (ds);
-  output = sort_execute (proc_open (ds), ordering);
-  ordering = NULL;
+  output = sort_execute (proc_open (ds), &ordering);
   ok = proc_commit (ds);
   ok = proc_set_active_file_data (ds, output) && ok;
 
@@ -78,7 +77,7 @@ cmd_sort_cases (struct lexer *lexer, struct dataset *ds)
   min_buffers = 64;
   max_buffers = INT_MAX;
 
-  case_ordering_destroy (ordering);
+  subcase_destroy (&ordering);
   return ok ? lex_end_of_command (lexer) : CMD_CASCADING_FAILURE;
 }
 
index fd8c7c535f9e630b88acbfdbafeaa8367374fdcc..9ae299ee6c9e67da5513784eb266c56405e0da20 100644 (file)
@@ -20,8 +20,8 @@
 
 #include <stdlib.h>
 
-#include <data/case-ordering.h>
 #include <data/dictionary.h>
+#include <data/subcase.h>
 #include <data/variable.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* Parses a list of sort keys and returns a struct sort_criteria
-   based on it.  Returns a null pointer on error.
+/* Parses a list of sort fields and appends them to ORDERING,
+   which the caller must already have initialized.
+   Returns true if successful, false on error.
    If SAW_DIRECTION is nonnull, sets *SAW_DIRECTION to true if at
    least one parenthesized sort direction was specified, false
    otherwise. */
-struct case_ordering *
-parse_case_ordering (struct lexer *lexer, const struct dictionary *dict,
-                     bool *saw_direction)
+bool
+parse_sort_criteria (struct lexer *lexer, const struct dictionary *dict,
+                     struct subcase *ordering,
+                     const struct variable ***vars, bool *saw_direction)
 {
-  struct case_ordering *ordering = case_ordering_create ();
-  const struct variable **vars = NULL;
+  const struct variable **local_vars = NULL;
   size_t var_cnt = 0;
 
- if (saw_direction != NULL)
+  if (vars == NULL) 
+    vars = &local_vars;
+  *vars = NULL;
+
+  if (saw_direction != NULL)
     *saw_direction = false;
 
   do
     {
-      enum sort_direction direction;
+      size_t prev_var_cnt = var_cnt;
+      enum subcase_direction direction;
       size_t i;
 
       /* Variables. */
-      free (vars);
-      vars = NULL;
-      if (!parse_variables_const (lexer, dict, &vars, &var_cnt, PV_NO_SCRATCH))
+      if (!parse_variables_const (lexer, dict, vars, &var_cnt,
+                                  PV_APPEND | PV_NO_SCRATCH))
         goto error;
 
       /* Sort direction. */
       if (lex_match (lexer, '('))
        {
          if (lex_match_id (lexer, "D") || lex_match_id (lexer, "DOWN"))
-           direction = SRT_DESCEND;
+           direction = SC_DESCEND;
          else if (lex_match_id (lexer, "A") || lex_match_id (lexer, "UP"))
-            direction = SRT_ASCEND;
+            direction = SC_ASCEND;
           else
            {
              msg (SE, _("`A' or `D' expected inside parentheses."));
@@ -78,21 +83,25 @@ parse_case_ordering (struct lexer *lexer, const struct dictionary *dict,
             *saw_direction = true;
        }
       else
-        direction = SRT_ASCEND;
-
-      for (i = 0; i < var_cnt; i++)
-        if (!case_ordering_add_var (ordering, vars[i], direction))
-          msg (SW, _("Variable %s specified twice in sort criteria."),
-               var_get_name (vars[i]));
+        direction = SC_ASCEND;
+
+      for (i = prev_var_cnt; i < var_cnt; i++) 
+        {
+          const struct variable *var = (*vars)[i];
+          if (!subcase_add_var (ordering, var, direction))
+            msg (SW, _("Variable %s specified twice in sort criteria."),
+                 var_get_name (var)); 
+        }
     }
   while (lex_token (lexer) == T_ID
          && dict_lookup_var (dict, lex_tokid (lexer)) != NULL);
 
-  free (vars);
-  return ordering;
+  free (local_vars);
+  return true;
 
- error:
-  free (vars);
-  case_ordering_destroy (ordering);
-  return NULL;
+error:
+  free (local_vars);
+  if (vars)
+    *vars = NULL;
+  return false;
 }
index 02a4a6cbefb8c552828535978f8b37c4f3dc605d..18f79a9c89e19ff13d3cf3d763a36334e6607ff4 100644 (file)
 
 struct dictionary;
 struct lexer;
+struct variable;
+struct subcase;
 
-struct case_ordering *parse_case_ordering (struct lexer *,
-                                           const struct dictionary *,
-                                           bool *saw_direction);
+bool parse_sort_criteria (struct lexer *, const struct dictionary *,
+                          struct subcase *, const struct variable ***vars,
+                          bool *saw_direction);
 
 
-#endif /* SORT_PRS_H */
+#endif /* sort-criteria.h */
index 40bf97788b35e157082ebaf37a460f5216946c7c..5f5fc56f021d1ba60d125a8b23561b6dae79031b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -81,8 +81,8 @@ struct group_properties
   /* The comparison criterion */
   enum comparison criterion;
 
-  /* The width of the independent variable */
-  int indep_width ;
+  /* The independent variable */
+  struct variable *indep_var;
 
   union {
     /* The value of the independent variable at which groups are determined to
@@ -1497,7 +1497,7 @@ common_calc (const struct dictionary *dict,
 
          gs->n += weight;
          gs->sum += weight * val->f;
-         gs->ssq += weight * val->f * val->f;
+         gs->ssq += weight * pow2 (val->f);
        }
     }
   return 0;
@@ -1534,12 +1534,12 @@ common_postcalc (struct cmd_t_test *cmd)
 
       gs->mean=gs->sum / gs->n;
       gs->s_std_dev= sqrt (
-                        ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
+                        ( (gs->ssq / gs->n ) - pow2 (gs->mean))
                         ) ;
 
       gs->std_dev= sqrt (
                         gs->n/ (gs->n-1) *
-                        ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
+                        ( (gs->ssq / gs->n ) - pow2 (gs->mean))
                         ) ;
 
       gs->se_mean = gs->std_dev / sqrt (gs->n);
@@ -1714,7 +1714,7 @@ group_precalc (struct cmd_t_test *cmd )
       /* There's always 2 groups for a T - TEST */
       ttpr->n_groups = 2;
 
-      gp.indep_width = var_get_width (indep_var);
+      gp.indep_var = indep_var;
 
       ttpr->group_hash = hsh_create (2,
                                    (hsh_compare_func *) compare_group_binary,
@@ -1810,12 +1810,12 @@ group_postcalc ( struct cmd_t_test *cmd )
          gs->mean = gs->sum / gs->n;
 
          gs->s_std_dev= sqrt (
-                             ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
+                             ( (gs->ssq / gs->n ) - pow2 (gs->mean))
                              ) ;
 
          gs->std_dev= sqrt (
                            gs->n/ (gs->n-1) *
-                           ( (gs->ssq / gs->n ) - gs->mean * gs->mean )
+                           ( (gs->ssq / gs->n ) - pow2 (gs->mean))
                            ) ;
 
          gs->se_mean = gs->std_dev / sqrt (gs->n);
@@ -1837,23 +1837,24 @@ calculate (struct cmd_t_test *cmd,
 
   struct casereader *pass1, *pass2, *pass3;
   struct taint *taint;
-  struct ccase c;
+  struct ccase *c;
 
   enum mv_class exclude = cmd->miss != TTS_INCLUDE ? MV_ANY : MV_SYSTEM;
 
-  if (!casereader_peek (input, 0, &c))
+  c = casereader_peek (input, 0);
+  if (c == NULL)
     {
       casereader_destroy (input);
       return;
     }
-  output_split_file_values (ds, &c);
-  case_destroy (&c);
+  output_split_file_values (ds, c);
+  case_unref (c);
 
   if ( cmd->miss == TTS_LISTWISE )
     input = casereader_create_filter_missing (input,
                                               cmd->v_variables,
                                               cmd->n_variables,
-                                              exclude, NULL);
+                                              exclude, NULL, NULL);
 
   input = casereader_create_filter_weight (input, dict, NULL, NULL);
 
@@ -1861,8 +1862,8 @@ calculate (struct cmd_t_test *cmd,
   casereader_split (input, &pass1, &pass2);
 
   common_precalc (cmd);
-  for (; casereader_read (pass1, &c); case_destroy (&c))
-    common_calc (dict, &c, cmd, exclude);
+  for (; (c = casereader_read (pass1)) != NULL; case_unref (c))
+    common_calc (dict, c, cmd, exclude);
   casereader_destroy (pass1);
   common_postcalc (cmd);
 
@@ -1870,22 +1871,22 @@ calculate (struct cmd_t_test *cmd,
     {
     case T_1_SAMPLE:
       one_sample_precalc (cmd);
-      for (; casereader_read (pass2, &c); case_destroy (&c))
-        one_sample_calc (dict, &c, cmd, exclude);
+      for (; (c = casereader_read (pass2)) != NULL; case_unref (c))
+        one_sample_calc (dict, c, cmd, exclude);
       one_sample_postcalc (cmd);
       break;
     case T_PAIRED:
       paired_precalc (cmd);
-      for (; casereader_read (pass2, &c); case_destroy (&c))
-        paired_calc (dict, &c, cmd, exclude);
+      for (; (c = casereader_read (pass2)) != NULL; case_unref (c))
+        paired_calc (dict, c, cmd, exclude);
       paired_postcalc (cmd);
       break;
     case T_IND_SAMPLES:
       pass3 = casereader_clone (pass2);
 
       group_precalc (cmd);
-      for (; casereader_read (pass2, &c); case_destroy (&c))
-        group_calc (dict, &c, cmd, exclude);
+      for (; (c = casereader_read (pass2)) != NULL; case_unref (c))
+        group_calc (dict, c, cmd, exclude);
       group_postcalc (cmd);
 
       levene (dict, pass3, indep_var, cmd->n_variables, cmd->v_variables,
@@ -1926,10 +1927,6 @@ compare_group_binary (const struct group_statistics *a,
 
   if ( p->criterion == CMP_LE )
     {
-      /* less-than comparision is not meaningfull for
-        alpha variables, so we shouldn't ever arrive here */
-      assert (p->indep_width == 0 ) ;
-
       flag_a = ( a->id.f < p->v.critical_value ) ;
       flag_b = ( b->id.f < p->v.critical_value ) ;
     }
@@ -1956,8 +1953,6 @@ hash_group_binary (const struct group_statistics *g,
 
   if ( p->criterion == CMP_LE )
     {
-      /* Not meaningfull to do a less than compare for alpha values ? */
-      assert (p->indep_width == 0 ) ;
       flag = ( g->id.f < p->v.critical_value ) ;
     }
   else if ( p->criterion == CMP_EQ)
@@ -1977,10 +1972,10 @@ short
 which_group (const struct group_statistics *g,
            const struct group_properties *p)
 {
-  if ( 0 == compare_values (&g->id, &p->v.g_value[0], p->indep_width))
+  if ( 0 == compare_values_short (&g->id, &p->v.g_value[0], p->indep_var))
     return 0;
 
-  if ( 0 == compare_values (&g->id, &p->v.g_value[1], p->indep_width))
+  if ( 0 == compare_values_short (&g->id, &p->v.g_value[1], p->indep_var))
     return 1;
 
   return 2;
diff --git a/src/language/stats/wilcoxon.c b/src/language/stats/wilcoxon.c
new file mode 100644 (file)
index 0000000..c0329f6
--- /dev/null
@@ -0,0 +1,369 @@
+/* Pspp - a program for statistical analysis.
+   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
+   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 "wilcoxon.h"
+#include <data/variable.h>
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <data/subcase.h>
+#include <math/sort.h>
+#include <libpspp/message.h>
+#include <xalloc.h>
+#include <output/table.h>
+#include <data/procedure.h>
+#include <data/dictionary.h>
+#include <math/wilcoxon-sig.h>
+#include <gsl/gsl_cdf.h>
+#include <unistd.h>
+#include <signal.h>
+#include <libpspp/assertion.h>
+#include <data/format.h>
+
+static double
+append_difference (const struct ccase *c, casenumber n UNUSED, void *aux)
+{
+  const variable_pair *vp = aux;
+
+  return case_data (c, (*vp)[0])->f - case_data (c, (*vp)[1])->f;
+}
+
+static void show_ranks_box (const struct wilcoxon_state *,
+                           const struct two_sample_test *,
+                           const struct dictionary *);
+
+static void show_tests_box (const struct wilcoxon_state *,
+                           const struct two_sample_test *,
+                           bool exact, double timer);
+
+
+
+static void
+distinct_callback (double v UNUSED, casenumber n, double w UNUSED, void *aux)
+{
+  struct wilcoxon_state *ws = aux;
+
+  ws->tiebreaker += pow3 (n) - n;
+}
+
+#define WEIGHT_IDX 2
+
+void
+wilcoxon_execute (const struct dataset *ds,
+                 struct casereader *input,
+                 enum mv_class exclude,
+                 const struct npar_test *test,
+                 bool exact,
+                 double timer)
+{
+  int i;
+  bool warn = true;
+  const struct dictionary *dict = dataset_dict (ds);
+  const struct two_sample_test *t2s = (struct two_sample_test *) test;
+
+  struct wilcoxon_state *ws = xcalloc (sizeof (*ws), t2s->n_pairs);
+  const struct variable *weight = dict_get_weight (dict);
+  struct variable *weightx = var_create_internal (WEIGHT_IDX);
+
+  input =
+    casereader_create_filter_weight (input, dict, &warn, NULL);
+
+  for (i = 0 ; i < t2s->n_pairs; ++i )
+    {
+      struct casereader *r = casereader_clone (input);
+      struct casewriter *writer;
+      struct ccase *c;
+      struct subcase ordering;
+      variable_pair *vp = &t2s->pairs[i];
+
+      const int reader_width = weight ? 3 : 2;
+
+      ws[i].sign = var_create_internal (0);
+      ws[i].absdiff = var_create_internal (1);
+
+      r = casereader_create_filter_missing (r, *vp, 2,
+                                           exclude,
+                                           NULL, NULL);
+
+      subcase_init_var (&ordering, ws[i].absdiff, SC_ASCEND);
+      writer = sort_create_writer (&ordering, reader_width);
+      subcase_destroy (&ordering);
+
+      for (; (c = casereader_read (r)) != NULL; case_unref (c))
+       {
+         struct ccase *output = case_create (reader_width);
+         double d = append_difference (c, 0, vp);
+
+         if (d > 0)
+           {
+             case_data_rw (output, ws[i].sign)->f = 1.0;
+
+           }
+         else if (d < 0)
+           {
+             case_data_rw (output, ws[i].sign)->f = -1.0;
+           }
+         else
+           {
+             double w = 1.0;
+             if (weight)
+               w = case_data (c, weight)->f;
+
+             /* Central point values should be dropped */
+             ws[i].n_zeros += w;
+              case_unref (output);
+              continue;
+           }
+
+         case_data_rw (output, ws[i].absdiff)->f = fabs (d);
+
+         if (weight)
+          case_data_rw (output, weightx)->f = case_data (c, weight)->f;
+
+         casewriter_write (writer, output);
+       }
+      casereader_destroy (r);
+      ws[i].reader = casewriter_make_reader (writer);
+    }
+
+  for (i = 0 ; i < t2s->n_pairs; ++i )
+    {
+      struct casereader *rr ;
+      struct ccase *c;
+      enum rank_error err = 0;
+
+      rr = casereader_create_append_rank (ws[i].reader, ws[i].absdiff,
+                                         weight ? weightx : NULL, &err,
+                                         distinct_callback, &ws[i]
+                                         );
+
+      for (; (c = casereader_read (rr)) != NULL; case_unref (c))
+       {
+         double sign = case_data (c, ws[i].sign)->f;
+         double rank = case_data_idx (c, weight ? 3 : 2)->f;
+         double w = 1.0;
+         if (weight)
+           w = case_data (c, weightx)->f;
+
+         if ( sign > 0 )
+           {
+             ws[i].positives.sum += rank * w;
+             ws[i].positives.n += w;
+           }
+         else if (sign < 0)
+           {
+             ws[i].negatives.sum += rank * w;
+             ws[i].negatives.n += w;
+           }
+         else
+           NOT_REACHED ();
+       }
+
+      casereader_destroy (rr);
+    }
+
+  casereader_destroy (input);
+
+  var_destroy (weightx);
+
+  show_ranks_box (ws, t2s, dict);
+  show_tests_box (ws, t2s, exact, timer);
+
+  for (i = 0 ; i < t2s->n_pairs; ++i )
+    {
+      var_destroy (ws[i].sign);
+      var_destroy (ws[i].absdiff);
+    }
+
+  free (ws);
+}
+
+
+\f
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static void
+show_ranks_box (const struct wilcoxon_state *ws,
+               const struct two_sample_test *t2s,
+               const struct dictionary *dict)
+{
+  size_t i;
+
+  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);
+
+  tab_dim (table, tab_natural_dimensions);
+
+  tab_title (table, _("Ranks"));
+
+  tab_headers (table, 2, 0, 1, 0);
+
+  /* Vertical lines inside the box */
+  tab_box (table, 0, 0, -1, TAL_1,
+          1, 0, table->nc - 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 );
+
+
+  tab_text (table,  2, 0,  TAB_CENTER, _("N"));
+  tab_text (table,  3, 0,  TAB_CENTER, _("Mean Rank"));
+  tab_text (table,  4, 0,  TAB_CENTER, _("Sum of Ranks"));
+
+
+  for (i = 0 ; i < t2s->n_pairs; ++i)
+    {
+      variable_pair *vp = &t2s->pairs[i];
+
+      struct string pair_name;
+      ds_init_cstr (&pair_name, var_to_string ((*vp)[0]));
+      ds_put_cstr (&pair_name, " - ");
+      ds_put_cstr (&pair_name, var_to_string ((*vp)[1]));
+
+      tab_text (table, 1, 1 + i * 4, TAB_LEFT, _("Negative Ranks"));
+      tab_text (table, 1, 2 + i * 4, TAB_LEFT, _("Positive Ranks"));
+      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_text (table, 0, 1 + i * 4, TAB_LEFT, ds_cstr (&pair_name));
+      ds_destroy (&pair_name);
+
+
+      /* N */
+      tab_double (table, 2, 1 + i * 4, TAB_RIGHT, ws[i].negatives.n, wfmt);
+      tab_double (table, 2, 2 + i * 4, TAB_RIGHT, ws[i].positives.n, wfmt);
+      tab_double (table, 2, 3 + i * 4, TAB_RIGHT, ws[i].n_zeros, wfmt);
+
+      tab_double (table, 2, 4 + i * 4, TAB_RIGHT,
+                ws[i].n_zeros + ws[i].positives.n + ws[i].negatives.n, wfmt);
+
+      /* Sums */
+      tab_double (table, 4, 1 + i * 4, TAB_RIGHT, ws[i].negatives.sum, NULL);
+      tab_double (table, 4, 2 + i * 4, TAB_RIGHT, ws[i].positives.sum, NULL);
+
+
+      /* Means */
+      tab_double (table, 3, 1 + i * 4, TAB_RIGHT,
+                ws[i].negatives.sum / (double) ws[i].negatives.n, NULL);
+
+      tab_double (table, 3, 2 + i * 4, TAB_RIGHT,
+                ws[i].positives.sum / (double) ws[i].positives.n, NULL);
+
+    }
+
+  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
+  tab_vline (table, TAL_2, 2, 0, table->nr - 1);
+
+
+  tab_submit (table);
+}
+
+
+static void
+show_tests_box (const struct wilcoxon_state *ws,
+               const struct two_sample_test *t2s,
+               bool exact,
+               double timer UNUSED
+               )
+{
+  size_t i;
+  struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3, 0);
+
+  tab_dim (table, tab_natural_dimensions);
+
+  tab_title (table, _("Test Statistics"));
+
+  tab_headers (table, 1, 0, 1, 0);
+
+  /* Vertical lines inside the box */
+  tab_box (table, 0, 0, -1, TAL_1,
+          0, 0, table->nc - 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 );
+
+
+  tab_text (table,  0, 1,  TAB_LEFT, _("Z"));
+  tab_text (table,  0, 2,  TAB_LEFT, _("Asymp. Sig (2-tailed)"));
+
+  if ( exact )
+    {
+      tab_text (table,  0, 3,  TAB_LEFT, _("Exact Sig (2-tailed)"));
+      tab_text (table,  0, 4,  TAB_LEFT, _("Exact Sig (1-tailed)"));
+
+#if 0
+      tab_text (table,  0, 5,  TAB_LEFT, _("Point Probability"));
+#endif
+    }
+
+  for (i = 0 ; i < t2s->n_pairs; ++i)
+    {
+      double z;
+      double n = ws[i].positives.n + ws[i].negatives.n;
+      variable_pair *vp = &t2s->pairs[i];
+
+      struct string pair_name;
+      ds_init_cstr (&pair_name, var_to_string ((*vp)[0]));
+      ds_put_cstr (&pair_name, " - ");
+      ds_put_cstr (&pair_name, var_to_string ((*vp)[1]));
+
+
+      tab_text (table, 1 + i, 0, TAB_CENTER, ds_cstr (&pair_name));
+      ds_destroy (&pair_name);
+
+      z = MIN (ws[i].positives.sum, ws[i].negatives.sum);
+      z -= n * (n + 1)/ 4.0;
+
+      z /= sqrt (n * (n + 1) * (2*n + 1)/24.0 - ws[i].tiebreaker / 48.0);
+
+      tab_double (table, 1 + i, 1, TAB_RIGHT, z, NULL);
+
+      tab_double (table, 1 + i, 2, TAB_RIGHT,
+                2.0 * gsl_cdf_ugaussian_P (z),
+                NULL);
+
+      if (exact)
+       {
+         double p = LevelOfSignificanceWXMPSR (ws[i].positives.sum, n);
+         if (p < 0)
+           {
+             msg (MW, ("Too many pairs to calculate exact significance."));
+           }
+         else
+           {
+             tab_double (table, 1 + i, 3, TAB_RIGHT, p, NULL);
+             tab_double (table, 1 + i, 4, TAB_RIGHT, p / 2.0, NULL);
+           }
+       }
+    }
+
+  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
+  tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+
+
+  tab_submit (table);
+}
diff --git a/src/language/stats/wilcoxon.h b/src/language/stats/wilcoxon.h
new file mode 100644 (file)
index 0000000..b0f86a2
--- /dev/null
@@ -0,0 +1,65 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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/>. */
+
+#if !wilcoxon_h
+#define wilcoxon_h 1
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <language/stats/npar.h>
+#include <data/case.h>
+
+
+struct rank_sum
+{
+  double n;
+  double sum;
+};
+
+struct wilcoxon_state
+{
+  struct casereader *reader;
+  struct variable *sign;
+  struct variable *absdiff;
+
+  struct rank_sum positives;
+  struct rank_sum negatives;
+  double n_zeros;
+
+  double tiebreaker;
+};
+
+
+struct wilcoxon_test
+{
+  struct two_sample_test parent;
+};
+
+struct casereader;
+struct dataset;
+
+
+void wilcoxon_execute (const struct dataset *ds,
+                      struct casereader *input,
+                      enum mv_class exclude,
+                      const struct npar_test *test,
+                      bool exact,
+                      double timer
+                      );
+
+
+
+#endif
diff --git a/src/language/tests/.gitignore b/src/language/tests/.gitignore
new file mode 100644 (file)
index 0000000..61e8b4e
--- /dev/null
@@ -0,0 +1 @@
+check-model.c
index 00fcab373454eec0bfc6735f1adba97328d0fe63..54d95107d94794011bb9f184550f8b10ae55c87b 100644 (file)
@@ -6,9 +6,13 @@ language_tests_built_sources = \
 language_tests_sources = \
        src/language/tests/check-model.h \
        src/language/tests/datasheet-test.c \
+       src/language/tests/datasheet-check.c \
+       src/language/tests/datasheet-check.h \
        src/language/tests/format-guesser-test.c \
        src/language/tests/float-format.c \
        src/language/tests/moments-test.c \
+       src/language/tests/model-checker.c \
+       src/language/tests/model-checker.h \
        src/language/tests/paper-size.c \
        src/language/tests/pool-test.c 
 
index a2654717888d5232f679eacb57190b2284587b50..b1f44ffa6bef06ad0a3e6c927281e6762c6f02c0 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <errno.h>
 
-#include <libpspp/model-checker.h>
+#include <language/tests/model-checker.h>
 #include <language/lexer/lexer.h>
 
 #include "error.h"
diff --git a/src/language/tests/datasheet-check.c b/src/language/tests/datasheet-check.c
new file mode 100644 (file)
index 0000000..ccfee80
--- /dev/null
@@ -0,0 +1,474 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 <data/datasheet.h>
+#include "datasheet-check.h"
+#include "model-checker.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <data/casereader-provider.h>
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <data/lazy-casereader.h>
+#include <data/sparse-cases.h>
+#include <libpspp/array.h>
+#include <libpspp/assertion.h>
+#include <libpspp/range-map.h>
+#include <libpspp/range-set.h>
+#include <libpspp/taint.h>
+#include <libpspp/tower.h>
+
+#include "minmax.h"
+#include "md4.h"
+#include "xalloc.h"
+
+
+/* lazy_casereader callback function to instantiate a casereader
+   from the datasheet. */
+static struct casereader *
+lazy_callback (void *ds_)
+{
+  struct datasheet *ds = ds_;
+  return datasheet_make_reader (ds);
+}
+
+
+/* Maximum size of datasheet supported for model checking
+   purposes. */
+#define MAX_ROWS 5
+#define MAX_COLS 5
+
+
+/* Checks that READER contains the ROW_CNT rows and COLUMN_CNT
+   columns of data in ARRAY, reporting any errors via MC. */
+static void
+check_datasheet_casereader (struct mc *mc, struct casereader *reader,
+                            double array[MAX_ROWS][MAX_COLS],
+                            size_t row_cnt, size_t column_cnt)
+{
+  if (casereader_get_case_cnt (reader) != row_cnt)
+    {
+      if (casereader_get_case_cnt (reader) == CASENUMBER_MAX
+          && casereader_count_cases (reader) == row_cnt)
+        mc_error (mc, "datasheet casereader has unknown case count");
+      else
+        mc_error (mc, "casereader row count (%lu) does not match "
+                  "expected (%zu)",
+                  (unsigned long int) casereader_get_case_cnt (reader),
+                  row_cnt);
+    }
+  else if (casereader_get_value_cnt (reader) != column_cnt)
+    mc_error (mc, "casereader column count (%zu) does not match "
+              "expected (%zu)",
+              casereader_get_value_cnt (reader), column_cnt);
+  else
+    {
+      struct ccase *c;
+      size_t row;
+
+      for (row = 0; row < row_cnt; row++)
+        {
+          size_t col;
+
+          c = casereader_read (reader);
+          if (c == NULL)
+            {
+              mc_error (mc, "casereader_read failed reading row %zu of %zu "
+                        "(%zu columns)", row, row_cnt, column_cnt);
+              return;
+            }
+
+          for (col = 0; col < column_cnt; col++)
+            if (case_num_idx (c, col) != array[row][col])
+              mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
+                        "%g != %g",
+                        row, col, row_cnt, column_cnt,
+                        case_num_idx (c, col), array[row][col]);
+
+         case_unref (c);
+        }
+
+      c = casereader_read (reader);
+      if (c != NULL)
+        mc_error (mc, "casereader has extra cases (expected %zu)", row_cnt);
+    }
+}
+
+/* Checks that datasheet DS contains has ROW_CNT rows, COLUMN_CNT
+   columns, and the same contents as ARRAY, reporting any
+   mismatches via mc_error.  Then, adds DS to MC as a new state. */
+static void
+check_datasheet (struct mc *mc, struct datasheet *ds,
+                 double array[MAX_ROWS][MAX_COLS],
+                 size_t row_cnt, size_t column_cnt)
+{
+  struct datasheet *ds2;
+  struct casereader *reader;
+  unsigned long int serial = 0;
+
+  assert (row_cnt < MAX_ROWS);
+  assert (column_cnt < MAX_COLS);
+
+  /* If it is a duplicate hash, discard the state before checking
+     its consistency, to save time. */
+  if (mc_discard_dup_state (mc, hash_datasheet (ds)))
+    {
+      datasheet_destroy (ds);
+      return;
+    }
+
+  /* Check contents of datasheet via datasheet functions. */
+  if (row_cnt != datasheet_get_row_cnt (ds))
+    mc_error (mc, "row count (%lu) does not match expected (%zu)",
+              (unsigned long int) datasheet_get_row_cnt (ds), row_cnt);
+  else if (column_cnt != datasheet_get_column_cnt (ds))
+    mc_error (mc, "column count (%zu) does not match expected (%zu)",
+              datasheet_get_column_cnt (ds), column_cnt);
+  else
+    {
+      size_t row, col;
+
+      for (row = 0; row < row_cnt; row++)
+        for (col = 0; col < column_cnt; col++)
+          {
+            union value v;
+            if (!datasheet_get_value (ds, row, col, &v, 1))
+              NOT_REACHED ();
+            if (v.f != array[row][col])
+              mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: %g != %g",
+                        row, col, row_cnt, column_cnt, v.f, array[row][col]);
+          }
+    }
+
+  /* Check that datasheet contents are correct when read through
+     casereader. */
+  ds2 = clone_datasheet (ds);
+  reader = datasheet_make_reader (ds2);
+  check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt);
+  casereader_destroy (reader);
+
+  /* Check that datasheet contents are correct when read through
+     casereader with lazy_casereader wrapped around it.  This is
+     valuable because otherwise there is no non-GUI code that
+     uses the lazy_casereader. */
+  ds2 = clone_datasheet (ds);
+  reader = lazy_casereader_create (column_cnt, row_cnt,
+                                   lazy_callback, ds2, &serial);
+  check_datasheet_casereader (mc, reader, array, row_cnt, column_cnt);
+  if (lazy_casereader_destroy (reader, serial))
+    {
+      /* Lazy casereader was never instantiated.  This will
+         only happen if there are no rows (because in that case
+         casereader_read never gets called). */
+      datasheet_destroy (ds2);
+      if (row_cnt != 0)
+        mc_error (mc, "lazy casereader not instantiated, but should "
+                  "have been (size %zu,%zu)", row_cnt, column_cnt);
+    }
+  else
+    {
+      /* Lazy casereader was instantiated.  This is the common
+         case, in which some casereader operation
+         (casereader_read in this case) was performed on the
+         lazy casereader. */
+      casereader_destroy (reader);
+      if (row_cnt == 0)
+        mc_error (mc, "lazy casereader instantiated, but should not "
+                  "have been (size %zu,%zu)", row_cnt, column_cnt);
+    }
+
+  mc_add_state (mc, ds);
+}
+
+/* Extracts the contents of DS into DATA. */
+static void
+extract_data (const struct datasheet *ds, double data[MAX_ROWS][MAX_COLS])
+{
+  size_t column_cnt = datasheet_get_column_cnt (ds);
+  size_t row_cnt = datasheet_get_row_cnt (ds);
+  size_t row, col;
+
+  assert (row_cnt < MAX_ROWS);
+  assert (column_cnt < MAX_COLS);
+  for (row = 0; row < row_cnt; row++)
+    for (col = 0; col < column_cnt; col++)
+      {
+        union value v;
+        if (!datasheet_get_value (ds, row, col, &v, 1))
+          NOT_REACHED ();
+        data[row][col] = v.f;
+      }
+}
+
+/* Clones the structure and contents of ODS into *DS,
+   and the contents of ODATA into DATA. */
+static void
+clone_model (const struct datasheet *ods, double odata[MAX_ROWS][MAX_COLS],
+             struct datasheet **ds, double data[MAX_ROWS][MAX_COLS])
+{
+  *ds = clone_datasheet (ods);
+  memcpy (data, odata, MAX_ROWS * MAX_COLS * sizeof **data);
+}
+
+/* "init" function for struct mc_class. */
+static void
+datasheet_mc_init (struct mc *mc)
+{
+  struct datasheet_test_params *params = mc_get_aux (mc);
+  struct datasheet *ds;
+
+  if (params->backing_rows == 0 && params->backing_cols == 0)
+    {
+      /* Create unbacked datasheet. */
+      ds = datasheet_create (NULL);
+      mc_name_operation (mc, "empty datasheet");
+      check_datasheet (mc, ds, NULL, 0, 0);
+    }
+  else
+    {
+      /* Create datasheet with backing. */
+      struct casewriter *writer;
+      struct casereader *reader;
+      double data[MAX_ROWS][MAX_COLS];
+      int row;
+
+      assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
+      assert (params->backing_cols > 0 && params->backing_cols <= MAX_COLS);
+
+      writer = mem_writer_create (params->backing_cols);
+      for (row = 0; row < params->backing_rows; row++)
+        {
+          struct ccase *c;
+          int col;
+
+          c = case_create (params->backing_cols);
+          for (col = 0; col < params->backing_cols; col++)
+            {
+              double value = params->next_value++;
+              data[row][col] = value;
+              case_data_rw_idx (c, col)->f = value;
+            }
+          casewriter_write (writer, c);
+        }
+      reader = casewriter_make_reader (writer);
+      assert (reader != NULL);
+
+      ds = datasheet_create (reader);
+      mc_name_operation (mc, "datasheet with (%d,%d) backing",
+                         params->backing_rows, params->backing_cols);
+      check_datasheet (mc, ds, data,
+                       params->backing_rows, params->backing_cols);
+    }
+}
+
+/* "mutate" function for struct mc_class. */
+static void
+datasheet_mc_mutate (struct mc *mc, const void *ods_)
+{
+  struct datasheet_test_params *params = mc_get_aux (mc);
+
+  const struct datasheet *ods = ods_;
+  double odata[MAX_ROWS][MAX_COLS];
+  double data[MAX_ROWS][MAX_COLS];
+  size_t column_cnt = datasheet_get_column_cnt (ods);
+  size_t row_cnt = datasheet_get_row_cnt (ods);
+  size_t pos, new_pos, cnt;
+
+  extract_data (ods, odata);
+
+  /* Insert all possible numbers of columns in all possible
+     positions. */
+  for (pos = 0; pos <= column_cnt; pos++)
+    for (cnt = 0; cnt <= params->max_cols - column_cnt; cnt++)
+      if (mc_include_state (mc))
+        {
+          struct datasheet *ds;
+          union value new[MAX_COLS];
+          size_t i, j;
+
+          mc_name_operation (mc, "insert %zu columns at %zu", cnt, pos);
+          clone_model (ods, odata, &ds, data);
+
+          for (i = 0; i < cnt; i++)
+            new[i].f = params->next_value++;
+
+          if (!datasheet_insert_columns (ds, new, cnt, pos))
+            mc_error (mc, "datasheet_insert_columns failed");
+
+          for (i = 0; i < row_cnt; i++)
+            {
+              insert_range (&data[i][0], column_cnt, sizeof data[i][0],
+                            pos, cnt);
+              for (j = 0; j < cnt; j++)
+                data[i][pos + j] = new[j].f;
+            }
+
+          check_datasheet (mc, ds, data, row_cnt, column_cnt + cnt);
+        }
+
+  /* Delete all possible numbers of columns from all possible
+     positions. */
+  for (pos = 0; pos < column_cnt; pos++)
+    for (cnt = 0; cnt < column_cnt - pos; cnt++)
+      if (mc_include_state (mc))
+        {
+          struct datasheet *ds;
+          size_t i;
+
+          mc_name_operation (mc, "delete %zu columns at %zu", cnt, pos);
+          clone_model (ods, odata, &ds, data);
+
+          datasheet_delete_columns (ds, pos, cnt);
+
+          for (i = 0; i < row_cnt; i++)
+            remove_range (&data[i], column_cnt, sizeof *data[i], pos, cnt);
+
+          check_datasheet (mc, ds, data, row_cnt, column_cnt - cnt);
+        }
+
+  /* Move all possible numbers of columns from all possible
+     existing positions to all possible new positions. */
+  for (pos = 0; pos < column_cnt; pos++)
+    for (cnt = 0; cnt < column_cnt - pos; cnt++)
+      for (new_pos = 0; new_pos < column_cnt - cnt; new_pos++)
+        if (mc_include_state (mc))
+          {
+            struct datasheet *ds;
+            size_t i;
+
+            clone_model (ods, odata, &ds, data);
+            mc_name_operation (mc, "move %zu columns from %zu to %zu",
+                               cnt, pos, new_pos);
+
+            datasheet_move_columns (ds, pos, new_pos, cnt);
+
+            for (i = 0; i < row_cnt; i++)
+              move_range (&data[i], column_cnt, sizeof data[i][0],
+                          pos, new_pos, cnt);
+
+            check_datasheet (mc, ds, data, row_cnt, column_cnt);
+          }
+
+  /* Insert all possible numbers of rows in all possible
+     positions. */
+  for (pos = 0; pos <= row_cnt; pos++)
+    for (cnt = 0; cnt <= params->max_rows - row_cnt; cnt++)
+      if (mc_include_state (mc))
+        {
+          struct datasheet *ds;
+          struct ccase *c[MAX_ROWS];
+          size_t i, j;
+
+          clone_model (ods, odata, &ds, data);
+          mc_name_operation (mc, "insert %zu rows at %zu", cnt, pos);
+
+          for (i = 0; i < cnt; i++)
+            {
+              c[i] = case_create (column_cnt);
+              for (j = 0; j < column_cnt; j++)
+                case_data_rw_idx (c[i], j)->f = params->next_value++;
+            }
+
+          insert_range (data, row_cnt, sizeof data[pos], pos, cnt);
+          for (i = 0; i < cnt; i++)
+            for (j = 0; j < column_cnt; j++)
+              data[i + pos][j] = case_num_idx (c[i], j);
+
+          if (!datasheet_insert_rows (ds, pos, c, cnt))
+            mc_error (mc, "datasheet_insert_rows failed");
+
+          check_datasheet (mc, ds, data, row_cnt + cnt, column_cnt);
+        }
+
+  /* Delete all possible numbers of rows from all possible
+     positions. */
+  for (pos = 0; pos < row_cnt; pos++)
+    for (cnt = 0; cnt < row_cnt - pos; cnt++)
+      if (mc_include_state (mc))
+        {
+          struct datasheet *ds;
+
+          clone_model (ods, odata, &ds, data);
+          mc_name_operation (mc, "delete %zu rows at %zu", cnt, pos);
+
+          datasheet_delete_rows (ds, pos, cnt);
+
+          remove_range (&data[0], row_cnt, sizeof data[0], pos, cnt);
+
+          check_datasheet (mc, ds, data, row_cnt - cnt, column_cnt);
+        }
+
+  /* Move all possible numbers of rows from all possible existing
+     positions to all possible new positions. */
+  for (pos = 0; pos < row_cnt; pos++)
+    for (cnt = 0; cnt < row_cnt - pos; cnt++)
+      for (new_pos = 0; new_pos < row_cnt - cnt; new_pos++)
+        if (mc_include_state (mc))
+          {
+            struct datasheet *ds;
+
+            clone_model (ods, odata, &ds, data);
+            mc_name_operation (mc, "move %zu rows from %zu to %zu",
+                               cnt, pos, new_pos);
+
+            datasheet_move_rows (ds, pos, new_pos, cnt);
+
+            move_range (&data[0], row_cnt, sizeof data[0],
+                        pos, new_pos, cnt);
+
+            check_datasheet (mc, ds, data, row_cnt, column_cnt);
+          }
+}
+
+/* "destroy" function for struct mc_class. */
+static void
+datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
+{
+  struct datasheet *ds = ds_;
+  datasheet_destroy (ds);
+}
+
+/* Executes the model checker on the datasheet test driver with
+   the given OPTIONS and passing in the given PARAMS, which must
+   point to a modifiable "struct datasheet_test_params".  If any
+   value in PARAMS is out of range, it will be adjusted into the
+   valid range before running the test.
+
+   Returns the results of the model checking run. */
+struct mc_results *
+datasheet_test (struct mc_options *options, void *params_)
+{
+  struct datasheet_test_params *params = params_;
+  static const struct mc_class datasheet_mc_class =
+    {
+      datasheet_mc_init,
+      datasheet_mc_mutate,
+      datasheet_mc_destroy,
+    };
+
+  params->next_value = 1;
+  params->max_rows = MIN (params->max_rows, MAX_ROWS);
+  params->max_cols = MIN (params->max_cols, MAX_COLS);
+  params->backing_rows = MIN (params->backing_rows, params->max_rows);
+  params->backing_cols = MIN (params->backing_cols, params->max_cols);
+
+  mc_options_set_aux (options, params);
+  return mc_run (&datasheet_mc_class, options);
+}
diff --git a/src/language/tests/datasheet-check.h b/src/language/tests/datasheet-check.h
new file mode 100644 (file)
index 0000000..d5a1c09
--- /dev/null
@@ -0,0 +1,89 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 DATA_DATASHEET_TEST_H
+#define DATA_DATASHEET_TEST_H 1
+
+#if 0
+#include <data/case.h>
+#include <data/value.h>
+
+struct casereader;
+
+/* A datasheet is a 2-d array of data that may be stored in
+   memory or on disk.  It efficiently supports data storage and
+   retrieval, as well as adding, removing, and rearranging both
+   rows and columns.  */
+
+struct datasheet *datasheet_create (struct casereader *);
+void datasheet_destroy (struct datasheet *);
+struct datasheet *datasheet_rename (struct datasheet *);
+
+bool datasheet_error (const struct datasheet *);
+void datasheet_force_error (struct datasheet *);
+const struct taint *datasheet_get_taint (const struct datasheet *);
+
+struct casereader *datasheet_make_reader (struct datasheet *);
+
+/* Columns. */
+size_t datasheet_get_column_cnt (const struct datasheet *);
+bool datasheet_insert_columns (struct datasheet *,
+                               const union value[], size_t cnt,
+                               size_t before);
+void datasheet_delete_columns (struct datasheet *, size_t start, size_t cnt);
+void datasheet_move_columns (struct datasheet *,
+                             size_t old_start, size_t new_start,
+                             size_t cnt);
+
+/* Rows. */
+casenumber datasheet_get_row_cnt (const struct datasheet *);
+bool datasheet_insert_rows (struct datasheet *,
+                            casenumber before, struct ccase *rows[],
+                            casenumber cnt);
+void datasheet_delete_rows (struct datasheet *,
+                            casenumber first, casenumber cnt);
+void datasheet_move_rows (struct datasheet *,
+                          size_t old_start, size_t new_start,
+                          size_t cnt);
+
+/* Data. */
+struct ccase *datasheet_get_row (const struct datasheet *, casenumber);
+bool datasheet_put_row (struct datasheet *, casenumber, struct ccase *);
+bool datasheet_get_value (const struct datasheet *, casenumber, size_t column,
+                          union value *, int width);
+bool datasheet_put_value (struct datasheet *, casenumber, size_t column,
+                          const union value *, int width);
+
+#endif
+
+/* Testing. */
+struct mc_options;
+
+struct datasheet_test_params
+  {
+    /* Parameters. */
+    int max_rows;
+    int max_cols;
+    int backing_rows;
+    int backing_cols;
+
+    /* State. */
+    int next_value;
+  };
+
+struct mc_results *datasheet_test (struct mc_options *options, void *params);
+
+#endif /* data/datasheet.h */
index 67f68377e14ed987fc42671ba55989ef4510aee5..dfe8b6b2f0a4b1a341412522a5f6f579d0f8fa33 100644 (file)
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#include <data/datasheet.h>
+#include "datasheet-check.h"
 
 #include <language/command.h>
 #include <language/lexer/lexer.h>
@@ -49,6 +49,7 @@ cmd_debug_datasheet (struct lexer *lexer, struct dataset *dataset UNUSED)
   params.backing_rows = 0;
   params.backing_cols = 0;
 
+
   for (;;)
     {
       if (lex_match_id (lexer, "MAX"))
diff --git a/src/language/tests/model-checker.c b/src/language/tests/model-checker.c
new file mode 100644 (file)
index 0000000..a040750
--- /dev/null
@@ -0,0 +1,1466 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007 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 "model-checker.h"
+
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <data/val-type.h>
+#include <libpspp/bit-vector.h>
+#include <libpspp/compiler.h>
+#include <libpspp/deque.h>
+#include <libpspp/str.h>
+#include <math/moments.h>
+
+#include "error.h"
+#include "minmax.h"
+#include "xalloc.h"
+\f
+/* Initializes PATH as an empty path. */
+void
+mc_path_init (struct mc_path *path)
+{
+  path->ops = NULL;
+  path->length = 0;
+  path->capacity = 0;
+}
+
+/* Copies the contents of OLD into NEW. */
+void
+mc_path_copy (struct mc_path *new, const struct mc_path *old)
+{
+  if (old->length > new->capacity)
+    {
+      new->capacity = old->length;
+      free (new->ops);
+      new->ops = xnmalloc (new->capacity, sizeof *new->ops);
+    }
+  new->length = old->length;
+  memcpy (new->ops, old->ops, old->length * sizeof *new->ops);
+}
+
+/* Adds OP to the end of PATH. */
+void
+mc_path_push (struct mc_path *path, int op)
+{
+  if (path->length >= path->capacity)
+    path->ops = xnrealloc (path->ops, ++path->capacity, sizeof *path->ops);
+  path->ops[path->length++] = op;
+}
+
+/* Removes and returns the operation at the end of PATH. */
+int
+mc_path_pop (struct mc_path *path)
+{
+  int back = mc_path_back (path);
+  path->length--;
+  return back;
+}
+
+/* Returns the operation at the end of PATH. */
+int
+mc_path_back (const struct mc_path *path)
+{
+  assert (path->length > 0);
+  return path->ops[path->length - 1];
+}
+
+/* Destroys PATH. */
+void
+mc_path_destroy (struct mc_path *path)
+{
+  free (path->ops);
+  path->ops = NULL;
+}
+
+/* Returns the operation in position INDEX in PATH.
+   INDEX must be less than the length of PATH. */
+int
+mc_path_get_operation (const struct mc_path *path, size_t index)
+{
+  assert (index < path->length);
+  return path->ops[index];
+}
+
+/* Returns the number of operations in PATH. */
+size_t
+mc_path_get_length (const struct mc_path *path)
+{
+  return path->length;
+}
+
+/* Appends the operations in PATH to STRING, separating each one
+   with a single space. */
+void
+mc_path_to_string (const struct mc_path *path, struct string *string)
+{
+  size_t i;
+
+  for (i = 0; i < mc_path_get_length (path); i++)
+    {
+      if (i > 0)
+        ds_put_char (string, ' ');
+      ds_put_format (string, "%d", mc_path_get_operation (path, i));
+    }
+}
+\f
+/* Search options. */
+struct mc_options
+  {
+    /* Search strategy. */
+    enum mc_strategy strategy;          /* Type of strategy. */
+    int max_depth;                      /* Limit on depth (or INT_MAX). */
+    int hash_bits;                      /* Number of bits to hash (or 0). */
+    unsigned int seed;                  /* Random seed for MC_RANDOM
+                                           or MC_DROP_RANDOM. */
+    struct mc_path follow_path;         /* Path for MC_PATH. */
+
+    /* Queue configuration. */
+    int queue_limit;                    /* Maximum length of queue. */
+    enum mc_queue_limit_strategy queue_limit_strategy;
+                                        /* How to choose state to drop
+                                           from queue. */
+
+    /* Stop conditions. */
+    int max_unique_states;              /* Maximum unique states to process. */
+    int max_errors;                     /* Maximum errors to detect. */
+    double time_limit;                  /* Maximum time in seconds. */
+
+    /* Output configuration. */
+    int verbosity;                      /* 0=low, 1=normal, 2+=high. */
+    int failure_verbosity;              /* If greater than verbosity,
+                                           verbosity of error replays. */
+    FILE *output_file;                  /* File to receive output. */
+
+    /* How to report intermediate progress. */
+    int progress_usec;                  /* Microseconds between reports. */
+    mc_progress_func *progress_func;    /* Function to call on each report. */
+
+    /* Client data. */
+    void *aux;
+  };
+
+/* Default progress function. */
+static bool
+default_progress (struct mc *mc)
+{
+  if (mc_results_get_stop_reason (mc_get_results (mc)) == MC_CONTINUING)
+    putc ('.', stderr);
+  else
+    putc ('\n', stderr);
+  return true;
+}
+
+/* Do-nothing progress function. */
+static bool
+null_progress (struct mc *mc UNUSED)
+{
+  return true;
+}
+
+/* Creates and returns a set of options initialized to the
+   defaults. */
+struct mc_options *
+mc_options_create (void)
+{
+  struct mc_options *options = xmalloc (sizeof *options);
+
+  options->strategy = MC_BROAD;
+  options->max_depth = INT_MAX;
+  options->hash_bits = 20;
+  options->seed = 0;
+  mc_path_init (&options->follow_path);
+
+  options->queue_limit = 10000;
+  options->queue_limit_strategy = MC_DROP_RANDOM;
+
+  options->max_unique_states = INT_MAX;
+  options->max_errors = 1;
+  options->time_limit = 0.0;
+
+  options->verbosity = 1;
+  options->failure_verbosity = 2;
+  options->output_file = stdout;
+  options->progress_usec = 250000;
+  options->progress_func = default_progress;
+
+  options->aux = NULL;
+
+  return options;
+}
+
+/* Returns a copy of the given OPTIONS. */
+struct mc_options *
+mc_options_clone (const struct mc_options *options)
+{
+  return xmemdup (options, sizeof *options);
+}
+
+/* Destroys OPTIONS. */
+void
+mc_options_destroy (struct mc_options *options)
+{
+  mc_path_destroy (&options->follow_path);
+  free (options);
+}
+
+/* Returns the search strategy used for OPTIONS.  The choices
+   are:
+
+   - MC_BROAD (the default): Breadth-first search.  First tries
+     all the operations with depth 1, then those with depth 2,
+     then those with depth 3, and so on.
+
+     This search algorithm finds the least number of operations
+     needed to trigger a given bug.
+
+   - MC_DEEP: Depth-first search.  Searches downward in the tree
+     of states as fast as possible.  Good for finding bugs that
+     require long sequences of operations to trigger.
+
+   - MC_RANDOM: Random-first search.  Searches through the tree
+     of states in random order.  The standard C library's rand
+     function selects the search path; you can control the seed
+     passed to srand using mc_options_set_seed.
+
+   - MC_PATH: Explicit path.  Applies an explicitly specified
+     sequence of operations. */
+enum mc_strategy
+mc_options_get_strategy (const struct mc_options *options)
+{
+  return options->strategy;
+}
+
+/* Sets the search strategy used for OPTIONS to STRATEGY.
+
+   This function cannot be used to set MC_PATH as the search
+   strategy.  Use mc_options_set_follow_path instead. */
+void
+mc_options_set_strategy (struct mc_options *options, enum mc_strategy strategy)
+{
+  assert (strategy == MC_BROAD
+          || strategy == MC_DEEP
+          || strategy == MC_RANDOM);
+  options->strategy = strategy;
+}
+
+/* Returns OPTION's random seed used by MC_RANDOM and
+   MC_DROP_RANDOM. */
+unsigned int
+mc_options_get_seed (const struct mc_options *options)
+{
+  return options->seed;
+}
+
+/* Set OPTION's random seed used by MC_RANDOM and MC_DROP_RANDOM
+   to SEED. */
+void
+mc_options_set_seed (struct mc_options *options, unsigned int seed)
+{
+  options->seed = seed;
+}
+
+/* Returns the maximum depth to which OPTIONS's search will
+   descend.  The initial states are at depth 1, states produced
+   as their mutations are at depth 2, and so on. */
+int
+mc_options_get_max_depth (const struct mc_options *options)
+{
+  return options->max_depth;
+}
+
+/* Sets the maximum depth to which OPTIONS's search will descend
+   to MAX_DEPTH.  The initial states are at depth 1, states
+   produced as their mutations are at depth 2, and so on. */
+void
+mc_options_set_max_depth (struct mc_options *options, int max_depth)
+{
+  options->max_depth = max_depth;
+}
+
+/* Returns the base-2 log of the number of bits in OPTIONS's hash
+   table.  The hash table is used for dropping states that are
+   probably duplicates: any state with a given hash value, as
+   will only be processed once.  A return value of 0 indicates
+   that the model checker will not discard duplicate states based
+   on their hashes.
+
+   The hash table is a power of 2 bits long, by default 2**20
+   bits (128 kB).  Depending on how many states you expect the
+   model checker to check, how much memory you're willing to let
+   the hash table take up, and how worried you are about missing
+   states due to hash collisions, you could make it larger or
+   smaller.
+
+   The "birthday paradox" points to a reasonable way to size your
+   hash table.  If you expect the model checker to check about
+   2**N states, then, assuming a perfect hash, you need a hash
+   table of 2**(N+1) bits to have a 50% chance of seeing a hash
+   collision, 2**(N+2) bits to have a 25% chance, and so on. */
+int
+mc_options_get_hash_bits (const struct mc_options *options)
+{
+  return options->hash_bits;
+}
+
+/* Sets the base-2 log of the number of bits in OPTIONS's hash
+   table to HASH_BITS.  A HASH_BITS value of 0 requests that the
+   model checker not discard duplicate states based on their
+   hashes.  (This causes the model checker to never terminate in
+   many cases.) */
+void
+mc_options_set_hash_bits (struct mc_options *options, int hash_bits)
+{
+  assert (hash_bits >= 0);
+  options->hash_bits = MIN (hash_bits, CHAR_BIT * sizeof (unsigned int) - 1);
+}
+
+/* Returns the path set in OPTIONS by mc_options_set_follow_path.
+   May be used only if the search strategy is MC_PATH. */
+const struct mc_path *
+mc_options_get_follow_path (const struct mc_options *options)
+{
+  assert (options->strategy == MC_PATH);
+  return &options->follow_path;
+}
+
+/* Sets, in OPTIONS, the search algorithm to MC_PATH and the path
+   to be the explicit path specified in FOLLOW_PATH. */
+void
+mc_options_set_follow_path (struct mc_options *options,
+                            const struct mc_path *follow_path)
+{
+  assert (mc_path_get_length (follow_path) > 0);
+  options->strategy = MC_PATH;
+  mc_path_copy (&options->follow_path, follow_path);
+}
+
+/* Returns the maximum number of queued states in OPTIONS.  The
+   default value is 10,000.  The primary reason to limit the
+   number of queued states is to conserve memory, so if you can
+   afford the memory and your model needs more room in the queue,
+   you can raise the limit.  Conversely, if your models are large
+   or memory is constrained, you can reduce the limit.
+
+   Following the execution of the model checker, you can find out
+   the maximum queue length during the run by calling
+   mc_results_get_max_queue_length. */
+int
+mc_options_get_queue_limit (const struct mc_options *options)
+{
+  return options->queue_limit;
+}
+
+/* Sets the maximum number of queued states in OPTIONS to
+   QUEUE_LIMIT.  */
+void
+mc_options_set_queue_limit (struct mc_options *options, int queue_limit)
+{
+  assert (queue_limit > 0);
+  options->queue_limit = queue_limit;
+}
+
+/* Returns the queue limit strategy used by OPTIONS, that is,
+   when a new state must be inserted into a full state queue is
+   full, how the state to be dropped is chosen.  The choices are:
+
+   - MC_DROP_NEWEST: Drop the newest state; that is, do not
+     insert the new state into the queue at all.
+
+   - MC_DROP_OLDEST: Drop the state that has been enqueued for
+     the longest.
+
+   - MC_DROP_RANDOM (the default): Drop a randomly selected state
+     from the queue.  The standard C library's rand function
+     selects the state to drop; you can control the seed passed
+     to srand using mc_options_set_seed. */
+enum mc_queue_limit_strategy
+mc_options_get_queue_limit_strategy (const struct mc_options *options)
+{
+  return options->queue_limit_strategy;
+}
+
+/* Sets the queue limit strategy used by OPTIONS to STRATEGY.
+
+   This setting has no effect unless the model being checked
+   causes the state queue to overflow (see
+   mc_options_get_queue_limit). */
+void
+mc_options_set_queue_limit_strategy (struct mc_options *options,
+                                     enum mc_queue_limit_strategy strategy)
+{
+  assert (strategy == MC_DROP_NEWEST
+          || strategy == MC_DROP_OLDEST
+          || strategy == MC_DROP_RANDOM);
+  options->queue_limit_strategy = strategy;
+}
+
+/* Returns OPTIONS's maximum number of unique states that the
+   model checker will examine before terminating.  The default is
+   INT_MAX. */
+int
+mc_options_get_max_unique_states (const struct mc_options *options)
+{
+  return options->max_unique_states;
+}
+
+/* Sets OPTIONS's maximum number of unique states that the model
+   checker will examine before terminating to
+   MAX_UNIQUE_STATE. */
+void
+mc_options_set_max_unique_states (struct mc_options *options,
+                                  int max_unique_states)
+{
+  options->max_unique_states = max_unique_states;
+}
+
+/* Returns the maximum number of errors that OPTIONS will allow
+   the model checker to encounter before terminating.  The
+   default is 1. */
+int
+mc_options_get_max_errors (const struct mc_options *options)
+{
+  return options->max_errors;
+}
+
+/* Sets the maximum number of errors that OPTIONS will allow the
+   model checker to encounter before terminating to
+   MAX_ERRORS. */
+void
+mc_options_set_max_errors (struct mc_options *options, int max_errors)
+{
+  options->max_errors = max_errors;
+}
+
+/* Returns the maximum amount of time, in seconds, that OPTIONS will allow the
+   model checker to consume before terminating.  The
+   default of 0.0 means that time consumption is unlimited. */
+double
+mc_options_get_time_limit (const struct mc_options *options)
+{
+  return options->time_limit;
+}
+
+/* Sets the maximum amount of time, in seconds, that OPTIONS will
+   allow the model checker to consume before terminating to
+   TIME_LIMIT.  A value of 0.0 means that time consumption is
+   unlimited; otherwise, the return value will be positive. */
+void
+mc_options_set_time_limit (struct mc_options *options, double time_limit)
+{
+  assert (time_limit >= 0.0);
+  options->time_limit = time_limit;
+}
+
+/* Returns the level of verbosity for output messages specified
+   by OPTIONS.  The default verbosity level is 1.
+
+   A verbosity level of 0 inhibits all messages except for
+   errors; a verbosity level of 1 also allows warnings; a
+   verbosity level of 2 also causes a description of each state
+   added to be output; a verbosity level of 3 also causes a
+   description of each duplicate state to be output.  Verbosity
+   levels less than 0 or greater than 3 are allowed but currently
+   have no additional effect. */
+int
+mc_options_get_verbosity (const struct mc_options *options)
+{
+  return options->verbosity;
+}
+
+/* Sets the level of verbosity for output messages specified
+   by OPTIONS to VERBOSITY. */
+void
+mc_options_set_verbosity (struct mc_options *options, int verbosity)
+{
+  options->verbosity = verbosity;
+}
+
+/* Returns the level of verbosity for failures specified by
+   OPTIONS.  The default failure verbosity level is 2.
+
+   The failure verbosity level has an effect only when an error
+   is reported, and only when the failure verbosity level is
+   higher than the regular verbosity level.  When this is the
+   case, the model checker replays the error path at the higher
+   verbosity level specified.  This has the effect of outputting
+   an explicit, human-readable description of the sequence of
+   operations that caused the error. */
+int
+mc_options_get_failure_verbosity (const struct mc_options *options)
+{
+  return options->failure_verbosity;
+}
+
+/* Sets the level of verbosity for failures specified by OPTIONS
+   to FAILURE_VERBOSITY. */
+void
+mc_options_set_failure_verbosity (struct mc_options *options,
+                                  int failure_verbosity)
+{
+  options->failure_verbosity = failure_verbosity;
+}
+
+/* Returns the output file used for messages printed by the model
+   checker specified by OPTIONS.  The default is stdout. */
+FILE *
+mc_options_get_output_file (const struct mc_options *options)
+{
+  return options->output_file;
+}
+
+/* Sets the output file used for messages printed by the model
+   checker specified by OPTIONS to OUTPUT_FILE.
+
+   The model checker does not automatically close the specified
+   output file.  If this is desired, the model checker's client
+   must do so. */
+void
+mc_options_set_output_file (struct mc_options *options,
+                            FILE *output_file)
+{
+  options->output_file = output_file;
+}
+
+/* Returns the number of microseconds between calls to the
+   progress function specified by OPTIONS.   The default is
+   250,000 (1/4 second).  A value of 0 disables progress
+   reporting. */
+int
+mc_options_get_progress_usec (const struct mc_options *options)
+{
+  return options->progress_usec;
+}
+
+/* Sets the number of microseconds between calls to the progress
+   function specified by OPTIONS to PROGRESS_USEC.  A value of 0
+   disables progress reporting. */
+void
+mc_options_set_progress_usec (struct mc_options *options, int progress_usec)
+{
+  assert (progress_usec >= 0);
+  options->progress_usec = progress_usec;
+}
+
+/* Returns the function called to report progress specified by
+   OPTIONS.  The function used by default prints '.' to
+   stderr. */
+mc_progress_func *
+mc_options_get_progress_func (const struct mc_options *options)
+{
+  return options->progress_func;
+}
+
+/* Sets the function called to report progress specified by
+   OPTIONS to PROGRESS_FUNC.  A non-null function must be
+   specified; to disable progress reporting, set the progress
+   reporting interval to 0.
+
+   PROGRESS_FUNC will be called zero or more times while the
+   model checker's run is ongoing.  For these calls to the
+   progress function, mc_results_get_stop_reason will return
+   MC_CONTINUING.  It will also be called exactly once soon
+   before mc_run returns, in which case
+   mc_results_get_stop_reason will return a different value. */
+void
+mc_options_set_progress_func (struct mc_options *options,
+                              mc_progress_func *progress_func)
+{
+  assert (options->progress_func != NULL);
+  options->progress_func = progress_func;
+}
+
+/* Returns the auxiliary data set in OPTIONS by the client.  The
+   default is a null pointer.
+
+   This auxiliary data value can be retrieved by the
+   client-specified functions in struct mc_class during a model
+   checking run using mc_get_aux. */
+void *
+mc_options_get_aux (const struct mc_options *options)
+{
+  return options->aux;
+}
+
+/* Sets the auxiliary data in OPTIONS to AUX. */
+void
+mc_options_set_aux (struct mc_options *options, void *aux)
+{
+  options->aux = aux;
+}
+\f
+/* Results of a model checking run. */
+struct mc_results
+  {
+    /* Overall results. */
+    enum mc_stop_reason stop_reason;    /* Why the run ended. */
+    int unique_state_count;             /* Number of unique states checked. */
+    int error_count;                    /* Number of errors found. */
+
+    /* Depth statistics. */
+    int max_depth_reached;              /* Max depth state examined. */
+    struct moments1 *depth_moments;     /* Enables reporting mean depth. */
+
+    /* If error_count > 0, path to the last error reported. */
+    struct mc_path error_path;
+
+    /* States dropped... */
+    int duplicate_dropped_states;       /* ...as duplicates. */
+    int off_path_dropped_states;        /* ...as off-path (MC_PATH only). */
+    int depth_dropped_states;           /* ...due to excessive depth. */
+    int queue_dropped_states;           /* ...due to queue overflow. */
+
+    /* Queue statistics. */
+    int queued_unprocessed_states;      /* Enqueued but never dequeued. */
+    int max_queue_length;               /* Maximum queue length observed. */
+
+    /* Timing. */
+    struct timeval start;               /* Start of model checking run. */
+    struct timeval end;                 /* End of model checking run. */
+  };
+
+/* Creates, initializes, and returns a new set of results. */
+static struct mc_results *
+mc_results_create (void)
+{
+  struct mc_results *results = xcalloc (1, sizeof (struct mc_results));
+  results->stop_reason = MC_CONTINUING;
+  results->depth_moments = moments1_create (MOMENT_MEAN);
+  gettimeofday (&results->start, NULL);
+  return results;
+}
+
+/* Destroys RESULTS. */
+void
+mc_results_destroy (struct mc_results *results)
+{
+  if (results != NULL)
+    {
+      moments1_destroy (results->depth_moments);
+      mc_path_destroy (&results->error_path);
+      free (results);
+    }
+}
+
+/* Returns RESULTS's reason that the model checking run
+   terminated.  The possible reasons are:
+
+   - MC_CONTINUING: The run is not actually yet complete.  This
+     can only be returned before mc_run has returned, e.g. when
+     the progress function set by mc_options_set_progress_func
+     examines the run's results.
+
+   - MC_SUCCESS: The run completed because the queue emptied.
+     The entire state space might not have been explored due to a
+     requested limit on maximum depth, hash collisions, etc.
+
+   - MC_MAX_UNIQUE_STATES: The run completed because as many
+     unique states have been checked as were requested (using
+     mc_options_set_max_unique_states).
+
+   - MC_MAX_ERROR_COUNT: The run completed because the maximum
+     requested number of errors (by default, 1 error) was
+     reached.
+
+   - MC_END_OF_PATH: The run completed because the path specified
+     with mc_options_set_follow_path was fully traversed.
+
+   - MC_TIMEOUT: The run completed because the time limit set
+     with mc_options_set_time_limit was exceeded.
+
+   - MC_INTERRUPTED: The run completed because SIGINT was caught
+     (typically, due to the user typing Ctrl+C). */
+enum mc_stop_reason
+mc_results_get_stop_reason (const struct mc_results *results)
+{
+  return results->stop_reason;
+}
+
+/* Returns the number of unique states checked specified by
+   RESULTS. */
+int
+mc_results_get_unique_state_count (const struct mc_results *results)
+{
+  return results->unique_state_count;
+}
+
+/* Returns the number of errors found specified by RESULTS. */
+int
+mc_results_get_error_count (const struct mc_results *results)
+{
+  return results->error_count;
+}
+
+/* Returns the maximum depth reached during the model checker run
+   represented by RESULTS.  The initial states are at depth 1,
+   their child states at depth 2, and so on. */
+int
+mc_results_get_max_depth_reached (const struct mc_results *results)
+{
+  return results->max_depth_reached;
+}
+
+/* Returns the mean depth reached during the model checker run
+   represented by RESULTS. */
+double
+mc_results_get_mean_depth_reached (const struct mc_results *results)
+{
+  double mean;
+  moments1_calculate (results->depth_moments, NULL, &mean, NULL, NULL, NULL);
+  return mean != SYSMIS ? mean : 0.0;
+}
+
+/* Returns the path traversed to obtain the last error
+   encountered during the model checker run represented by
+   RESULTS.  Returns a null pointer if the run did not report any
+   errors. */
+const struct mc_path *
+mc_results_get_error_path (const struct mc_results *results)
+{
+  return results->error_count > 0 ? &results->error_path : NULL;
+}
+
+/* Returns the number of states dropped as duplicates (based on
+   hash value) during the model checker run represented by
+   RESULTS. */
+int
+mc_results_get_duplicate_dropped_states (const struct mc_results *results)
+{
+  return results->duplicate_dropped_states;
+}
+
+/* Returns the number of states dropped because they were off the
+   path specified by mc_options_set_follow_path during the model
+   checker run represented by RESULTS.  A nonzero value here
+   indicates a missing call to mc_include_state in the
+   client-supplied mutation function. */
+int
+mc_results_get_off_path_dropped_states (const struct mc_results *results)
+{
+  return results->off_path_dropped_states;
+}
+
+/* Returns the number of states dropped because their depth
+   exceeded the maximum specified with mc_options_set_max_depth
+   during the model checker run represented by RESULTS. */
+int
+mc_results_get_depth_dropped_states (const struct mc_results *results)
+{
+  return results->depth_dropped_states;
+}
+
+/* Returns the number of states dropped from the queue due to
+   queue overflow during the model checker run represented by
+   RESULTS. */
+int
+mc_results_get_queue_dropped_states (const struct mc_results *results)
+{
+  return results->queue_dropped_states;
+}
+
+/* Returns the number of states that were checked and enqueued
+   but never dequeued and processed during the model checker run
+   represented by RESULTS.  This is zero if the stop reason is
+   MC_CONTINUING or MC_SUCCESS; otherwise, it is the number of
+   states in the queue at the time that the checking run
+   stopped. */
+int
+mc_results_get_queued_unprocessed_states (const struct mc_results *results)
+{
+  return results->queued_unprocessed_states;
+}
+
+/* Returns the maximum length of the queue during the model
+   checker run represented by RESULTS.  If this is equal to the
+   maximum queue length, then the queue (probably) overflowed
+   during the run; otherwise, it did not overflow. */
+int
+mc_results_get_max_queue_length (const struct mc_results *results)
+{
+  return results->max_queue_length;
+}
+
+/* Returns the time at which the model checker run represented by
+   RESULTS started. */
+struct timeval
+mc_results_get_start (const struct mc_results *results)
+{
+  return results->start;
+}
+
+/* Returns the time at which the model checker run represented by
+   RESULTS ended.  (This function may not be called while the run
+   is still ongoing.) */
+struct timeval
+mc_results_get_end (const struct mc_results *results)
+{
+  assert (results->stop_reason != MC_CONTINUING);
+  return results->end;
+}
+
+/* Returns the number of seconds obtained by subtracting time Y
+   from time X. */
+static double
+timeval_subtract (struct timeval x, struct timeval y)
+{
+  /* From libc.info. */
+  double difference;
+
+  /* Perform the carry for the later subtraction by updating Y. */
+  if (x.tv_usec < y.tv_usec) {
+    int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
+    y.tv_usec -= 1000000 * nsec;
+    y.tv_sec += nsec;
+  }
+  if (x.tv_usec - y.tv_usec > 1000000) {
+    int nsec = (x.tv_usec - y.tv_usec) / 1000000;
+    y.tv_usec += 1000000 * nsec;
+    y.tv_sec -= nsec;
+  }
+
+  /* Compute the time remaining to wait.
+     `tv_usec' is certainly positive. */
+  difference = (x.tv_sec - y.tv_sec) + (x.tv_usec - y.tv_usec) / 1000000.0;
+  if (x.tv_sec < y.tv_sec)
+    difference = -difference;
+  return difference;
+}
+
+
+/* Returns the duration, in seconds, of the model checker run
+   represented by RESULTS.  (This function may not be called
+   while the run is still ongoing.) */
+double
+mc_results_get_duration (const struct mc_results *results)
+{
+  assert (results->stop_reason != MC_CONTINUING);
+  return timeval_subtract (results->end, results->start);
+}
+\f
+/* An active model checking run. */
+struct mc
+  {
+    /* Related data structures. */
+    const struct mc_class *class;
+    struct mc_options *options;
+    struct mc_results *results;
+
+    /* Array of 2**(options->hash_bits) bits representing states
+       already visited. */
+    unsigned char *hash;
+
+    /* State queue. */
+    struct mc_state **queue;            /* Array of pointers to states. */
+    struct deque queue_deque;           /* Deque. */
+
+    /* State currently being built by "init" or "mutate". */
+    struct mc_path path;                /* Path to current state. */
+    struct string path_string;          /* Buffer for path_string function. */
+    bool state_named;                   /* mc_name_operation called? */
+    bool state_error;                   /* mc_error called? */
+
+    /* Statistics for calling the progress function. */
+    unsigned int progress;              /* Current progress value. */
+    unsigned int next_progress;         /* Next value to call progress func. */
+    unsigned int prev_progress;         /* Last value progress func called. */
+    struct timeval prev_progress_time;  /* Last time progress func called. */
+
+    /* Information for handling and restoring SIGINT. */
+    bool interrupted;                   /* SIGINT received? */
+    bool *saved_interrupted_ptr;        /* Saved value of interrupted_ptr. */
+    void (*saved_sigint) (int);         /* Saved SIGINT handler. */
+  };
+
+/* A state in the queue. */
+struct mc_state
+  {
+    struct mc_path path;                /* Path to this state. */
+    void *data;                         /* Client-supplied data. */
+  };
+
+/* Points to the current struct mc's "interrupted" member. */
+static bool *interrupted_ptr = NULL;
+
+static const char *path_string (struct mc *);
+static void free_state (const struct mc *, struct mc_state *);
+static void stop (struct mc *, enum mc_stop_reason);
+static struct mc_state *make_state (const struct mc *, void *);
+static size_t random_queue_index (struct mc *);
+static void enqueue_state (struct mc *, struct mc_state *);
+static void do_error_state (struct mc *);
+static void next_operation (struct mc *);
+static bool is_off_path (const struct mc *);
+static void sigint_handler (int signum);
+static void init_mc (struct mc *,
+                     const struct mc_class *, struct mc_options *);
+static void finish_mc (struct mc *);
+
+/* Runs the model checker on the client-specified CLASS with the
+   client-specified OPTIONS.  OPTIONS may be a null pointer if
+   the defaults are acceptable.  Destroys OPTIONS; use
+   mc_options_clone if a copy is needed.
+
+   Returns the results of the model checking run, which must be
+   destroyed by the client with mc_results_destroy.
+
+   To pass auxiliary data to the functions in CLASS, use
+   mc_options_set_aux on OPTIONS, which may be retrieved from the
+   CLASS functions using mc_get_aux. */
+struct mc_results *
+mc_run (const struct mc_class *class, struct mc_options *options)
+{
+  struct mc mc;
+
+  init_mc (&mc, class, options);
+  while (!deque_is_empty (&mc.queue_deque)
+         && mc.results->stop_reason == MC_CONTINUING)
+    {
+      struct mc_state *state = mc.queue[deque_pop_front (&mc.queue_deque)];
+      mc_path_copy (&mc.path, &state->path);
+      mc_path_push (&mc.path, 0);
+      class->mutate (&mc, state->data);
+      free_state (&mc, state);
+      if (mc.interrupted)
+        stop (&mc, MC_INTERRUPTED);
+    }
+  finish_mc (&mc);
+
+  return mc.results;
+}
+
+/* Tests whether the current operation is one that should be
+   performed, checked, and enqueued.  If so, returns true.
+   Otherwise, returns false and, unless checking is stopped,
+   advances to the next state.  The caller should then advance
+   to the next operation.
+
+   This function should be called from the client-provided
+   "mutate" function, according to the pattern explained in the
+   big comment at the top of model-checker.h. */
+bool
+mc_include_state (struct mc *mc)
+{
+  if (mc->results->stop_reason != MC_CONTINUING)
+    return false;
+  else if (is_off_path (mc))
+    {
+      next_operation (mc);
+      return false;
+    }
+  else
+    return true;
+}
+
+/* Tests whether HASH represents a state that has (probably)
+   already been enqueued.  If not, returns false and marks HASH
+   so that it will be treated as a duplicate in the future.  If
+   so, returns true and advances to the next state.  The
+   caller should then advance to the next operation.
+
+   This function should be called from the client-provided
+   "mutate" function, according to the pattern explained in the
+   big comment at the top of model-checker.h. */
+bool
+mc_discard_dup_state (struct mc *mc, unsigned int hash)
+{
+  if (mc->options->hash_bits > 0)
+    {
+      hash &= (1u << mc->options->hash_bits) - 1;
+      if (TEST_BIT (mc->hash, hash))
+        {
+          if (mc->options->verbosity > 2)
+            fprintf (mc->options->output_file,
+                     "    [%s] discard duplicate state\n", path_string (mc));
+          mc->results->duplicate_dropped_states++;
+          next_operation (mc);
+          return true;
+        }
+      SET_BIT (mc->hash, hash);
+    }
+  return false;
+}
+
+/* Names the current state NAME, which may contain
+   printf-style format specifications.  NAME should be a
+   human-readable name for the current operation.
+
+   This function should be called from the client-provided
+   "mutate" function, according to the pattern explained in the
+   big comment at the top of model-checker.h. */
+void
+mc_name_operation (struct mc *mc, const char *name, ...)
+{
+  va_list args;
+
+  va_start (args, name);
+  mc_vname_operation (mc, name, args);
+  va_end (args);
+}
+
+/* Names the current state NAME, which may contain
+   printf-style format specifications, for which the
+   corresponding arguments must be given in ARGS.  NAME should be
+   a human-readable name for the current operation.
+
+   This function should be called from the client-provided
+   "mutate" function, according to the pattern explained in the
+   big comment at the top of model-checker.h. */
+void
+mc_vname_operation (struct mc *mc, const char *name, va_list args)
+{
+  if (mc->state_named && mc->options->verbosity > 0)
+    fprintf (mc->options->output_file, "  [%s] warning: duplicate call "
+             "to mc_name_operation (missing call to mc_add_state?)\n",
+             path_string (mc));
+  mc->state_named = true;
+
+  if (mc->options->verbosity > 1)
+    {
+      fprintf (mc->options->output_file, "  [%s] ", path_string (mc));
+      vfprintf (mc->options->output_file, name, args);
+      putc ('\n', mc->options->output_file);
+    }
+}
+
+/* Reports the given error MESSAGE for the current operation.
+   The resulting state should still be passed to mc_add_state
+   when all relevant error messages have been issued.  The state
+   will not, however, be enqueued for later mutation of its own.
+
+   By default, model checking stops after the first error
+   encountered.
+
+   This function should be called from the client-provided
+   "mutate" function, according to the pattern explained in the
+   big comment at the top of model-checker.h. */
+void
+mc_error (struct mc *mc, const char *message, ...)
+{
+  va_list args;
+
+  if (mc->results->stop_reason != MC_CONTINUING)
+    return;
+
+  if (mc->options->verbosity > 1)
+    fputs ("    ", mc->options->output_file);
+  fprintf (mc->options->output_file, "[%s] error: ",
+           path_string (mc));
+  va_start (args, message);
+  vfprintf (mc->options->output_file, message, args);
+  va_end (args);
+  putc ('\n', mc->options->output_file);
+
+  mc->state_error = true;
+}
+
+/* Enqueues DATA as the state corresponding to the current
+   operation.  The operation should have been named with a call
+   to mc_name_operation, and it should have been checked by the
+   caller (who should have reported any errors with mc_error).
+
+   This function should be called from the client-provided
+   "mutate" function, according to the pattern explained in the
+   big comment at the top of model-checker.h. */
+void
+mc_add_state (struct mc *mc, void *data)
+{
+  if (!mc->state_named && mc->options->verbosity > 0)
+    fprintf (mc->options->output_file, "  [%s] warning: unnamed state\n",
+             path_string (mc));
+
+  if (mc->results->stop_reason != MC_CONTINUING)
+    {
+      /* Nothing to do. */
+    }
+  else if (mc->state_error)
+    do_error_state (mc);
+  else if (is_off_path (mc))
+    mc->results->off_path_dropped_states++;
+  else if (mc->path.length + 1 > mc->options->max_depth)
+    mc->results->depth_dropped_states++;
+  else
+    {
+      /* This is the common case. */
+      mc->results->unique_state_count++;
+      if (mc->results->unique_state_count >= mc->options->max_unique_states)
+        stop (mc, MC_MAX_UNIQUE_STATES);
+      enqueue_state (mc, make_state (mc, data));
+      next_operation (mc);
+      return;
+    }
+
+  mc->class->destroy (mc, data);
+  next_operation (mc);
+}
+
+/* Returns the options that were passed to mc_run for model
+   checker MC. */
+const struct mc_options *
+mc_get_options (const struct mc *mc)
+{
+  return mc->options;
+}
+
+/* Returns the current state of the results for model checker
+   MC.  This function is appropriate for use from the progress
+   function set by mc_options_set_progress_func.
+
+   Not all of the results are meaningful before model checking
+   completes. */
+const struct mc_results *
+mc_get_results (const struct mc *mc)
+{
+  return mc->results;
+}
+
+/* Returns the auxiliary data set on the options passed to mc_run
+   with mc_options_set_aux. */
+void *
+mc_get_aux (const struct mc *mc)
+{
+  return mc_options_get_aux (mc_get_options (mc));
+}
+\f
+/* Expresses MC->path as a string and returns the string. */
+static const char *
+path_string (struct mc *mc)
+{
+  ds_clear (&mc->path_string);
+  mc_path_to_string (&mc->path, &mc->path_string);
+  return ds_cstr (&mc->path_string);
+}
+
+/* Frees STATE, including client data. */
+static void
+free_state (const struct mc *mc, struct mc_state *state)
+{
+  mc->class->destroy (mc, state->data);
+  mc_path_destroy (&state->path);
+  free (state);
+}
+
+/* Sets STOP_REASON as the reason that MC's processing stopped,
+   unless MC is already stopped. */
+static void
+stop (struct mc *mc, enum mc_stop_reason stop_reason)
+{
+  if (mc->results->stop_reason == MC_CONTINUING)
+    mc->results->stop_reason = stop_reason;
+}
+
+/* Creates and returns a new state whose path is copied from
+   MC->path and whose data is specified by DATA. */
+static struct mc_state *
+make_state (const struct mc *mc, void *data)
+{
+  struct mc_state *new = xmalloc (sizeof *new);
+  mc_path_init (&new->path);
+  mc_path_copy (&new->path, &mc->path);
+  new->data = data;
+  return new;
+}
+
+/* Returns the index in MC->queue of a random element in the
+   queue. */
+static size_t
+random_queue_index (struct mc *mc)
+{
+  assert (!deque_is_empty (&mc->queue_deque));
+  return deque_front (&mc->queue_deque,
+                      rand () % deque_count (&mc->queue_deque));
+}
+
+/* Adds NEW to MC's state queue, dropping a state if necessary
+   due to overflow. */
+static void
+enqueue_state (struct mc *mc, struct mc_state *new)
+{
+  size_t idx;
+
+  if (new->path.length > mc->results->max_depth_reached)
+    mc->results->max_depth_reached = new->path.length;
+  moments1_add (mc->results->depth_moments, new->path.length, 1.0);
+
+  if (deque_count (&mc->queue_deque) < mc->options->queue_limit)
+    {
+      /* Add new state to queue. */
+      if (deque_is_full (&mc->queue_deque))
+        mc->queue = deque_expand (&mc->queue_deque,
+                                   mc->queue, sizeof *mc->queue);
+      switch (mc->options->strategy)
+        {
+        case MC_BROAD:
+          idx = deque_push_back (&mc->queue_deque);
+          break;
+        case MC_DEEP:
+          idx = deque_push_front (&mc->queue_deque);
+          break;
+        case MC_RANDOM:
+          if (!deque_is_empty (&mc->queue_deque))
+            {
+              idx = random_queue_index (mc);
+              mc->queue[deque_push_front (&mc->queue_deque)]
+                = mc->queue[idx];
+            }
+          else
+            idx = deque_push_front (&mc->queue_deque);
+          break;
+        case MC_PATH:
+          assert (deque_is_empty (&mc->queue_deque));
+          assert (!is_off_path (mc));
+          idx = deque_push_back (&mc->queue_deque);
+          if (mc->path.length
+              >= mc_path_get_length (&mc->options->follow_path))
+            stop (mc, MC_END_OF_PATH);
+          break;
+        default:
+          NOT_REACHED ();
+        }
+      if (deque_count (&mc->queue_deque) > mc->results->max_queue_length)
+        mc->results->max_queue_length = deque_count (&mc->queue_deque);
+    }
+  else
+    {
+      /* Queue has reached limit, so replace an existing
+         state. */
+      assert (mc->options->strategy != MC_PATH);
+      assert (!deque_is_empty (&mc->queue_deque));
+      mc->results->queue_dropped_states++;
+      switch (mc->options->queue_limit_strategy)
+        {
+        case MC_DROP_NEWEST:
+          free_state (mc, new);
+          return;
+        case MC_DROP_OLDEST:
+          switch (mc->options->strategy)
+            {
+            case MC_BROAD:
+              idx = deque_front (&mc->queue_deque, 0);
+              break;
+            case MC_DEEP:
+              idx = deque_back (&mc->queue_deque, 0);
+              break;
+            case MC_RANDOM:
+            case MC_PATH:
+            default:
+              NOT_REACHED ();
+            }
+          break;
+        case MC_DROP_RANDOM:
+          idx = random_queue_index (mc);
+          break;
+        default:
+          NOT_REACHED ();
+        }
+      free_state (mc, mc->queue[idx]);
+    }
+  mc->queue[idx] = new;
+}
+
+/* Process an error state being added to MC. */
+static void
+do_error_state (struct mc *mc)
+{
+  mc->results->error_count++;
+  if (mc->results->error_count >= mc->options->max_errors)
+    stop (mc, MC_MAX_ERROR_COUNT);
+
+  mc_path_copy (&mc->results->error_path, &mc->path);
+
+  if (mc->options->failure_verbosity > mc->options->verbosity)
+    {
+      struct mc_options *path_options;
+
+      fprintf (mc->options->output_file, "[%s] retracing error path:\n",
+               path_string (mc));
+      path_options = mc_options_clone (mc->options);
+      mc_options_set_verbosity (path_options, mc->options->failure_verbosity);
+      mc_options_set_failure_verbosity (path_options, 0);
+      mc_options_set_follow_path (path_options, &mc->path);
+
+      mc_results_destroy (mc_run (mc->class, path_options));
+
+      putc ('\n', mc->options->output_file);
+    }
+}
+
+/* Advances MC to start processing the operation following the
+   current one. */
+static void
+next_operation (struct mc *mc)
+{
+  mc_path_push (&mc->path, mc_path_pop (&mc->path) + 1);
+  mc->state_error = false;
+  mc->state_named = false;
+
+  if (++mc->progress >= mc->next_progress)
+    {
+      struct timeval now;
+      double elapsed, delta;
+
+      if (mc->results->stop_reason == MC_CONTINUING
+          && !mc->options->progress_func (mc))
+        stop (mc, MC_INTERRUPTED);
+
+      gettimeofday (&now, NULL);
+
+      if (mc->options->time_limit > 0.0
+          && (timeval_subtract (now, mc->results->start)
+              > mc->options->time_limit))
+        stop (mc, MC_TIMEOUT);
+
+      elapsed = timeval_subtract (now, mc->prev_progress_time);
+      if (elapsed > 0.0)
+        {
+          /* Re-estimate the amount of progress to take
+             progress_usec microseconds. */
+          unsigned int progress = mc->progress - mc->prev_progress;
+          double progress_sec = mc->options->progress_usec / 1000000.0;
+          delta = progress / elapsed * progress_sec;
+        }
+      else
+        {
+          /* No measurable time at all elapsed during that amount
+             of progress.  Try doubling the amount of progress
+             required. */
+          delta = (mc->progress - mc->prev_progress) * 2;
+        }
+
+      if (delta > 0.0 && delta + mc->progress + 1.0 < UINT_MAX)
+        mc->next_progress = mc->progress + delta + 1.0;
+      else
+        mc->next_progress = mc->progress + (mc->progress - mc->prev_progress);
+
+      mc->prev_progress = mc->progress;
+      mc->prev_progress_time = now;
+    }
+}
+
+/* Returns true if we're tracing an explicit path but the current
+   operation produces a state off that path, false otherwise. */
+static bool
+is_off_path (const struct mc *mc)
+{
+  return (mc->options->strategy == MC_PATH
+          && (mc_path_back (&mc->path)
+              != mc_path_get_operation (&mc->options->follow_path,
+                                        mc->path.length - 1)));
+}
+
+/* Handler for SIGINT. */
+static void
+sigint_handler (int signum UNUSED)
+{
+  /* Just mark the model checker as interrupted. */
+  *interrupted_ptr = true;
+}
+
+/* Initializes MC as a model checker with the given CLASS and
+   OPTIONS.  OPTIONS may be null to use the default options. */
+static void
+init_mc (struct mc *mc, const struct mc_class *class,
+         struct mc_options *options)
+{
+  /* Validate and adjust OPTIONS. */
+  if (options == NULL)
+    options = mc_options_create ();
+  assert (options->queue_limit_strategy != MC_DROP_OLDEST
+          || options->strategy != MC_RANDOM);
+  if (options->strategy == MC_PATH)
+    {
+      options->max_depth = INT_MAX;
+      options->hash_bits = 0;
+    }
+  if (options->progress_usec == 0)
+    {
+      options->progress_func = null_progress;
+      if (options->time_limit > 0.0)
+        options->progress_usec = 250000;
+    }
+
+  /* Initialize MC. */
+  mc->class = class;
+  mc->options = options;
+  mc->results = mc_results_create ();
+
+  mc->hash = (mc->options->hash_bits > 0
+              ? xcalloc (1, DIV_RND_UP (1 << mc->options->hash_bits, CHAR_BIT))
+              : NULL);
+
+  mc->queue = NULL;
+  deque_init_null (&mc->queue_deque);
+
+  mc_path_init (&mc->path);
+  mc_path_push (&mc->path, 0);
+  ds_init_empty (&mc->path_string);
+  mc->state_named = false;
+  mc->state_error = false;
+
+  mc->progress = 0;
+  mc->next_progress = mc->options->progress_usec != 0 ? 100 : UINT_MAX;
+  mc->prev_progress = 0;
+  mc->prev_progress_time = mc->results->start;
+
+  if (mc->options->strategy == MC_RANDOM
+      || options->queue_limit_strategy == MC_DROP_RANDOM)
+    srand (mc->options->seed);
+
+  mc->interrupted = false;
+  mc->saved_interrupted_ptr = interrupted_ptr;
+  interrupted_ptr = &mc->interrupted;
+  mc->saved_sigint = signal (SIGINT, sigint_handler);
+
+  class->init (mc);
+}
+
+/* Complete the model checker run for MC. */
+static void
+finish_mc (struct mc *mc)
+{
+  /* Restore signal handlers. */
+  signal (SIGINT, mc->saved_sigint);
+  interrupted_ptr = mc->saved_interrupted_ptr;
+
+  /* Mark the run complete. */
+  stop (mc, MC_SUCCESS);
+  gettimeofday (&mc->results->end, NULL);
+
+  /* Empty the queue. */
+  mc->results->queued_unprocessed_states = deque_count (&mc->queue_deque);
+  while (!deque_is_empty (&mc->queue_deque))
+    {
+      struct mc_state *state = mc->queue[deque_pop_front (&mc->queue_deque)];
+      free_state (mc, state);
+    }
+
+  /* Notify the progress function of completion. */
+  mc->options->progress_func (mc);
+
+  /* Free memory. */
+  mc_path_destroy (&mc->path);
+  ds_destroy (&mc->path_string);
+  free (mc->options);
+  free (mc->queue);
+  free (mc->hash);
+}
diff --git a/src/language/tests/model-checker.h b/src/language/tests/model-checker.h
new file mode 100644 (file)
index 0000000..8c86fae
--- /dev/null
@@ -0,0 +1,463 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007 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/>. */
+
+/* Implementation-level model checker.
+
+   A model checker is a tool for software testing and
+   verification that works by exploring all the possible states
+   in a system and verifying their internal consistency.  A
+   conventional model checker requires that the code in a system
+   be translated into a specification language.  The model
+   checker then verifies the specification, rather than the code.
+
+   This is instead an implementation-level model checker, which
+   does not require a separate specification.  Instead, the model
+   checker requires writing a second implementation of the system
+   being checked.  The second implementation can usually be made
+   almost trivial in comparison to the one being checked, because
+   it's usually acceptable for its performance to be
+   comparatively poor, e.g. O(N^2) instead of O(lg N), and thus
+   to use much simpler algorithms.
+
+   For introduction to the implementation-level model checking
+   approach used here, please refer to the following papers:
+
+     Musuvathi, Park, Chou, Engler, Dill, "CMC: A Pragmatic
+     Approach to Model Checking Real Code", Proceedings of the
+     Fifth Symposium on Operating Systems Design and
+     Implementation (OSDI), Dec 2002.
+     http://sprout.stanford.edu/PAPERS/CMC-OSDI-2002/CMC-OSDI-2002.pdf
+
+     Yang, Twohey, Engler, Musuvathi, "Using Model Checking to
+     Find Serious File System Errors", Proceedings of the Sixth
+     Symposium on Operating System Design and Implementation
+     (OSDI), Dec 2004.
+     http://www.stanford.edu/~engler/osdi04-fisc.pdf
+
+     Yang, Twohey, Pfaff, Sar, Engler, "EXPLODE: A Lightweight,
+     General Approach to Finding Serious Errors in Storage
+     Systems", First Workshop on the Evaluation of Software
+     Defect Detection Tools (BUGS), June 2005.
+     http://benpfaff.org/papers/explode.pdf
+
+   Use of a model checker is appropriate when the system being
+   checked is difficult to test using handwritten tests.  This
+   can be the case, for example, when the system has a
+   complicated internal state that is difficult to reason about
+   over a long series of operations.
+
+   The implementation model checker works by putting a set of one
+   of more initial states in a queue (and checking them for
+   consistency).  Then the model checker removes a state from the
+   queue and applies all possible operations of interest to it
+   ("mutates" it), obtaining a set of zero or more child states
+   (and checking each of them for consistency).  Each of these
+   states is itself added to the queue.  The model checker
+   continues dequeuing states and mutating and checking them
+   until the queue is empty.
+
+   In pseudo-code, the process looks like this:
+
+     Q = { initial states }
+     while Q is not empty:
+       S = dequeue(Q)
+       for each operation applicable to S do:
+         T = operation(S)
+         check(T)
+         enqueue(Q, T)
+
+   In many cases this process will never terminate, because every
+   state has one or more child states.  For some systems this is
+   unavoidable, but in others we can make the process finite by
+   pursuing a few stratagems:
+
+     1. Limit the maximum size of the system; for example, limit
+        the number of rows and columns in the implementation of a
+        table being checked.  The client of the model checker is
+        responsible for implementing such limits.
+
+     2. Avoid checking a single state more than one time.  This
+        model checker provides assistance for this function by
+        allowing the client to provide a hash of the system state.
+        States with identical hashes will only be checked once
+        during a single run.
+
+   When a system cannot be made finite, or when a finite system
+   is too large to check in a practical amount of time, the model
+   checker provides multiple ways to limit the checking run:
+   based on maximum depth, maximum unique states checked, maximum
+   errors found (by default, 1), or maximum time used for
+   checking.
+
+   The client of the model checker must provide three functions
+   via function pointers embedded into a "struct mc_class":
+
+     1. void init (struct mc *mc);
+
+        This function is called once at the beginning of a
+        checking run.  It checks one or more initial states and
+        adds them to the model checker's queue.  (If it does not
+        add any states to the queue, then there is nothing to
+        check.)
+
+        Here's an outline for writing the init function:
+
+          void
+          init_foo (struct mc *mc)
+          {
+            struct foo *foo;
+
+            mc_name_operation (mc, "initial state");
+            foo = generate_initial_foo ();
+            if (!state_is_consistent (foo))
+              mc_error (mc, "inconsistent state");
+            mc_add_state (mc, foo);
+          }
+
+     2. void mutate (struct mc *mc, const void *data);
+
+        This function is called when a dequeued state is ready to
+        be mutated.  For each operation that can be applied to
+        the client-specified DATA, it applies that operation to a
+        clone of the DATA, checks that the clone is consistent,
+        and adds the clone to the model checker's queue.
+
+        Here's an outline for writing the mutate function:
+
+          void
+          mutate_foo (struct mc *mc, void *state_)
+          {
+            struct foo *state = state_;
+
+            for (...each operation...)
+              if (mc_include_state (mc))
+                {
+                  struct foo *clone;
+
+                  mc_name_operation (mc, "do operation %s", ...);
+                  clone = clone_foo (state);
+                  do_operation (clone);
+                  if (!mc_discard_dup_state (mc, hash_foo (clone)))
+                    {
+                      if (!state_is_consistent (clone))
+                        mc_error (mc, "inconsistent state");
+                      mc_add_state (mc, clone);
+                    }
+                  else
+                    destroy_foo (clone);
+                }
+          }
+
+        Notes on the above outline:
+
+          - The call to mc_include_state allows currently
+            uninteresting operations to be skipped.  It is not
+            essential.
+
+          - The call to mc_name_operation should give the current
+            operation a human-readable name.  The name may
+            include printf-style format specifications.
+
+            When an error occurs, the model checker (by default)
+            replays the sequence of operations performed to reach
+            the error, printing the name of the operation at each
+            step, which is often sufficient information in itself
+            to debug the error.
+
+            At higher levels of verbosity, the name is printed
+            for each operation.
+
+          - Operations should be performed on a copy of the data
+            provided.  The data provided should not be destroyed
+            or modified.
+
+          - The call to mc_discard_dup_state is needed to discard
+            (probably) duplicate states.  It is otherwise
+            optional.
+
+            To reduce the probability of collisions, use a
+            high-quality hash function.  MD4 is a reasonable
+            choice: it is fast but high-quality.  In one test,
+            switching to MD4 from MD5 increased overall speed of
+            model checking by 8% and actually reduced (!) the
+            number of collisions.
+
+            The hash value needs to include enough of the state
+            to ensure that interesting states are not excluded,
+            but it need not include the entire state.  For
+            example, in many cases, the structure of complex data
+            (metadata) is often much more important than the
+            contents (data), so it may be reasonable to hash only
+            the metadata.
+
+            mc_discard_dup_state may be called before or after
+            checking for consistency, but calling it first avoids
+            wasting time checking duplicate states for
+            consistency, which again can be a significant
+            performance boost.
+
+          - The mc_error function reports errors.  It may be
+            called as many times as desired to report each kind
+            of inconsistency in a state.
+
+          - The mc_add_state function adds the new state to the
+            queue.  It should be called regardless of whether an
+            error was reported, to indicate to the model checker
+            that state processing has finished.
+
+          - The mutation function should be deterministic, to
+            make it possible to reliably reproduce errors.
+
+     3. void destroy (struct mc *mc, void *data);
+
+        This function is called to discard the client-specified
+        DATA associated with a state.
+
+   Numerous options are available for configuring the model
+   checker.  The most important of these are:
+
+     - Search algorithm:
+
+       * Breadth-first search (the default): First try all the
+         operations with depth 1, then those with depth 2, then
+         those with depth 3, and so on.
+
+         This search algorithm finds the least number of
+         operations needed to trigger a given bug.
+
+       * Depth-first search: Searches downward in the tree of
+         states as fast as possible.  Good for finding bugs that
+         require long sequences of operations to trigger.
+
+       * Random-first search: Searches through the tree of
+         states in random order.
+
+       * Explicit path: Applies an explicitly specified sequence
+         of operations.
+
+     - Verbosity: By default, messages are printed only when an
+       error is encountered, but you can cause the checker to
+       print a message on every state transition.  This is most
+       useful when the errors in your code cause segfaults or
+       some other kind of sudden termination.
+
+     - Treatment of errors: By default, when an error is
+       encountered, the model checker recursively invokes itself
+       with an increased verbosity level and configured to follow
+       only the error path.  As long as the mutation function is
+       deterministic, this quickly and concisely replays the
+       error and describes the path followed to reach it in an
+       easily human-readable manner.
+
+     - Limits:
+
+       * Maximum depth: You can limit the depth of the operations
+         performed.  Most often useful with depth-first search.
+         By default, depth is unlimited.
+
+       * Maximum queue length: You can limit the number of states
+         kept in the queue at any given time.  The main reason to
+         do so is to limit memory consumption.  The default
+         limit is 10,000 states.  Several strategies are
+         available for choosing which state to drop when the
+         queue overflows.
+
+     - Stop conditions: based on maximum unique states checked,
+       maximum errors found (by default, 1), or maximum time used
+       for checking.
+
+     - Progress: by default, the checker prints a '.' on stderr
+       every .25 seconds, but you can substitute another progress
+       function or disable progress printing.
+
+   This model checker does not (yet) include two features
+   described in the papers cited above: utility scoring
+   heuristics to guide the search strategy and "choice points" to
+   explore alternative cases.  The former feature is less
+   interesting for this model checker, because the data
+   structures we are thus far using it to model are much smaller
+   than those discussed in the paper.  The latter feature we
+   should implement at some point. */
+
+#ifndef LIBPSPP_MODEL_CHECKER_H
+#define LIBPSPP_MODEL_CHECKER_H 1
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include <libpspp/compiler.h>
+
+/* An active model checking run. */
+struct mc;
+
+/* Provided by each client of the model checker. */
+struct mc_class
+  {
+    void (*init) (struct mc *);
+    void (*mutate) (struct mc *, const void *);
+    void (*destroy) (const struct mc *, void *);
+  };
+
+/* Results of a model checking run. */
+struct mc_results;
+
+/* Configuration for running the model checker. */
+struct mc_options;
+
+/* Primary external interface to model checker. */
+struct mc_results *mc_run (const struct mc_class *, struct mc_options *);
+
+/* Functions for use from client-supplied "init" and "mutate"
+   functions. */
+bool mc_include_state (struct mc *);
+bool mc_discard_dup_state (struct mc *, unsigned int hash);
+void mc_name_operation (struct mc *, const char *, ...) PRINTF_FORMAT (2, 3);
+void mc_vname_operation (struct mc *, const char *, va_list)
+     PRINTF_FORMAT (2, 0);
+void mc_error (struct mc *, const char *, ...) PRINTF_FORMAT (2, 3);
+void mc_add_state (struct mc *, void *data);
+
+/* Functions for use from client-supplied "init", "mutate", and
+   "destroy" functions. */
+const struct mc_options *mc_get_options (const struct mc *);
+const struct mc_results *mc_get_results (const struct mc *);
+void *mc_get_aux (const struct mc *);
+\f
+/* A path of operations through a model to arrive at some
+   particular state. */
+struct mc_path
+  {
+    int *ops;           /* Sequence of operations. */
+    size_t length;      /* Number of operations. */
+    size_t capacity;    /* Number of operations for which room is allocated. */
+  };
+
+void mc_path_init (struct mc_path *);
+void mc_path_copy (struct mc_path *, const struct mc_path *);
+void mc_path_push (struct mc_path *, int new_state);
+int mc_path_pop (struct mc_path *);
+int mc_path_back (const struct mc_path *);
+void mc_path_destroy (struct mc_path *);
+
+int mc_path_get_operation (const struct mc_path *, size_t index);
+size_t mc_path_get_length (const struct mc_path *);
+
+struct string;
+void mc_path_to_string (const struct mc_path *, struct string *);
+\f
+struct mc_options *mc_options_create (void);
+struct mc_options *mc_options_clone (const struct mc_options *);
+void mc_options_destroy (struct mc_options *);
+
+/* Search strategy. */
+enum mc_strategy
+  {
+    MC_BROAD,           /* Breadth-first search. */
+    MC_DEEP,            /* Depth-first search. */
+    MC_RANDOM,          /* Randomly ordered search. */
+    MC_PATH             /* Follow one explicit path. */
+  };
+
+enum mc_strategy mc_options_get_strategy (const struct mc_options *);
+void mc_options_set_strategy (struct mc_options *, enum mc_strategy);
+unsigned int mc_options_get_seed (const struct mc_options *);
+void mc_options_set_seed (struct mc_options *, unsigned int seed);
+int mc_options_get_max_depth (const struct mc_options *);
+void mc_options_set_max_depth (struct mc_options *, int max_depth);
+int mc_options_get_hash_bits (const struct mc_options *);
+void mc_options_set_hash_bits (struct mc_options *, int hash_bits);
+
+const struct mc_path *mc_options_get_follow_path (const struct mc_options *);
+void mc_options_set_follow_path (struct mc_options *, const struct mc_path *);
+
+/* Strategy for dropped states from the queue when it
+   overflows. */
+enum mc_queue_limit_strategy
+  {
+    MC_DROP_NEWEST,     /* Don't enqueue the new state at all. */
+    MC_DROP_OLDEST,     /* Drop the oldest state in the queue. */
+    MC_DROP_RANDOM      /* Drop a random state from the queue. */
+  };
+
+int mc_options_get_queue_limit (const struct mc_options *);
+void mc_options_set_queue_limit (struct mc_options *, int queue_limit);
+enum mc_queue_limit_strategy mc_options_get_queue_limit_strategy (
+  const struct mc_options *);
+void mc_options_set_queue_limit_strategy (struct mc_options *,
+                                          enum mc_queue_limit_strategy);
+
+int mc_options_get_max_unique_states (const struct mc_options *);
+void mc_options_set_max_unique_states (struct mc_options *,
+                                       int max_unique_states);
+int mc_options_get_max_errors (const struct mc_options *);
+void mc_options_set_max_errors (struct mc_options *, int max_errors);
+double mc_options_get_time_limit (const struct mc_options *);
+void mc_options_set_time_limit (struct mc_options *, double time_limit);
+
+int mc_options_get_verbosity (const struct mc_options *);
+void mc_options_set_verbosity (struct mc_options *, int verbosity);
+int mc_options_get_failure_verbosity (const struct mc_options *);
+void mc_options_set_failure_verbosity (struct mc_options *,
+                                       int failure_verbosity);
+FILE *mc_options_get_output_file (const struct mc_options *);
+void mc_options_set_output_file (struct mc_options *, FILE *);
+
+typedef bool mc_progress_func (struct mc *);
+int mc_options_get_progress_usec (const struct mc_options *);
+void mc_options_set_progress_usec (struct mc_options *, int progress_usec);
+mc_progress_func *mc_options_get_progress_func (const struct mc_options *);
+void mc_options_set_progress_func (struct mc_options *, mc_progress_func *);
+
+void *mc_options_get_aux (const struct mc_options *);
+void mc_options_set_aux (struct mc_options *, void *aux);
+\f
+/* Reason that a model checking run terminated. */
+enum mc_stop_reason
+  {
+    MC_CONTINUING,              /* Run has not yet terminated. */
+    MC_SUCCESS,                 /* Queue emptied (ran out of states). */
+    MC_MAX_UNIQUE_STATES,       /* Did requested number of unique states. */
+    MC_MAX_ERROR_COUNT,         /* Too many errors. */
+    MC_END_OF_PATH,             /* Processed requested path (MC_PATH only). */
+    MC_TIMEOUT,                 /* Timeout. */
+    MC_INTERRUPTED              /* Received SIGINT (Ctrl+C). */
+  };
+
+void mc_results_destroy (struct mc_results *);
+
+enum mc_stop_reason mc_results_get_stop_reason (const struct mc_results *);
+int mc_results_get_unique_state_count (const struct mc_results *);
+int mc_results_get_error_count (const struct mc_results *);
+
+int mc_results_get_max_depth_reached (const struct mc_results *);
+double mc_results_get_mean_depth_reached (const struct mc_results *);
+
+const struct mc_path *mc_results_get_error_path (const struct mc_results *);
+
+int mc_results_get_duplicate_dropped_states (const struct mc_results *);
+int mc_results_get_off_path_dropped_states (const struct mc_results *);
+int mc_results_get_depth_dropped_states (const struct mc_results *);
+int mc_results_get_queue_dropped_states (const struct mc_results *);
+int mc_results_get_queued_unprocessed_states (const struct mc_results *);
+int mc_results_get_max_queue_length (const struct mc_results *);
+
+struct timeval mc_results_get_start (const struct mc_results *);
+struct timeval mc_results_get_end (const struct mc_results *);
+double mc_results_get_duration (const struct mc_results *);
+
+#endif /* libpspp/model-checker.h */
index f3c87779787188ad986e2be99cdb16d335c9354f..fe98aeb87f3347b82b61853adc70360cee87f132 100644 (file)
@@ -28,7 +28,6 @@
 #include <data/file-name.h>
 
 #include "dirname.h"
-#include "canonicalize.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -193,7 +192,7 @@ parse_insert (struct lexer *lexer, char **filename)
       return CMD_FAILURE;
     }
 
-  *filename = canonicalize_file_name (relative_filename);
+  *filename = relative_filename;
   if (*filename == NULL) 
     {
       msg (SE, _("Unable to open `%s': %s."),
@@ -201,7 +200,6 @@ parse_insert (struct lexer *lexer, char **filename)
       free (relative_filename);
       return CMD_FAILURE;
     }
-  free (relative_filename);
 
   return CMD_SUCCESS;
 }
index f17798f77722a35cb2597a82f5ecaea4ccfcced7..9f65491ef50afdd1485d74e7faf84d49e635704d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -112,14 +112,17 @@ cmd_compute (struct lexer *lexer, struct dataset *ds)
 
 /* Handle COMPUTE or IF with numeric target variable. */
 static int
-compute_num (void *compute_, struct ccase *c, casenumber case_num)
+compute_num (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
   if (compute->test == NULL
-      || expr_evaluate_num (compute->test, c, case_num) == 1.0)
-    case_data_rw (c, compute->variable)->f
-      = expr_evaluate_num (compute->rvalue, c, case_num);
+      || expr_evaluate_num (compute->test, *c, case_num) == 1.0)
+    {
+      *c = case_unshare (*c);
+      case_data_rw (*c, compute->variable)->f
+        = expr_evaluate_num (compute->rvalue, *c, case_num);
+    }
 
   return TRNS_CONTINUE;
 }
@@ -127,17 +130,17 @@ compute_num (void *compute_, struct ccase *c, casenumber case_num)
 /* Handle COMPUTE or IF with numeric vector element target
    variable. */
 static int
-compute_num_vec (void *compute_, struct ccase *c, casenumber case_num)
+compute_num_vec (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
   if (compute->test == NULL
-      || expr_evaluate_num (compute->test, c, case_num) == 1.0)
+      || expr_evaluate_num (compute->test, *c, case_num) == 1.0)
     {
       double index;     /* Index into the vector. */
       int rindx;        /* Rounded index value. */
 
-      index = expr_evaluate_num (compute->element, c, case_num);
+      index = expr_evaluate_num (compute->element, *c, case_num);
       rindx = floor (index + EPSILON);
       if (index == SYSMIS
           || rindx < 1 || rindx > vector_get_var_cnt (compute->vector))
@@ -152,8 +155,10 @@ compute_num_vec (void *compute_, struct ccase *c, casenumber case_num)
                  index, vector_get_name (compute->vector));
           return TRNS_CONTINUE;
         }
-      case_data_rw (c, vector_get_var (compute->vector, rindx - 1))->f
-        = expr_evaluate_num (compute->rvalue, c, case_num);
+
+      *c = case_unshare (*c);
+      case_data_rw (*c, vector_get_var (compute->vector, rindx - 1))->f
+        = expr_evaluate_num (compute->rvalue, *c, case_num);
     }
 
   return TRNS_CONTINUE;
@@ -161,14 +166,18 @@ compute_num_vec (void *compute_, struct ccase *c, casenumber case_num)
 
 /* Handle COMPUTE or IF with string target variable. */
 static int
-compute_str (void *compute_, struct ccase *c, casenumber case_num)
+compute_str (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
   if (compute->test == NULL
-      || expr_evaluate_num (compute->test, c, case_num) == 1.0)
-    expr_evaluate_str (compute->rvalue, c, case_num,
-                       case_data_rw (c, compute->variable)->s, compute->width);
+      || expr_evaluate_num (compute->test, *c, case_num) == 1.0)
+    {
+      *c = case_unshare (*c);
+      expr_evaluate_str (compute->rvalue, *c, case_num,
+                         case_data_rw (*c, compute->variable)->s,
+                         compute->width);
+    }
 
   return TRNS_CONTINUE;
 }
@@ -176,18 +185,18 @@ compute_str (void *compute_, struct ccase *c, casenumber case_num)
 /* Handle COMPUTE or IF with string vector element target
    variable. */
 static int
-compute_str_vec (void *compute_, struct ccase *c, casenumber case_num)
+compute_str_vec (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
   if (compute->test == NULL
-      || expr_evaluate_num (compute->test, c, case_num) == 1.0)
+      || expr_evaluate_num (compute->test, *c, case_num) == 1.0)
     {
       double index;             /* Index into the vector. */
       int rindx;                /* Rounded index value. */
       struct variable *vr;      /* Variable reference by indexed vector. */
 
-      index = expr_evaluate_num (compute->element, c, case_num);
+      index = expr_evaluate_num (compute->element, *c, case_num);
       rindx = floor (index + EPSILON);
       if (index == SYSMIS)
         {
@@ -205,8 +214,9 @@ compute_str_vec (void *compute_, struct ccase *c, casenumber case_num)
         }
 
       vr = vector_get_var (compute->vector, rindx - 1);
-      expr_evaluate_str (compute->rvalue, c, case_num,
-                         case_data_rw (c, vr)->s,
+      *c = case_unshare (*c);
+      expr_evaluate_str (compute->rvalue, *c, case_num,
+                         case_data_rw (*c, vr)->s,
                          var_get_width (vr));
     }
 
index 1c9b4d65735f9487b147da120585465b7501ea0d..6e234c4b23728fa9e1e886db9a9585e9fd5c5274 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -268,8 +268,8 @@ parse_string_criteria (struct lexer *lexer, struct pool *pool, struct criteria *
 /* Transformation. */
 
 /* Counts the number of values in case C matching CRIT. */
-static inline int
-count_numeric (struct criteria *crit, struct ccase *c)
+static int
+count_numeric (struct criteria *crit, const struct ccase *c)
 {
   int counter = 0;
   size_t i;
@@ -302,8 +302,8 @@ count_numeric (struct criteria *crit, struct ccase *c)
 }
 
 /* Counts the number of values in case C matching CRIT. */
-static inline int
-count_string (struct criteria *crit, struct ccase *c)
+static int
+count_string (struct criteria *crit, const struct ccase *c)
 {
   int counter = 0;
   size_t i;
@@ -325,12 +325,13 @@ count_string (struct criteria *crit, struct ccase *c)
 
 /* Performs the COUNT transformation T on case C. */
 static int
-count_trns_proc (void *trns_, struct ccase *c,
+count_trns_proc (void *trns_, struct ccase **c,
                  casenumber case_num UNUSED)
 {
   struct count_trns *trns = trns_;
   struct dst_var *dv;
 
+  *c = case_unshare (*c);
   for (dv = trns->dst_vars; dv; dv = dv->next)
     {
       struct criteria *crit;
@@ -339,10 +340,10 @@ count_trns_proc (void *trns_, struct ccase *c,
       counter = 0;
       for (crit = dv->crit; crit; crit = crit->next)
        if (var_is_numeric (crit->vars[0]))
-         counter += count_numeric (crit, c);
+         counter += count_numeric (crit, *c);
        else
-         counter += count_string (crit, c);
-      case_data_rw (c, dv->var)->f = counter;
+         counter += count_string (crit, *c);
+      case_data_rw (*c, dv->var)->f = counter;
     }
   return TRNS_CONTINUE;
 }
index ad3425c8a28dadd966ef5a59d26bd618c0f64e20..d1cfabf6676478deb66baca38b371431c5ddd1d6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 
-static int trns_fail (void *x, struct ccase *c, casenumber n);
+static int trns_fail (void *x, struct ccase **c, casenumber n);
 
 
 \f
 /* A transformation which is guaranteed to fail. */
 
 static int
-trns_fail (void *x UNUSED, struct ccase *c UNUSED,
+trns_fail (void *x UNUSED, struct ccase **c UNUSED,
           casenumber n UNUSED)
 {
   return TRNS_ERROR;
index fb02c910a4ebfe29a3a85dd4a9c38631dc417abe..e2074823f9df4185aa9db4bcd29a6c6615e25bd3 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -161,7 +161,7 @@ cmd_recode (struct lexer *lexer, struct dataset *ds)
          This must be the final step; otherwise we'd have to
          delete destination variables on failure. */
       if (trns->src_vars != trns->dst_vars)
-        create_dst_vars (trns, dataset_dict (ds));
+       create_dst_vars (trns, dataset_dict (ds));
 
       /* Done. */
       add_transformation (ds,
@@ -230,6 +230,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns)
           do
             {
               struct map_in in;
+
               if (!parse_map_in (lexer, &in, trns->pool,
                                  trns->src_type, max_src_width))
                 return false;
@@ -240,7 +241,11 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns)
 
           if (!parse_map_out (lexer, trns->pool, &out))
             return false;
-          dst_type = val_type_from_width (out.width);
+
+         if (out.copy_input)
+           dst_type = trns->src_type;
+         else
+           dst_type = val_type_from_width (out.width);
           if (have_dst_type && dst_type != trns->dst_type)
             {
               msg (SE, _("Inconsistent target variable types.  "
@@ -289,8 +294,11 @@ static bool
 parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool,
               enum val_type src_type, size_t max_src_width)
 {
+
   if (lex_match_id (lexer, "ELSE"))
+    {
     set_map_in_generic (in, MAP_ELSE);
+    }
   else if (src_type == VAL_NUMERIC)
     {
       if (lex_match_id (lexer, "MISSING"))
@@ -307,16 +315,21 @@ parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool,
     }
   else
     {
-      if (!lex_force_string (lexer))
+      if (lex_match_id (lexer, "MISSING"))
+        set_map_in_generic (in, MAP_MISSING);
+      else if (!lex_force_string (lexer))
         return false;
-      set_map_in_str (in, pool, lex_tokstr (lexer), max_src_width);
-      lex_get (lexer);
-      if (lex_token (lexer) == T_ID
-          && lex_id_match (ss_cstr ("THRU"), ss_cstr (lex_tokid (lexer))))
-        {
-          msg (SE, _("THRU is not allowed with string variables."));
-          return false;
-        }
+      else 
+       {
+         set_map_in_str (in, pool, lex_tokstr (lexer), max_src_width);
+         lex_get (lexer);
+         if (lex_token (lexer) == T_ID
+             && lex_id_match (ss_cstr ("THRU"), ss_cstr (lex_tokid (lexer))))
+           {
+             msg (SE, _("THRU is not allowed with string variables."));
+             return false;
+           }
+       }
     }
 
   return true;
@@ -384,8 +397,11 @@ parse_map_out (struct lexer *lexer, struct pool *pool, struct map_out *out)
       set_map_out_str (out, pool, lex_tokstr (lexer));
       lex_get (lexer);
     }
-  else if (lex_match_id (lexer, "COPY"))
-    out->copy_input = true;
+  else if (lex_match_id (lexer, "COPY")) 
+    {
+      out->copy_input = true;
+      out->width = 0; 
+    }
   else
     {
       lex_error (lexer, _("expecting output value"));
@@ -461,6 +477,7 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns,
               return false;
             }
         }
+
     }
   else
     {
@@ -584,9 +601,10 @@ find_src_numeric (struct recode_trns *trns, double value, const struct variable
 /* Returns the output mapping in TRNS for an input of VALUE with
    the given WIDTH, or a null pointer if there is no mapping. */
 static const struct map_out *
-find_src_string (struct recode_trns *trns, const char *value, int width)
+find_src_string (struct recode_trns *trns, const char *value, const struct variable *src_var)
 {
   struct mapping *m;
+  int width = var_get_width (src_var);
 
   for (m = trns->mappings; m < trns->mappings + trns->map_cnt; m++)
     {
@@ -613,6 +631,9 @@ find_src_string (struct recode_trns *trns, const char *value, int width)
             out->value.f = uv.f;
             break;
           }
+       case MAP_MISSING:
+         match = var_is_str_missing (src_var, value, MV_ANY);
+         break;
         default:
           NOT_REACHED ();
         }
@@ -626,25 +647,26 @@ find_src_string (struct recode_trns *trns, const char *value, int width)
 
 /* Performs RECODE transformation. */
 static int
-recode_trns_proc (void *trns_, struct ccase *c, casenumber case_idx UNUSED)
+recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED)
 {
   struct recode_trns *trns = trns_;
   size_t i;
 
+  *c = case_unshare (*c);
   for (i = 0; i < trns->var_cnt; i++)
     {
       const struct variable *src_var = trns->src_vars[i];
       const struct variable *dst_var = trns->dst_vars[i];
 
-      const union value *src_data = case_data (c, src_var);
-      union value *dst_data = case_data_rw (c, dst_var);
+      const union value *src_data = case_data (*c, src_var);
+      union value *dst_data = case_data_rw (*c, dst_var);
 
       const struct map_out *out;
 
       if (trns->src_type == VAL_NUMERIC)
         out = find_src_numeric (trns, src_data->f, src_var);
       else
-        out = find_src_string (trns, src_data->s, var_get_width (src_var));
+        out = find_src_string (trns, src_data->s, src_var);
 
       if (trns->dst_type == VAL_NUMERIC)
         {
index 632009eddc84cabb12dc026df09388b0a9136a12..6fbc758c2e1329f41d57d626daa9eb8ca75b495a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -116,7 +116,7 @@ cmd_sample (struct lexer *lexer, struct dataset *ds)
 
 /* Executes a SAMPLE transformation. */
 static int
-sample_trns_proc (void *t_, struct ccase *c UNUSED,
+sample_trns_proc (void *t_, struct ccase **c UNUSED,
                   casenumber case_num UNUSED)
 {
   struct sample_trns *t = t_;
index 5576c420fd903ee483ea5f5859e4ee89f31d9727..85f616d536fb3ad39e670f517f9f892488a9a8b8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -70,11 +70,11 @@ cmd_select_if (struct lexer *lexer, struct dataset *ds)
 
 /* Performs the SELECT IF transformation T on case C. */
 static int
-select_if_proc (void *t_, struct ccase *c,
+select_if_proc (void *t_, struct ccase **c,
                 casenumber case_num)
 {
   struct select_if_trns *t = t_;
-  return (expr_evaluate_num (t->e, c, case_num) == 1.0
+  return (expr_evaluate_num (t->e, *c, case_num) == 1.0
           ? TRNS_CONTINUE : TRNS_DROP_CASE);
 }
 
index b4fa926edd8cf3969c48070b93a1e95488102f42..dc512bb288907f59cc668e1ea989e8c7858f6916 100644 (file)
@@ -1,9 +1,9 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 
-noinst_LIBRARIES += src/libpspp/libpspp.a
+noinst_LTLIBRARIES += src/libpspp/libpspp.la
 
-src_libpspp_libpspp_a_SOURCES = \
+src_libpspp_libpspp_la_SOURCES = \
        src/libpspp/abt.c \
        src/libpspp/abt.h \
        src/libpspp/array.c \
@@ -23,10 +23,16 @@ src_libpspp_libpspp_a_SOURCES = \
        src/libpspp/freaderror.h \
        src/libpspp/getl.c \
        src/libpspp/getl.h \
+       src/libpspp/hash-functions.c \
+       src/libpspp/hash-functions.h \
        src/libpspp/hash.c \
        src/libpspp/hash.h \
        src/libpspp/heap.c \
        src/libpspp/heap.h \
+       src/libpspp/hmap.c \
+       src/libpspp/hmap.h \
+       src/libpspp/hmapx.c \
+       src/libpspp/hmapx.h \
        src/libpspp/i18n.c \
        src/libpspp/i18n.h \
        src/libpspp/integer-format.c \
@@ -41,8 +47,6 @@ src_libpspp_libpspp_a_SOURCES = \
        src/libpspp/message.h \
        src/libpspp/misc.c \
        src/libpspp/misc.h \
-       src/libpspp/model-checker.c \
-       src/libpspp/model-checker.h \
        src/libpspp/msg-locator.c \
        src/libpspp/msg-locator.h \
        src/libpspp/pool.c \
@@ -67,14 +71,18 @@ src_libpspp_libpspp_a_SOURCES = \
 
 DISTCLEANFILES+=src/libpspp/version.c
 
-src_libpspp_libpspp_a_CPPFLAGS = -I $(top_srcdir)/src/libpspp $(AM_CPPFLAGS)
+src_libpspp_libpspp_la_CPPFLAGS = -I $(top_srcdir)/src/libpspp $(AM_CPPFLAGS)
 
-nodist_src_libpspp_libpspp_a_SOURCES = src/libpspp/version.c
+nodist_src_libpspp_libpspp_la_SOURCES = src/libpspp/version.c
 
 src/libpspp/version.c: $(top_srcdir)/AUTHORS Makefile
        @$(MKDIR_P) src/libpspp
        echo "/*        -*- mode: c; buffer-read-only: t -*-" > $@
-       echo "   Generated by src/libpspp/automake.mk --- Do not edit. */">> $@
+       echo "   Generated by src/libpspp/automake.mk --- Do not edit.">> $@
+       echo "" >> $@
+       echo "   The following line is for the benefit of the perl module" >>$@
+       echo "\$$VERSION='"@VERSION@"';" >> $@
+       echo "*/" >> $@
        echo "#include \"version.h\"" >> $@
        echo "const char bare_version[] = \"@VERSION@\";" >> $@
        echo "const char version[] = \"GNU @PACKAGE@ @VERSION@\";" >> $@
diff --git a/src/libpspp/hash-functions.c b/src/libpspp/hash-functions.c
new file mode 100644 (file)
index 0000000..2318777
--- /dev/null
@@ -0,0 +1,90 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2008 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 <libpspp/hash-functions.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+
+/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
+#define FNV_32_PRIME 16777619u
+#define FNV_32_BASIS 2166136261u
+
+/* Fowler-Noll-Vo 32-bit hash, for bytes. */
+unsigned
+hsh_hash_bytes (const void *buf_, size_t size)
+{
+  const unsigned char *buf = (const unsigned char *) buf_;
+  unsigned hash;
+
+  assert (buf != NULL);
+
+  hash = FNV_32_BASIS;
+  while (size-- > 0)
+    hash = (hash * FNV_32_PRIME) ^ *buf++;
+
+  return hash;
+}
+
+/* Fowler-Noll-Vo 32-bit hash, for strings. */
+unsigned
+hsh_hash_string (const char *s_)
+{
+  const unsigned char *s = (const unsigned char *) s_;
+  unsigned hash;
+
+  assert (s != NULL);
+
+  hash = FNV_32_BASIS;
+  while (*s != '\0')
+    hash = (hash * FNV_32_PRIME) ^ *s++;
+
+  return hash;
+}
+
+/* Fowler-Noll-Vo 32-bit hash, for case-insensitive strings. */
+unsigned
+hsh_hash_case_string (const char *s_)
+{
+  const unsigned char *s = (const unsigned char *) s_;
+  unsigned hash;
+
+  assert (s != NULL);
+
+  hash = FNV_32_BASIS;
+  while (*s != '\0')
+    hash = (hash * FNV_32_PRIME) ^ toupper (*s++);
+
+  return hash;
+}
+
+/* Hash for ints. */
+unsigned
+hsh_hash_int (int i)
+{
+  return hsh_hash_bytes (&i, sizeof i);
+}
+
+/* Hash for double. */
+unsigned
+hsh_hash_double (double d)
+{
+  if (!isnan (d))
+    return hsh_hash_bytes (&d, sizeof d);
+  else
+    return 0;
+}
diff --git a/src/libpspp/hash-functions.h b/src/libpspp/hash-functions.h
new file mode 100644 (file)
index 0000000..328f4de
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000 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_HASH_FUNCTIONS_H
+#define LIBPSPP_HASH_FUNCTIONS_H 1
+
+#include <stddef.h>
+
+unsigned hsh_hash_bytes (const void *, size_t);
+unsigned hsh_hash_string (const char *);
+unsigned hsh_hash_case_string (const char *);
+unsigned hsh_hash_int (int);
+unsigned hsh_hash_double (double);
+
+#endif /* libpspp/hash-functions.h */
index 9da3deb120a92ba0ee3423f78441ad85dd9ab22f..eb43b54e7fedf24d7db0dd569cc34d885bb91dd4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2008 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
 #include "hash.h"
 #include "message.h"
 #include <assert.h>
-#include <ctype.h>
 #include <limits.h>
 #include <stdbool.h>
 #include <stdlib.h>
@@ -70,74 +69,6 @@ next_power_of_2 (size_t x)
     }
 }
 
-/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
-#define FNV_32_PRIME 16777619u
-#define FNV_32_BASIS 2166136261u
-
-/* Fowler-Noll-Vo 32-bit hash, for bytes. */
-unsigned
-hsh_hash_bytes (const void *buf_, size_t size)
-{
-  const unsigned char *buf = (const unsigned char *) buf_;
-  unsigned hash;
-
-  assert (buf != NULL);
-
-  hash = FNV_32_BASIS;
-  while (size-- > 0)
-    hash = (hash * FNV_32_PRIME) ^ *buf++;
-
-  return hash;
-}
-
-/* Fowler-Noll-Vo 32-bit hash, for strings. */
-unsigned
-hsh_hash_string (const char *s_)
-{
-  const unsigned char *s = (const unsigned char *) s_;
-  unsigned hash;
-
-  assert (s != NULL);
-
-  hash = FNV_32_BASIS;
-  while (*s != '\0')
-    hash = (hash * FNV_32_PRIME) ^ *s++;
-
-  return hash;
-}
-
-/* Fowler-Noll-Vo 32-bit hash, for case-insensitive strings. */
-unsigned
-hsh_hash_case_string (const char *s_)
-{
-  const unsigned char *s = (const unsigned char *) s_;
-  unsigned hash;
-
-  assert (s != NULL);
-
-  hash = FNV_32_BASIS;
-  while (*s != '\0')
-    hash = (hash * FNV_32_PRIME) ^ toupper (*s++);
-
-  return hash;
-}
-
-/* Hash for ints. */
-unsigned
-hsh_hash_int (int i)
-{
-  return hsh_hash_bytes (&i, sizeof i);
-}
-
-/* Hash for double. */
-unsigned
-hsh_hash_double (double d)
-{
-  if (!isnan (d))
-    return hsh_hash_bytes (&d, sizeof d);
-  else
-    return 0;
-}
 \f
 /* Hash tables. */
 
index 59efbe56223a21e8bb253ea972d914148544b1d5..57fc2678d09548ad50922b9130298afb6cf80cac 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <stddef.h>
 #include <stdbool.h>
+#include <libpspp/hash-functions.h>
 
 typedef int hsh_compare_func (const void *, const void *, const void *aux);
 typedef unsigned hsh_hash_func (const void *, const void *aux);
@@ -30,13 +31,6 @@ struct hsh_iterator
     size_t next;               /* Index of next entry. */
   };
 
-/* Hash functions. */
-unsigned hsh_hash_bytes (const void *, size_t);
-unsigned hsh_hash_string (const char *);
-unsigned hsh_hash_case_string (const char *);
-unsigned hsh_hash_int (int);
-unsigned hsh_hash_double (double);
-
 /* Hash tables. */
 struct hsh_table *hsh_create (int m, hsh_compare_func *,
                               hsh_hash_func *, hsh_free_func *,
diff --git a/src/libpspp/hmap.c b/src/libpspp/hmap.c
new file mode 100644 (file)
index 0000000..081d7cb
--- /dev/null
@@ -0,0 +1,186 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/hmap.h>
+#include <assert.h>
+#include <stdlib.h>
+
+static size_t capacity_to_mask (size_t capacity);
+
+/* Initializes MAP as a new hash map that is initially empty. */
+void
+hmap_init (struct hmap *map)
+{
+  map->count = 0;
+  map->mask = 0;
+  map->buckets = &map->one;
+  map->one = NULL;
+}
+
+/* Exchanges the contents of hash maps A and B. */
+void
+hmap_swap (struct hmap *a, struct hmap *b)
+{
+  struct hmap tmp = *a;
+  *a = *b;
+  *b = tmp;
+  if (!a->mask)
+    a->buckets = &a->one;
+  if (!b->mask)
+    b->buckets = &b->one;
+}
+
+/* Frees the memory, if any, allocated by hash map MAP.  This has
+   no effect on the actual data items in MAP, if any, because the
+   client is responsible for allocating and freeing them.  It
+   could, however, render them inaccessible if the only pointers
+   to them were from MAP itself, so in such a situation one
+   should iterate through the map and free the data items before
+   destroying it. */
+void
+hmap_destroy (struct hmap *map) 
+{
+  if (map != NULL && map->buckets != &map->one) 
+    free (map->buckets);
+}
+
+/* Reallocates MAP's hash buckets so that NEW_MASK becomes the
+   hash value bit-mask used to choose a hash bucket, then
+   rehashes any data elements in MAP into the new hash buckets.
+
+   NEW_MASK must be a power of 2 minus 1 (including 0), that is,
+   its value in binary must be all 1-bits.  */
+static void
+hmap_rehash (struct hmap *map, size_t new_mask) 
+{
+  struct hmap_node **new_buckets;
+  struct hmap_node *node, *next;
+
+  assert ((new_mask & (new_mask + 1)) == 0);
+  if (new_mask)
+    new_buckets = calloc (new_mask + 1, sizeof *new_buckets);
+  else 
+    {
+      new_buckets = &map->one;
+      new_buckets[0] = NULL;
+    }
+      
+  if (map->count > 0)
+    {
+      for (node = hmap_first (map); node != NULL; node = next)
+        {
+          size_t new_idx = node->hash & new_mask;
+          struct hmap_node **new_bucket = &new_buckets[new_idx];
+          next = hmap_next (map, node);
+          node->next = *new_bucket;
+          *new_bucket = node;
+        } 
+    }
+  if (map->buckets != &map->one)
+    free (map->buckets);
+  map->buckets = new_buckets;
+  map->mask = new_mask;
+}
+
+/* Ensures that MAP has sufficient space to store at least
+   CAPACITY data elements, allocating a new set of buckets and
+   rehashing if necessary. */
+void
+hmap_reserve (struct hmap *map, size_t capacity)
+{
+  if (capacity > hmap_capacity (map))
+    hmap_rehash (map, capacity_to_mask (capacity));
+}
+
+/* Shrinks MAP's set of buckets to the minimum number needed to
+   store its current number of elements, allocating a new set of
+   buckets and rehashing if that would save space. */
+void
+hmap_shrink (struct hmap *map) 
+{
+  size_t new_mask = capacity_to_mask (map->count);
+  if (new_mask < map->mask) 
+    hmap_rehash (map, new_mask); 
+}
+
+/* Moves NODE around in MAP to compensate for its hash value
+   having changed to NEW_HASH.
+
+   This function does not verify that MAP does not already
+   contain a data item that duplicates NODE's new value.  If
+   duplicates should be disallowed (which is the usual case),
+   then the client must check for duplicates before changing
+   NODE's value. */
+void
+hmap_changed (struct hmap *map, struct hmap_node *node, size_t new_hash)
+{
+  if ((new_hash ^ node->hash) & map->mask) 
+    {
+      hmap_delete (map, node);
+      hmap_insert_fast (map, node, new_hash);
+    }
+  else
+    node->hash = new_hash;
+}
+
+/* Hash map nodes may be moved around in memory as necessary,
+   e.g. as the result of an realloc operation on a block that
+   contains a node.  Once this is done, call this function
+   passing NODE that was moved, its former location in memory
+   OLD, and its hash map MAP before attempting any other
+   operation on MAP, NODE, or any other node in MAP.
+
+   It is not safe to move more than one node, then to call this
+   function for each node.  Instead, move a single node, call
+   this function, move another node, and so on.  Alternatively,
+   remove all affected nodes from the hash map, move them, then
+   re-insert all of them.
+
+   Assuming uniform hashing and no duplicate data items in MAP,
+   this function runs in constant time. */
+void
+hmap_moved (struct hmap *map,
+            struct hmap_node *node, const struct hmap_node *old) 
+{
+  struct hmap_node **p = &map->buckets[node->hash & map->mask];
+  while (*p != old)
+    p = &(*p)->next;
+  *p = node;
+}
+\f
+/* Returns the minimum-value mask required to allow for a hash
+   table capacity of at least CAPACITY.  The return value will be
+   a bit-mask suitable for use as the "mask" member of struct
+   hmap, that is, a power of 2 minus 1 (including 0). */
+static size_t
+capacity_to_mask (size_t capacity) 
+{
+  /* Calculate the minimum mask necesary to support the given
+     capacity. */
+  size_t mask = 0;
+  while (hmap_mask_to_capacity__ (mask) < capacity)
+    mask = (mask << 1) | 1;
+
+  /* If the mask is nonzero, make it at least 3, because there is
+     little point in allocating an array of just 2 pointers. */
+  mask |= (mask & 1) << 1;
+
+  return mask;
+}
diff --git a/src/libpspp/hmap.h b/src/libpspp/hmap.h
new file mode 100644 (file)
index 0000000..e73d84f
--- /dev/null
@@ -0,0 +1,509 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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/>. */
+
+/* Hash table with separate chaining.
+
+   This header (hmap.h) supplies an "embedded" implementation of
+   a hash table that uses linked lists to resolve collisions
+   ("separate chaining").  Its companion header (hmapx.h)
+   supplies a "external" implementation that is otherwise
+   similar.  The two variants are described briefly here.  The
+   embedded variant, for which this is the header, is described
+   in slightly more detail below.  Each function also has a
+   detailed usage comment at its point of definition.  (Many of
+   those definitions are inline in this file, because they are so
+   simple.  Others are in hmap.c.)
+
+   The "hmap" embedded hash table implementation puts the hash
+   table node (which includes the linked list used for resolving
+   collisions) within the data structure that the hash table
+   contains.  This makes allocation efficient, in space and time,
+   because no additional call into an allocator is needed to
+   obtain memory for the hash table node.  It also makes it easy
+   to find the hash table node associated with a given object.
+   However, it's difficult to include a given object in an
+   arbitrary number of hash tables.
+
+   The "hmapx" external hash table implementation allocates hash
+   table nodes separately from the objects in the hash table.
+   Inserting and removing hash table elements requires dynamic
+   allocation, so it is normally slower and takes more memory
+   than the embedded implementation.  It also requires searching
+   the table to find the node associated with a given object.
+   However, it's easy to include a given object in an arbitrary
+   number of hash tables.  It's also possible to create an
+   external hash table without adding a member to the data
+   structure that the hash table contains. */
+
+#ifndef LIBPSPP_HMAP_H
+#define LIBPSPP_HMAP_H 1
+
+/* Embedded hash table with separate chaining.
+
+   To create an embedded hash table, declare an instance of
+   struct hmap, then initialize it with hmap_init():
+     struct hmap map;
+     hmap_init (&map);
+   or, alternatively:
+     struct hmap map = HMAP_INITIALIZER (map);
+   
+   Each node in the hash table, presumably a structure type, must
+   include a struct hmap_node member.  Here's an example:
+     struct foo
+       {
+         struct hmap_node node;   // hmap_node member.
+         const char *string;      // Another member.
+       };
+   The hash table functions work with pointers to struct
+   hmap_node.  To obtain a pointer to your structure type given a
+   pointer to struct hmap_node, use the HMAP_DATA macro.
+
+   Inserting and deleting elements is straightforward.  Use
+   hmap_insert() to insert an element and hmap_delete() to delete
+   an element, e.g.:
+     struct foo my_foo;
+     my_foo.string = "My string";
+     hmap_insert (&map, &my_foo.node, hsh_hash_string (my_foo.string));
+     ...
+     hmap_delete (&map, &my_foo.node);
+   You must pass the element's hash value as one of
+   hmap_insert()'s arguments.  The hash table saves this hash
+   value for use later to speed searches and to rehash as the
+   hash table grows.
+
+   hmap_insert() does not check whether the newly inserted
+   element duplicates an element already in the hash table.  The
+   client is responsible for doing so, if this is desirable.
+
+   The hash table does not provide a direct way to search for an
+   existing element.  Instead, it provides the means to iterate
+   over all the elements in the hash table with a given hash
+   value.  It is easy to compose a search function from such a
+   building block.  For example:
+     const struct foo *
+     find_foo (const struct hmap *map, const char *name)
+     {
+       const struct foo *foo;
+       size_t hash;
+
+       hash = hsh_hash_string (name);
+       HMAP_FOR_EACH_WITH_HASH (foo, struct foo, node, hash, map)
+         if (!strcmp (foo->name, name))
+           break;
+       return foo;
+     }
+
+   Here is how to iterate through the elements currently in the
+   hash table:
+     struct foo *foo;
+     HMAP_FOR_EACH (foo, struct foo, node, &map)
+       {
+         ...do something with foo...
+       }
+   */
+
+#include <stddef.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)))
+
+/* Like HMAP_DATA, except that a null NODE yields a null pointer
+   result. */
+#define HMAP_NULLABLE_DATA(NODE, STRUCT, MEMBER)        \
+  hmap_nullable_data__ (NODE, offsetof (STRUCT, MEMBER))
+
+/* Hash table node. */
+struct hmap_node
+  {
+    struct hmap_node *next;     /* Next in chain. */
+    size_t hash;                /* Hash value. */
+  };
+
+static inline size_t hmap_node_hash (const struct hmap_node *);
+
+/* Hash table. */
+struct hmap
+  {
+    size_t count;               /* Number of inserted nodes. */
+    size_t mask;                /* Number of buckets (power of 2), minus 1. */
+    struct hmap_node **buckets; /* Array of buckets. */
+    struct hmap_node *one;      /* One bucket, to eliminate corner cases. */
+  };
+
+/* Suitable for use as the initializer for a struct hmap named
+   MAP.  Typical usage:
+       struct hmap map = HMAP_INITIALIZER (map);
+   HMAP_INITIALIZER() is an alternative to hmap_init(). */
+#define HMAP_INITIALIZER(MAP) { 0, 0, &(MAP).one, NULL }
+
+/* Creation and destruction. */
+void hmap_init (struct hmap *);
+void hmap_swap (struct hmap *, struct hmap *);
+void hmap_destroy (struct hmap *);
+
+/* Storage management. */
+void hmap_reserve (struct hmap *, size_t capacity);
+void hmap_shrink (struct hmap *);
+
+/* Search.  Refer to the large comment near the top of this file
+   for an example.*/
+static inline struct hmap_node *hmap_first_with_hash (const struct hmap *,
+                                                      size_t hash);
+static inline struct hmap_node *hmap_next_with_hash (const struct hmap_node *);
+
+/* Insertion and deletion. */
+static inline void hmap_insert (struct hmap *, struct hmap_node *,
+                                size_t hash);
+static inline void hmap_insert_fast (struct hmap *, struct hmap_node *,
+                                     size_t hash);
+static inline void hmap_delete (struct hmap *, struct hmap_node *);
+
+/* Iteration. */
+static inline struct hmap_node *hmap_first (const struct hmap *);
+static inline struct hmap_node *hmap_next (const struct hmap *,
+                                           const struct hmap_node *);
+
+/* Counting. */
+static inline size_t hmap_count (const struct hmap *);
+static inline size_t hmap_capacity (const struct hmap *);
+
+/* Updating data elements. */
+void hmap_changed (struct hmap *, struct hmap_node *, size_t new_hash);
+void hmap_moved (struct hmap *,
+                 struct hmap_node *, const struct hmap_node *old);
+
+/* Convenience macros for search.
+
+   These macros automatically use HMAP_DATA to obtain the data
+   elements that encapsulate hmap nodes, which often saves typing
+   and can make code easier to read.  Refer to the large comment
+   near the top of this file for an example.
+
+   These macros evaluate HASH only once.  They evaluate their
+   other arguments many times. */
+#define HMAP_FIRST_WITH_HASH(STRUCT, MEMBER, HMAP, HASH)                \
+  HMAP_NULLABLE_DATA (hmap_first_with_hash (HMAP, HASH), STRUCT, MEMBER)
+#define HMAP_NEXT_WITH_HASH(DATA, STRUCT, MEMBER)                       \
+  HMAP_NULLABLE_DATA (hmap_next_with_hash (&(DATA)->MEMBER), STRUCT, MEMBER)
+#define HMAP_FOR_EACH_WITH_HASH(DATA, STRUCT, MEMBER, HASH, HMAP)       \
+  for ((DATA) = HMAP_FIRST_WITH_HASH (STRUCT, MEMBER, HMAP, HASH);      \
+       (DATA) != NULL;                                                  \
+       (DATA) = HMAP_NEXT_WITH_HASH (DATA, STRUCT, MEMBER))
+#define HMAP_FOR_EACH_WITH_HASH_SAFE(DATA, NEXT, STRUCT, MEMBER, HASH, HMAP) \
+  for ((DATA) = HMAP_FIRST_WITH_HASH (STRUCT, MEMBER, HMAP, HASH);      \
+       ((DATA) != NULL                                                  \
+        ? ((NEXT) = HMAP_NEXT_WITH_HASH (DATA, STRUCT, MEMBER), 1)      \
+        : 0);                                                           \
+       (DATA) = (NEXT))
+
+/* Convenience macros for iteration.
+
+   These macros automatically use HMAP_DATA to obtain the data
+   elements that encapsulate hmap nodes, which often saves typing
+   and can make code easier to read.  Refer to the large comment
+   near the top of this file for an example.
+
+   These macros evaluate their arguments many times. */
+#define HMAP_FIRST(STRUCT, MEMBER, HMAP)                        \
+  HMAP_NULLABLE_DATA (hmap_first (HMAP), STRUCT, MEMBER)
+#define HMAP_NEXT(DATA, STRUCT, MEMBER, HMAP)                           \
+  HMAP_NULLABLE_DATA (hmap_next (HMAP, &(DATA)->MEMBER), STRUCT, MEMBER)
+#define HMAP_FOR_EACH(DATA, STRUCT, MEMBER, HMAP)       \
+  for ((DATA) = HMAP_FIRST (STRUCT, MEMBER, HMAP);      \
+       (DATA) != NULL;                                  \
+       (DATA) = HMAP_NEXT (DATA, STRUCT, MEMBER, HMAP))
+#define HMAP_FOR_EACH_SAFE(DATA, NEXT, STRUCT, MEMBER, HMAP)    \
+  for ((DATA) = HMAP_FIRST (STRUCT, MEMBER, HMAP);              \
+       ((DATA) != NULL                                          \
+        ? ((NEXT) = HMAP_NEXT (DATA, STRUCT, MEMBER, HMAP), 1)  \
+        : 0);                                                   \
+       (DATA) = (NEXT))
+\f
+/* Inline definitions. */
+
+static inline struct hmap_node *hmap_find_hash__ (struct hmap_node *, size_t);
+static inline struct hmap_node *hmap_first_nonempty_bucket__ (
+  const struct hmap *, size_t start);
+static inline size_t hmap_mask_to_capacity__ (size_t mask);
+
+/* Returns the hash value associated with NODE. */
+size_t
+hmap_node_hash (const struct hmap_node *node) 
+{
+  return node->hash;
+}
+
+/* Returns the first node in MAP that has hash value HASH, or a
+   null pointer if MAP does not contain any node with that hash
+   value.
+
+   Assuming uniform hashing and no duplicate data items in MAP,
+   this function runs in constant time.  (Amortized over an
+   iteration over all data items with a given HASH, its runtime
+   is proportional to the length of the hash chain for HASH, so
+   given a pathological hash function, e.g. one that returns a
+   constant value, its runtime degenerates to linear in the
+   length of NODE's hash chain.)
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmap_capacity().  Calls to hmap_insert(), hmap_reserve(), and
+   hmap_shrink() can change the capacity of a hash map.
+   Inserting a node with hmap_insert_fast() or deleting one with
+   hmap_delete() will not change the relative ordering of nodes.
+
+   The HMAP_FOR_EACH_WITH_HASH and HMAP_FOR_EACH_WITH_HASH_SAFE
+   macros provide convenient ways to iterate over all the nodes
+   with a given hash.  The HMAP_FIRST_WITH_HASH macro is an
+   interface to this particular function that is often more
+   convenient. */
+static inline struct hmap_node *
+hmap_first_with_hash (const struct hmap *map, size_t hash)
+{
+  return hmap_find_hash__ (map->buckets[hash & map->mask], hash);
+}
+
+/* Returns the next node in MAP after NODE that has the same hash
+   value as NODE, or a null pointer if MAP does not contain any
+   more nodes with that hash value.
+
+   Assuming uniform hashing and no duplicate data items in MAP,
+   this function runs in constant time.  (Amortized over an
+   iteration over all data items with a given HASH, its runtime
+   is proportional to the length of the hash chain for HASH, so
+   given a pathological hash function, e.g. one that returns a
+   constant value, its runtime degenerates to linear in the
+   length of NODE's hash chain.)
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmap_capacity().  Calls to hmap_insert(), hmap_reserve(), and
+   hmap_shrink() can change the capacity of a hash map.
+   Inserting a node with hmap_insert_fast() or deleting one with
+   hmap_delete() will not change the relative ordering of nodes.
+
+   The HMAP_FOR_EACH_WITH_HASH and HMAP_FOR_EACH_WITH_HASH_SAFE
+   macros provide convenient ways to iterate over all the nodes
+   with a given hash.  The HMAP_NEXT_WITH_HASH macro is an
+   interface to this particular function that is often more
+   convenient. */
+static inline struct hmap_node *
+hmap_next_with_hash (const struct hmap_node *node) 
+{
+  return hmap_find_hash__ (node->next, node->hash);
+}
+
+/* Inserts NODE into MAP with hash value HASH.  If the insertion
+   causes MAP's current capacity, as reported by hmap_capacity(),
+   to be exceeded, rehashes MAP with an increased number of hash
+   buckets.
+
+   This function runs in constant time amortized over all the
+   insertions into MAP.
+
+   This function does not verify that MAP does not already
+   contain a data item with the same value as NODE.  If
+   duplicates should be disallowed (which is the usual case),
+   then the client must check for duplicates itself before
+   inserting the new node. */
+static inline void
+hmap_insert (struct hmap *map, struct hmap_node *node, size_t hash)
+{
+  hmap_insert_fast (map, node, hash);
+  if (map->count > hmap_capacity (map))
+    hmap_reserve (map, map->count);
+}
+
+/* Inserts NODE into MAP with hash value HASH.  Does not check
+   whether this causes MAP's current capacity to be exceeded.
+   The caller must take responsibility for that (or use
+   hmap_insert() instead).
+
+   This function runs in constant time.
+
+   This function does not verify that MAP does not already
+   contain a data item with the same value as NODE.  If
+   duplicates should be disallowed (which is the usual case),
+   then the client must check for duplicates itself before
+   inserting the new node. */
+static inline void
+hmap_insert_fast (struct hmap *map, struct hmap_node *node, size_t hash) 
+{
+  struct hmap_node **bucket = &map->buckets[hash & map->mask];
+  node->hash = hash;
+  node->next = *bucket;
+  *bucket = node;
+  map->count++;
+}
+
+/* Removes NODE from MAP.  The client is responsible for freeing
+   any data associated with NODE, if necessary.
+
+   Assuming uniform hashing, this function runs in constant time.
+   (Its runtime is proportional to the position of NODE in its
+   hash chain, so given a pathological hash function, e.g. one
+   that returns a constant value, its runtime degenerates to
+   linear in the length of NODE's hash chain.)
+
+   This function never reduces the number of buckets in MAP.
+   When one deletes a large number of nodes from a hash table,
+   calling hmap_shrink() afterward may therefore save a small
+   amount of memory.  It is also more expensive to iterate
+   through a very sparse hash table than a denser one, so
+   shrinking the hash table could also save some time.  However,
+   rehashing has an immediate cost that must be weighed against
+   these benefits.
+
+   hmap_delete() does not change NODE's hash value reported by
+   hmap_node_hash(). */
+static inline void
+hmap_delete (struct hmap *map, struct hmap_node *node)
+{
+  struct hmap_node **bucket = &map->buckets[node->hash & map->mask];
+  while (*bucket != node)
+    bucket = &(*bucket)->next;
+  *bucket = (*bucket)->next;
+  map->count--;
+}
+
+/* Returns the first node in MAP, or a null pointer if MAP is
+   empty.
+
+   Amortized over iterating through every data element in MAP,
+   this function runs in constant time.  However, this assumes
+   that MAP is not excessively sparse, that is, that
+   hmap_capacity(MAP) is at most a constant factor greater than
+   hmap_count(MAP).  This will always be true unless many nodes
+   have been inserted into MAP and then most or all of them
+   deleted; in such a case, calling hmap_shrink() is advised.
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmap_capacity().  Calls to hmap_insert(), hmap_reserve(), and
+   hmap_shrink() can change the capacity of a hash map.
+   Inserting a node with hmap_insert_fast() or deleting one with
+   hmap_delete() will not change the relative ordering of nodes.
+
+   The HMAP_FOR_EACH and HMAP_FOR_EACH_SAFE macros provide
+   convenient ways to iterate over all the nodes in a hash map.
+   The HMAP_FIRST macro is an interface to this particular
+   function that is often more convenient. */
+static inline struct hmap_node *
+hmap_first (const struct hmap *map) 
+{
+  return hmap_first_nonempty_bucket__ (map, 0);
+}
+
+/* Returns the next node in MAP following NODE, or a null pointer
+   if NODE is the last node in MAP.
+
+   Amortized over iterating through every data element in MAP,
+   this function runs in constant time.  However, this assumes
+   that MAP is not excessively sparse, that is, that
+   hmap_capacity(MAP) is at most a constant factor greater than
+   hmap_count(MAP).  This will always be true unless many nodes
+   have been inserted into MAP and then most or all of them
+   deleted; in such a case, calling hmap_shrink() is advised.
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmap_capacity().  Calls to hmap_insert(), hmap_reserve(), and
+   hmap_shrink() can change the capacity of a hash map.
+   Inserting a node with hmap_insert_fast() or deleting one with
+   hmap_delete() will not change the relative ordering of nodes.
+
+   The HMAP_FOR_EACH and HMAP_FOR_EACH_SAFE macros provide
+   convenient ways to iterate over all the nodes in a hash map.
+   The HMAP_NEXT macro is an interface to this particular
+   function that is often more convenient. */
+static inline struct hmap_node *
+hmap_next (const struct hmap *map, const struct hmap_node *node) 
+{
+  return (node->next != NULL
+          ? node->next
+          : hmap_first_nonempty_bucket__ (map, (node->hash & map->mask) + 1));
+}
+
+/* Returns the number of data items currently in MAP. */
+static inline size_t
+hmap_count (const struct hmap *map) 
+{
+  return map->count;
+}
+
+/* Returns the current capacity of MAP, that is, the maximum
+   number of data elements that MAP may hold before it becomes
+   advisable to rehash.
+
+   The capacity is advisory only: it is possible to insert any
+   number of data elements into a hash map regardless of its
+   capacity.  However, inserting many more elements than the
+   map's capacity will degrade search performance. */
+static inline size_t
+hmap_capacity (const struct hmap *map) 
+{
+  return hmap_mask_to_capacity__ (map->mask);
+}
+\f
+/* Implementation details. */
+
+/* Returns the first node at or after NODE in NODE's chain that
+   has hash value HASH. */
+static inline struct hmap_node *
+hmap_find_hash__ (struct hmap_node *node, size_t hash) 
+{
+  for (; node != NULL; node = node->next) 
+    if (node->hash == hash)
+      break;
+  return node;
+}
+
+/* Returns the first node in the lowest-numbered nonempty bucket
+   in MAP whose index is START or higher, or a null pointer if
+   all such buckets are empty. */
+static inline struct hmap_node *
+hmap_first_nonempty_bucket__ (const struct hmap *map, size_t start)
+{
+  size_t i;
+
+  for (i = start; i <= map->mask; i++)
+    if (map->buckets[i] != NULL)
+      return map->buckets[i];
+  return NULL;
+}
+
+/* Returns the hash table capacity associated with a given MASK,
+   which should be a value for the "mask" member of struct hmap.
+   MASK must be a power of 2 minus 1 (including 0), that is, its
+   value in binary must be all 1-bits.  */
+static inline size_t
+hmap_mask_to_capacity__ (size_t mask) 
+{
+  return (mask + 1) * 2;
+}
+
+/* Helper for HMAP_NULLABLE_DATA (to avoid evaluating its NODE
+   argument more than once).  */
+static inline void *
+hmap_nullable_data__ (struct hmap_node *node, size_t member_offset)
+{ 
+  return node != NULL ? (char *) node - member_offset : NULL;
+}
+
+#endif /* libpspp/hmap.h */
diff --git a/src/libpspp/hmapx.c b/src/libpspp/hmapx.c
new file mode 100644 (file)
index 0000000..d732450
--- /dev/null
@@ -0,0 +1,99 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/hmapx.h>
+#include <stdlib.h>
+#include "xalloc.h"
+
+/* Frees the memory, if any, allocated by hash map MAP, including
+   all hmapx_nodes that it contains.  The user-defined data items
+   that the hmapx_nodes point to are not affected.  If those
+   items should be freed, then it should be done by iterating
+   through MAP's contents before destroying MAP. */
+void
+hmapx_destroy (struct hmapx *map) 
+{
+  if (map != NULL) 
+    {
+      if (hmapx_count (map) > 0) 
+        {
+          struct hmapx_node *node, *next;
+          for (node = hmapx_first (map); node != NULL; node = next)
+            {
+              next = hmapx_next (map, node);
+              free (node); 
+            }
+        }
+      hmap_destroy (&map->hmap);
+    }
+}
+
+/* Allocates and returns a new hmapx_node with DATA as its data
+   item. */
+static struct hmapx_node *
+make_hmapx_node (void *data) 
+{
+  struct hmapx_node *node = xmalloc (sizeof *node);
+  node->data = data;
+  return node;
+}
+
+/* Inserts DATA into MAP with hash value HASH and returns the new
+   hmapx_node created to contain DATA.  If the insertion causes
+   MAP's current capacity, as reported by hmapx_capacity(), to be
+   exceeded, rehashes MAP with an increased number of hash
+   buckets.
+
+   This function runs in constant time amortized over all the
+   insertions into MAP.
+
+   This function does not verify that MAP does not already
+   contain a data item with the same value as DATA.  If
+   duplicates should be disallowed (which is the usual case),
+   then the client must check for duplicates itself before
+   inserting the new item. */
+struct hmapx_node *
+hmapx_insert (struct hmapx *map, void *data, size_t hash) 
+{
+  struct hmapx_node *node = make_hmapx_node (data);
+  hmap_insert (&map->hmap, &node->hmap_node, hash);
+  return node;
+}
+
+/* Inserts DATA into MAP with hash value HASH and returns the new
+   hmapx_node created to contain DATA.  Does not check whether
+   this causes MAP's current capacity to be exceeded.  The caller
+   must take responsibility for that (or use hmapx_insert()
+   instead).
+
+   This function runs in constant time.
+
+   This function does not verify that MAP does not already
+   contain a data item with the same value as DATA.  If
+   duplicates should be disallowed (which is the usual case),
+   then the client must check for duplicates itself before
+   inserting the new node. */
+struct hmapx_node *
+hmapx_insert_fast (struct hmapx *map, void *data, size_t hash) 
+{
+  struct hmapx_node *node = make_hmapx_node (data);
+  hmap_insert_fast (&map->hmap, &node->hmap_node, hash);
+  return node;
+}
diff --git a/src/libpspp/hmapx.h b/src/libpspp/hmapx.h
new file mode 100644 (file)
index 0000000..32a4452
--- /dev/null
@@ -0,0 +1,468 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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/>. */
+
+/* Hash table with separate chaining.
+
+   This header (hmapx.h) supplies an "external" implementation of
+   a hash table that uses linked lists to resolve collisions
+   ("separate chaining").  Its companion header (hmap.h) supplies
+   a "embedded" implementation that is otherwise similar.  The
+   two variants are described briefly here.  The external
+   variant, for which this is the header, is described in
+   slightly more detail below.  Each function also has a detailed
+   usage comment at its point of definition.  (Many of those
+   definitions are inline in this file, because they are so
+   simple.  Others are in hmapx.c.)
+
+   The "hmap" embedded hash table implementation puts the hash
+   table node (which includes the linked list used for resolving
+   collisions) within the data structure that the hash table
+   contains.  This makes allocation efficient, in space and time,
+   because no additional call into an allocator is needed to
+   obtain memory for the hash table node.  It also makes it easy
+   to find the hash table node associated with a given object.
+   However, it's difficult to include a given object in an
+   arbitrary number of hash tables.
+
+   The "hmapx" external hash table implementation allocates hash
+   table nodes separately from the objects in the hash table.
+   Inserting and removing hash table elements requires dynamic
+   allocation, so it is normally slower and takes more memory
+   than the embedded implementation.  It also requires searching
+   the table to find the node associated with a given object.
+   However, it's easy to include a given object in an arbitrary
+   number of hash tables.  It's also possible to create an
+   external hash table without adding a member to the data
+   structure that the hash table contains. */
+
+#ifndef LIBPSPP_HMAPX_H
+#define LIBPSPP_HMAPX_H 1
+
+/* External hash table with separate chaining.
+
+   To create an external hash table, declare an instance of
+   struct hmapx, then initialize it with hmapx_init():
+     struct hmapx map;
+     hmapx_init (&map);
+   or, alternatively:
+     struct hmapx map = HMAPX_INITIALIZER (map);
+
+   An hmapx data structure contains data represented as void *.
+   The hmapx_insert() function inserts such a datum and returns
+   the address of a newly created struct hmapx_node that
+   represents the new element:
+     struct foo {
+       const char *key;
+       const char *value;
+     };
+     struct foo foo = {"key", "value"};
+     struct hmapx_node *node;
+     node = hmapx_insert (&map, &foo, hsh_hash_string (foo.key));
+   The element's hash value must be passed as one of
+   hmapx_insert()'s arguments.  The hash table saves this hash
+   value for use later to speed searches and to rehash as the
+   hash table grows.
+
+   hmapx_insert() does not check whether the newly inserted
+   element duplicates an element already in the hash table.  The
+   client is responsible for doing so, if this is desirable.
+
+   Use hmapx_delete() to delete an element from the hash table,
+   passing in its hmapx_node:
+     hmapx_delete (&map, node);
+   Deleting an element frees its node.
+
+   The hash table does not provide a direct way to search for an
+   existing element.  Instead, it provides the means to iterate
+   over all the elements in the hash table with a given hash
+   value.  It is easy to compose a search function from such a
+   building block.  For example:
+     struct hmapx_node *
+     find_node (const struct hmapx *map, const char *target)
+     {
+       struct hmapx_node *node;
+       struct foo *foo;
+       HMAPX_FOR_EACH_WITH_HASH (foo, node, hsh_hash_string (target), map)
+         if (!strcmp (foo->key, target))
+           break;
+       return node;
+     }
+   This function's client can extract the data item from the
+   returned hmapx_node using the hmapx_node_data() function.  The
+   hmapx_node can also be useful directly as an argument to other
+   hmapx functions, such as hmapx_delete().
+
+   Here is how to iterate through the elements currently in the
+   hash table:
+     struct hmapx_node *node;
+     const char *string;
+     HMAPX_FOR_EACH (data, node, &map)
+       {
+         ...do something with string...
+       }
+   */
+
+#include <libpspp/hmap.h>
+#include <stdlib.h>
+
+/* Hash table node. */
+struct hmapx_node
+  {
+    struct hmap_node hmap_node; /* Underlying hash node. */
+    void *data;                 /* User data. */
+  };
+
+static inline void *hmapx_node_data (const struct hmapx_node *);
+static inline size_t hmapx_node_hash (const struct hmapx_node *);
+
+/* Hash table. */
+struct hmapx
+  {
+    struct hmap hmap;
+  };
+
+/* Suitable for use as the initializer for a struct hmapx named
+   MAP.  Typical usage:
+       struct hmap map = HMAPX_INITIALIZER (map);
+   HMAPX_INITIALIZER() is an alternative to hmapx_init(). */
+#define HMAPX_INITIALIZER(MAP) { HMAP_INITIALIZER (MAP.hmap) }
+
+/* Creation and destruction. */
+static inline void hmapx_init (struct hmapx *);
+static inline void hmapx_swap (struct hmapx *, struct hmapx *);
+void hmapx_destroy (struct hmapx *);
+
+/* Storage management. */
+static inline void hmapx_reserve (struct hmapx *, size_t capacity);
+static inline void hmapx_shrink (struct hmapx *);
+
+/* Search. */
+static inline struct hmapx_node *hmapx_first_with_hash (struct hmapx *,
+                                                        size_t hash);
+static inline struct hmapx_node *hmapx_next_with_hash (struct hmapx_node *);
+
+/* Insertion and deletion. */
+struct hmapx_node *hmapx_insert (struct hmapx *, void *, size_t hash);
+struct hmapx_node *hmapx_insert_fast (struct hmapx *, void *, size_t hash);
+static inline void hmapx_delete (struct hmapx *, struct hmapx_node *);
+
+/* Iteration. */
+static inline struct hmapx_node *hmapx_first (const struct hmapx *);
+static inline struct hmapx_node *hmapx_next (const struct hmapx *,
+                                             const struct hmapx_node *);
+
+/* Counting. */
+static inline size_t hmapx_count (const struct hmapx *);
+static inline size_t hmapx_capacity (const struct hmapx *);
+
+/* Updating data elements. */
+static inline void hmapx_change (struct hmapx *,
+                                 struct hmapx_node *, void *, size_t new_hash);
+static inline void hmapx_changed (struct hmapx *, struct hmapx_node *,
+                                  size_t new_hash);
+static inline void hmapx_move (struct hmapx_node *, void *);
+
+/* Convenience macros for search.
+
+   These macros automatically use hmapx_node_data() to obtain the
+   data elements that encapsulate hmap nodes, which often saves
+   typing and can make code easier to read.  Refer to the large
+   comment near the top of this file for an example.
+
+   These macros evaluate HASH only once.  They evaluate their
+   other arguments many times. */
+#define HMAPX_FOR_EACH_WITH_HASH(DATA, NODE, HASH, HMAPX)               \
+  for ((NODE) = hmapx_first_with_hash (HMAPX, HASH);                    \
+       (NODE) != NULL ? ((DATA) = hmapx_node_data (NODE), 1) : 0;       \
+       (NODE) = hmapx_next_with_hash (NODE))
+#define HMAPX_FOR_EACH_WITH_HASH_SAFE(DATA, NODE, NEXT, HASH, HMAPX)    \
+  for ((NODE) = hmapx_first_with_hash (HMAPX, HASH);                    \
+       ((NODE) != NULL                                                  \
+        ? ((DATA) = hmapx_node_data (NODE),                             \
+           (NEXT) = hmapx_next_with_hash (NODE),                        \
+           1)                                                           \
+        : 0);                                                           \
+       (NODE) = (NEXT))
+
+/* Convenience macros for iteration.
+
+   These macros automatically use hmapx_node_data() to obtain the
+   data elements that encapsulate hmap nodes, which often saves
+   typing and can make code easier to read.  Refer to the large
+   comment near the top of this file for an example. 
+
+   These macros evaluate their arguments many times. */
+#define HMAPX_FOR_EACH(DATA, NODE, HMAPX)                               \
+  for ((NODE) = hmapx_first (HMAPX);                                    \
+       (NODE) != NULL ? ((DATA) = hmapx_node_data (NODE), 1) : 0;       \
+       (NODE) = hmapx_next (HMAPX, NODE))
+#define HMAPX_FOR_EACH_SAFE(DATA, NODE, NEXT, HMAPX)                    \
+  for ((NODE) = hmapx_first (HMAPX);                                    \
+       ((NODE) != NULL                                                  \
+        ? ((DATA) = hmapx_node_data (NODE),                             \
+           (NEXT) = hmapx_next (HMAPX, NODE),                           \
+           1)                                                           \
+        : 0);                                                           \
+       (NODE) = (NEXT))
+\f
+/* Inline definitions. */
+
+/* Returns the data stored in NODE. */
+static inline void *
+hmapx_node_data (const struct hmapx_node *node)
+{
+  return node->data;
+}
+
+/* Returns the hash value stored in NODE */
+static inline size_t
+hmapx_node_hash (const struct hmapx_node *node)
+{
+  return hmap_node_hash (&node->hmap_node);
+}
+
+/* Initializes MAP as a new hash map that is initially empty. */
+static inline void
+hmapx_init (struct hmapx *map) 
+{
+  hmap_init (&map->hmap);
+}
+
+/* Exchanges the contents of hash maps A and B. */
+static inline void
+hmapx_swap (struct hmapx *a, struct hmapx *b)
+{
+  hmap_swap (&a->hmap, &b->hmap);
+}
+
+/* Ensures that MAP has sufficient space to store at least
+   CAPACITY data elements, allocating a new set of buckets and
+   rehashing if necessary. */
+static inline void
+hmapx_reserve (struct hmapx *map, size_t capacity)
+{
+  hmap_reserve (&map->hmap, capacity);
+}
+
+/* Shrinks MAP's set of buckets to the minimum number needed to
+   store its current number of elements, allocating a new set of
+   buckets and rehashing if that would save space. */
+static inline void
+hmapx_shrink (struct hmapx *map) 
+{
+  hmap_shrink (&map->hmap);
+}
+
+/* Returns the first node in MAP that has hash value HASH, or a
+   null pointer if MAP does not contain any node with that hash
+   value.
+
+   Assuming uniform hashing and no duplicate data items in MAP,
+   this function runs in constant time.  (Amortized over an
+   iteration over all data items with a given HASH, its runtime
+   is proportional to the length of the hash chain for HASH, so
+   given a pathological hash function, e.g. one that returns a
+   constant value, its runtime degenerates to linear in the
+   length of NODE's hash chain.)
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmapx_capacity().  Calls to hmapx_insert(), hmapx_reserve(),
+   and hmapx_shrink() can change the capacity of a hash map.
+   Inserting a node with hmapx_insert_fast() or deleting one with
+   hmapx_delete() will not change the relative ordering of nodes.
+
+   The HMAPX_FOR_EACH_WITH_HASH and HMAPX_FOR_EACH_WITH_HASH_SAFE
+   macros provide convenient ways to iterate over all the nodes
+   with a given hash. */
+static inline struct hmapx_node *
+hmapx_first_with_hash (struct hmapx *map, size_t hash) 
+{
+  return HMAP_FIRST_WITH_HASH (struct hmapx_node, hmap_node, &map->hmap, hash);
+}
+
+/* Returns the next node in MAP after NODE that has the same hash
+   value as NODE, or a null pointer if MAP does not contain any
+   more nodes with that hash value.
+
+   Assuming uniform hashing and no duplicate data items in MAP,
+   this function runs in constant time.  (Amortized over an
+   iteration over all data items with a given HASH, its runtime
+   is proportional to the length of the hash chain for HASH, so
+   given a pathological hash function, e.g. one that returns a
+   constant value, its runtime degenerates to linear in the
+   length of NODE's hash chain.)
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmapx_capacity().  Calls to hmapx_insert(), hmapx_reserve(),
+   and hmapx_shrink() can change the capacity of a hash map.
+   Inserting a node with hmapx_insert_fast() or deleting one with
+   hmapx_delete() will not change the relative ordering of nodes.
+
+   The HMAPX_FOR_EACH_WITH_HASH and HMAPX_FOR_EACH_WITH_HASH_SAFE
+   macros provide convenient ways to iterate over all the nodes
+   with a given hash. */
+static inline struct hmapx_node *
+hmapx_next_with_hash (struct hmapx_node *node) 
+{
+  return HMAP_NEXT_WITH_HASH (node, struct hmapx_node, hmap_node);
+}
+
+/* Removes NODE from MAP and frees NODE.  The client is
+   responsible for freeing the user data associated with NODE, if
+   appropriate.
+
+   Assuming uniform hashing, this function runs in constant time.
+   (Its runtime is proportional to the position of NODE in its
+   hash chain, so given a pathological hash function, e.g. one
+   that returns a constant value, its runtime degenerates to
+   linear in the length of NODE's hash chain.)
+
+   This function never reduces the number of buckets in MAP.
+   When one deletes a large number of nodes from a hash table,
+   calling hmapx_shrink() afterward may therefore save a small
+   amount of memory.  It is also more expensive to iterate
+   through a very sparse hash table than a denser one, so
+   shrinking the hash table could also save some time.  However,
+   rehashing has an immediate cost that must be weighed against
+   these benefits.
+
+   hmapx_delete() does not change NODE's hash value reported by
+   hmapx_node_hash(). */
+static inline void
+hmapx_delete (struct hmapx *map, struct hmapx_node *node) 
+{
+  hmap_delete (&map->hmap, &node->hmap_node);
+  free (node);
+}
+
+/* Returns the first node in MAP, or a null pointer if MAP is
+   empty.
+
+   Amortized over iterating through every data element in MAP,
+   this function runs in constant time.  However, this assumes
+   that MAP is not excessively sparse, that is, that
+   hmapx_capacity(MAP) is at most a constant factor greater than
+   hmapx_count(MAP).  This will always be true unless many nodes
+   have been inserted into MAP and then most or all of them
+   deleted; in such a case, calling hmapx_shrink() is advised.
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmapx_capacity().  Calls to hmapx_insert(), hmapx_reserve(),
+   and hmapx_shrink() can change the capacity of a hash map.
+   Inserting a node with hmapx_insert_fast() or deleting one with
+   hmapx_delete() will not change the relative ordering of nodes.
+
+   The HMAPX_FOR_EACH and HMAPX_FOR_EACH_SAFE macros provide
+   convenient ways to iterate over all the nodes in a hash
+   map. */
+static inline struct hmapx_node *
+hmapx_first (const struct hmapx *map) 
+{
+  return HMAP_FIRST (struct hmapx_node, hmap_node, &map->hmap);
+}
+
+/* Returns the next node in MAP following NODE, or a null pointer
+   if NODE is the last node in MAP.
+
+   Amortized over iterating through every data element in MAP,
+   this function runs in constant time.  However, this assumes
+   that MAP is not excessively sparse, that is, that
+   hmapx_capacity(MAP) is at most a constant factor greater than
+   hmapx_count(MAP).  This will always be true unless many nodes
+   have been inserted into MAP and then most or all of them
+   deleted; in such a case, calling hmapx_shrink() is advised.
+
+   Nodes are returned in arbitrary order that may change whenever
+   the hash table's current capacity changes, as reported by
+   hmapx_capacity().  Calls to hmapx_insert(), hmapx_reserve(),
+   and hmapx_shrink() can change the capacity of a hash map.
+   Inserting a node with hmapx_insert_fast() or deleting one with
+   hmapx_delete() will not change the relative ordering of nodes.
+
+   The HMAPX_FOR_EACH and HMAPX_FOR_EACH_SAFE macros provide
+   convenient ways to iterate over all the nodes in a hash
+   map. */
+static inline struct hmapx_node *
+hmapx_next (const struct hmapx *map, const struct hmapx_node *node) 
+{
+  return HMAP_NEXT (node, struct hmapx_node, hmap_node, &map->hmap);
+}
+
+/* Returns the number of data items currently in MAP. */
+static inline size_t
+hmapx_count (const struct hmapx *map) 
+{
+  return hmap_count (&map->hmap);
+}
+
+/* Returns the current capacity of MAP, that is, the maximum
+   number of data elements that MAP may hold before it becomes
+   advisable to rehash.
+
+   The capacity is advisory only: it is possible to insert any
+   number of data elements into a hash map regardless of its
+   capacity.  However, inserting many more elements than the
+   map's capacity will degrade search performance. */
+static inline size_t
+hmapx_capacity (const struct hmapx *map) 
+{
+  return hmap_capacity (&map->hmap);
+}
+
+/* Changes NODE's data to DATA and its hash value to NEW_HASH.
+   NODE must reside in MAP.
+
+   This function does not verify that MAP does not already
+   contain a data item that duplicates DATA.  If duplicates
+   should be disallowed (which is the usual case), then the
+   client must check for duplicates before changing NODE's
+   value. */
+static inline void
+hmapx_change (struct hmapx *map,
+              struct hmapx_node *node, void *data, size_t new_hash) 
+{
+  hmapx_move (node, data);
+  hmapx_changed (map, node, new_hash);
+}
+
+/* Moves NODE around in MAP to compensate for its hash value
+   having changed to NEW_HASH.
+
+   This function does not verify that MAP does not already
+   contain a data item that duplicates the new value of NODE's
+   data.  If duplicates should be disallowed (which is the usual
+   case), then the client must check for duplicates before
+   changing NODE's value. */
+static inline void
+hmapx_changed (struct hmapx *map, struct hmapx_node *node, size_t new_hash) 
+{
+  hmap_changed (&map->hmap, &node->hmap_node, new_hash);
+}
+
+/* Updates NODE to compensate for its data item having moved
+   around in memory to new location DATA.  The data item's value
+   and hash value should not have changed.  (If they have
+   changed, call hmapx_change() instead.) */
+static inline void
+hmapx_move (struct hmapx_node *node, void *data)
+{
+  node->data = data;
+}
+
+#endif /* libpspp/hmapx.h */
index 70780eff968a6ec62c071da23229a4ebaac49fcc..f8e5e396e865efe267155f29f02aa29fb024649f 100644 (file)
 #include <localcharset.h>
 #include "xstrndup.h"
 
+#if HAVE_NL_LANGINFO
+#include <langinfo.h>
+#endif
 
-static char *locale = 0;
-static const char *charset;
+
+static char *locale;
+static char *charset;
 
 
 static iconv_t convertor[n_CONV];
@@ -161,16 +165,17 @@ void
 set_pspp_locale (const char *l)
 {
   char *current_locale;
-  const char *current_charset;
+  char *current_charset;
 
   free(locale);
   locale = strdup(l);
 
-  current_locale = setlocale (LC_CTYPE, 0);
-  current_charset = locale_charset ();
+  current_locale = strdup (setlocale (LC_CTYPE, 0));
+  current_charset = strdup (locale_charset ());
   setlocale (LC_CTYPE, locale);
 
-  charset = locale_charset ();
+  free (charset);
+  charset = strdup (locale_charset ());
   setlocale (LC_CTYPE, current_locale);
 
   iconv_close (convertor[CONV_PSPP_TO_UTF8]);
@@ -181,6 +186,9 @@ set_pspp_locale (const char *l)
 
   iconv_close (convertor[CONV_UTF8_TO_PSPP]);
   convertor[CONV_UTF8_TO_PSPP] = create_iconv (charset, "UTF-8");
+
+  free (current_locale);
+  free (current_charset);
 }
 
 void
@@ -190,7 +198,9 @@ i18n_init (void)
   locale = strdup (setlocale (LC_CTYPE, NULL));
 
   setlocale (LC_CTYPE, locale);
-  charset = locale_charset ();
+
+  free (charset);
+  charset = strdup (locale_charset ());
 
   convertor[CONV_PSPP_TO_UTF8]   = create_iconv ("UTF-8", charset);
   convertor[CONV_SYSTEM_TO_PSPP] = create_iconv (charset, charset);
@@ -213,3 +223,33 @@ i18n_done (void)
     }
 }
 
+
+
+
+/* Return the system local's idea of the
+   decimal seperator character */
+char
+get_system_decimal (void)
+{
+  char radix_char;
+
+  char *ol = strdup (setlocale (LC_NUMERIC, NULL));
+  setlocale (LC_NUMERIC, "");
+
+#if HAVE_NL_LANGINFO
+  radix_char = nl_langinfo (RADIXCHAR)[0];
+#else
+  {
+    char buf[10];
+    snprintf (buf, sizeof buf, "%f", 2.5);
+    radix_char = buf[1];
+  }
+#endif
+
+  /* We MUST leave LC_NUMERIC untouched, since it would
+     otherwise interfere with data_{in,out} */
+  setlocale (LC_NUMERIC, ol);
+  free (ol);
+  return radix_char;
+}
+
index 0633ebc82f807b9fb1d89429c3bf99fa9699930c..db15bad86940a955f0a0a942af548a8a7b177edb 100644 (file)
@@ -36,5 +36,8 @@ enum conv_id
 char * recode_string (enum conv_id how,  const char *text, int len);
 
 
+/* Return the decimal separator according to the
+   system locale */
+char get_system_decimal (void);
 
 #endif /* i18n.h */
index 96ef9a2033502ea47483e83be00ed0549def21ce..65ecf55f2cad759a50085a85e90f1e3c79cf7f02 100644 (file)
@@ -336,9 +336,9 @@ struct ll *ll_find_partition (const struct ll *r0, const struct ll *r1,
 #define ll_tail__(STRUCT, MEMBER, LIST)                         \
         ll_data__ (ll_tail (LIST), STRUCT, MEMBER, LIST)
 #define ll_next__(DATA, STRUCT, MEMBER, LIST)                           \
-        ll_data__ (ll_next (&DATA->MEMBER), STRUCT, MEMBER, LIST)
+        ll_data__ (ll_next (&(DATA)->MEMBER), STRUCT, MEMBER, LIST)
 #define ll_prev__(DATA, STRUCT, MEMBER, LIST)                           \
-        ll_data__ (ll_prev (&DATA->MEMBER), STRUCT, MEMBER, LIST)
+        ll_data__ (ll_prev (&(DATA)->MEMBER), STRUCT, MEMBER, LIST)
 \f
 /* Inline functions. */
 
index 695b713287b4d55096cd4ddab330d30ea7f35f03..227109145528d318f3963e7b3e295d11ef8602e9 100644 (file)
@@ -99,9 +99,11 @@ msg_destroy(struct msg *m)
 void
 msg_emit (struct msg *m)
 {
-  get_msg_location (s_stream, &m->where);
+  if ( s_stream )
+    get_msg_location (s_stream, &m->where);
+
   if (!messages_disabled)
-    msg_handler (m);
+     msg_handler (m);
   free (m->text);
 }
 
index e33d588fd8c0db6ca722278eb3c77260a6081d82..c2f865d90c95512d7ec4d3517c68dff96e0181d4 100644 (file)
@@ -14,8 +14,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#if !math_misc_h
-#define math_misc_h 1
+#if !libpspp_misc_h
+#define libpspp_misc_h 1
 
 #include <float.h>
 #include <math.h>
@@ -60,4 +60,40 @@ pow4 (double x)
   return y;
 }
 
-#endif /* math/misc.h */
+/* Set *DEST to the lower of *DEST and SRC */
+static inline void
+minimize (double *dest, double src)
+{
+  if (src < *dest)
+    *dest = src;
+}
+
+
+/* Set *DEST to the greater of *DEST and SRC */
+static inline void
+maximize (double *dest, double src)
+{
+  if (src > *dest)
+    *dest = src;
+}
+
+
+/* Set *DEST to the lower of *DEST and SRC */
+static inline void
+minimize_int (int *dest, int src)
+{
+  if (src < *dest)
+    *dest = src;
+}
+
+
+/* Set *DEST to the greater of *DEST and SRC */
+static inline void
+maximize_int (int *dest, int src)
+{
+  if (src > *dest)
+    *dest = src;
+}
+
+
+#endif /* libpspp/misc.h */
diff --git a/src/libpspp/model-checker.c b/src/libpspp/model-checker.c
deleted file mode 100644 (file)
index cb51c48..0000000
+++ /dev/null
@@ -1,1466 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 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 <libpspp/model-checker.h>
-
-#include <limits.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include <data/val-type.h>
-#include <libpspp/bit-vector.h>
-#include <libpspp/compiler.h>
-#include <libpspp/deque.h>
-#include <libpspp/str.h>
-#include <math/moments.h>
-
-#include "error.h"
-#include "minmax.h"
-#include "xalloc.h"
-\f
-/* Initializes PATH as an empty path. */
-void
-mc_path_init (struct mc_path *path)
-{
-  path->ops = NULL;
-  path->length = 0;
-  path->capacity = 0;
-}
-
-/* Copies the contents of OLD into NEW. */
-void
-mc_path_copy (struct mc_path *new, const struct mc_path *old)
-{
-  if (old->length > new->capacity)
-    {
-      new->capacity = old->length;
-      free (new->ops);
-      new->ops = xnmalloc (new->capacity, sizeof *new->ops);
-    }
-  new->length = old->length;
-  memcpy (new->ops, old->ops, old->length * sizeof *new->ops);
-}
-
-/* Adds OP to the end of PATH. */
-void
-mc_path_push (struct mc_path *path, int op)
-{
-  if (path->length >= path->capacity)
-    path->ops = xnrealloc (path->ops, ++path->capacity, sizeof *path->ops);
-  path->ops[path->length++] = op;
-}
-
-/* Removes and returns the operation at the end of PATH. */
-int
-mc_path_pop (struct mc_path *path)
-{
-  int back = mc_path_back (path);
-  path->length--;
-  return back;
-}
-
-/* Returns the operation at the end of PATH. */
-int
-mc_path_back (const struct mc_path *path)
-{
-  assert (path->length > 0);
-  return path->ops[path->length - 1];
-}
-
-/* Destroys PATH. */
-void
-mc_path_destroy (struct mc_path *path)
-{
-  free (path->ops);
-  path->ops = NULL;
-}
-
-/* Returns the operation in position INDEX in PATH.
-   INDEX must be less than the length of PATH. */
-int
-mc_path_get_operation (const struct mc_path *path, size_t index)
-{
-  assert (index < path->length);
-  return path->ops[index];
-}
-
-/* Returns the number of operations in PATH. */
-size_t
-mc_path_get_length (const struct mc_path *path)
-{
-  return path->length;
-}
-
-/* Appends the operations in PATH to STRING, separating each one
-   with a single space. */
-void
-mc_path_to_string (const struct mc_path *path, struct string *string)
-{
-  size_t i;
-
-  for (i = 0; i < mc_path_get_length (path); i++)
-    {
-      if (i > 0)
-        ds_put_char (string, ' ');
-      ds_put_format (string, "%d", mc_path_get_operation (path, i));
-    }
-}
-\f
-/* Search options. */
-struct mc_options
-  {
-    /* Search strategy. */
-    enum mc_strategy strategy;          /* Type of strategy. */
-    int max_depth;                      /* Limit on depth (or INT_MAX). */
-    int hash_bits;                      /* Number of bits to hash (or 0). */
-    unsigned int seed;                  /* Random seed for MC_RANDOM
-                                           or MC_DROP_RANDOM. */
-    struct mc_path follow_path;         /* Path for MC_PATH. */
-
-    /* Queue configuration. */
-    int queue_limit;                    /* Maximum length of queue. */
-    enum mc_queue_limit_strategy queue_limit_strategy;
-                                        /* How to choose state to drop
-                                           from queue. */
-
-    /* Stop conditions. */
-    int max_unique_states;              /* Maximum unique states to process. */
-    int max_errors;                     /* Maximum errors to detect. */
-    double time_limit;                  /* Maximum time in seconds. */
-
-    /* Output configuration. */
-    int verbosity;                      /* 0=low, 1=normal, 2+=high. */
-    int failure_verbosity;              /* If greater than verbosity,
-                                           verbosity of error replays. */
-    FILE *output_file;                  /* File to receive output. */
-
-    /* How to report intermediate progress. */
-    int progress_usec;                  /* Microseconds between reports. */
-    mc_progress_func *progress_func;    /* Function to call on each report. */
-
-    /* Client data. */
-    void *aux;
-  };
-
-/* Default progress function. */
-static bool
-default_progress (struct mc *mc)
-{
-  if (mc_results_get_stop_reason (mc_get_results (mc)) == MC_CONTINUING)
-    putc ('.', stderr);
-  else
-    putc ('\n', stderr);
-  return true;
-}
-
-/* Do-nothing progress function. */
-static bool
-null_progress (struct mc *mc UNUSED)
-{
-  return true;
-}
-
-/* Creates and returns a set of options initialized to the
-   defaults. */
-struct mc_options *
-mc_options_create (void)
-{
-  struct mc_options *options = xmalloc (sizeof *options);
-
-  options->strategy = MC_BROAD;
-  options->max_depth = INT_MAX;
-  options->hash_bits = 20;
-  options->seed = 0;
-  mc_path_init (&options->follow_path);
-
-  options->queue_limit = 10000;
-  options->queue_limit_strategy = MC_DROP_RANDOM;
-
-  options->max_unique_states = INT_MAX;
-  options->max_errors = 1;
-  options->time_limit = 0.0;
-
-  options->verbosity = 1;
-  options->failure_verbosity = 2;
-  options->output_file = stdout;
-  options->progress_usec = 250000;
-  options->progress_func = default_progress;
-
-  options->aux = NULL;
-
-  return options;
-}
-
-/* Returns a copy of the given OPTIONS. */
-struct mc_options *
-mc_options_clone (const struct mc_options *options)
-{
-  return xmemdup (options, sizeof *options);
-}
-
-/* Destroys OPTIONS. */
-void
-mc_options_destroy (struct mc_options *options)
-{
-  mc_path_destroy (&options->follow_path);
-  free (options);
-}
-
-/* Returns the search strategy used for OPTIONS.  The choices
-   are:
-
-   - MC_BROAD (the default): Breadth-first search.  First tries
-     all the operations with depth 1, then those with depth 2,
-     then those with depth 3, and so on.
-
-     This search algorithm finds the least number of operations
-     needed to trigger a given bug.
-
-   - MC_DEEP: Depth-first search.  Searches downward in the tree
-     of states as fast as possible.  Good for finding bugs that
-     require long sequences of operations to trigger.
-
-   - MC_RANDOM: Random-first search.  Searches through the tree
-     of states in random order.  The standard C library's rand
-     function selects the search path; you can control the seed
-     passed to srand using mc_options_set_seed.
-
-   - MC_PATH: Explicit path.  Applies an explicitly specified
-     sequence of operations. */
-enum mc_strategy
-mc_options_get_strategy (const struct mc_options *options)
-{
-  return options->strategy;
-}
-
-/* Sets the search strategy used for OPTIONS to STRATEGY.
-
-   This function cannot be used to set MC_PATH as the search
-   strategy.  Use mc_options_set_follow_path instead. */
-void
-mc_options_set_strategy (struct mc_options *options, enum mc_strategy strategy)
-{
-  assert (strategy == MC_BROAD
-          || strategy == MC_DEEP
-          || strategy == MC_RANDOM);
-  options->strategy = strategy;
-}
-
-/* Returns OPTION's random seed used by MC_RANDOM and
-   MC_DROP_RANDOM. */
-unsigned int
-mc_options_get_seed (const struct mc_options *options)
-{
-  return options->seed;
-}
-
-/* Set OPTION's random seed used by MC_RANDOM and MC_DROP_RANDOM
-   to SEED. */
-void
-mc_options_set_seed (struct mc_options *options, unsigned int seed)
-{
-  options->seed = seed;
-}
-
-/* Returns the maximum depth to which OPTIONS's search will
-   descend.  The initial states are at depth 1, states produced
-   as their mutations are at depth 2, and so on. */
-int
-mc_options_get_max_depth (const struct mc_options *options)
-{
-  return options->max_depth;
-}
-
-/* Sets the maximum depth to which OPTIONS's search will descend
-   to MAX_DEPTH.  The initial states are at depth 1, states
-   produced as their mutations are at depth 2, and so on. */
-void
-mc_options_set_max_depth (struct mc_options *options, int max_depth)
-{
-  options->max_depth = max_depth;
-}
-
-/* Returns the base-2 log of the number of bits in OPTIONS's hash
-   table.  The hash table is used for dropping states that are
-   probably duplicates: any state with a given hash value, as
-   will only be processed once.  A return value of 0 indicates
-   that the model checker will not discard duplicate states based
-   on their hashes.
-
-   The hash table is a power of 2 bits long, by default 2**20
-   bits (128 kB).  Depending on how many states you expect the
-   model checker to check, how much memory you're willing to let
-   the hash table take up, and how worried you are about missing
-   states due to hash collisions, you could make it larger or
-   smaller.
-
-   The "birthday paradox" points to a reasonable way to size your
-   hash table.  If you expect the model checker to check about
-   2**N states, then, assuming a perfect hash, you need a hash
-   table of 2**(N+1) bits to have a 50% chance of seeing a hash
-   collision, 2**(N+2) bits to have a 25% chance, and so on. */
-int
-mc_options_get_hash_bits (const struct mc_options *options)
-{
-  return options->hash_bits;
-}
-
-/* Sets the base-2 log of the number of bits in OPTIONS's hash
-   table to HASH_BITS.  A HASH_BITS value of 0 requests that the
-   model checker not discard duplicate states based on their
-   hashes.  (This causes the model checker to never terminate in
-   many cases.) */
-void
-mc_options_set_hash_bits (struct mc_options *options, int hash_bits)
-{
-  assert (hash_bits >= 0);
-  options->hash_bits = MIN (hash_bits, CHAR_BIT * sizeof (unsigned int) - 1);
-}
-
-/* Returns the path set in OPTIONS by mc_options_set_follow_path.
-   May be used only if the search strategy is MC_PATH. */
-const struct mc_path *
-mc_options_get_follow_path (const struct mc_options *options)
-{
-  assert (options->strategy == MC_PATH);
-  return &options->follow_path;
-}
-
-/* Sets, in OPTIONS, the search algorithm to MC_PATH and the path
-   to be the explicit path specified in FOLLOW_PATH. */
-void
-mc_options_set_follow_path (struct mc_options *options,
-                            const struct mc_path *follow_path)
-{
-  assert (mc_path_get_length (follow_path) > 0);
-  options->strategy = MC_PATH;
-  mc_path_copy (&options->follow_path, follow_path);
-}
-
-/* Returns the maximum number of queued states in OPTIONS.  The
-   default value is 10,000.  The primary reason to limit the
-   number of queued states is to conserve memory, so if you can
-   afford the memory and your model needs more room in the queue,
-   you can raise the limit.  Conversely, if your models are large
-   or memory is constrained, you can reduce the limit.
-
-   Following the execution of the model checker, you can find out
-   the maximum queue length during the run by calling
-   mc_results_get_max_queue_length. */
-int
-mc_options_get_queue_limit (const struct mc_options *options)
-{
-  return options->queue_limit;
-}
-
-/* Sets the maximum number of queued states in OPTIONS to
-   QUEUE_LIMIT.  */
-void
-mc_options_set_queue_limit (struct mc_options *options, int queue_limit)
-{
-  assert (queue_limit > 0);
-  options->queue_limit = queue_limit;
-}
-
-/* Returns the queue limit strategy used by OPTIONS, that is,
-   when a new state must be inserted into a full state queue is
-   full, how the state to be dropped is chosen.  The choices are:
-
-   - MC_DROP_NEWEST: Drop the newest state; that is, do not
-     insert the new state into the queue at all.
-
-   - MC_DROP_OLDEST: Drop the state that has been enqueued for
-     the longest.
-
-   - MC_DROP_RANDOM (the default): Drop a randomly selected state
-     from the queue.  The standard C library's rand function
-     selects the state to drop; you can control the seed passed
-     to srand using mc_options_set_seed. */
-enum mc_queue_limit_strategy
-mc_options_get_queue_limit_strategy (const struct mc_options *options)
-{
-  return options->queue_limit_strategy;
-}
-
-/* Sets the queue limit strategy used by OPTIONS to STRATEGY.
-
-   This setting has no effect unless the model being checked
-   causes the state queue to overflow (see
-   mc_options_get_queue_limit). */
-void
-mc_options_set_queue_limit_strategy (struct mc_options *options,
-                                     enum mc_queue_limit_strategy strategy)
-{
-  assert (strategy == MC_DROP_NEWEST
-          || strategy == MC_DROP_OLDEST
-          || strategy == MC_DROP_RANDOM);
-  options->queue_limit_strategy = strategy;
-}
-
-/* Returns OPTIONS's maximum number of unique states that the
-   model checker will examine before terminating.  The default is
-   INT_MAX. */
-int
-mc_options_get_max_unique_states (const struct mc_options *options)
-{
-  return options->max_unique_states;
-}
-
-/* Sets OPTIONS's maximum number of unique states that the model
-   checker will examine before terminating to
-   MAX_UNIQUE_STATE. */
-void
-mc_options_set_max_unique_states (struct mc_options *options,
-                                  int max_unique_states)
-{
-  options->max_unique_states = max_unique_states;
-}
-
-/* Returns the maximum number of errors that OPTIONS will allow
-   the model checker to encounter before terminating.  The
-   default is 1. */
-int
-mc_options_get_max_errors (const struct mc_options *options)
-{
-  return options->max_errors;
-}
-
-/* Sets the maximum number of errors that OPTIONS will allow the
-   model checker to encounter before terminating to
-   MAX_ERRORS. */
-void
-mc_options_set_max_errors (struct mc_options *options, int max_errors)
-{
-  options->max_errors = max_errors;
-}
-
-/* Returns the maximum amount of time, in seconds, that OPTIONS will allow the
-   model checker to consume before terminating.  The
-   default of 0.0 means that time consumption is unlimited. */
-double
-mc_options_get_time_limit (const struct mc_options *options)
-{
-  return options->time_limit;
-}
-
-/* Sets the maximum amount of time, in seconds, that OPTIONS will
-   allow the model checker to consume before terminating to
-   TIME_LIMIT.  A value of 0.0 means that time consumption is
-   unlimited; otherwise, the return value will be positive. */
-void
-mc_options_set_time_limit (struct mc_options *options, double time_limit)
-{
-  assert (time_limit >= 0.0);
-  options->time_limit = time_limit;
-}
-
-/* Returns the level of verbosity for output messages specified
-   by OPTIONS.  The default verbosity level is 1.
-
-   A verbosity level of 0 inhibits all messages except for
-   errors; a verbosity level of 1 also allows warnings; a
-   verbosity level of 2 also causes a description of each state
-   added to be output; a verbosity level of 3 also causes a
-   description of each duplicate state to be output.  Verbosity
-   levels less than 0 or greater than 3 are allowed but currently
-   have no additional effect. */
-int
-mc_options_get_verbosity (const struct mc_options *options)
-{
-  return options->verbosity;
-}
-
-/* Sets the level of verbosity for output messages specified
-   by OPTIONS to VERBOSITY. */
-void
-mc_options_set_verbosity (struct mc_options *options, int verbosity)
-{
-  options->verbosity = verbosity;
-}
-
-/* Returns the level of verbosity for failures specified by
-   OPTIONS.  The default failure verbosity level is 2.
-
-   The failure verbosity level has an effect only when an error
-   is reported, and only when the failure verbosity level is
-   higher than the regular verbosity level.  When this is the
-   case, the model checker replays the error path at the higher
-   verbosity level specified.  This has the effect of outputting
-   an explicit, human-readable description of the sequence of
-   operations that caused the error. */
-int
-mc_options_get_failure_verbosity (const struct mc_options *options)
-{
-  return options->failure_verbosity;
-}
-
-/* Sets the level of verbosity for failures specified by OPTIONS
-   to FAILURE_VERBOSITY. */
-void
-mc_options_set_failure_verbosity (struct mc_options *options,
-                                  int failure_verbosity)
-{
-  options->failure_verbosity = failure_verbosity;
-}
-
-/* Returns the output file used for messages printed by the model
-   checker specified by OPTIONS.  The default is stdout. */
-FILE *
-mc_options_get_output_file (const struct mc_options *options)
-{
-  return options->output_file;
-}
-
-/* Sets the output file used for messages printed by the model
-   checker specified by OPTIONS to OUTPUT_FILE.
-
-   The model checker does not automatically close the specified
-   output file.  If this is desired, the model checker's client
-   must do so. */
-void
-mc_options_set_output_file (struct mc_options *options,
-                            FILE *output_file)
-{
-  options->output_file = output_file;
-}
-
-/* Returns the number of microseconds between calls to the
-   progress function specified by OPTIONS.   The default is
-   250,000 (1/4 second).  A value of 0 disables progress
-   reporting. */
-int
-mc_options_get_progress_usec (const struct mc_options *options)
-{
-  return options->progress_usec;
-}
-
-/* Sets the number of microseconds between calls to the progress
-   function specified by OPTIONS to PROGRESS_USEC.  A value of 0
-   disables progress reporting. */
-void
-mc_options_set_progress_usec (struct mc_options *options, int progress_usec)
-{
-  assert (progress_usec >= 0);
-  options->progress_usec = progress_usec;
-}
-
-/* Returns the function called to report progress specified by
-   OPTIONS.  The function used by default prints '.' to
-   stderr. */
-mc_progress_func *
-mc_options_get_progress_func (const struct mc_options *options)
-{
-  return options->progress_func;
-}
-
-/* Sets the function called to report progress specified by
-   OPTIONS to PROGRESS_FUNC.  A non-null function must be
-   specified; to disable progress reporting, set the progress
-   reporting interval to 0.
-
-   PROGRESS_FUNC will be called zero or more times while the
-   model checker's run is ongoing.  For these calls to the
-   progress function, mc_results_get_stop_reason will return
-   MC_CONTINUING.  It will also be called exactly once soon
-   before mc_run returns, in which case
-   mc_results_get_stop_reason will return a different value. */
-void
-mc_options_set_progress_func (struct mc_options *options,
-                              mc_progress_func *progress_func)
-{
-  assert (options->progress_func != NULL);
-  options->progress_func = progress_func;
-}
-
-/* Returns the auxiliary data set in OPTIONS by the client.  The
-   default is a null pointer.
-
-   This auxiliary data value can be retrieved by the
-   client-specified functions in struct mc_class during a model
-   checking run using mc_get_aux. */
-void *
-mc_options_get_aux (const struct mc_options *options)
-{
-  return options->aux;
-}
-
-/* Sets the auxiliary data in OPTIONS to AUX. */
-void
-mc_options_set_aux (struct mc_options *options, void *aux)
-{
-  options->aux = aux;
-}
-\f
-/* Results of a model checking run. */
-struct mc_results
-  {
-    /* Overall results. */
-    enum mc_stop_reason stop_reason;    /* Why the run ended. */
-    int unique_state_count;             /* Number of unique states checked. */
-    int error_count;                    /* Number of errors found. */
-
-    /* Depth statistics. */
-    int max_depth_reached;              /* Max depth state examined. */
-    struct moments1 *depth_moments;     /* Enables reporting mean depth. */
-
-    /* If error_count > 0, path to the last error reported. */
-    struct mc_path error_path;
-
-    /* States dropped... */
-    int duplicate_dropped_states;       /* ...as duplicates. */
-    int off_path_dropped_states;        /* ...as off-path (MC_PATH only). */
-    int depth_dropped_states;           /* ...due to excessive depth. */
-    int queue_dropped_states;           /* ...due to queue overflow. */
-
-    /* Queue statistics. */
-    int queued_unprocessed_states;      /* Enqueued but never dequeued. */
-    int max_queue_length;               /* Maximum queue length observed. */
-
-    /* Timing. */
-    struct timeval start;               /* Start of model checking run. */
-    struct timeval end;                 /* End of model checking run. */
-  };
-
-/* Creates, initializes, and returns a new set of results. */
-static struct mc_results *
-mc_results_create (void)
-{
-  struct mc_results *results = xcalloc (1, sizeof (struct mc_results));
-  results->stop_reason = MC_CONTINUING;
-  results->depth_moments = moments1_create (MOMENT_MEAN);
-  gettimeofday (&results->start, NULL);
-  return results;
-}
-
-/* Destroys RESULTS. */
-void
-mc_results_destroy (struct mc_results *results)
-{
-  if (results != NULL)
-    {
-      moments1_destroy (results->depth_moments);
-      mc_path_destroy (&results->error_path);
-      free (results);
-    }
-}
-
-/* Returns RESULTS's reason that the model checking run
-   terminated.  The possible reasons are:
-
-   - MC_CONTINUING: The run is not actually yet complete.  This
-     can only be returned before mc_run has returned, e.g. when
-     the progress function set by mc_options_set_progress_func
-     examines the run's results.
-
-   - MC_SUCCESS: The run completed because the queue emptied.
-     The entire state space might not have been explored due to a
-     requested limit on maximum depth, hash collisions, etc.
-
-   - MC_MAX_UNIQUE_STATES: The run completed because as many
-     unique states have been checked as were requested (using
-     mc_options_set_max_unique_states).
-
-   - MC_MAX_ERROR_COUNT: The run completed because the maximum
-     requested number of errors (by default, 1 error) was
-     reached.
-
-   - MC_END_OF_PATH: The run completed because the path specified
-     with mc_options_set_follow_path was fully traversed.
-
-   - MC_TIMEOUT: The run completed because the time limit set
-     with mc_options_set_time_limit was exceeded.
-
-   - MC_INTERRUPTED: The run completed because SIGINT was caught
-     (typically, due to the user typing Ctrl+C). */
-enum mc_stop_reason
-mc_results_get_stop_reason (const struct mc_results *results)
-{
-  return results->stop_reason;
-}
-
-/* Returns the number of unique states checked specified by
-   RESULTS. */
-int
-mc_results_get_unique_state_count (const struct mc_results *results)
-{
-  return results->unique_state_count;
-}
-
-/* Returns the number of errors found specified by RESULTS. */
-int
-mc_results_get_error_count (const struct mc_results *results)
-{
-  return results->error_count;
-}
-
-/* Returns the maximum depth reached during the model checker run
-   represented by RESULTS.  The initial states are at depth 1,
-   their child states at depth 2, and so on. */
-int
-mc_results_get_max_depth_reached (const struct mc_results *results)
-{
-  return results->max_depth_reached;
-}
-
-/* Returns the mean depth reached during the model checker run
-   represented by RESULTS. */
-double
-mc_results_get_mean_depth_reached (const struct mc_results *results)
-{
-  double mean;
-  moments1_calculate (results->depth_moments, NULL, &mean, NULL, NULL, NULL);
-  return mean != SYSMIS ? mean : 0.0;
-}
-
-/* Returns the path traversed to obtain the last error
-   encountered during the model checker run represented by
-   RESULTS.  Returns a null pointer if the run did not report any
-   errors. */
-const struct mc_path *
-mc_results_get_error_path (const struct mc_results *results)
-{
-  return results->error_count > 0 ? &results->error_path : NULL;
-}
-
-/* Returns the number of states dropped as duplicates (based on
-   hash value) during the model checker run represented by
-   RESULTS. */
-int
-mc_results_get_duplicate_dropped_states (const struct mc_results *results)
-{
-  return results->duplicate_dropped_states;
-}
-
-/* Returns the number of states dropped because they were off the
-   path specified by mc_options_set_follow_path during the model
-   checker run represented by RESULTS.  A nonzero value here
-   indicates a missing call to mc_include_state in the
-   client-supplied mutation function. */
-int
-mc_results_get_off_path_dropped_states (const struct mc_results *results)
-{
-  return results->off_path_dropped_states;
-}
-
-/* Returns the number of states dropped because their depth
-   exceeded the maximum specified with mc_options_set_max_depth
-   during the model checker run represented by RESULTS. */
-int
-mc_results_get_depth_dropped_states (const struct mc_results *results)
-{
-  return results->depth_dropped_states;
-}
-
-/* Returns the number of states dropped from the queue due to
-   queue overflow during the model checker run represented by
-   RESULTS. */
-int
-mc_results_get_queue_dropped_states (const struct mc_results *results)
-{
-  return results->queue_dropped_states;
-}
-
-/* Returns the number of states that were checked and enqueued
-   but never dequeued and processed during the model checker run
-   represented by RESULTS.  This is zero if the stop reason is
-   MC_CONTINUING or MC_SUCCESS; otherwise, it is the number of
-   states in the queue at the time that the checking run
-   stopped. */
-int
-mc_results_get_queued_unprocessed_states (const struct mc_results *results)
-{
-  return results->queued_unprocessed_states;
-}
-
-/* Returns the maximum length of the queue during the model
-   checker run represented by RESULTS.  If this is equal to the
-   maximum queue length, then the queue (probably) overflowed
-   during the run; otherwise, it did not overflow. */
-int
-mc_results_get_max_queue_length (const struct mc_results *results)
-{
-  return results->max_queue_length;
-}
-
-/* Returns the time at which the model checker run represented by
-   RESULTS started. */
-struct timeval
-mc_results_get_start (const struct mc_results *results)
-{
-  return results->start;
-}
-
-/* Returns the time at which the model checker run represented by
-   RESULTS ended.  (This function may not be called while the run
-   is still ongoing.) */
-struct timeval
-mc_results_get_end (const struct mc_results *results)
-{
-  assert (results->stop_reason != MC_CONTINUING);
-  return results->end;
-}
-
-/* Returns the number of seconds obtained by subtracting time Y
-   from time X. */
-static double
-timeval_subtract (struct timeval x, struct timeval y)
-{
-  /* From libc.info. */
-  double difference;
-
-  /* Perform the carry for the later subtraction by updating Y. */
-  if (x.tv_usec < y.tv_usec) {
-    int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
-    y.tv_usec -= 1000000 * nsec;
-    y.tv_sec += nsec;
-  }
-  if (x.tv_usec - y.tv_usec > 1000000) {
-    int nsec = (x.tv_usec - y.tv_usec) / 1000000;
-    y.tv_usec += 1000000 * nsec;
-    y.tv_sec -= nsec;
-  }
-
-  /* Compute the time remaining to wait.
-     `tv_usec' is certainly positive. */
-  difference = (x.tv_sec - y.tv_sec) + (x.tv_usec - y.tv_usec) / 1000000.0;
-  if (x.tv_sec < y.tv_sec)
-    difference = -difference;
-  return difference;
-}
-
-
-/* Returns the duration, in seconds, of the model checker run
-   represented by RESULTS.  (This function may not be called
-   while the run is still ongoing.) */
-double
-mc_results_get_duration (const struct mc_results *results)
-{
-  assert (results->stop_reason != MC_CONTINUING);
-  return timeval_subtract (results->end, results->start);
-}
-\f
-/* An active model checking run. */
-struct mc
-  {
-    /* Related data structures. */
-    const struct mc_class *class;
-    struct mc_options *options;
-    struct mc_results *results;
-
-    /* Array of 2**(options->hash_bits) bits representing states
-       already visited. */
-    unsigned char *hash;
-
-    /* State queue. */
-    struct mc_state **queue;            /* Array of pointers to states. */
-    struct deque queue_deque;           /* Deque. */
-
-    /* State currently being built by "init" or "mutate". */
-    struct mc_path path;                /* Path to current state. */
-    struct string path_string;          /* Buffer for path_string function. */
-    bool state_named;                   /* mc_name_operation called? */
-    bool state_error;                   /* mc_error called? */
-
-    /* Statistics for calling the progress function. */
-    unsigned int progress;              /* Current progress value. */
-    unsigned int next_progress;         /* Next value to call progress func. */
-    unsigned int prev_progress;         /* Last value progress func called. */
-    struct timeval prev_progress_time;  /* Last time progress func called. */
-
-    /* Information for handling and restoring SIGINT. */
-    bool interrupted;                   /* SIGINT received? */
-    bool *saved_interrupted_ptr;        /* Saved value of interrupted_ptr. */
-    void (*saved_sigint) (int);         /* Saved SIGINT handler. */
-  };
-
-/* A state in the queue. */
-struct mc_state
-  {
-    struct mc_path path;                /* Path to this state. */
-    void *data;                         /* Client-supplied data. */
-  };
-
-/* Points to the current struct mc's "interrupted" member. */
-static bool *interrupted_ptr = NULL;
-
-static const char *path_string (struct mc *);
-static void free_state (const struct mc *, struct mc_state *);
-static void stop (struct mc *, enum mc_stop_reason);
-static struct mc_state *make_state (const struct mc *, void *);
-static size_t random_queue_index (struct mc *);
-static void enqueue_state (struct mc *, struct mc_state *);
-static void do_error_state (struct mc *);
-static void next_operation (struct mc *);
-static bool is_off_path (const struct mc *);
-static void sigint_handler (int signum);
-static void init_mc (struct mc *,
-                     const struct mc_class *, struct mc_options *);
-static void finish_mc (struct mc *);
-
-/* Runs the model checker on the client-specified CLASS with the
-   client-specified OPTIONS.  OPTIONS may be a null pointer if
-   the defaults are acceptable.  Destroys OPTIONS; use
-   mc_options_clone if a copy is needed.
-
-   Returns the results of the model checking run, which must be
-   destroyed by the client with mc_results_destroy.
-
-   To pass auxiliary data to the functions in CLASS, use
-   mc_options_set_aux on OPTIONS, which may be retrieved from the
-   CLASS functions using mc_get_aux. */
-struct mc_results *
-mc_run (const struct mc_class *class, struct mc_options *options)
-{
-  struct mc mc;
-
-  init_mc (&mc, class, options);
-  while (!deque_is_empty (&mc.queue_deque)
-         && mc.results->stop_reason == MC_CONTINUING)
-    {
-      struct mc_state *state = mc.queue[deque_pop_front (&mc.queue_deque)];
-      mc_path_copy (&mc.path, &state->path);
-      mc_path_push (&mc.path, 0);
-      class->mutate (&mc, state->data);
-      free_state (&mc, state);
-      if (mc.interrupted)
-        stop (&mc, MC_INTERRUPTED);
-    }
-  finish_mc (&mc);
-
-  return mc.results;
-}
-
-/* Tests whether the current operation is one that should be
-   performed, checked, and enqueued.  If so, returns true.
-   Otherwise, returns false and, unless checking is stopped,
-   advances to the next state.  The caller should then advance
-   to the next operation.
-
-   This function should be called from the client-provided
-   "mutate" function, according to the pattern explained in the
-   big comment at the top of model-checker.h. */
-bool
-mc_include_state (struct mc *mc)
-{
-  if (mc->results->stop_reason != MC_CONTINUING)
-    return false;
-  else if (is_off_path (mc))
-    {
-      next_operation (mc);
-      return false;
-    }
-  else
-    return true;
-}
-
-/* Tests whether HASH represents a state that has (probably)
-   already been enqueued.  If not, returns false and marks HASH
-   so that it will be treated as a duplicate in the future.  If
-   so, returns true and advances to the next state.  The
-   caller should then advance to the next operation.
-
-   This function should be called from the client-provided
-   "mutate" function, according to the pattern explained in the
-   big comment at the top of model-checker.h. */
-bool
-mc_discard_dup_state (struct mc *mc, unsigned int hash)
-{
-  if (mc->options->hash_bits > 0)
-    {
-      hash &= (1u << mc->options->hash_bits) - 1;
-      if (TEST_BIT (mc->hash, hash))
-        {
-          if (mc->options->verbosity > 2)
-            fprintf (mc->options->output_file,
-                     "    [%s] discard duplicate state\n", path_string (mc));
-          mc->results->duplicate_dropped_states++;
-          next_operation (mc);
-          return true;
-        }
-      SET_BIT (mc->hash, hash);
-    }
-  return false;
-}
-
-/* Names the current state NAME, which may contain
-   printf-style format specifications.  NAME should be a
-   human-readable name for the current operation.
-
-   This function should be called from the client-provided
-   "mutate" function, according to the pattern explained in the
-   big comment at the top of model-checker.h. */
-void
-mc_name_operation (struct mc *mc, const char *name, ...)
-{
-  va_list args;
-
-  va_start (args, name);
-  mc_vname_operation (mc, name, args);
-  va_end (args);
-}
-
-/* Names the current state NAME, which may contain
-   printf-style format specifications, for which the
-   corresponding arguments must be given in ARGS.  NAME should be
-   a human-readable name for the current operation.
-
-   This function should be called from the client-provided
-   "mutate" function, according to the pattern explained in the
-   big comment at the top of model-checker.h. */
-void
-mc_vname_operation (struct mc *mc, const char *name, va_list args)
-{
-  if (mc->state_named && mc->options->verbosity > 0)
-    fprintf (mc->options->output_file, "  [%s] warning: duplicate call "
-             "to mc_name_operation (missing call to mc_add_state?)\n",
-             path_string (mc));
-  mc->state_named = true;
-
-  if (mc->options->verbosity > 1)
-    {
-      fprintf (mc->options->output_file, "  [%s] ", path_string (mc));
-      vfprintf (mc->options->output_file, name, args);
-      putc ('\n', mc->options->output_file);
-    }
-}
-
-/* Reports the given error MESSAGE for the current operation.
-   The resulting state should still be passed to mc_add_state
-   when all relevant error messages have been issued.  The state
-   will not, however, be enqueued for later mutation of its own.
-
-   By default, model checking stops after the first error
-   encountered.
-
-   This function should be called from the client-provided
-   "mutate" function, according to the pattern explained in the
-   big comment at the top of model-checker.h. */
-void
-mc_error (struct mc *mc, const char *message, ...)
-{
-  va_list args;
-
-  if (mc->results->stop_reason != MC_CONTINUING)
-    return;
-
-  if (mc->options->verbosity > 1)
-    fputs ("    ", mc->options->output_file);
-  fprintf (mc->options->output_file, "[%s] error: ",
-           path_string (mc));
-  va_start (args, message);
-  vfprintf (mc->options->output_file, message, args);
-  va_end (args);
-  putc ('\n', mc->options->output_file);
-
-  mc->state_error = true;
-}
-
-/* Enqueues DATA as the state corresponding to the current
-   operation.  The operation should have been named with a call
-   to mc_name_operation, and it should have been checked by the
-   caller (who should have reported any errors with mc_error).
-
-   This function should be called from the client-provided
-   "mutate" function, according to the pattern explained in the
-   big comment at the top of model-checker.h. */
-void
-mc_add_state (struct mc *mc, void *data)
-{
-  if (!mc->state_named && mc->options->verbosity > 0)
-    fprintf (mc->options->output_file, "  [%s] warning: unnamed state\n",
-             path_string (mc));
-
-  if (mc->results->stop_reason != MC_CONTINUING)
-    {
-      /* Nothing to do. */
-    }
-  else if (mc->state_error)
-    do_error_state (mc);
-  else if (is_off_path (mc))
-    mc->results->off_path_dropped_states++;
-  else if (mc->path.length + 1 > mc->options->max_depth)
-    mc->results->depth_dropped_states++;
-  else
-    {
-      /* This is the common case. */
-      mc->results->unique_state_count++;
-      if (mc->results->unique_state_count >= mc->options->max_unique_states)
-        stop (mc, MC_MAX_UNIQUE_STATES);
-      enqueue_state (mc, make_state (mc, data));
-      next_operation (mc);
-      return;
-    }
-
-  mc->class->destroy (mc, data);
-  next_operation (mc);
-}
-
-/* Returns the options that were passed to mc_run for model
-   checker MC. */
-const struct mc_options *
-mc_get_options (const struct mc *mc)
-{
-  return mc->options;
-}
-
-/* Returns the current state of the results for model checker
-   MC.  This function is appropriate for use from the progress
-   function set by mc_options_set_progress_func.
-
-   Not all of the results are meaningful before model checking
-   completes. */
-const struct mc_results *
-mc_get_results (const struct mc *mc)
-{
-  return mc->results;
-}
-
-/* Returns the auxiliary data set on the options passed to mc_run
-   with mc_options_set_aux. */
-void *
-mc_get_aux (const struct mc *mc)
-{
-  return mc_options_get_aux (mc_get_options (mc));
-}
-\f
-/* Expresses MC->path as a string and returns the string. */
-static const char *
-path_string (struct mc *mc)
-{
-  ds_clear (&mc->path_string);
-  mc_path_to_string (&mc->path, &mc->path_string);
-  return ds_cstr (&mc->path_string);
-}
-
-/* Frees STATE, including client data. */
-static void
-free_state (const struct mc *mc, struct mc_state *state)
-{
-  mc->class->destroy (mc, state->data);
-  mc_path_destroy (&state->path);
-  free (state);
-}
-
-/* Sets STOP_REASON as the reason that MC's processing stopped,
-   unless MC is already stopped. */
-static void
-stop (struct mc *mc, enum mc_stop_reason stop_reason)
-{
-  if (mc->results->stop_reason == MC_CONTINUING)
-    mc->results->stop_reason = stop_reason;
-}
-
-/* Creates and returns a new state whose path is copied from
-   MC->path and whose data is specified by DATA. */
-static struct mc_state *
-make_state (const struct mc *mc, void *data)
-{
-  struct mc_state *new = xmalloc (sizeof *new);
-  mc_path_init (&new->path);
-  mc_path_copy (&new->path, &mc->path);
-  new->data = data;
-  return new;
-}
-
-/* Returns the index in MC->queue of a random element in the
-   queue. */
-static size_t
-random_queue_index (struct mc *mc)
-{
-  assert (!deque_is_empty (&mc->queue_deque));
-  return deque_front (&mc->queue_deque,
-                      rand () % deque_count (&mc->queue_deque));
-}
-
-/* Adds NEW to MC's state queue, dropping a state if necessary
-   due to overflow. */
-static void
-enqueue_state (struct mc *mc, struct mc_state *new)
-{
-  size_t idx;
-
-  if (new->path.length > mc->results->max_depth_reached)
-    mc->results->max_depth_reached = new->path.length;
-  moments1_add (mc->results->depth_moments, new->path.length, 1.0);
-
-  if (deque_count (&mc->queue_deque) < mc->options->queue_limit)
-    {
-      /* Add new state to queue. */
-      if (deque_is_full (&mc->queue_deque))
-        mc->queue = deque_expand (&mc->queue_deque,
-                                   mc->queue, sizeof *mc->queue);
-      switch (mc->options->strategy)
-        {
-        case MC_BROAD:
-          idx = deque_push_back (&mc->queue_deque);
-          break;
-        case MC_DEEP:
-          idx = deque_push_front (&mc->queue_deque);
-          break;
-        case MC_RANDOM:
-          if (!deque_is_empty (&mc->queue_deque))
-            {
-              idx = random_queue_index (mc);
-              mc->queue[deque_push_front (&mc->queue_deque)]
-                = mc->queue[idx];
-            }
-          else
-            idx = deque_push_front (&mc->queue_deque);
-          break;
-        case MC_PATH:
-          assert (deque_is_empty (&mc->queue_deque));
-          assert (!is_off_path (mc));
-          idx = deque_push_back (&mc->queue_deque);
-          if (mc->path.length
-              >= mc_path_get_length (&mc->options->follow_path))
-            stop (mc, MC_END_OF_PATH);
-          break;
-        default:
-          NOT_REACHED ();
-        }
-      if (deque_count (&mc->queue_deque) > mc->results->max_queue_length)
-        mc->results->max_queue_length = deque_count (&mc->queue_deque);
-    }
-  else
-    {
-      /* Queue has reached limit, so replace an existing
-         state. */
-      assert (mc->options->strategy != MC_PATH);
-      assert (!deque_is_empty (&mc->queue_deque));
-      mc->results->queue_dropped_states++;
-      switch (mc->options->queue_limit_strategy)
-        {
-        case MC_DROP_NEWEST:
-          free_state (mc, new);
-          return;
-        case MC_DROP_OLDEST:
-          switch (mc->options->strategy)
-            {
-            case MC_BROAD:
-              idx = deque_front (&mc->queue_deque, 0);
-              break;
-            case MC_DEEP:
-              idx = deque_back (&mc->queue_deque, 0);
-              break;
-            case MC_RANDOM:
-            case MC_PATH:
-            default:
-              NOT_REACHED ();
-            }
-          break;
-        case MC_DROP_RANDOM:
-          idx = random_queue_index (mc);
-          break;
-        default:
-          NOT_REACHED ();
-        }
-      free_state (mc, mc->queue[idx]);
-    }
-  mc->queue[idx] = new;
-}
-
-/* Process an error state being added to MC. */
-static void
-do_error_state (struct mc *mc)
-{
-  mc->results->error_count++;
-  if (mc->results->error_count >= mc->options->max_errors)
-    stop (mc, MC_MAX_ERROR_COUNT);
-
-  mc_path_copy (&mc->results->error_path, &mc->path);
-
-  if (mc->options->failure_verbosity > mc->options->verbosity)
-    {
-      struct mc_options *path_options;
-
-      fprintf (mc->options->output_file, "[%s] retracing error path:\n",
-               path_string (mc));
-      path_options = mc_options_clone (mc->options);
-      mc_options_set_verbosity (path_options, mc->options->failure_verbosity);
-      mc_options_set_failure_verbosity (path_options, 0);
-      mc_options_set_follow_path (path_options, &mc->path);
-
-      mc_results_destroy (mc_run (mc->class, path_options));
-
-      putc ('\n', mc->options->output_file);
-    }
-}
-
-/* Advances MC to start processing the operation following the
-   current one. */
-static void
-next_operation (struct mc *mc)
-{
-  mc_path_push (&mc->path, mc_path_pop (&mc->path) + 1);
-  mc->state_error = false;
-  mc->state_named = false;
-
-  if (++mc->progress >= mc->next_progress)
-    {
-      struct timeval now;
-      double elapsed, delta;
-
-      if (mc->results->stop_reason == MC_CONTINUING
-          && !mc->options->progress_func (mc))
-        stop (mc, MC_INTERRUPTED);
-
-      gettimeofday (&now, NULL);
-
-      if (mc->options->time_limit > 0.0
-          && (timeval_subtract (now, mc->results->start)
-              > mc->options->time_limit))
-        stop (mc, MC_TIMEOUT);
-
-      elapsed = timeval_subtract (now, mc->prev_progress_time);
-      if (elapsed > 0.0)
-        {
-          /* Re-estimate the amount of progress to take
-             progress_usec microseconds. */
-          unsigned int progress = mc->progress - mc->prev_progress;
-          double progress_sec = mc->options->progress_usec / 1000000.0;
-          delta = progress / elapsed * progress_sec;
-        }
-      else
-        {
-          /* No measurable time at all elapsed during that amount
-             of progress.  Try doubling the amount of progress
-             required. */
-          delta = (mc->progress - mc->prev_progress) * 2;
-        }
-
-      if (delta > 0.0 && delta + mc->progress + 1.0 < UINT_MAX)
-        mc->next_progress = mc->progress + delta + 1.0;
-      else
-        mc->next_progress = mc->progress + (mc->progress - mc->prev_progress);
-
-      mc->prev_progress = mc->progress;
-      mc->prev_progress_time = now;
-    }
-}
-
-/* Returns true if we're tracing an explicit path but the current
-   operation produces a state off that path, false otherwise. */
-static bool
-is_off_path (const struct mc *mc)
-{
-  return (mc->options->strategy == MC_PATH
-          && (mc_path_back (&mc->path)
-              != mc_path_get_operation (&mc->options->follow_path,
-                                        mc->path.length - 1)));
-}
-
-/* Handler for SIGINT. */
-static void
-sigint_handler (int signum UNUSED)
-{
-  /* Just mark the model checker as interrupted. */
-  *interrupted_ptr = true;
-}
-
-/* Initializes MC as a model checker with the given CLASS and
-   OPTIONS.  OPTIONS may be null to use the default options. */
-static void
-init_mc (struct mc *mc, const struct mc_class *class,
-         struct mc_options *options)
-{
-  /* Validate and adjust OPTIONS. */
-  if (options == NULL)
-    options = mc_options_create ();
-  assert (options->queue_limit_strategy != MC_DROP_OLDEST
-          || options->strategy != MC_RANDOM);
-  if (options->strategy == MC_PATH)
-    {
-      options->max_depth = INT_MAX;
-      options->hash_bits = 0;
-    }
-  if (options->progress_usec == 0)
-    {
-      options->progress_func = null_progress;
-      if (options->time_limit > 0.0)
-        options->progress_usec = 250000;
-    }
-
-  /* Initialize MC. */
-  mc->class = class;
-  mc->options = options;
-  mc->results = mc_results_create ();
-
-  mc->hash = (mc->options->hash_bits > 0
-              ? xcalloc (1, DIV_RND_UP (1 << mc->options->hash_bits, CHAR_BIT))
-              : NULL);
-
-  mc->queue = NULL;
-  deque_init_null (&mc->queue_deque);
-
-  mc_path_init (&mc->path);
-  mc_path_push (&mc->path, 0);
-  ds_init_empty (&mc->path_string);
-  mc->state_named = false;
-  mc->state_error = false;
-
-  mc->progress = 0;
-  mc->next_progress = mc->options->progress_usec != 0 ? 100 : UINT_MAX;
-  mc->prev_progress = 0;
-  mc->prev_progress_time = mc->results->start;
-
-  if (mc->options->strategy == MC_RANDOM
-      || options->queue_limit_strategy == MC_DROP_RANDOM)
-    srand (mc->options->seed);
-
-  mc->interrupted = false;
-  mc->saved_interrupted_ptr = interrupted_ptr;
-  interrupted_ptr = &mc->interrupted;
-  mc->saved_sigint = signal (SIGINT, sigint_handler);
-
-  class->init (mc);
-}
-
-/* Complete the model checker run for MC. */
-static void
-finish_mc (struct mc *mc)
-{
-  /* Restore signal handlers. */
-  signal (SIGINT, mc->saved_sigint);
-  interrupted_ptr = mc->saved_interrupted_ptr;
-
-  /* Mark the run complete. */
-  stop (mc, MC_SUCCESS);
-  gettimeofday (&mc->results->end, NULL);
-
-  /* Empty the queue. */
-  mc->results->queued_unprocessed_states = deque_count (&mc->queue_deque);
-  while (!deque_is_empty (&mc->queue_deque))
-    {
-      struct mc_state *state = mc->queue[deque_pop_front (&mc->queue_deque)];
-      free_state (mc, state);
-    }
-
-  /* Notify the progress function of completion. */
-  mc->options->progress_func (mc);
-
-  /* Free memory. */
-  mc_path_destroy (&mc->path);
-  ds_destroy (&mc->path_string);
-  free (mc->options);
-  free (mc->queue);
-  free (mc->hash);
-}
diff --git a/src/libpspp/model-checker.h b/src/libpspp/model-checker.h
deleted file mode 100644 (file)
index 8c86fae..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 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/>. */
-
-/* Implementation-level model checker.
-
-   A model checker is a tool for software testing and
-   verification that works by exploring all the possible states
-   in a system and verifying their internal consistency.  A
-   conventional model checker requires that the code in a system
-   be translated into a specification language.  The model
-   checker then verifies the specification, rather than the code.
-
-   This is instead an implementation-level model checker, which
-   does not require a separate specification.  Instead, the model
-   checker requires writing a second implementation of the system
-   being checked.  The second implementation can usually be made
-   almost trivial in comparison to the one being checked, because
-   it's usually acceptable for its performance to be
-   comparatively poor, e.g. O(N^2) instead of O(lg N), and thus
-   to use much simpler algorithms.
-
-   For introduction to the implementation-level model checking
-   approach used here, please refer to the following papers:
-
-     Musuvathi, Park, Chou, Engler, Dill, "CMC: A Pragmatic
-     Approach to Model Checking Real Code", Proceedings of the
-     Fifth Symposium on Operating Systems Design and
-     Implementation (OSDI), Dec 2002.
-     http://sprout.stanford.edu/PAPERS/CMC-OSDI-2002/CMC-OSDI-2002.pdf
-
-     Yang, Twohey, Engler, Musuvathi, "Using Model Checking to
-     Find Serious File System Errors", Proceedings of the Sixth
-     Symposium on Operating System Design and Implementation
-     (OSDI), Dec 2004.
-     http://www.stanford.edu/~engler/osdi04-fisc.pdf
-
-     Yang, Twohey, Pfaff, Sar, Engler, "EXPLODE: A Lightweight,
-     General Approach to Finding Serious Errors in Storage
-     Systems", First Workshop on the Evaluation of Software
-     Defect Detection Tools (BUGS), June 2005.
-     http://benpfaff.org/papers/explode.pdf
-
-   Use of a model checker is appropriate when the system being
-   checked is difficult to test using handwritten tests.  This
-   can be the case, for example, when the system has a
-   complicated internal state that is difficult to reason about
-   over a long series of operations.
-
-   The implementation model checker works by putting a set of one
-   of more initial states in a queue (and checking them for
-   consistency).  Then the model checker removes a state from the
-   queue and applies all possible operations of interest to it
-   ("mutates" it), obtaining a set of zero or more child states
-   (and checking each of them for consistency).  Each of these
-   states is itself added to the queue.  The model checker
-   continues dequeuing states and mutating and checking them
-   until the queue is empty.
-
-   In pseudo-code, the process looks like this:
-
-     Q = { initial states }
-     while Q is not empty:
-       S = dequeue(Q)
-       for each operation applicable to S do:
-         T = operation(S)
-         check(T)
-         enqueue(Q, T)
-
-   In many cases this process will never terminate, because every
-   state has one or more child states.  For some systems this is
-   unavoidable, but in others we can make the process finite by
-   pursuing a few stratagems:
-
-     1. Limit the maximum size of the system; for example, limit
-        the number of rows and columns in the implementation of a
-        table being checked.  The client of the model checker is
-        responsible for implementing such limits.
-
-     2. Avoid checking a single state more than one time.  This
-        model checker provides assistance for this function by
-        allowing the client to provide a hash of the system state.
-        States with identical hashes will only be checked once
-        during a single run.
-
-   When a system cannot be made finite, or when a finite system
-   is too large to check in a practical amount of time, the model
-   checker provides multiple ways to limit the checking run:
-   based on maximum depth, maximum unique states checked, maximum
-   errors found (by default, 1), or maximum time used for
-   checking.
-
-   The client of the model checker must provide three functions
-   via function pointers embedded into a "struct mc_class":
-
-     1. void init (struct mc *mc);
-
-        This function is called once at the beginning of a
-        checking run.  It checks one or more initial states and
-        adds them to the model checker's queue.  (If it does not
-        add any states to the queue, then there is nothing to
-        check.)
-
-        Here's an outline for writing the init function:
-
-          void
-          init_foo (struct mc *mc)
-          {
-            struct foo *foo;
-
-            mc_name_operation (mc, "initial state");
-            foo = generate_initial_foo ();
-            if (!state_is_consistent (foo))
-              mc_error (mc, "inconsistent state");
-            mc_add_state (mc, foo);
-          }
-
-     2. void mutate (struct mc *mc, const void *data);
-
-        This function is called when a dequeued state is ready to
-        be mutated.  For each operation that can be applied to
-        the client-specified DATA, it applies that operation to a
-        clone of the DATA, checks that the clone is consistent,
-        and adds the clone to the model checker's queue.
-
-        Here's an outline for writing the mutate function:
-
-          void
-          mutate_foo (struct mc *mc, void *state_)
-          {
-            struct foo *state = state_;
-
-            for (...each operation...)
-              if (mc_include_state (mc))
-                {
-                  struct foo *clone;
-
-                  mc_name_operation (mc, "do operation %s", ...);
-                  clone = clone_foo (state);
-                  do_operation (clone);
-                  if (!mc_discard_dup_state (mc, hash_foo (clone)))
-                    {
-                      if (!state_is_consistent (clone))
-                        mc_error (mc, "inconsistent state");
-                      mc_add_state (mc, clone);
-                    }
-                  else
-                    destroy_foo (clone);
-                }
-          }
-
-        Notes on the above outline:
-
-          - The call to mc_include_state allows currently
-            uninteresting operations to be skipped.  It is not
-            essential.
-
-          - The call to mc_name_operation should give the current
-            operation a human-readable name.  The name may
-            include printf-style format specifications.
-
-            When an error occurs, the model checker (by default)
-            replays the sequence of operations performed to reach
-            the error, printing the name of the operation at each
-            step, which is often sufficient information in itself
-            to debug the error.
-
-            At higher levels of verbosity, the name is printed
-            for each operation.
-
-          - Operations should be performed on a copy of the data
-            provided.  The data provided should not be destroyed
-            or modified.
-
-          - The call to mc_discard_dup_state is needed to discard
-            (probably) duplicate states.  It is otherwise
-            optional.
-
-            To reduce the probability of collisions, use a
-            high-quality hash function.  MD4 is a reasonable
-            choice: it is fast but high-quality.  In one test,
-            switching to MD4 from MD5 increased overall speed of
-            model checking by 8% and actually reduced (!) the
-            number of collisions.
-
-            The hash value needs to include enough of the state
-            to ensure that interesting states are not excluded,
-            but it need not include the entire state.  For
-            example, in many cases, the structure of complex data
-            (metadata) is often much more important than the
-            contents (data), so it may be reasonable to hash only
-            the metadata.
-
-            mc_discard_dup_state may be called before or after
-            checking for consistency, but calling it first avoids
-            wasting time checking duplicate states for
-            consistency, which again can be a significant
-            performance boost.
-
-          - The mc_error function reports errors.  It may be
-            called as many times as desired to report each kind
-            of inconsistency in a state.
-
-          - The mc_add_state function adds the new state to the
-            queue.  It should be called regardless of whether an
-            error was reported, to indicate to the model checker
-            that state processing has finished.
-
-          - The mutation function should be deterministic, to
-            make it possible to reliably reproduce errors.
-
-     3. void destroy (struct mc *mc, void *data);
-
-        This function is called to discard the client-specified
-        DATA associated with a state.
-
-   Numerous options are available for configuring the model
-   checker.  The most important of these are:
-
-     - Search algorithm:
-
-       * Breadth-first search (the default): First try all the
-         operations with depth 1, then those with depth 2, then
-         those with depth 3, and so on.
-
-         This search algorithm finds the least number of
-         operations needed to trigger a given bug.
-
-       * Depth-first search: Searches downward in the tree of
-         states as fast as possible.  Good for finding bugs that
-         require long sequences of operations to trigger.
-
-       * Random-first search: Searches through the tree of
-         states in random order.
-
-       * Explicit path: Applies an explicitly specified sequence
-         of operations.
-
-     - Verbosity: By default, messages are printed only when an
-       error is encountered, but you can cause the checker to
-       print a message on every state transition.  This is most
-       useful when the errors in your code cause segfaults or
-       some other kind of sudden termination.
-
-     - Treatment of errors: By default, when an error is
-       encountered, the model checker recursively invokes itself
-       with an increased verbosity level and configured to follow
-       only the error path.  As long as the mutation function is
-       deterministic, this quickly and concisely replays the
-       error and describes the path followed to reach it in an
-       easily human-readable manner.
-
-     - Limits:
-
-       * Maximum depth: You can limit the depth of the operations
-         performed.  Most often useful with depth-first search.
-         By default, depth is unlimited.
-
-       * Maximum queue length: You can limit the number of states
-         kept in the queue at any given time.  The main reason to
-         do so is to limit memory consumption.  The default
-         limit is 10,000 states.  Several strategies are
-         available for choosing which state to drop when the
-         queue overflows.
-
-     - Stop conditions: based on maximum unique states checked,
-       maximum errors found (by default, 1), or maximum time used
-       for checking.
-
-     - Progress: by default, the checker prints a '.' on stderr
-       every .25 seconds, but you can substitute another progress
-       function or disable progress printing.
-
-   This model checker does not (yet) include two features
-   described in the papers cited above: utility scoring
-   heuristics to guide the search strategy and "choice points" to
-   explore alternative cases.  The former feature is less
-   interesting for this model checker, because the data
-   structures we are thus far using it to model are much smaller
-   than those discussed in the paper.  The latter feature we
-   should implement at some point. */
-
-#ifndef LIBPSPP_MODEL_CHECKER_H
-#define LIBPSPP_MODEL_CHECKER_H 1
-
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <sys/time.h>
-
-#include <libpspp/compiler.h>
-
-/* An active model checking run. */
-struct mc;
-
-/* Provided by each client of the model checker. */
-struct mc_class
-  {
-    void (*init) (struct mc *);
-    void (*mutate) (struct mc *, const void *);
-    void (*destroy) (const struct mc *, void *);
-  };
-
-/* Results of a model checking run. */
-struct mc_results;
-
-/* Configuration for running the model checker. */
-struct mc_options;
-
-/* Primary external interface to model checker. */
-struct mc_results *mc_run (const struct mc_class *, struct mc_options *);
-
-/* Functions for use from client-supplied "init" and "mutate"
-   functions. */
-bool mc_include_state (struct mc *);
-bool mc_discard_dup_state (struct mc *, unsigned int hash);
-void mc_name_operation (struct mc *, const char *, ...) PRINTF_FORMAT (2, 3);
-void mc_vname_operation (struct mc *, const char *, va_list)
-     PRINTF_FORMAT (2, 0);
-void mc_error (struct mc *, const char *, ...) PRINTF_FORMAT (2, 3);
-void mc_add_state (struct mc *, void *data);
-
-/* Functions for use from client-supplied "init", "mutate", and
-   "destroy" functions. */
-const struct mc_options *mc_get_options (const struct mc *);
-const struct mc_results *mc_get_results (const struct mc *);
-void *mc_get_aux (const struct mc *);
-\f
-/* A path of operations through a model to arrive at some
-   particular state. */
-struct mc_path
-  {
-    int *ops;           /* Sequence of operations. */
-    size_t length;      /* Number of operations. */
-    size_t capacity;    /* Number of operations for which room is allocated. */
-  };
-
-void mc_path_init (struct mc_path *);
-void mc_path_copy (struct mc_path *, const struct mc_path *);
-void mc_path_push (struct mc_path *, int new_state);
-int mc_path_pop (struct mc_path *);
-int mc_path_back (const struct mc_path *);
-void mc_path_destroy (struct mc_path *);
-
-int mc_path_get_operation (const struct mc_path *, size_t index);
-size_t mc_path_get_length (const struct mc_path *);
-
-struct string;
-void mc_path_to_string (const struct mc_path *, struct string *);
-\f
-struct mc_options *mc_options_create (void);
-struct mc_options *mc_options_clone (const struct mc_options *);
-void mc_options_destroy (struct mc_options *);
-
-/* Search strategy. */
-enum mc_strategy
-  {
-    MC_BROAD,           /* Breadth-first search. */
-    MC_DEEP,            /* Depth-first search. */
-    MC_RANDOM,          /* Randomly ordered search. */
-    MC_PATH             /* Follow one explicit path. */
-  };
-
-enum mc_strategy mc_options_get_strategy (const struct mc_options *);
-void mc_options_set_strategy (struct mc_options *, enum mc_strategy);
-unsigned int mc_options_get_seed (const struct mc_options *);
-void mc_options_set_seed (struct mc_options *, unsigned int seed);
-int mc_options_get_max_depth (const struct mc_options *);
-void mc_options_set_max_depth (struct mc_options *, int max_depth);
-int mc_options_get_hash_bits (const struct mc_options *);
-void mc_options_set_hash_bits (struct mc_options *, int hash_bits);
-
-const struct mc_path *mc_options_get_follow_path (const struct mc_options *);
-void mc_options_set_follow_path (struct mc_options *, const struct mc_path *);
-
-/* Strategy for dropped states from the queue when it
-   overflows. */
-enum mc_queue_limit_strategy
-  {
-    MC_DROP_NEWEST,     /* Don't enqueue the new state at all. */
-    MC_DROP_OLDEST,     /* Drop the oldest state in the queue. */
-    MC_DROP_RANDOM      /* Drop a random state from the queue. */
-  };
-
-int mc_options_get_queue_limit (const struct mc_options *);
-void mc_options_set_queue_limit (struct mc_options *, int queue_limit);
-enum mc_queue_limit_strategy mc_options_get_queue_limit_strategy (
-  const struct mc_options *);
-void mc_options_set_queue_limit_strategy (struct mc_options *,
-                                          enum mc_queue_limit_strategy);
-
-int mc_options_get_max_unique_states (const struct mc_options *);
-void mc_options_set_max_unique_states (struct mc_options *,
-                                       int max_unique_states);
-int mc_options_get_max_errors (const struct mc_options *);
-void mc_options_set_max_errors (struct mc_options *, int max_errors);
-double mc_options_get_time_limit (const struct mc_options *);
-void mc_options_set_time_limit (struct mc_options *, double time_limit);
-
-int mc_options_get_verbosity (const struct mc_options *);
-void mc_options_set_verbosity (struct mc_options *, int verbosity);
-int mc_options_get_failure_verbosity (const struct mc_options *);
-void mc_options_set_failure_verbosity (struct mc_options *,
-                                       int failure_verbosity);
-FILE *mc_options_get_output_file (const struct mc_options *);
-void mc_options_set_output_file (struct mc_options *, FILE *);
-
-typedef bool mc_progress_func (struct mc *);
-int mc_options_get_progress_usec (const struct mc_options *);
-void mc_options_set_progress_usec (struct mc_options *, int progress_usec);
-mc_progress_func *mc_options_get_progress_func (const struct mc_options *);
-void mc_options_set_progress_func (struct mc_options *, mc_progress_func *);
-
-void *mc_options_get_aux (const struct mc_options *);
-void mc_options_set_aux (struct mc_options *, void *aux);
-\f
-/* Reason that a model checking run terminated. */
-enum mc_stop_reason
-  {
-    MC_CONTINUING,              /* Run has not yet terminated. */
-    MC_SUCCESS,                 /* Queue emptied (ran out of states). */
-    MC_MAX_UNIQUE_STATES,       /* Did requested number of unique states. */
-    MC_MAX_ERROR_COUNT,         /* Too many errors. */
-    MC_END_OF_PATH,             /* Processed requested path (MC_PATH only). */
-    MC_TIMEOUT,                 /* Timeout. */
-    MC_INTERRUPTED              /* Received SIGINT (Ctrl+C). */
-  };
-
-void mc_results_destroy (struct mc_results *);
-
-enum mc_stop_reason mc_results_get_stop_reason (const struct mc_results *);
-int mc_results_get_unique_state_count (const struct mc_results *);
-int mc_results_get_error_count (const struct mc_results *);
-
-int mc_results_get_max_depth_reached (const struct mc_results *);
-double mc_results_get_mean_depth_reached (const struct mc_results *);
-
-const struct mc_path *mc_results_get_error_path (const struct mc_results *);
-
-int mc_results_get_duplicate_dropped_states (const struct mc_results *);
-int mc_results_get_off_path_dropped_states (const struct mc_results *);
-int mc_results_get_depth_dropped_states (const struct mc_results *);
-int mc_results_get_queue_dropped_states (const struct mc_results *);
-int mc_results_get_queued_unprocessed_states (const struct mc_results *);
-int mc_results_get_max_queue_length (const struct mc_results *);
-
-struct timeval mc_results_get_start (const struct mc_results *);
-struct timeval mc_results_get_end (const struct mc_results *);
-double mc_results_get_duration (const struct mc_results *);
-
-#endif /* libpspp/model-checker.h */
index eba5d8327a2f0b8e9202c8e3244d8b90ac17cd89..7af219bcb080cd0a75b8b6bf9f823d2f397618a1 100644 (file)
@@ -88,7 +88,7 @@ struct sparse_array
 /* An internal node in the radix tree. */
 struct internal_node
   {
-    int count;                  /* Number of nonnul children. */
+    int count;                  /* Number of nonnull children. */
     union pointer down[PTRS_PER_LEVEL]; /* Children. */
   };
 
index 552968b5c837d02411e4e101a7ba419b1b0d7a32..3cff0492c7d508d99d9ce0b5c77c57879bc1faf7 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -195,15 +195,18 @@ buf_copy_rpad (char *dst, size_t dst_size,
 void
 str_copy_rpad (char *dst, size_t dst_size, const char *src)
 {
-  size_t src_len = strlen (src);
-  if (src_len < dst_size - 1)
+  if (dst_size > 0) 
     {
-      memcpy (dst, src, src_len);
-      memset (&dst[src_len], ' ', dst_size - 1 - src_len);
+      size_t src_len = strlen (src);
+      if (src_len < dst_size - 1)
+        {
+          memcpy (dst, src, src_len);
+          memset (&dst[src_len], ' ', dst_size - 1 - src_len);
+        }
+      else
+        memcpy (dst, src, dst_size - 1);
+      dst[dst_size - 1] = 0;
     }
-  else
-    memcpy (dst, src, dst_size - 1);
-  dst[dst_size - 1] = 0;
 }
 
 /* Copies SRC to DST, which is in a buffer DST_SIZE bytes long.
@@ -1188,48 +1191,42 @@ ds_cstr (const struct string *st_)
   return st->ss.string;
 }
 
-/* Appends to ST a newline-terminated line read from STREAM, but
-   no more than MAX_LENGTH characters.
-   Newline is the last character of ST on return, if encountering
-   a newline was the reason for terminating.
-   Returns true if at least one character was read from STREAM
-   and appended to ST, false if no characters at all were read
-   before an I/O error or end of file was encountered (or
-   MAX_LENGTH was 0). */
+/* Reads characters from STREAM and appends them to ST, stopping
+   after MAX_LENGTH characters, after appending a newline, or
+   after an I/O error or end of file was encountered, whichever
+   comes first.  Returns true if at least one character was added
+   to ST, false if no characters were read before an I/O error or
+   end of file (or if MAX_LENGTH was 0).
+
+   This function accepts LF, CR LF, and CR sequences as new-line,
+   and translates each of them to a single '\n' new-line
+   character in ST. */
 bool
 ds_read_line (struct string *st, FILE *stream, size_t max_length)
 {
-  if (!st->ss.length && max_length == SIZE_MAX)
-    {
-      size_t capacity = st->capacity ? st->capacity + 1 : 0;
-      ssize_t n = getline (&st->ss.string, &capacity, stream);
-      if (capacity)
-        st->capacity = capacity - 1;
-      if (n > 0)
-        {
-          st->ss.length = n;
-          return true;
-        }
-      else
-        return false;
-    }
-  else
+  size_t length;
+
+  for (length = 0; length < max_length; length++)
     {
-      size_t length;
+      int c = getc (stream);
+      if (c == EOF)
+        break;
 
-      for (length = 0; length < max_length; length++)
+      if (c == '\r')
         {
-          int c = getc (stream);
-          if (c == EOF)
-            break;
-
-          ds_put_char (st, c);
-          if (c == '\n')
-            return true;
+          c = getc (stream);
+          if (c != '\n')
+            {
+              ungetc (c, stream);
+              c = '\n';
+            }
         }
-
-      return length > 0;
+      ds_put_char (st, c);
+      if (c == '\n')
+        return true;
     }
+
+  return length > 0;
 }
 
 /* Removes a comment introduced by `#' from ST,
index 02447d76237d335170dbbf877f099b41e20d8d8e..e8d253d086731c61dd539962cbc79f9c2b3b0750 100644 (file)
@@ -30,12 +30,59 @@ static struct tower_node *next_node (const struct tower *,
                                      const struct tower_node *);
 static struct tower_node *prev_node (const struct tower *,
                                      const struct tower_node *);
-static unsigned long int get_subtree_height (const struct abt_node *);
+static unsigned long int get_subtree_size (const struct abt_node *);
+static unsigned long int get_subtree_count (const struct abt_node *);
 static void reaugment_tower_node (struct abt_node *,
                                   const struct abt_node *,
                                   const struct abt_node *,
                                   const void *aux);
 
+/* Returns the height of the bottom of the given tower NODE.
+
+   The performance of this function is O(lg n) in the number of
+   nodes in the tower.  It is often possible to avoid calling
+   this function, either by taking advantage of the NODE_START
+   parameter to tower_lookup or by incrementally keeping track of
+   height while iterating through a tower.  In the former case
+   the asymptotic performance is no different, since tower_lookup
+   is also O(lg n), but in the latter case performance improves
+   from O(lg n) to O(1). */
+unsigned long int
+tower_node_get_level (const struct tower_node *node)
+{
+  const struct abt_node *p = &node->abt_node;
+  unsigned long level = get_subtree_size (p->down[0]);
+  while (p->up != NULL) 
+    {
+      if (p == p->up->down[1])
+        level += (get_subtree_size (p->up->down[0]) 
+                  + abt_to_tower_node (p->up)->size);
+      p = p->up;
+    }
+  return level;
+}
+
+/* Returns the index of the given tower NODE.
+
+   The performance of this function is O(lg n) in the number of
+   nodes in the tower.  It is often possible to avoid calling
+   this function by keeping track of the index while iterating
+   through a tower.  Doing so when possible will improve
+   performance from O(lg n) to O(1). */
+unsigned long int
+tower_node_get_index (const struct tower_node *node)
+{
+  const struct abt_node *p = &node->abt_node;
+  unsigned long index = get_subtree_count (p->down[0]);
+  while (p->up != NULL) 
+    {
+      if (p == p->up->down[1])
+        index += get_subtree_count (p->up->down[0]) + 1;
+      p = p->up;
+    }
+  return index;
+}
+
 /* Initializes T as an empty tower. */
 void
 tower_init (struct tower *t)
@@ -51,21 +98,28 @@ tower_is_empty (const struct tower *t)
   return t->abt.root == NULL;
 }
 
+/* Returns the number of nodes in tower T. */
+unsigned long int
+tower_count (const struct tower *t)
+{
+  return get_subtree_count (t->abt.root);
+}
+
 /* Returns the total height of tower T. */
 unsigned long
 tower_height (const struct tower *t)
 {
-  return get_subtree_height (t->abt.root);
+  return get_subtree_size (t->abt.root);
 }
 
-/* Inserts node NEW with the specified HEIGHT into T just below
+/* Inserts node NEW with the specified SIZE into T just below
    node UNDER, or at the top of T if UNDER is a null pointer. */
 void
-tower_insert (struct tower *t, unsigned long height, struct tower_node *new,
+tower_insert (struct tower *t, unsigned long size, struct tower_node *new,
               struct tower_node *under)
 {
-  assert (height > 0);
-  new->height = height;
+  assert (size > 0);
+  new->size = size;
   abt_insert_before (&t->abt, under ? &under->abt_node : NULL,
                      &new->abt_node);
   t->cache_bottom = ULONG_MAX;
@@ -81,13 +135,13 @@ tower_delete (struct tower *t, struct tower_node *node)
   return next;
 }
 
-/* Changes the height of NODE in tower T to NEW_HEIGHT. */
+/* Changes the size of NODE in tower T to NEW_SIZE. */
 void
 tower_resize (struct tower *t, struct tower_node *node,
-              unsigned long new_height)
+              unsigned long new_size)
 {
-  assert (new_height > 0);
-  node->height = new_height;
+  assert (new_size > 0);
+  node->size = new_size;
   abt_reaugmented (&t->abt, &node->abt_node);
   t->cache_bottom = ULONG_MAX;
 }
@@ -135,7 +189,7 @@ tower_lookup (const struct tower *t_,
 
   assert (height < tower_height (t));
 
-  if (height >= t->cache_bottom && height - t->cache_bottom < t->cache->height)
+  if (height >= t->cache_bottom && height - t->cache_bottom < t->cache->size)
     {
       *node_start = t->cache_bottom;
       return t->cache;
@@ -145,8 +199,8 @@ tower_lookup (const struct tower *t_,
   p = t->abt.root;
   for (;;)
     {
-      unsigned long left_height = get_subtree_height (p->down[0]);
-      if (height < left_height)
+      unsigned long left_size = get_subtree_size (p->down[0]);
+      if (height < left_size)
         {
           /* Our goal height must lie within the left subtree. */
           p = p->down[0];
@@ -155,11 +209,11 @@ tower_lookup (const struct tower *t_,
         {
           /* Our goal height cannot be in the left subtree. */
           struct tower_node *node = abt_to_tower_node (p);
-          unsigned long int node_height = node->height;
+          unsigned long int node_size = node->size;
 
-          height -= left_height;
-          *node_start += left_height;
-          if (height < node_height)
+          height -= left_size;
+          *node_start += left_size;
+          if (height < node_size)
             {
               /* Our goal height is in P. */
               t->cache = node;
@@ -170,13 +224,40 @@ tower_lookup (const struct tower *t_,
             {
               /* Our goal height is in the right subtree. */
               p = p->down[1];
-              height -= node_height;
-              *node_start += node_height;
+              height -= node_size;
+              *node_start += node_size;
             }
         }
     }
 }
 
+/* Returns the node with the given 0-based INDEX, which must be
+   less than the number of nodes in T (as returned by
+   tower_count). */
+struct tower_node *
+tower_get (const struct tower *t_, unsigned long int index) 
+{
+  struct tower *t = (struct tower *) t_;
+  struct abt_node *p;
+
+  assert (index < tower_count (t));
+
+  p = t->abt.root;
+  for (;;)
+    {
+      unsigned long left_count = get_subtree_count (p->down[0]);
+      if (index < left_count)
+        p = p->down[0];
+      else if (index == left_count)
+        return abt_to_tower_node (p);
+      else
+        {
+          p = p->down[1];
+          index -= left_count + 1;
+        }
+    }
+}
+
 /* Returns the node at height 0 in tower T, or a null pointer if
    T is empty. */
 struct tower_node *
@@ -253,16 +334,24 @@ prev_node (const struct tower *t, const struct tower_node *node)
   return abt_to_tower_node_null (abt_prev (&t->abt, &node->abt_node));
 }
 
-/* Returns the total height of the nodes in the subtree rooted at
+/* Returns the total size of the nodes in the subtree rooted at
    P, or 0 if P is null. */
 static unsigned long int
-get_subtree_height (const struct abt_node *p)
+get_subtree_size (const struct abt_node *p)
+{
+  return p != NULL ? abt_to_tower_node (p)->subtree_size : 0;
+}
+
+/* Returns the total number of nodes in the subtree rooted at P,
+   or 0 if P is null. */
+static unsigned long int
+get_subtree_count (const struct abt_node *p)
 {
-  return p != NULL ? abt_to_tower_node (p)->subtree_height : 0;
+  return p != NULL ? abt_to_tower_node (p)->subtree_count : 0;
 }
 
-/* Recalculates the subtree_height of NODE based on its LEFT and
-   RIGHT children's subtree_heights. */
+/* Recalculates the subtree_size of NODE based on its LEFT and
+   RIGHT children's subtree_sizes. */
 static void
 reaugment_tower_node (struct abt_node *node_,
                       const struct abt_node *left,
@@ -270,9 +359,18 @@ reaugment_tower_node (struct abt_node *node_,
                       const void *aux UNUSED)
 {
   struct tower_node *node = abt_to_tower_node (node_);
-  node->subtree_height = node->height;
-  if (left != NULL)
-    node->subtree_height += abt_to_tower_node (left)->subtree_height;
-  if (right != NULL)
-    node->subtree_height += abt_to_tower_node (right)->subtree_height;
+  node->subtree_size = node->size;
+  node->subtree_count = 1;
+  if (left != NULL) 
+    {
+      struct tower_node *left_node = abt_to_tower_node (left);
+      node->subtree_size += left_node->subtree_size;
+      node->subtree_count += left_node->subtree_count; 
+    }
+  if (right != NULL) 
+    {
+      struct tower_node *right_node = abt_to_tower_node (right);
+      node->subtree_size += right_node->subtree_size;
+      node->subtree_count += right_node->subtree_count; 
+    }
 }
index 55184e51cdc54b0276a2a557950569221931309f..246984a2c1bfd6cd1c46100fbe95f84140843a92 100644 (file)
 
    This is the analogy behind this data structure.  Each node in
    the data structure has a "thickness", which is actually called
-   the node's "height" because "thickness" is just too awkward a
+   the node's "size" because "thickness" is just too awkward a
    name.  The primary way to look up nodes is by a height from
    the bottom of the tower; any height within a node retrieves
    that node, not just the distance to the bottom of the node.
    You can insert a new node between any two existing nodes, or
    at either end, which shifts up the height of all the nodes
    above it.  You can also delete any node, which shifts down the
-   height of all the nodes above it. */
+   height of all the nodes above it.
+
+   The tower data structure also implements efficient access to
+   nodes by index, i.e. by 0-based count of nodes from the bottom
+   of the tower. */
 
 #ifndef LIBPSPP_TOWER_H
 #define LIBPSPP_TOWER_H
 struct tower_node
   {
     struct abt_node abt_node;         /* ABT node. */
-    unsigned long int subtree_height; /* Node's plus descendants' heights. */
-    unsigned long int height;         /* Height. */
+    unsigned long int subtree_size;   /* Node size plus descendants' sizes. */
+    unsigned long int size;           /* Size. */
+    unsigned long int subtree_count;  /* Number of descendants, plus 1. */
   };
 
-/* Returns the height of a tower node. */
+/* Returns the size of a tower node. */
 static inline unsigned long
-tower_node_get_height (const struct tower_node *node)
+tower_node_get_size (const struct tower_node *node)
 {
-  return node->height;
+  return node->size;
 }
 
+unsigned long int tower_node_get_level (const struct tower_node *);
+unsigned long int tower_node_get_index (const struct tower_node *);
+
 /* A tower. */
 struct tower
   {
@@ -80,13 +88,14 @@ struct tower
 void tower_init (struct tower *);
 
 bool tower_is_empty (const struct tower *);
+unsigned long int tower_count (const struct tower *);
 unsigned long int tower_height (const struct tower *);
 
-void tower_insert (struct tower *, unsigned long int height,
+void tower_insert (struct tower *, unsigned long int size,
                    struct tower_node *new, struct tower_node *under);
 struct tower_node *tower_delete (struct tower *, struct tower_node *);
 void tower_resize (struct tower *, struct tower_node *,
-                   unsigned long int new_height);
+                   unsigned long int new_size);
 void tower_splice (struct tower *dst, struct tower_node *under,
                    struct tower *src,
                    struct tower_node *first, struct tower_node *last);
@@ -94,6 +103,7 @@ void tower_splice (struct tower *dst, struct tower_node *under,
 struct tower_node *tower_lookup (const struct tower *,
                                  unsigned long int level,
                                  unsigned long int *node_start);
+struct tower_node *tower_get (const struct tower *, unsigned long int index);
 struct tower_node *tower_first (const struct tower *);
 struct tower_node *tower_last (const struct tower *);
 struct tower_node *tower_next (const struct tower *,
index 107a985d443ace43a1e3828e9b72a135b215bc4b..cadfb90079ebf429083edede4d7cfbb9ec4a8f8f 100644 (file)
@@ -2,32 +2,35 @@
 
 include $(top_srcdir)/src/math/ts/automake.mk
 
-noinst_LIBRARIES += src/math/libpspp_math.a
+noinst_LTLIBRARIES += src/math/libpspp-math.la
 
-src_math_libpspp_math_a_SOURCES = \
-       src/math/factor-stats.c \
-       src/math/factor-stats.h \
+src_math_libpspp_math_la_LIBADD = \
+       lib/linreg/liblinreg.la
+
+src_math_libpspp_math_la_SOURCES = \
        src/math/chart-geometry.c \
        src/math/chart-geometry.h \
+       src/math/box-whisker.c src/math/box-whisker.h \
        src/math/coefficient.c \
        src/math/coefficient.h \
        src/math/covariance-matrix.c \
        src/math/covariance-matrix.h \
+       src/math/design-matrix.c src/math/design-matrix.h \
+       src/math/extrema.c src/math/extrema.h \
        src/math/group.c  src/math/group.h \
        src/math/group-proc.h \
        src/math/histogram.c src/math/histogram.h \
-       src/math/interaction.c \
-       src/math/interaction.h \
-       src/math/levene.c \
-       src/math/levene.h \
-       src/math/linreg.c \
-       src/math/linreg.h \
-       src/math/merge.c \
-       src/math/merge.h \
+       src/math/interaction.c src/math/interaction.h \
+       src/math/levene.c src/math/levene.h \
+       src/math/linreg.c src/math/linreg.h \
+       src/math/merge.c  src/math/merge.h \
        src/math/moments.c  src/math/moments.h \
+       src/math/np.c src/math/np.h \
+       src/math/order-stats.c src/math/order-stats.h \
        src/math/percentiles.c src/math/percentiles.h \
-       src/math/design-matrix.c src/math/design-matrix.h \
        src/math/random.c src/math/random.h \
-       src/math/sort.c src/math/sort.h 
-
-EXTRA_DIST += src/math/OChangeLog
+        src/math/statistic.h \
+       src/math/sort.c src/math/sort.h \
+       src/math/trimmed-mean.c src/math/trimmed-mean.h \
+       src/math/tukey-hinges.c src/math/tukey-hinges.h \
+       src/math/wilcoxon-sig.c src/math/wilcoxon-sig.h
diff --git a/src/math/box-whisker.c b/src/math/box-whisker.c
new file mode 100644 (file)
index 0000000..288fc07
--- /dev/null
@@ -0,0 +1,139 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 "box-whisker.h"
+#include "order-stats.h"
+#include "tukey-hinges.h"
+#include <gl/xalloc.h>
+#include <libpspp/assertion.h>
+#include <math.h>
+#include <float.h>
+#include <data/val-type.h>
+#include <libpspp/str.h>
+#include <data/case.h>
+#include <data/variable.h>
+
+static void
+destroy (struct statistic *s)
+{
+  struct order_stats *os = (struct order_stats *) s;
+  struct box_whisker *bw = (struct box_whisker *) s;
+  struct ll *ll;
+
+  for (ll = ll_head (&bw->outliers); ll != ll_null (&bw->outliers); )
+    {
+      struct outlier *e = ll_data (ll, struct outlier, ll);
+
+      ll = ll_next (ll);
+
+      ds_destroy (&e->label);
+      free (e);
+    }
+
+  free (os->k);
+  free (s);
+};
+
+
+static void
+acc (struct statistic *s, const struct ccase *cx,
+     double c UNUSED, double cc UNUSED, double y)
+{
+  struct box_whisker *bw = (struct box_whisker *) s;
+  bool extreme;
+  struct outlier *o;
+
+  if ( y < bw->hinges[2] + bw->step)
+      bw->whiskers[1] = y;
+
+  if (bw->whiskers[0] == SYSMIS ||  bw->hinges[0] - bw->step > y)
+      bw->whiskers[0] = y;
+
+  if ( y > bw->hinges[2] + bw->step)
+    extreme = (y > bw->hinges[2] + 2 * bw->step) ;
+
+  else if (y < bw->hinges[0] - bw->step)
+    extreme = (y < bw->hinges[0] - 2 * bw->step) ;
+
+  else
+    return;
+
+  o = xzalloc (sizeof *o) ;
+  o->value = y;
+  o->extreme = extreme;
+  ds_init_empty (&o->label);
+
+  if (bw->id_var)
+    var_append_value_name (bw->id_var,
+                          case_data (cx, bw->id_var),
+                          &o->label);
+  else
+    ds_put_format (&o->label,
+                  "%ld",
+                  (casenumber) case_data_idx (cx, bw->casenumber_idx)->f);
+
+  ll_push_head (&bw->outliers, &o->ll);
+}
+
+void
+box_whisker_whiskers (const struct box_whisker *bw, double whiskers[2])
+{
+  whiskers[0] = bw->whiskers[0];
+  whiskers[1] = bw->whiskers[1];
+}
+
+void
+box_whisker_hinges (const struct box_whisker *bw, double hinges[3])
+{
+  hinges[0] = bw->hinges[0];
+  hinges[1] = bw->hinges[1];
+  hinges[2] = bw->hinges[2];
+}
+
+const struct ll_list *
+box_whisker_outliers (const struct box_whisker *bw)
+{
+  return &bw->outliers;
+}
+
+struct statistic *
+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;
+
+  os->n_k = 0;
+
+  stat->destroy = destroy;
+  stat->accumulate = acc;
+
+  tukey_hinges_calculate (th, w->hinges);
+
+  w->casenumber_idx = casenumber_idx;
+  w->id_var = id_var;
+
+  w->step = (w->hinges[2] - w->hinges[0]) * 1.5;
+
+  w->whiskers[1] = w->hinges[2];
+  w->whiskers[0] = SYSMIS;
+
+  ll_init (&w->outliers);
+
+  return stat;
+}
diff --git a/src/math/box-whisker.h b/src/math/box-whisker.h
new file mode 100644 (file)
index 0000000..5202b64
--- /dev/null
@@ -0,0 +1,65 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 __MATH_BOX_WHISKER_H__
+#define __MATH_BOX_WHISKER_H__
+
+#include <stddef.h>
+#include <libpspp/ll.h>
+#include <libpspp/str.h>
+#include "order-stats.h"
+
+/* This module calculates the statistics typically displayed by box-plots.
+   However, there's no reason not to use it for other purposes too.
+ */
+struct tukey_hinges;
+
+
+struct outlier
+{
+  double value;
+  struct string label;
+  bool extreme;
+  struct ll ll;
+};
+
+
+struct box_whisker
+{
+  struct order_stats parent;
+
+  double hinges[3];
+  double whiskers[2];
+
+  struct ll_list outliers;
+
+  double step;
+
+  size_t casenumber_idx;
+  const struct variable *id_var;
+};
+
+struct statistic * box_whisker_create (const struct tukey_hinges *,
+                                        const struct variable *, size_t);
+
+void box_whisker_whiskers (const struct box_whisker *bw, double whiskers[2]);
+
+void box_whisker_hinges (const struct box_whisker *bw, double hinges[2]);
+
+const struct ll_list * box_whisker_outliers (const struct box_whisker *bw);
+
+
+#endif
index 5872b576fabfc82227aac28dae759ec43ded96ac..1f157433ee21a901eda70718313a2f5edc18c676 100644 (file)
@@ -160,13 +160,21 @@ pspp_coeff_var_to_coeff (const struct variable *v, struct pspp_coeff **coefs,
   size_t i = 0;
   size_t j = 0;
   size_t v_idx;
+
   struct pspp_coeff *result = NULL;
 
   if (v != NULL)
     {
       v_idx = var_get_dict_index (v);
-      while (i < n_coef && var_get_dict_index (coefs[i]->v_info->v) != v_idx)
+      while (i < n_coef)
        {
+         if (coefs[i]->v_info != NULL)
+           {
+             if (var_get_dict_index (coefs[i]->v_info->v) == v_idx)
+               {
+                 break;
+               }
+           }
          i++;
        }
       result = coefs[i];
@@ -178,8 +186,8 @@ pspp_coeff_var_to_coeff (const struct variable *v, struct pspp_coeff **coefs,
          if (val != NULL)
            {
              j = i;
-             while (j < n_coef && compare_values (pspp_coeff_get_value (coefs[j], v),
-                                                  val, var_get_width (v)) != 0)
+             while (j < n_coef && compare_values_short (pspp_coeff_get_value (coefs[j], v),
+                                                         val, v) != 0)
                {
                  j++;
                }
index 5414379119d1cecba94bf342c877209f58fc9b2f..95895eaeaca3c8337da4e55da49dc7f78f624419 100644 (file)
 */
 #include <assert.h>
 #include <config.h>
+#include <data/case.h>
+#include <data/category.h>
 #include <data/variable.h>
 #include <data/value.h>
-#include "covariance-matrix.h"
-#include "moments.h"
+#include <libpspp/hash.h>
+#include <libpspp/hash-functions.h>
+#include <math/covariance-matrix.h>
+#include <math/moments.h>
+#include <string.h>
+#include <xalloc.h>
 
+/*
+  Structure used to accumulate the covariance matrix in a single data
+  pass.  Before passing the data, we do not know how many categories
+  there are in each categorical variable. Therefore we do not know the
+  size of the covariance matrix. To get around this problem, we
+  accumulate the elements of the covariance matrix in pointers to
+  COVARIANC_ACCUMULATOR. These values are then used to populate
+  the covariance matrix.
+ */
+struct covariance_accumulator
+{
+  const struct variable *v1;
+  const struct variable *v2;
+  const union value *val1;
+  const union value *val2;
+  double dot_product;
+  double sum1;
+  double sum2;
+  double ssize;
+};
+
+
+
+struct covariance_matrix
+{
+  struct design_matrix *cov;
+  struct design_matrix *ssize;
+  struct design_matrix *sums;
+  struct hsh_table *ca;
+  struct moments1 **m1;
+  struct moments **m;
+  const struct variable **v_variables;
+  size_t n_variables;
+  int n_pass;
+  int missing_handling;
+  enum mv_class missing_value;
+  void (*accumulate) (struct covariance_matrix *, const struct ccase *,
+                     const struct interaction_variable **, size_t);
+  void (*update_moments) (struct covariance_matrix *, size_t, double);
+};
+
+
+
+static struct hsh_table *covariance_hsh_create (size_t *);
+static hsh_hash_func covariance_accumulator_hash;
+static unsigned int hash_numeric_alpha (const struct variable *,
+                                       const struct variable *,
+                                       const union value *, size_t);
+static hsh_compare_func covariance_accumulator_compare;
+static hsh_free_func covariance_accumulator_free;
+static void update_moments1 (struct covariance_matrix *, size_t, double);
+static void update_moments2 (struct covariance_matrix *, size_t, double);
+static struct covariance_accumulator *get_new_covariance_accumulator (const
+                                                                     struct
+                                                                     variable
+                                                                     *,
+                                                                     const
+                                                                     struct
+                                                                     variable
+                                                                     *,
+                                                                     const
+                                                                     union
+                                                                     value *,
+                                                                     const
+                                                                     union
+                                                                     value
+                                                                     *);
+static void covariance_accumulate_listwise (struct covariance_matrix *,
+                                           const struct ccase *,
+                                           const struct interaction_variable **,
+                                           size_t);
+static void covariance_accumulate_pairwise (struct covariance_matrix *,
+                                           const struct ccase *,
+                                           const struct interaction_variable **,
+                                           size_t);
+
+struct covariance_matrix *
+covariance_matrix_init (size_t n_variables,
+                       const struct variable *v_variables[], int n_pass,
+                       int missing_handling, enum mv_class missing_value)
+{
+  size_t i;
+  struct covariance_matrix *result = NULL;
+
+  result = xmalloc (sizeof (*result));
+  result->cov = NULL;
+  result->n_variables = n_variables;
+  result->ca = covariance_hsh_create (&result->n_variables);
+  result->m = NULL;
+  result->m1 = NULL;
+  result->missing_handling = missing_handling;
+  result->missing_value = missing_value;
+  result->accumulate = (result->missing_handling == LISTWISE) ?
+    covariance_accumulate_listwise : covariance_accumulate_pairwise;
+  if (n_pass == ONE_PASS)
+    {
+      result->update_moments = update_moments1;
+      result->m1 = xnmalloc (n_variables, sizeof (*result->m1));
+      for (i = 0; i < n_variables; i++)
+       {
+         result->m1[i] = moments1_create (MOMENT_MEAN);
+       }
+    }
+  else
+    {
+      result->update_moments = update_moments2;
+      result->m = xnmalloc (n_variables, sizeof (*result->m));
+      for (i = 0; i < n_variables; i++)
+       {
+         result->m[i] = moments_create (MOMENT_MEAN);
+       }
+    }
+  result->v_variables = v_variables;
+
+  result->n_pass = n_pass;
+
+  return result;
+}
+static size_t 
+get_n_rows (size_t n_variables, size_t *v_variables[])
+{
+  size_t i;
+  size_t result = 0;
+  for (i = 0; i < n_variables; i++)
+    {
+      if (var_is_numeric (v_variables[i]))
+       {
+         result++;
+       }
+      else if (var_is_alpha (v_variables[i]))
+       {
+         size_t n_categories = cat_get_n_categories (v_variables[i]);
+         result += n_categories - 1;
+       }
+    }
+  return result;
+}
 /*
   The covariances are stored in a DESIGN_MATRIX structure.
  */
 struct design_matrix *
-covariance_matrix_create (int n_variables, const struct variable *v_variables[])
+covariance_matrix_create (size_t n_variables,
+                         const struct variable *v_variables[])
 {
-  return design_matrix_create (n_variables, v_variables, (size_t) n_variables);
+  size_t n_rows = get_n_rows (n_variables, v_variables);
+  return design_matrix_create (n_variables, v_variables, n_rows);
 }
 
-void covariance_matrix_destroy (struct design_matrix *x)
+static void
+update_moments1 (struct covariance_matrix *cov, size_t i, double x)
+{
+  assert (cov->m1 != NULL);
+  moments1_add (cov->m1[i], x, 1.0);
+}
+
+static void
+update_moments2 (struct covariance_matrix *cov, size_t i, double x)
 {
-  design_matrix_destroy (x);
+  assert (cov->m != NULL);
+  moments_pass_one (cov->m[i], x, 1.0);
+}
+
+void
+covariance_matrix_destroy (struct covariance_matrix *cov)
+{
+  size_t i;
+
+  assert (cov != NULL);
+  design_matrix_destroy (cov->cov);
+  design_matrix_destroy (cov->ssize);
+  design_matrix_destroy (cov->sums);
+  hsh_destroy (cov->ca);
+  if (cov->n_pass == ONE_PASS)
+    {
+      for (i = 0; i < cov->n_variables; i++)
+       {
+         moments1_destroy (cov->m1[i]);
+       }
+      free (cov->m1);
+    }
+  else
+    {
+      for (i = 0; i < cov->n_variables; i++)
+       {
+         moments_destroy (cov->m[i]);
+       }
+      free (cov->m);
+    }
 }
 
 /*
@@ -44,19 +226,20 @@ void covariance_matrix_destroy (struct design_matrix *x)
  */
 static void
 covariance_update_categorical_numeric (struct design_matrix *cov, double mean,
-                         size_t row, 
-                         const struct variable *v2, double x, const union value *val2)
+                                      size_t row,
+                                      const struct variable *v2, double x,
+                                      const union value *val2)
 {
   size_t col;
   double tmp;
-  
+
   assert (var_is_numeric (v2));
 
   col = design_matrix_var_to_column (cov, v2);
   assert (val2 != NULL);
-  tmp = gsl_matrix_get (cov->m, row, col);
-  gsl_matrix_set (cov->m, row, col, (val2->f - mean) * x + tmp);
-  gsl_matrix_set (cov->m, col, row, (val2->f - mean) * x + tmp);
+  tmp = design_matrix_get_element (cov, row, col);
+  design_matrix_set_element (cov, row, col, (val2->f - mean) * x + tmp);
+  design_matrix_set_element (cov, col, row, (val2->f - mean) * x + tmp);
 }
 static void
 column_iterate (struct design_matrix *cov, const struct variable *v,
@@ -68,29 +251,32 @@ column_iterate (struct design_matrix *cov, const struct variable *v,
   double tmp;
   const union value *tmp_val;
 
-  col = design_matrix_var_to_column (cov, v);  
+  col = design_matrix_var_to_column (cov, v);
   for (i = 0; i < cat_get_n_categories (v) - 1; i++)
     {
       col += i;
       y = -1.0 * cat_get_category_count (i, v) / ssize;
       tmp_val = cat_subscript_to_value (i, v);
-      if (compare_values (tmp_val, val1, var_get_width (v)))
+      if (!compare_values_short (tmp_val, val1, v))
        {
          y += -1.0;
        }
-      tmp = gsl_matrix_get (cov->m, row, col);
-      gsl_matrix_set (cov->m, row, col, x * y + tmp);
-      gsl_matrix_set (cov->m, col, row, x * y + tmp);
+      tmp = design_matrix_get_element (cov, row, col);
+      design_matrix_set_element (cov, row, col, x * y + tmp);
+      design_matrix_set_element (cov, col, row, x * y + tmp);
     }
 }
+
 /*
   Call this function in the second data pass. The central moments are
   MEAN1 and MEAN2. Any categorical variables should already have their
   values summarized in in its OBS_VALS element.
  */
-void covariance_pass_two (struct design_matrix *cov, double mean1, double mean2,
-                         double ssize, const struct variable *v1, 
-                         const struct variable *v2, const union value *val1, const union value *val2)
+void
+covariance_pass_two (struct design_matrix *cov, double mean1, double mean2,
+                    double ssize, const struct variable *v1,
+                    const struct variable *v2, const union value *val1,
+                    const union value *val2)
 {
   size_t row;
   size_t col;
@@ -106,13 +292,13 @@ void covariance_pass_two (struct design_matrix *cov, double mean1, double mean2,
          row += i;
          x = -1.0 * cat_get_category_count (i, v1) / ssize;
          tmp_val = cat_subscript_to_value (i, v1);
-         if (compare_values (tmp_val, val1, var_get_width (v1)))
+         if (!compare_values_short (tmp_val, val1, v1))
            {
              x += 1.0;
            }
          if (var_is_numeric (v2))
            {
-             covariance_update_categorical_numeric (cov, mean2, row, 
+             covariance_update_categorical_numeric (cov, mean2, row,
                                                     v2, x, val2);
            }
          else
@@ -125,22 +311,640 @@ void covariance_pass_two (struct design_matrix *cov, double mean1, double mean2,
   else if (var_is_alpha (v2))
     {
       /*
-       Reverse the orders of V1, V2, etc. and put ourselves back
-       in the previous IF scope.
+         Reverse the orders of V1, V2, etc. and put ourselves back
+         in the previous IF scope.
        */
       covariance_pass_two (cov, mean2, mean1, ssize, v2, v1, val2, val1);
     }
   else
     {
       /*
-       Both variables are numeric.
-      */
-      row = design_matrix_var_to_column (cov, v1);  
+         Both variables are numeric.
+       */
+      row = design_matrix_var_to_column (cov, v1);
       col = design_matrix_var_to_column (cov, v2);
       x = (val1->f - mean1) * (val2->f - mean2);
-      x += gsl_matrix_get (cov->m, col, row);
-      gsl_matrix_set (cov->m, row, col, x);
-      gsl_matrix_set (cov->m, col, row, x);
+      x += design_matrix_get_element (cov, col, row);
+      design_matrix_set_element (cov, row, col, x);
+      design_matrix_set_element (cov, col, row, x);
+    }
+}
+
+static unsigned int
+covariance_accumulator_hash (const void *h, const void *aux)
+{
+  struct covariance_accumulator *ca = (struct covariance_accumulator *) h;
+  size_t *n_vars = (size_t *) aux;
+  size_t idx_max;
+  size_t idx_min;
+  const struct variable *v_min;
+  const struct variable *v_max;
+  const union value *val_min;
+  const union value *val_max;
+
+  /*
+     Order everything by the variables' indices. This ensures we get the
+     same key regardless of the order in which the variables are stored
+     and passed around.
+   */
+  v_min =
+    (var_get_dict_index (ca->v1) <
+     var_get_dict_index (ca->v2)) ? ca->v1 : ca->v2;
+  v_max = (ca->v1 == v_min) ? ca->v2 : ca->v1;
+
+  val_min = (v_min == ca->v1) ? ca->val1 : ca->val2;
+  val_max = (ca->val1 == val_min) ? ca->val2 : ca->val1;
+
+  idx_min = var_get_dict_index (v_min);
+  idx_max = var_get_dict_index (v_max);
+
+  if (var_is_numeric (v_max) && var_is_numeric (v_min))
+    {
+      return (*n_vars * idx_max + idx_min);
     }
+  if (var_is_numeric (v_max) && var_is_alpha (v_min))
+    {
+      return hash_numeric_alpha (v_max, v_min, val_min, *n_vars);
+    }
+  if (var_is_alpha (v_max) && var_is_numeric (v_min))
+    {
+      return (hash_numeric_alpha (v_min, v_max, val_max, *n_vars));
+    }
+  if (var_is_alpha (v_max) && var_is_alpha (v_min))
+    {
+      unsigned tmp = hsh_hash_bytes (val_max, var_get_width (v_max));
+      tmp ^= hsh_hash_bytes (val_min, var_get_width (v_min));
+      tmp += *n_vars * (*n_vars + 1 + idx_max) + idx_min;
+      return (size_t) tmp;
+    }
+  return -1u;
+}
+
+/*
+  Make a hash table consisting of struct covariance_accumulators.
+  This allows the accumulation of the elements of a covariance matrix
+  in a single data pass. Call covariance_accumulate () for each case 
+  in the data.
+ */
+static struct hsh_table *
+covariance_hsh_create (size_t *n_vars)
+{
+  return hsh_create (*n_vars * *n_vars, covariance_accumulator_compare,
+                    covariance_accumulator_hash, covariance_accumulator_free,
+                    n_vars);
+}
+
+static void
+covariance_accumulator_free (void *c_, const void *aux UNUSED)
+{
+  struct covariance_accumulator *c = c_;
+  assert (c != NULL);
+  free (c);
+}
+
+/*
+  Hash comparison. Returns 0 for a match, or a non-zero int
+  otherwise. The sign of a non-zero return value *should* indicate the
+  position of C relative to the covariance_accumulator described by
+  the other arguments. But for now, it just returns 1 for any
+  non-match.  This should be changed when someone figures out how to
+  compute a sensible sign for the return value.
+ */
+static int
+match_nodes (const struct covariance_accumulator *c,
+            const struct variable *v1, const struct variable *v2,
+            const union value *val1, const union value *val2)
+{
+  if (var_get_dict_index (v1) == var_get_dict_index (c->v1))
+    if (var_get_dict_index (v2) == var_get_dict_index (c->v2))
+      {
+       if (var_is_numeric (v1) && var_is_numeric (v2))
+         {
+           return 0;
+         }
+       if (var_is_numeric (v1) && var_is_alpha (v2))
+         {
+           if (!compare_values_short (val2, c->val2, v2))
+             {
+               return 0;
+             }
+         }
+       if (var_is_alpha (v1) && var_is_numeric (v2))
+         {
+           if (!compare_values_short (val1, c->val1, v1))
+             {
+               return 0;
+             }
+         }
+       if (var_is_alpha (v1) && var_is_alpha (v2))
+         {
+           if (!compare_values_short (val1, c->val1, v1))
+             {
+               if (!compare_values_short (val2, c->val2, v2))
+                 {
+                   return 0;
+                 }
+             }
+         }
+      }
+  return 1;
+}
+
+/*
+  This function is meant to be used as a comparison function for
+  a struct hsh_table in src/libpspp/hash.c.
+*/
+static int
+covariance_accumulator_compare (const void *a1_, const void *a2_,
+                               const void *aux UNUSED)
+{
+  const struct covariance_accumulator *a1 = a1_;
+  const struct covariance_accumulator *a2 = a2_;
+
+  if (a1 == NULL && a2 == NULL)
+    return 0;
+
+  if (a1 == NULL || a2 == NULL)
+    return 1;
+
+  return match_nodes (a1, a2->v1, a2->v2, a2->val1, a2->val2);
+}
+
+static unsigned int
+hash_numeric_alpha (const struct variable *v1, const struct variable *v2,
+                   const union value *val, size_t n_vars)
+{
+  unsigned int result = -1u;
+  if (var_is_numeric (v1) && var_is_alpha (v2))
+    {
+      result = n_vars * ((n_vars + 1) + var_get_dict_index (v1))
+       + var_get_dict_index (v2) + hsh_hash_string (val->s);
+    }
+  else if (var_is_alpha (v1) && var_is_numeric (v2))
+    {
+      result = hash_numeric_alpha (v2, v1, val, n_vars);
+    }
+  return result;
+}
+
+
+static double
+update_product (const struct variable *v1, const struct variable *v2,
+               const union value *val1, const union value *val2)
+{
+  assert (v1 != NULL);
+  assert (v2 != NULL);
+  assert (val1 != NULL);
+  assert (val2 != NULL);
+  if (var_is_alpha (v1) && var_is_alpha (v2))
+    {
+      return 1.0;
+    }
+  if (var_is_numeric (v1) && var_is_numeric (v2))
+    {
+      return (val1->f * val2->f);
+    }
+  if (var_is_numeric (v1) && var_is_alpha (v2))
+    {
+      return (val1->f);
+    }
+  if (var_is_numeric (v2) && var_is_alpha (v1))
+    {
+      update_product (v2, v1, val2, val1);
+    }
+  return 0.0;
+}
+static double
+update_sum (const struct variable *var, const union value *val, double weight)
+{
+  assert (var != NULL);
+  assert (val != NULL);
+  if (var_is_alpha (var))
+    {
+      return weight;
+    }
+  return val->f;
+}
+static struct covariance_accumulator *
+get_new_covariance_accumulator (const struct variable *v1,
+                               const struct variable *v2,
+                               const union value *val1,
+                               const union value *val2)
+{
+  if ((v1 != NULL) && (v2 != NULL) && (val1 != NULL) && (val2 != NULL))
+    {
+      struct covariance_accumulator *ca;
+      ca = xmalloc (sizeof (*ca));
+      ca->v1 = v1;
+      ca->v2 = v2;
+      ca->val1 = val1;
+      ca->val2 = val2;
+      return ca;
+    }
+  return NULL;
+}
+
+static const struct variable **
+get_covariance_variables (const struct covariance_matrix *cov)
+{
+  return cov->v_variables;
+}
+
+static void
+update_hash_entry (struct hsh_table *c,
+                  const struct variable *v1,
+                  const struct variable *v2,
+                  const union value *val1, const union value *val2, 
+                  const struct interaction_value *i_val1,
+                  const struct interaction_value *i_val2)
+{
+  struct covariance_accumulator *ca;
+  struct covariance_accumulator *new_entry;
+  double iv_f1;
+  double iv_f2;
+
+  iv_f1 = interaction_value_get_nonzero_entry (i_val1);
+  iv_f2 = interaction_value_get_nonzero_entry (i_val2);
+  ca = get_new_covariance_accumulator (v1, v2, val1, val2);
+  ca->dot_product = update_product (ca->v1, ca->v2, ca->val1, ca->val2);
+  ca->dot_product *= iv_f1 * iv_f2;
+  ca->sum1 = update_sum (ca->v1, ca->val1, iv_f1);
+  ca->sum2 = update_sum (ca->v2, ca->val2, iv_f2);
+  ca->ssize = 1.0;
+  new_entry = hsh_insert (c, ca);
+  
+  if (new_entry != NULL)
+    {
+      new_entry->dot_product += ca->dot_product;
+      new_entry->ssize += 1.0;
+      new_entry->sum1 += ca->sum1;
+      new_entry->sum2 += ca->sum2;
+      /*
+       If DOT_PRODUCT is null, CA was not already in the hash
+       hable, so we don't free it because it was just inserted.
+       If DOT_PRODUCT was not null, CA is already in the hash table.
+       Unnecessary now, it must be freed here.
+      */
+      free (ca);
+    }
+}
+
+/*
+  Compute the covariance matrix in a single data-pass. Cases with
+  missing values are dropped pairwise, in other words, only if one of
+  the two values necessary to accumulate the inner product is missing.
+
+  Do not call this function directly. Call it through the struct
+  covariance_matrix ACCUMULATE member function, for example,
+  cov->accumulate (cov, ccase).
+ */
+static void
+covariance_accumulate_pairwise (struct covariance_matrix *cov,
+                               const struct ccase *ccase, 
+                               const struct interaction_variable **i_var,
+                               size_t n_intr)
+{
+  size_t i;
+  size_t j;
+  const union value *val1;
+  const union value *val2;
+  const struct variable **v_variables;
+  struct interaction_value *i_val1 = NULL;
+  struct interaction_value *i_val2 = NULL;
+
+  assert (cov != NULL);
+  assert (ccase != NULL);
+
+  v_variables = get_covariance_variables (cov);
+  assert (v_variables != NULL);
+
+  for (i = 0; i < cov->n_variables; ++i)
+    {
+      if (is_interaction (v_variables[i], i_var, n_intr))
+       {
+         i_val1 = interaction_case_data (ccase, v_variables[i], i_var, n_intr);
+         val1 = interaction_value_get (i_val1);
+       }
+      else
+       {
+         val1 = case_data (ccase, v_variables[i]);
+       }
+      if (!var_is_value_missing (v_variables[i], val1, cov->missing_value))
+       {
+         cat_value_update (v_variables[i], val1);
+         if (var_is_numeric (v_variables[i]))
+           cov->update_moments (cov, i, val1->f);
+
+         for (j = i; j < cov->n_variables; j++)
+           {
+             if (is_interaction (v_variables[j], i_var, n_intr))
+               {
+                 i_val2 = interaction_case_data (ccase, v_variables[j], i_var, n_intr);
+                 val2 = interaction_value_get (i_val2);
+               }
+             else
+               {
+                 val2 = case_data (ccase, v_variables[j]);
+               }
+             if (!var_is_value_missing
+                 (v_variables[j], val2, cov->missing_value))
+               {
+                 update_hash_entry (cov->ca, v_variables[i], v_variables[j],
+                                    val1, val2, i_val1, i_val2);
+                 if (j != i)
+                   update_hash_entry (cov->ca, v_variables[j],
+                                      v_variables[i], val2, val1, i_val2, i_val1);
+               }
+           }
+       }
+    }
+}
+
+/*
+  Compute the covariance matrix in a single data-pass. Cases with
+  missing values are dropped listwise. In other words, if one of the
+  values for any variable in a case is missing, the entire case is
+  skipped. 
+
+  The caller must use a casefilter to remove the cases with missing
+  values before calling covariance_accumulate_listwise. This function
+  assumes that CCASE has already passed through this filter, and
+  contains no missing values.
+
+  Do not call this function directly. Call it through the struct
+  covariance_matrix ACCUMULATE member function, for example,
+  cov->accumulate (cov, ccase).
+ */
+static void
+covariance_accumulate_listwise (struct covariance_matrix *cov,
+                               const struct ccase *ccase,
+                               const struct interaction_variable **i_var,
+                               size_t n_intr)
+{
+  size_t i;
+  size_t j;
+  const union value *val1;
+  const union value *val2;
+  const struct variable **v_variables;
+  struct interaction_value *i_val1 = NULL;
+  struct interaction_value *i_val2 = NULL;
+
+  assert (cov != NULL);
+  assert (ccase != NULL);
+
+  v_variables = get_covariance_variables (cov);
+  assert (v_variables != NULL);
+
+  for (i = 0; i < cov->n_variables; ++i)
+    {
+      if (is_interaction (v_variables[i], i_var, n_intr))
+       {
+         i_val1 = interaction_case_data (ccase, v_variables[i], i_var, n_intr);
+         val1 = interaction_value_get (i_val1);
+       }
+      else
+       {
+         val1 = case_data (ccase, v_variables[i]);
+       }
+      cat_value_update (v_variables[i], val1);
+      if (var_is_numeric (v_variables[i]))
+       cov->update_moments (cov, i, val1->f);
+
+      for (j = i; j < cov->n_variables; j++)
+       {
+         if (is_interaction (v_variables[j], i_var, n_intr))
+           {
+             i_val2 = interaction_case_data (ccase, v_variables[j], i_var, n_intr);
+             val2 = interaction_value_get (i_val2);
+           }
+         else
+           {
+             val2 = case_data (ccase, v_variables[j]);
+           }
+         update_hash_entry (cov->ca, v_variables[i], v_variables[j],
+                            val1, val2, i_val1, i_val2);
+         if (j != i)
+           update_hash_entry (cov->ca, v_variables[j], v_variables[i],
+                              val2, val1, i_val2, i_val1);
+       }
+    }
+}
+
+/*
+  Call this function during the data pass. Each case will be added to
+  a hash containing all values of the covariance matrix. After the
+  data have been passed, call covariance_matrix_compute to put the
+  values in the struct covariance_matrix. 
+ */
+void
+covariance_matrix_accumulate (struct covariance_matrix *cov,
+                             const struct ccase *ccase, void **aux, size_t n_intr)
+{
+  cov->accumulate (cov, ccase, (const struct interaction_variable **) aux, n_intr);
+}
+/*
+  If VAR is categorical with d categories, its first category should
+  correspond to the origin in d-dimensional Euclidean space.
+ */
+static bool
+is_origin (const struct variable *var, const union value *val)
+{
+  if (cat_value_find (var, val) == 0)
+    {
+      return true;
+    }
+  return false;
+}
+
+/*
+  Return the subscript of the column of the design matrix
+  corresponding to VAL. If VAR is categorical with d categories, its
+  first category should correspond to the origin in d-dimensional
+  Euclidean space, so there is no subscript for this value.
+ */
+static size_t
+get_exact_subscript (const struct design_matrix *dm, const struct variable *var,
+                    const union value *val)
+{
+  size_t result;
+
+  if (is_origin (var, val))
+    {
+      return -1u;
+    }
+
+  result = design_matrix_var_to_column (dm, var);
+  if (var_is_alpha (var))
+    {
+      result += cat_value_find (var, val) - 1;
+    }
+  return result;
+}
+
+static void
+covariance_matrix_insert (struct design_matrix *cov,
+                         const struct variable *v1,
+                         const struct variable *v2, const union value *val1,
+                         const union value *val2, double product)
+{
+  size_t row;
+  size_t col;
+
+  assert (cov != NULL);
+
+  row = get_exact_subscript (cov, v1, val1);
+  col = get_exact_subscript (cov, v2, val2);
+  if (row != -1u && col != -1u)
+    {
+      design_matrix_set_element (cov, row, col, product);
+    }
+}
+
+
+static bool
+is_covariance_contributor (const struct covariance_accumulator *ca, const struct design_matrix *dm,
+                          size_t i, size_t j)
+{
+  size_t k;
+  const struct variable *v1;
+  const struct variable *v2;
+
+  assert (dm != NULL);
+  v1 = design_matrix_col_to_var (dm, i);
+  if (var_get_dict_index (v1) == var_get_dict_index(ca->v1))
+    {
+      v2 = design_matrix_col_to_var (dm, j);
+      if (var_get_dict_index (v2) == var_get_dict_index (ca->v2))
+       {
+         k = get_exact_subscript (dm, v1, ca->val1);
+         if (k == i)
+           {
+             k = get_exact_subscript (dm, v2, ca->val2);
+             if (k == j)
+               {
+                 return true;
+               }
+           }
+       }
+    }
+  return false;
+}
+static double
+get_sum (const struct covariance_matrix *cov, size_t i)
+{
+  size_t k;
+  const struct variable *var;
+  const union value *val = NULL;
+  struct covariance_accumulator ca;
+  struct covariance_accumulator *c;
+
+  assert ( cov != NULL);
+  var = design_matrix_col_to_var (cov->cov, i);
+  if (var != NULL)
+    {
+      if (var_is_alpha (var))
+       {
+         k = design_matrix_var_to_column (cov->cov, var);
+         i -= k;
+         val = cat_subscript_to_value (i, var);
+       }
+      ca.v1 = var;
+      ca.v2 = var;
+      ca.val1 = val;
+      ca.val2 = val;
+      c = (struct covariance_accumulator *) hsh_find (cov->ca, &ca);
+      if (c != NULL)
+       {
+         return c->sum1;
+       }
+    }
+  return 0.0;
+}
+static void
+update_ssize (struct design_matrix *dm, size_t i, size_t j, struct covariance_accumulator *ca)
+{
+  struct variable *var;
+  double tmp;
+  var = design_matrix_col_to_var (dm, i);
+  if (var_get_dict_index (ca->v1) == var_get_dict_index (var))
+    {
+      var = design_matrix_col_to_var (dm, j);
+      if (var_get_dict_index (ca->v2) == var_get_dict_index (var))
+       {
+         tmp = design_matrix_get_element (dm, i, j);
+         tmp += ca->ssize;
+         design_matrix_set_element (dm, i, j, tmp);
+       }
+    }
+}
+static void
+covariance_accumulator_to_matrix (struct covariance_matrix *cov)
+{
+  size_t i;
+  size_t j;
+  double sum_i = 0.0;
+  double sum_j = 0.0;
+  double tmp = 0.0;
+  struct covariance_accumulator *entry;
+  struct hsh_iterator iter;
+
+  cov->cov = covariance_matrix_create (cov->n_variables, cov->v_variables);
+  cov->ssize = covariance_matrix_create (cov->n_variables, cov->v_variables);
+  cov->sums = covariance_matrix_create (cov->n_variables, cov->v_variables);
+  for (i = 0; i < design_matrix_get_n_cols (cov->cov); i++)
+    {
+      sum_i = get_sum (cov, i);
+      for (j = i; j < design_matrix_get_n_cols (cov->cov); j++)
+       {
+         sum_j = get_sum (cov, j);
+         entry = hsh_first (cov->ca, &iter);
+         design_matrix_set_element (cov->sums, i, j, sum_i);     
+         while (entry != NULL)
+           {
+             update_ssize (cov->ssize, i, j, entry);
+             /*
+               We compute the centered, un-normalized covariance matrix.
+             */
+             if (is_covariance_contributor (entry, cov->cov, i, j))
+               {
+                 covariance_matrix_insert (cov->cov, entry->v1, entry->v2, entry->val1,
+                                           entry->val2, entry->dot_product);
+               }
+             entry = hsh_next (cov->ca, &iter);
+           }
+         tmp = design_matrix_get_element (cov->cov, i, j);
+         tmp -= sum_i * sum_j / design_matrix_get_element (cov->ssize, i, j);
+         design_matrix_set_element (cov->cov, i, j, tmp);
+       } 
+    }
+}
+
+
+/*
+  Call this function after passing the data.
+ */
+void
+covariance_matrix_compute (struct covariance_matrix *cov)
+{
+  if (cov->n_pass == ONE_PASS)
+    {
+      covariance_accumulator_to_matrix (cov);
+    }
+}
+
+struct design_matrix *
+covariance_to_design (const struct covariance_matrix *c)
+{
+  if (c != NULL)
+    {
+      return c->cov;
+    }
+  return NULL;
+}
+
+double 
+covariance_matrix_get_element (const struct covariance_matrix *c, size_t row, size_t col)
+{
+  return (design_matrix_get_element (c->cov, row, col));
 }
 
index 22f979c595c0baf76974fcba537cb7423bac9c52..24ce791cf5f74da3bb27ae48b11c4a9591a9f2b8 100644 (file)
 #ifndef COVARIANCE_MATRIX_H
 #define COVARIANCE_MATRIX_H
 
-#include "design-matrix.h"
-
-struct design_matrix *
-covariance_matrix_create (int, const struct variable *[]);
-
-void covariance_matrix_destroy (struct design_matrix *);
+#include <math/design-matrix.h>
+#include <math/interaction.h>
+
+struct moments1;
+struct ccase;
+struct hsh_table;
+struct covariance_matrix;
+enum
+{ ONE_PASS,
+  TWO_PASS
+};
 
+/*
+  How to deal with missing values.
+ */
+enum
+{ LISTWISE,
+  PAIRWISE
+};
+struct design_matrix *covariance_matrix_create (size_t,
+                                               const struct variable *[]);
+
+void covariance_matrix_destroy (struct covariance_matrix *cov);
 void covariance_pass_two (struct design_matrix *, double,
-                         double, double, const struct variable *, 
-                         const struct variable *, const union value *, const union value *);
+                         double, double, const struct variable *,
+                         const struct variable *, const union value *,
+                         const union value *);
+void covariance_matrix_compute (struct covariance_matrix *);
+struct covariance_matrix *covariance_matrix_init (size_t,
+                                                 const struct variable *[],
+                                                 int, int, enum mv_class);
+void covariance_matrix_free (struct covariance_matrix *);
+void covariance_matrix_accumulate (struct covariance_matrix *,
+                                  const struct ccase *, void **, size_t);
+struct design_matrix *covariance_to_design (const struct covariance_matrix *);
+double covariance_matrix_get_element (const struct covariance_matrix *, size_t, size_t);
 #endif
index 298d03357b7208e66392c0b15bec0eb9e0f0146b..b81859aa5a539099f6e7a437ab053f31ce5fa202 100644 (file)
@@ -54,10 +54,12 @@ design_matrix_create (int n_variables,
 
   dm = xmalloc (sizeof *dm);
   dm->vars = xnmalloc (n_variables, sizeof *dm->vars);
+  dm->n_cases = xnmalloc (n_variables, sizeof (*dm->n_cases));
   dm->n_vars = n_variables;
 
   for (i = 0; i < n_variables; i++)
     {
+      dm->n_cases[i] = 0;
       v = v_variables[i];
       assert ((dm->vars + i) != NULL);
       (dm->vars + i)->v = v;   /* Allows us to look up the variable from
@@ -79,6 +81,7 @@ design_matrix_create (int n_variables,
   dm->m = gsl_matrix_calloc (n_data, n_cols);
   col = 0;
 
+  
   return dm;
 }
 
@@ -87,6 +90,7 @@ design_matrix_destroy (struct design_matrix *dm)
 {
   free (dm->vars);
   gsl_matrix_free (dm->m);
+  free (dm->n_cases);
   free (dm);
 }
 
@@ -191,3 +195,89 @@ design_matrix_set_numeric (struct design_matrix *dm, size_t row,
   assert (col != DM_COLUMN_NOT_FOUND);
   gsl_matrix_set (dm->m, row, col, val->f);
 }
+
+struct design_matrix *
+design_matrix_clone (const struct design_matrix *dm)
+{
+  struct design_matrix *result;
+  size_t i;
+  
+  assert (dm != NULL);
+  result = xmalloc (sizeof *result);
+  result->vars = xnmalloc (dm->n_vars, sizeof *dm->vars);
+  result->n_vars = dm->n_vars;
+  result->m = gsl_matrix_alloc (dm->m->size1, dm->m->size2);
+  
+  gsl_matrix_memcpy (result->m, dm->m);
+  for (i = 0; i < result->n_vars; i++)
+    {
+      result->vars[i] = dm->vars[i];
+    }
+  return result;
+}
+
+/*
+  Increment the number of cases for V.
+ */
+void 
+design_matrix_increment_case_count (struct design_matrix *dm, const struct variable *v)
+{
+  size_t i;
+  assert (dm != NULL);
+  assert (dm->n_cases != NULL);
+  assert (v != NULL);
+  i = design_matrix_var_to_column (dm, v);
+  dm->n_cases[i]++;
+}
+
+/*
+  Set the number of cases for V.
+ */
+void 
+design_matrix_set_case_count (struct design_matrix *dm, const struct variable *v, size_t n)
+{
+  size_t i;
+  assert (dm != NULL);
+  assert (dm->n_cases != NULL);
+  assert (v != NULL);
+  i = design_matrix_var_to_column (dm, v);
+  dm->n_cases[i] = n;
+}
+
+/*
+  Get the number of cases for V.
+ */
+size_t 
+design_matrix_get_case_count (const struct design_matrix *dm, const struct variable *v)
+{
+  size_t i;
+  assert (dm != NULL);
+  assert (dm->n_cases != NULL);
+  assert (v != NULL);
+  i = design_matrix_var_to_column (dm, v);
+  return dm->n_cases[i];
+}
+
+size_t
+design_matrix_get_n_cols (const struct design_matrix *d)
+{
+  return d->m->size2;
+}
+
+size_t
+design_matrix_get_n_rows (const struct design_matrix *d)
+{
+  return d->m->size1;
+}
+
+double
+design_matrix_get_element (const struct design_matrix *d, size_t row, size_t col)
+{
+  return (gsl_matrix_get (d->m, row, col));
+}
+
+void
+design_matrix_set_element (const struct design_matrix *d, size_t row, size_t col, double x)
+{
+  gsl_matrix_set (d->m, row, col, x);
+}
index ad2b82585229c3d9c52d119865f7dfe76e2d9bd3..458145801fa0d4686776c923becc270b2a328056 100644 (file)
@@ -58,6 +58,9 @@ struct design_matrix
                                           design_matrix_var
                                           structure.
                                         */
+  size_t *n_cases; /* Element i is the number of valid cases for this
+                     variable.
+                   */
   size_t n_vars;
 };
 
@@ -75,10 +78,20 @@ void design_matrix_set_numeric (struct design_matrix *, size_t,
                                    const struct variable *,
                                    const union value *);
 
+struct design_matrix *design_matrix_clone (const struct design_matrix *);
+
 size_t design_matrix_var_to_column (const struct design_matrix *,
                                    const struct variable *);
 
 const struct variable *design_matrix_col_to_var (const struct design_matrix *,
                                           size_t);
+void design_matrix_increment_case_count (struct design_matrix *, const struct variable *);
+
+void design_matrix_set_case_count (struct design_matrix *, const struct variable *, size_t);
 
+size_t design_matrix_get_case_count (const struct design_matrix *, const struct variable *);
+size_t design_matrix_get_n_cols (const struct design_matrix *);
+size_t design_matrix_get_n_rows (const struct design_matrix *);
+double design_matrix_get_element (const struct design_matrix *, size_t, size_t);
+void design_matrix_set_element (const struct design_matrix *, size_t, size_t, double);
 #endif
diff --git a/src/math/extrema.c b/src/math/extrema.c
new file mode 100644 (file)
index 0000000..617c7ac
--- /dev/null
@@ -0,0 +1,144 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 "extrema.h"
+#include <xalloc.h>
+#include <data/case.h>
+#include <data/val-type.h>
+#include <libpspp/compiler.h>
+#include <libpspp/ll.h>
+#include <stdlib.h>
+
+struct extrema
+{
+  size_t capacity;
+  size_t n;
+  struct ll_list list;
+
+  ll_compare_func *cmp_func;
+};
+
+
+static int
+cmp_descending (const struct ll *a_, const struct ll *b_, void *aux UNUSED)
+{
+  const struct extremum *a = ll_data (a_, struct extremum, ll);
+  const struct extremum *b = ll_data (b_, struct extremum, ll);
+
+  if ( a->value > b->value) return -1;
+
+  return (a->value < b->value);
+}
+
+static int
+cmp_ascending (const struct ll *a_, const struct ll *b_, void *aux UNUSED)
+{
+  const struct extremum *a = ll_data (a_, struct extremum, ll);
+  const struct extremum *b = ll_data (b_, struct extremum, ll);
+
+  if ( a->value < b->value) return -1;
+
+  return (a->value > b->value);
+}
+
+
+struct extrema *
+extrema_create (size_t n, enum extreme_end end)
+{
+  struct extrema *extrema = xzalloc (sizeof *extrema);
+  extrema->capacity = n;
+
+  if ( end == EXTREME_MAXIMA )
+    extrema->cmp_func = cmp_descending;
+  else
+    extrema->cmp_func = cmp_ascending;
+
+  ll_init (&extrema->list);
+
+  return extrema;
+}
+
+void
+extrema_destroy (struct extrema *extrema)
+{
+  struct ll *ll = ll_head (&extrema->list);
+
+  while (ll != ll_null (&extrema->list))
+    {
+      struct extremum *e = ll_data (ll, struct extremum, ll);
+
+      ll = ll_next (ll);
+      free (e);
+    }
+
+  free (extrema);
+}
+
+
+void
+extrema_add (struct extrema *extrema, double val,
+            double weight,
+            casenumber location)
+{
+  struct extremum *e = xzalloc (sizeof *e) ;
+  e->value = val;
+  e->location = location;
+  e->weight = weight;
+
+  if ( val == SYSMIS)
+    {
+      free (e);
+      return;
+    }
+
+  ll_insert_ordered (ll_head (&extrema->list), ll_null (&extrema->list),
+                      &e->ll,  extrema->cmp_func, NULL);
+
+  if ( extrema->n++ > extrema->capacity)
+    {
+      struct ll *tail = ll_tail (&extrema->list);
+      struct extremum *et = ll_data (tail, struct extremum, ll);
+
+      ll_remove (tail);
+
+      free (et);
+    }
+}
+
+
+const struct ll_list *
+extrema_list (const struct extrema *ex)
+{
+  return &ex->list;
+}
+
+
+bool
+extrema_top (const struct extrema *ex, double *v)
+{
+  const struct extremum  *top;
+
+  if ( ll_is_empty (&ex->list))
+    return false;
+
+  top = (const struct extremum *)
+    ll_data (ll_head(&ex->list), struct extremum, ll);
+
+  *v = top->value;
+
+  return true;
+}
diff --git a/src/math/extrema.h b/src/math/extrema.h
new file mode 100644 (file)
index 0000000..d891c53
--- /dev/null
@@ -0,0 +1,58 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 __EXTREMA_H__
+#define __EXTREMA_H__ 1
+
+#include <stddef.h>
+#include <data/case.h>
+#include <libpspp/ll.h>
+
+struct extremum
+{
+  double value;
+  casenumber location;
+  double weight;
+
+  /* Internal use only */
+  struct ll ll;
+};
+
+
+enum extreme_end
+  {
+    EXTREME_MAXIMA,
+    EXTREME_MINIMA
+  };
+
+struct extrema;
+
+struct extrema *extrema_create (size_t n, enum extreme_end);
+
+void extrema_destroy (struct extrema *extrema);
+
+void extrema_add (struct extrema *extrema, double val,
+                 double weight,
+                 casenumber location);
+
+void extrema_show (const struct extrema *extrema);
+
+const struct ll_list * extrema_list (const struct extrema *);
+
+bool extrema_top (const struct extrema *, double *);
+
+
+#endif
diff --git a/src/math/factor-stats.c b/src/math/factor-stats.c
deleted file mode 100644 (file)
index a97d7f0..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-#include "factor-stats.h"
-#include <data/val-type.h>
-#include <data/value.h>
-#include <libpspp/hash.h>
-#include <libpspp/array.h>
-#include "moments.h"
-#include "percentiles.h"
-
-#include <stdlib.h>
-#include <math.h>
-#include <float.h>
-#include <assert.h>
-#include "histogram.h"
-
-#include "xalloc.h"
-
-void
-metrics_precalc(struct metrics *m)
-{
-  assert (m) ;
-
-  m->n_missing = 0;
-
-  m->min = DBL_MAX;
-  m->max = -DBL_MAX;
-
-  m->histogram = 0;
-
-  m->moments = moments1_create(MOMENT_KURTOSIS);
-
-  m->ordered_data = hsh_create(20,
-                               (hsh_compare_func *) compare_values,
-                               (hsh_hash_func *) hash_value,
-                               (hsh_free_func *) weighted_value_free,
-                               (void *) 0);
-}
-
-
-/* Include val in the calculation for the metrics.
-   If val is null, then treat it as MISSING
-*/
-void
-metrics_calc (struct metrics *fs, const union value *val,
-             double weight, int case_no)
-{
-  struct weighted_value **wv;
-  double x;
-
-  if ( ! val )
-    {
-      fs->n_missing += weight;
-      return ;
-    }
-
-  x = val->f;
-
-  moments1_add(fs->moments, x, weight);
-
-  if ( x < fs->min) fs->min = x;
-  if ( x > fs->max) fs->max = x;
-
-  wv = (struct weighted_value **) hsh_probe (fs->ordered_data,(void *) val );
-
-  if ( *wv  )
-    {
-      /* If this value has already been seen, then simply
-        increase its weight  and push a new case number */
-
-      struct case_node *cn;
-
-      assert( (*wv)->v.f == val->f );
-      (*wv)->w += weight;
-
-      cn = xmalloc ( sizeof *cn);
-      cn->next = (*wv)->case_nos ;
-      cn->num = case_no;
-
-      (*wv)->case_nos = cn;
-    }
-  else
-    {
-      struct case_node *cn;
-
-      *wv = weighted_value_create();
-      (*wv)->v = *val;
-      (*wv)->w = weight;
-
-      cn = xmalloc (sizeof *cn);
-      cn->next=0;
-      cn->num = case_no;
-      (*wv)->case_nos  = cn;
-
-    }
-
-}
-
-void
-metrics_postcalc(struct metrics *m)
-{
-  double cc = 0.0;
-  double tc ;
-  int k1, k2 ;
-  int i;
-  int j = 1;
-
-  moments1_calculate (m->moments, &m->n, &m->mean, &m->var,
-                     &m->skewness, &m->kurtosis);
-
-  moments1_destroy (m->moments);
-
-
-  m->stddev = sqrt(m->var);
-
-  /* FIXME: Check this is correct ???
-     Shouldn't we use the sample variance ??? */
-  m->se_mean = sqrt (m->var / m->n) ;
-
-
-
-  m->wvp = (struct weighted_value **) hsh_sort(m->ordered_data);
-  m->n_data = hsh_count(m->ordered_data);
-
-  /* Trimmed mean calculation */
-  if ( m->n_data <= 1 )
-    {
-      m->trimmed_mean = m->mean;
-      return;
-    }
-
-  m->histogram = histogram_create(10, m->min, m->max);
-
-  for ( i = 0 ; i < m->n_data ; ++i )
-    {
-      struct weighted_value **wv = (m->wvp) ;
-      gsl_histogram_accumulate(m->histogram, wv[i]->v.f, wv[i]->w);
-    }
-
-  tc = m->n * 0.05 ;
-  k1 = -1;
-  k2 = -1;
-
-  for ( i = 0 ; i < m->n_data ; ++i )
-    {
-      cc += m->wvp[i]->w;
-      m->wvp[i]->cc = cc;
-
-      m->wvp[i]->rank = j + (m->wvp[i]->w - 1) / 2.0 ;
-
-      j += m->wvp[i]->w;
-
-      if ( cc < tc )
-       k1 = i;
-    }
-
-
-
-  k2 = m->n_data;
-  for ( i = m->n_data -1  ; i >= 0; --i )
-    {
-      if ( tc > m->n - m->wvp[i]->cc)
-       k2 = i;
-    }
-
-
-  /* Calculate the percentiles */
-  ptiles (m->ptile_hash, (const struct weighted_value **) m->wvp,
-          m->n_data, m->n, m->ptile_alg);
-
-  tukey_hinges ((const struct weighted_value **) m->wvp,
-                m->n_data, m->n, m->hinge);
-
-  /* Special case here */
-  if ( k1 + 1 == k2 )
-    {
-      m->trimmed_mean = m->wvp[k2]->v.f;
-      return;
-    }
-
-  m->trimmed_mean = 0;
-  for ( i = k1 + 2 ; i <= k2 - 1 ; ++i )
-    {
-      m->trimmed_mean += m->wvp[i]->v.f * m->wvp[i]->w;
-    }
-
-
-  m->trimmed_mean += (m->n - m->wvp[k2 - 1]->cc - tc) * m->wvp[k2]->v.f ;
-  m->trimmed_mean += (m->wvp[k1 + 1]->cc - tc) * m->wvp[k1 + 1]->v.f ;
-  m->trimmed_mean /= 0.9 * m->n ;
-
-
-}
-
-
-struct weighted_value *
-weighted_value_create(void)
-{
-  struct weighted_value *wv;
-  wv = xmalloc (sizeof *wv);
-
-  wv->cc = 0;
-  wv->case_nos = 0;
-
-  return wv;
-}
-
-void
-weighted_value_free(struct weighted_value *wv)
-{
-  struct case_node *cn ;
-
-  if ( !wv )
-    return ;
-
-  cn = wv->case_nos;
-
-  while(cn)
-    {
-      struct case_node *next = cn->next;
-
-      free(cn);
-      cn = next;
-    }
-
-  free(wv);
-
-}
-
-
-
-
-
-/* Create a factor statistics object with for N dependent vars
-   and ID0 and ID1 as the values of the independent variable */
-struct factor_statistics *
-create_factor_statistics (int n,
-                         union value *id0,
-                         union value *id1)
-{
-  struct factor_statistics *f;
-
-  f = xmalloc (sizeof *f);
-
-  f->id[0] = id0;
-  f->id[1] = id1;
-  f->m = xnmalloc (n, sizeof *f->m);
-  memset (f->m, 0, sizeof(struct metrics) * n);
-  f->n_var = n;
-
-  return f;
-}
-
-void
-metrics_destroy(struct metrics *m)
-{
-  hsh_destroy(m->ordered_data);
-  hsh_destroy(m->ptile_hash);
-  if ( m-> histogram )
-    gsl_histogram_free(m->histogram);
-}
-
-void
-factor_statistics_free(struct factor_statistics *f)
-{
-
-  int i;
-  free (f->id[0]);
-  free (f->id[1]);
-  for ( i = 0 ; i < f->n_var; ++i )
-       metrics_destroy(&f->m[i]);
-  free(f->m) ;
-  free(f);
-}
-
-
-int
-factor_statistics_compare(const struct factor_statistics *f0,
-                         const struct factor_statistics *f1, int width)
-{
-
-  int cmp0;
-
-  assert(f0);
-  assert(f1);
-
-  cmp0 = compare_values(f0->id[0], f1->id[0], width);
-
-  if ( cmp0 != 0 )
-    return cmp0;
-
-
-  if ( ( f0->id[1]->f == SYSMIS ) && (f1->id[1]->f != SYSMIS) )
-    return 1;
-
-  if ( ( f0->id[1]->f != SYSMIS )  && (f1->id[1]->f == SYSMIS) )
-    return -1;
-
-  return compare_values (f0->id[1], f1->id[1], width);
-}
-
-unsigned int
-factor_statistics_hash (const struct factor_statistics *f, int width)
-{
-  unsigned int h;
-
-  h = hash_value (f->id[0], width);
-
-  if ( f->id[1]->f != SYSMIS )
-    h += hash_value(f->id[1], width);
-
-  return h;
-}
diff --git a/src/math/factor-stats.h b/src/math/factor-stats.h
deleted file mode 100644 (file)
index 3c1c7f9..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef FACTOR_STATS
-#define FACTOR_STATS
-
-
-/* FIXME: These things should probably be amalgamated with the
-   group_statistics struct */
-
-#include <libpspp/hash.h>
-#include <data/value.h>
-#include <string.h>
-#include <gsl/gsl_histogram.h>
-#include "percentiles.h"
-
-
-struct moments1;
-
-struct metrics
-{
-  double n;
-
-  double n_missing;
-
-  double min;
-
-  double max;
-
-  double mean;
-
-  double se_mean;
-
-  double var;
-
-  double stddev;
-
-  struct moments1 *moments;
-
-  gsl_histogram *histogram;
-
-  double skewness;
-  double kurtosis;
-
-  double trimmed_mean;
-
-  /* A hash of data for this factor. */
-  struct hsh_table *ordered_data;
-
-  /* A Pointer to this hash table AFTER it has been SORTED and crunched */
-  struct weighted_value **wvp;
-
-  /* The number of values in the above array
-     (if all the weights are 1, then this will
-     be the same as n) */
-  int n_data;
-
-  /* Percentile stuff */
-
-  /* A hash of struct percentiles */
-  struct hsh_table *ptile_hash;
-
-  /* Algorithm to be used for calculating percentiles */
-  enum pc_alg ptile_alg;
-
-  /* Tukey's Hinges */
-  double hinge[3];
-
-};
-
-
-struct metrics * metrics_create(void);
-
-void metrics_precalc(struct metrics *m);
-
-void metrics_calc(struct metrics *m, const union value *f, double weight,
-                 int case_no);
-
-void metrics_postcalc(struct metrics *m);
-
-void  metrics_destroy(struct metrics *m);
-
-
-
-/* Linked list of case nos */
-struct case_node
-{
-  int num;
-  struct case_node *next;
-};
-
-struct weighted_value
-{
-  union value v;
-
-  /* The weight */
-  double w;
-
-  /* The cumulative weight */
-  double cc;
-
-  /* The rank */
-  double rank;
-
-  /* Linked list of cases nos which have this value */
-  struct case_node *case_nos;
-
-};
-
-
-struct weighted_value *weighted_value_create(void);
-
-void weighted_value_free(struct weighted_value *wv);
-
-
-
-struct factor_statistics {
-
-  /* The values of the independent variables */
-  union value *id[2];
-
-  /* The an array stats for this factor, one for each dependent var */
-  struct metrics *m;
-
-  /* The number of dependent variables */
-  int n_var;
-};
-
-
-/* Create a factor statistics object with for N dependent vars
-   and ID as the value of the independent variable */
-struct factor_statistics * create_factor_statistics (int n,
-                         union value *id0,
-                         union value *id1);
-
-
-void factor_statistics_free(struct factor_statistics *f);
-
-
-/* Compare f0 and f1.
-   width is the width of the independent variable */
-int
-factor_statistics_compare(const struct factor_statistics *f0,
-                         const struct factor_statistics *f1, int width);
-
-unsigned int
-factor_statistics_hash(const struct factor_statistics *f, int width);
-
-#endif
index 29c5ab23db4b7bde28a7c7f2657dba7ede4e8533..b5c9e56cdccc7435b31659f33e537c53e4b6268d 100644 (file)
 /* Return -1 if the id of a is less than b; +1 if greater than and
    0 if equal */
 int
-compare_group (const struct group_statistics *a,
-                const struct group_statistics *b,
-                int width)
+compare_group (const void *a_,
+                const void *b_,
+                const void *var)
 {
-  return compare_values(&a->id, &b->id, width);
+  const struct group_statistics *a = a_;
+  const struct group_statistics *b = b_;
+  return compare_values_short (&a->id, &b->id, var);
 }
 
 
 
-unsigned
-hash_group (const struct group_statistics *g, int width)
+unsigned int
+hash_group (const void *g_, const void *var)
 {
   unsigned id_hash;
+  const struct group_statistics *g = g_;;
 
-  id_hash = hash_value(&g->id, width);
+  id_hash = hash_value_short (&g->id, var);
 
   return id_hash;
 }
index bc82c8ab8234424853cf2672764277145a22d90b..c5470b2578f2a1948e66a1ca6e6ae220d1616ecb 100644 (file)
 #ifndef GROUP_H
 #define GROUP_H
 
-
 #include <data/value.h>
 
-
 /* Statistics for grouped data */
 struct group_statistics
   {
@@ -67,17 +65,17 @@ struct group_statistics
   };
 
 
-
+struct variable ;
 
 /* These funcs are useful for hash tables */
 
 /* Return -1 if the id of a is less than b; +1 if greater than and
    0 if equal */
-int  compare_group (const struct group_statistics *a,
-                   const struct group_statistics *b,
-                   int width);
+int  compare_group (const void *a,
+                   const void *b,
+                   const void *var);
 
-unsigned hash_group (const struct group_statistics *g, int width);
+unsigned int hash_group (const void *g, const void *var);
 
 void  free_group (struct group_statistics *v, void *aux);
 
index 7b875d4089ad211d39a636f34fa8e8108a2098de..67079398d169ec58737c6f3b94a7d243b9fc8516 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008 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 <math.h>
-#include <gsl/gsl_histogram.h>
-#include <assert.h>
 #include "histogram.h"
+
+#include <gl/xalloc.h>
+#include <libpspp/assertion.h>
+
+#include <gsl/gsl_histogram.h>
 #include "chart-geometry.h"
+#include <math.h>
+
+
+void
+histogram_add (struct histogram *h, double y, double c)
+{
+  ((struct statistic *)h)->accumulate ((struct statistic *) h, NULL, c, 0, y);
+}
+
 
 
-gsl_histogram *
-histogram_create(double bins, double x_min, double x_max)
+static void
+acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc UNUSED, double y)
 {
-  int n;
-  double bin_width ;
-  double bin_width_2 ;
+  struct histogram *hist = (struct histogram *) s;
+
+  gsl_histogram_accumulate (hist->gsl_hist, y, c);
+}
+
+
+static void
+destroy (struct statistic *s)
+{
+  struct histogram *h = (struct histogram *) s;
+  gsl_histogram_free (h->gsl_hist);
+  free (s);
+}
+
+
+struct statistic *
+histogram_create (int bins, double min, double max)
+{
+  struct histogram *h = xmalloc (sizeof *h);
+  struct statistic *stat = (struct statistic *) h;
   double upper_limit, lower_limit;
 
-  gsl_histogram *hist = gsl_histogram_alloc(bins);
+  double bin_width = chart_rounded_tick ((max - min) / (double) bins);
+  double bin_width_2 = bin_width / 2.0;
 
-  bin_width = chart_rounded_tick((x_max - x_min)/ bins);
-  bin_width_2 = bin_width / 2.0;
+  int n =  ceil (max / (bin_width_2) ) ;
+
+  assert (max > min);
 
-  n =  ceil( x_max / (bin_width_2) ) ;
   if ( ! (n % 2 ) ) n++;
   upper_limit = n * bin_width_2;
 
-  n =  floor( x_min / (bin_width_2) ) ;
+  n =  floor (min / (bin_width_2) ) ;
   if ( ! (n % 2 ) ) n--;
   lower_limit = n * bin_width_2;
 
-  gsl_histogram_set_ranges_uniform(hist, lower_limit, upper_limit);
+  h->gsl_hist = gsl_histogram_alloc (bins);
+  gsl_histogram_set_ranges_uniform (h->gsl_hist, lower_limit, upper_limit);
+
+  stat->accumulate = acc;
+  stat->destroy = destroy;
 
-  return hist;
+  return stat;
 }
 
index e4c7819f331756d97bec69d6e56e580a71ee3f0b..b2b204ee808098c2bf378ef99e8419c6d4223988 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2008 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 HISTOGRAM_H
-#define HISTOGRAM_H
+#ifndef __NEW_HISTOGRAM_H__
+#define __NEW_HISTOGRAM_H__
+
+#include <stddef.h>
+
+#include "statistic.h"
 
 #include <gsl/gsl_histogram.h>
 
-gsl_histogram * histogram_create(double bins, double x_min, double x_max);
+
+struct histogram
+{
+  struct statistic parent;
+  gsl_histogram *gsl_hist;
+};
+
+struct statistic * histogram_create (int bins, double max, double min);
+
+void histogram_add (struct histogram *h, double y, double c);
+
 
 #endif
index c8ead42912f17787dc4c01e8e46ced248e255a59..33da8423bd7c7545a69e4c51a1d0a9a5abd4318d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 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
   OBS_VALS member. If there are K categorical variables, each with
   N_1, N_2, ..., N_K categories, then the interaction will have
   N_1 * N_2 * N_3 *...* N_K - 1 entries.
+
+  When using these functions, make sure the orders of variables and
+  values match when appropriate.
  */
 
 #include <config.h>
 #include <assert.h>
 #include <gsl/gsl_math.h>
 #include <gsl/gsl_vector.h>
-#include <data/category.h>
+#include <data/value.h>
 #include <data/variable.h>
-#include "interaction.h"
+#include <math/interaction.h>
+#include <string.h>
+#include <xalloc.h>
 
-#include "xalloc.h"
+struct interaction_variable
+{
+  int n_vars;
+  const struct variable **members;
+  struct variable *intr;
+  size_t n_alpha;
+};
+
+struct interaction_value
+{
+  const struct interaction_variable *intr;
+  union value *val; /* Concatenation of the string values in this
+                      interaction's value, or the product of a bunch
+                      of numeric values for a purely numeric
+                      interaction.
+                   */
+  double f; /* Product of the numerical values in this interaction's value. */
+};
 
 /*
-  Convert a list of values to a binary vector. The order of VALS must
-  correspond to the order of V.
+  An interaction_variable has type alpha if any of members have type
+  alpha. Otherwise, its type is numeric.
  */
-gsl_vector *
-get_interaction (union value **vals, const struct variable **v, size_t n_vars)
+struct interaction_variable *
+interaction_variable_create (const struct variable **vars, int n_vars)
 {
-  gsl_vector *result = NULL;
-  size_t *subs = NULL;
-  size_t length = 1;
+  struct interaction_variable *result = NULL;
   size_t i;
-  size_t j;
-  double tmp = 1.0;
 
-  assert (n_vars > 0);
-  for (i = 0; i < n_vars; i++)
+  if (n_vars > 0)
     {
-      if (var_is_alpha (v[i]))
+      result = xmalloc (sizeof (*result));
+      result->n_alpha = 0;
+      result->members = xnmalloc (n_vars, sizeof (*result->members));
+      result->intr = var_create_internal (0);
+      result->n_vars = n_vars;
+      for (i = 0; i < n_vars; i++)
        {
-         length *= cat_get_n_categories (v[i]);
+         result->members[i] = vars[i];
+         if (var_is_alpha (vars[i]))
+           {
+             result->n_alpha++;
+           }
        }
-      else
+    }
+  /*
+    VAR_SET_WIDTH sets the type of the variable.
+   */
+  var_set_width (result->intr, MAX_SHORT_STRING * result->n_alpha + 1);
+
+  return result;
+}
+void interaction_variable_destroy (struct interaction_variable *iv)
+{
+  var_destroy (iv->intr);
+  free (iv->members);
+  free (iv);
+}
+
+/*
+  Get one of the member variables.
+ */
+const struct variable *
+interaction_variable_get_member (const struct interaction_variable *iv, size_t i)
+{
+  return iv->members[i];
+}
+
+size_t
+interaction_get_n_vars (const struct interaction_variable *iv)
+{
+  return (iv == NULL) ? 0 : iv->n_vars;
+}
+
+size_t
+interaction_get_n_alpha (const struct interaction_variable *iv)
+{
+  return iv->n_alpha;
+}
+
+size_t
+interaction_get_n_numeric (const struct interaction_variable *iv)
+{
+  return (interaction_get_n_vars (iv) - interaction_get_n_alpha (iv));
+}
+
+/*
+  Get the interaction varibale itself.
+ */
+const struct variable *
+interaction_variable_get_var (const struct interaction_variable *iv)
+{
+  return iv->intr;
+}
+/*
+  Given list of values, compute the value of the corresponding
+  interaction.  This "value" is not stored as the typical vector of
+  0's and one double, but rather the string values are concatenated to
+  make one big string value, and the numerical values are multiplied
+  together to give the non-zero entry of the corresponding vector.
+ */
+struct interaction_value *
+interaction_value_create (const struct interaction_variable *var, const union value **vals)
+{
+  struct interaction_value *result = NULL;
+  const struct variable *member;
+  size_t i;
+  size_t n_vars;
+  
+  if (var != NULL)
+    {
+      result = xmalloc (sizeof (*result));
+      result->intr = var;
+      n_vars = interaction_get_n_vars (var);
+      result->val = value_create (n_vars * MAX_SHORT_STRING + 1);
+      result->f = 1.0;
+      for (i = 0; i < n_vars; i++)
+       {
+         member = interaction_variable_get_member (var, i);
+
+         if (var_is_value_missing (member, vals[i], MV_ANY))
+           {
+             value_set_missing (result->val, MAX_SHORT_STRING);
+             result->f = SYSMIS;
+             break;
+           }
+         else
+           {
+             if (var_is_alpha (var->members[i]))
+               {
+                 strncat (result->val->s, vals[i]->s, MAX_SHORT_STRING);
+               }
+             else if (var_is_numeric (var->members[i]))
+               {
+                 result->f *= vals[i]->f;
+               }
+           }
+       }
+      if (interaction_get_n_alpha (var) == 0)
        {
-         length = (length > 0) ? length : 1;
+         /*
+           If there are no categorical variables, then the
+           interaction consists of only numeric data. In this case,
+           code that uses this interaction_value will see the union
+           member as the numeric value. If we were to store that
+           numeric value in result->f as well, the calling code may
+           inadvertently square this value by multiplying by
+           result->val->f. Such multiplication would be correct for an
+           interaction consisting of both categorical and numeric
+           data, but a mistake for purely numerical interactions. To
+           avoid the error, we set result->f to 1.0 for numeric
+           interactions.
+          */
+         result->val->f = result->f;
+         result->f = 1.0;
        }
     }
-  if (length > 0)
+  return result;
+}
+
+union value *
+interaction_value_get (const struct interaction_value *val)
+{
+  return val->val;
+}
+
+/*
+  Returns the numeric value of the non-zero entry for the vector
+  corresponding to this interaction.  Do not use this function to get
+  the numeric value of a purley numeric interaction. Instead, use the
+  union value * returned by interaction_value_get.
+ */
+double 
+interaction_value_get_nonzero_entry (const struct interaction_value *val)
+{
+  if (val != NULL)
+    return val->f;
+  return 1.0;
+}
+
+void 
+interaction_value_destroy (struct interaction_value *val)
+{
+  if (val != NULL)
     {
-      length--;
+      free (val->val);
+      free (val);
     }
+}
 
-  result = gsl_vector_calloc (length);
-  subs = xnmalloc (n_vars, sizeof (*subs));
-  for (j = 0; j < n_vars; j++)
+/*
+  Return a value from a variable that is an interaction. 
+ */
+struct interaction_value *
+interaction_case_data (const struct ccase *ccase, const struct variable *var, 
+                      const struct interaction_variable **intr_vars, size_t n_intr)
+{
+  size_t i;
+  size_t n_vars;
+  const struct interaction_variable *iv;
+  const struct variable *intr;
+  const struct variable *member;
+  const union value **vals = NULL;
+
+  for (i = 0; i < n_intr; i++)
     {
-      if (var_is_alpha (v[j]))
+      iv = intr_vars[i];
+      intr = interaction_variable_get_var (iv);
+      if (var_get_dict_index (intr) == var_get_dict_index (var))
        {
-         subs[j] = cat_value_find (v[j], vals[j]);
+         break;
        }
     }
-  j = subs[0];
-  for (i = 1; i < n_vars; i++)
+  n_vars = interaction_get_n_vars (iv);
+  vals = xnmalloc (n_vars, sizeof (*vals));
+  for (i = 0; i < n_vars; i++)
     {
-      j = j * cat_get_n_categories (v[i]) + subs[i];
+      member = interaction_variable_get_member (iv, i);
+      vals[i] = case_data (ccase, member);
     }
-  gsl_vector_set (result, j, 1.0);
-  /*
-     If any of the variables are numeric, the interaction of that
-     variable with another is just a scalar product.
-   */
-  for (i = 1; i < n_vars; i++)
+  return interaction_value_create (iv, vals);
+}
+
+bool
+is_interaction (const struct variable *var, const struct interaction_variable **iv, size_t n_intr)
+{
+  size_t i;
+  const struct variable *intr;
+  
+  for (i = 0; i < n_intr; i++)
     {
-      if (var_is_numeric (v[i]))
+      intr = interaction_variable_get_var (iv[i]);
+      if (var_get_dict_index (intr) == var_get_dict_index (var))
        {
-         tmp *= vals[i]->f;
+         return true;
        }
     }
-  if (fabs (tmp - 1.0) > GSL_DBL_EPSILON)
-    {
-      gsl_vector_set (result, j, tmp);
-    }
-  free (subs);
-
-  return result;
+  return false;
 }
+  
index 070060781635131ca91f05fb6ac7e787f421f48d..73b440be32b014cc4da06c715266efb1b4e6da18 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 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/>. */
 
-gsl_vector *
-get_interaction (union value **, const struct variable **, size_t);
+#ifndef INTERACTION_H
+#define INTERACTION_H
+#include <data/case.h>
+
+struct interaction_variable;
+struct interaction_value;
+
+struct interaction_variable * interaction_variable_create (const struct variable **, int);
+void interaction_variable_destroy (struct interaction_variable *);
+struct interaction_value * interaction_value_create (const struct interaction_variable *, const union value **);
+void interaction_value_destroy (struct interaction_value *);
+size_t interaction_variable_get_n_vars (const struct interaction_variable *);
+double interaction_value_get_nonzero_entry (const struct interaction_value *);
+union value * interaction_value_get (const struct interaction_value *);
+const struct variable * interaction_variable_get_var (const struct interaction_variable *);
+size_t interaction_get_n_numeric (const struct interaction_variable *);
+size_t interaction_get_n_alpha (const struct interaction_variable *);
+size_t interaction_get_n_vars (const struct interaction_variable *);
+const struct variable * interaction_variable_get_member (const struct interaction_variable *, size_t);
+bool is_interaction (const struct variable *, const struct interaction_variable **, size_t);
+struct interaction_value *
+interaction_case_data (const struct ccase *, const struct variable *, 
+                      const struct interaction_variable **, size_t);
+double interaction_value_get_nonzero_entry (const struct interaction_value *);
+#endif
index 3f8ddb29bd0b3c76d524da12c3f273643acfe0ec..7bd582105f940c28ffeb26506d4c5fe941ff728d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -118,7 +118,7 @@ levene(const struct dictionary *dict,
        enum mv_class exclude)
 {
   struct casereader *pass1, *pass2;
-  struct ccase c;
+  struct ccase *c;
   struct levene_info l;
 
   l.n_dep      = n_dep;
@@ -131,14 +131,14 @@ levene(const struct dictionary *dict,
   casereader_split (reader, &pass1, &pass2);
 
   levene_precalc (&l);
-  for (; casereader_read (pass1, &c); case_destroy (&c))
-    levene_calc (dict, &c, &l);
+  for (; (c = casereader_read (pass1)) != NULL; case_unref (c))
+    levene_calc (dict, c, &l);
   casereader_destroy (pass1);
   levene_postcalc (&l);
 
   levene2_precalc(&l);
-  for (; casereader_read (pass2, &c); case_destroy (&c))
-    levene2_calc (dict, &c, &l);
+  for (; (c = casereader_read (pass2)) != NULL; case_unref (c))
+    levene2_calc (dict, c, &l);
   casereader_destroy (pass2);
   levene2_postcalc (&l);
 
index 609a78b6dc9cd98a1d228110e533c396578d996d..811f9d23a5f709877a55c6b535e0b61938e1ce1d 100644 (file)
@@ -24,7 +24,6 @@
 #include <math/coefficient.h>
 #include <math/linreg.h>
 #include <math/coefficient.h>
-#include <math/covariance-matrix.h>
 #include <math/design-matrix.h>
 #include <src/data/category.h>
 #include <src/data/variable.h>
@@ -138,12 +137,15 @@ pspp_linreg_get_vars (const void *c_, const struct variable **v)
   independent variables.
  */
 pspp_linreg_cache *
-pspp_linreg_cache_alloc (size_t n, size_t p)
+pspp_linreg_cache_alloc (const struct variable *depvar, const struct variable **indep_vars,
+                        size_t n, size_t p)
 {
+  size_t i;
   pspp_linreg_cache *c;
 
   c = (pspp_linreg_cache *) malloc (sizeof (pspp_linreg_cache));
-  c->depvar = NULL;
+  c->depvar = depvar;
+  c->indep_vars = indep_vars;
   c->indep_means = gsl_vector_alloc (p);
   c->indep_std = gsl_vector_alloc (p);
   c->ssx = gsl_vector_alloc (p);       /* Sums of squares for the
@@ -152,9 +154,22 @@ pspp_linreg_cache_alloc (size_t n, size_t p)
   c->ss_indeps = gsl_vector_alloc (p); /* Sums of squares for the
                                           model parameters.
                                         */
-  c->cov = gsl_matrix_alloc (p + 1, p + 1);    /* Covariance matrix. */
   c->n_obs = n;
   c->n_indeps = p;
+  c->n_coeffs = 0;
+  for (i = 0; i < p; i++)
+    {
+      if (var_is_numeric (indep_vars[i]))
+       {
+         c->n_coeffs++;
+       }
+      else
+       {
+         c->n_coeffs += cat_get_n_categories (indep_vars[i]) - 1;
+       }
+    }
+
+  c->cov = gsl_matrix_alloc (c->n_coeffs + 1, c->n_coeffs + 1);
   /*
      Default settings.
    */
@@ -194,13 +209,12 @@ pspp_linreg_cache_free (void *m)
   return true;
 }
 static void
-cache_init (pspp_linreg_cache *cache, const struct design_matrix *dm)
+cache_init (pspp_linreg_cache *cache)
 {
   assert (cache != NULL);
   cache->dft = cache->n_obs - 1;
   cache->dfm = cache->n_indeps;
   cache->dfe = cache->dft - cache->dfm;
-  cache->n_coeffs = dm->m->size2;
   cache->intercept = 0.0;
 }
 
@@ -320,7 +334,8 @@ pspp_linreg (const gsl_vector * Y, const struct design_matrix *dm,
       cache->depvar_std = s;
       cache->sst = ss;
     }
-  cache_init (cache, dm);
+  cache_init (cache);
+  cache->n_coeffs = dm->m->size2;
   for (i = 0; i < dm->m->size2; i++)
     {
       if (opts->get_indep_mean_std[i])
@@ -645,100 +660,81 @@ void pspp_linreg_set_indep_variable_mean (pspp_linreg_cache *c, const struct var
   only variables in the model are in the covariance matrix. 
  */
 static struct design_matrix *
-rearrange_covariance_matrix (const struct design_matrix *cov, pspp_linreg_cache *c)
+rearrange_covariance_matrix (const struct covariance_matrix *cm, pspp_linreg_cache *c)
 {
-  struct variable **v;
-  struct variable **model_vars;
-  struct variable *tmp;
+  const struct variable **model_vars;
+  struct design_matrix *cov;
   struct design_matrix *result;
-  int n_vars;
-  int found;
-  size_t *columns;
+  size_t *permutation;
   size_t i;
   size_t j;
   size_t k;
-  size_t dep_col;
 
+  assert (cm != NULL);
+  cov = covariance_to_design (cm);
   assert (cov != NULL);
   assert (c != NULL);
   assert (cov->m->size1 > 0);
   assert (cov->m->size2 == cov->m->size1);
-  v = xnmalloc (c->n_coeffs, sizeof (*v));
-  model_vars = xnmalloc (c->n_coeffs, sizeof (*model_vars));
-  columns = xnmalloc (cov->m->size2, sizeof (*columns));
-  n_vars = pspp_linreg_get_vars (c, (const struct variable **) v);
-  dep_col = 0;
-  k = 0;
-  for (i = 0; i < cov->m->size2; i++)
-    {
-      tmp = design_matrix_col_to_var (cov, i);
-      found = 0;
-      j = 0;
-      while (!found && j < n_vars)
-       {
-         if (tmp == v[j])
-           {
-             found = 1;
-             if (tmp == c->depvar)
-               {
-                 dep_col = j;
-               }
-             else
-               {
-                 columns[k] = j;
-                 k++;
-               }
-           }
-         j++;
-       }
-    }
-  k++;
-  columns[k] = dep_col;
-  /*
-    K should now be equal to C->N_INDEPS + 1. If it is not, then
-    either the code above is wrong or the caller didn't send us the
-    correct values in C.
-   */
-  assert (k == c->n_indeps + 1);
+  permutation = xnmalloc (1 + c->n_indeps, sizeof (*permutation));
+  model_vars = xnmalloc (1 + c->n_indeps, sizeof (*model_vars));
+
   /*
     Put the model variables in the right order in MODEL_VARS.
    */
-  for (i = 0; i < k; i++)
+  for (i = 0; i < c->n_indeps; i++)
     {
-      model_vars[i] = v[columns[i]];
+      model_vars[i] = c->indep_vars[i];
     }
-
-  result = covariance_matrix_create (k, model_vars);
-  for (i = 0; i < result->m->size1; i++)
+  model_vars[i] = c->depvar;
+  result = covariance_matrix_create (1 + c->n_indeps, model_vars);
+  for (j = 0; j < cov->m->size2; j++)
     {
-      for (j = 0; j < result->m->size2; j++)
+      k = 0;
+      while (k < result->m->size2)
        {
-         gsl_matrix_set (result->m, i, j, gsl_matrix_get (cov->m, columns[i], columns[j]));
+         if (design_matrix_col_to_var (cov, j) == design_matrix_col_to_var (result, k)) 
+           {
+             permutation[k] = j;
+           }
+         k++;
        }
     }
-  free (columns);
-  free (v);
+  for (i = 0; i < result->m->size1; i++)
+    for (j = 0; j < result->m->size2; j++)
+      {
+       gsl_matrix_set (result->m, i, j, gsl_matrix_get (cov->m, permutation[i], permutation[j]));
+      }
+  free (permutation);
+  free (model_vars);
   return result;
 }
 /*
   Estimate the model parameters from the covariance matrix only. This
   method uses less memory than PSPP_LINREG, which requires the entire
   data set to be stored in memory.
+
+  The function assumes FULL_COV may contain columns corresponding to
+  variables that are not in the model. It fixes this in
+  REARRANG_COVARIANCE_MATRIX. This allows the caller to compute a
+  large covariance matrix once before, then pass it to this without
+  having to alter it. The problem is that this means the caller must
+  set CACHE->N_COEFFS.
 */
-int
-pspp_linreg_with_cov (const struct design_matrix *full_cov, 
+void
+pspp_linreg_with_cov (const struct covariance_matrix *full_cov, 
                      pspp_linreg_cache * cache)
 {
   struct design_matrix *cov;
 
-  assert (cov != NULL);
+  assert (full_cov != NULL);
   assert (cache != NULL);
 
   cov = rearrange_covariance_matrix (full_cov, cache);
-  cache_init (cache, cov);
+  cache_init (cache);
   reg_sweep (cov->m);
   post_sweep_computations (cache, cov, cov->m);  
-  covariance_matrix_destroy (cov);
+  design_matrix_destroy (cov);
 }
 
 double pspp_linreg_mse (const pspp_linreg_cache *c)
index 3b19e53a260ff5aa740d69e130200b9e43ac055c..f9d8c9b3f740abfc161ea7df2ecf20131985f700 100644 (file)
@@ -21,6 +21,7 @@
 #include <gsl/gsl_vector.h>
 #include <gsl/gsl_matrix.h>
 #include <src/math/coefficient.h>
+#include <math/covariance-matrix.h>
 
 enum
 {
@@ -96,9 +97,10 @@ struct pspp_linreg_cache_struct
                                   coefficient here. */
 
   /*
-    Pointer to the dependent variable.
+    Pointers to the variables.
    */
   const struct variable *depvar;
+  const struct variable **indep_vars;
 
   gsl_vector *residuals;
   struct pspp_coeff **coeff;
@@ -175,7 +177,8 @@ typedef struct pspp_linreg_cache_struct pspp_linreg_cache;
   to it. n is the number of cases, p is the number of
   independent variables.
  */
-pspp_linreg_cache *pspp_linreg_cache_alloc (size_t n, size_t p);
+pspp_linreg_cache *pspp_linreg_cache_alloc (const struct variable *, const struct variable **, 
+                                           size_t, size_t);
 
 bool pspp_linreg_cache_free (void *);
 
@@ -214,5 +217,10 @@ void pspp_linreg_set_indep_variable_sd (pspp_linreg_cache *, const struct variab
  */
 double pspp_linreg_get_indep_variable_mean (pspp_linreg_cache *, const struct variable *);
 void pspp_linreg_set_indep_variable_mean (pspp_linreg_cache *, const struct variable *, double);
+
+/*
+  Regression using only the covariance matrix.
+ */
+void pspp_linreg_with_cov (const struct covariance_matrix *, pspp_linreg_cache *);
 double pspp_linreg_mse (const pspp_linreg_cache *);
 #endif
index 4fc7c8dcf21433a7c23692c275a0ed398c850789..2ff57c6e5ba6fa0a88a9237f95782cb55332bbd6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <math/merge.h>
 
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
+#include <data/subcase.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
 #include <libpspp/taint.h>
 struct merge_input
   {
     struct casereader *reader;
-    struct ccase c;
+    struct ccase *c;
   };
 
 struct merge
   {
-    struct case_ordering *ordering;
+    struct subcase ordering;
     struct merge_input inputs[MAX_MERGE_ORDER];
     size_t input_cnt;
     size_t value_cnt;
@@ -50,10 +50,10 @@ struct merge
 static void do_merge (struct merge *m);
 
 struct merge *
-merge_create (const struct case_ordering *ordering, size_t value_cnt)
+merge_create (const struct subcase *ordering, size_t value_cnt)
 {
   struct merge *m = xmalloc (sizeof *m);
-  m->ordering = case_ordering_clone (ordering);
+  subcase_clone (&m->ordering, ordering);
   m->input_cnt = 0;
   m->value_cnt = value_cnt;
   return m;
@@ -66,7 +66,7 @@ merge_destroy (struct merge *m)
     {
       size_t i;
 
-      case_ordering_destroy (m->ordering);
+      subcase_destroy (&m->ordering);
       for (i = 0; i < m->input_cnt; i++)
         casereader_destroy (m->inputs[i].reader);
       free (m);
@@ -111,7 +111,8 @@ read_input_case (struct merge *m, size_t idx)
 {
   struct merge_input *i = &m->inputs[idx];
 
-  if (casereader_read (i->reader, &i->c))
+  i->c = casereader_read (i->reader);
+  if (i->c)
     return true;
   else
     {
@@ -144,11 +145,11 @@ do_merge (struct merge *m)
 
       min = 0;
       for (i = 1; i < m->input_cnt; i++)
-        if (case_ordering_compare_cases (&m->inputs[i].c, &m->inputs[min].c,
-                                         m->ordering) < 0)
+        if (subcase_compare_3way (&m->ordering, m->inputs[i].c,
+                                  &m->ordering, m->inputs[min].c) < 0)
           min = i;
 
-      casewriter_write (w, &m->inputs[min].c);
+      casewriter_write (w, m->inputs[min].c);
       read_input_case (m, min);
     }
 
index 18322e84bf7f9c2a614a2f9d666412d0b467cfe5..8bd3384a401818e1352bd7cd94a2a61cc9371695 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 
-struct case_ordering;
+struct subcase;
 struct casereader;
 
-struct merge *merge_create (const struct case_ordering *, size_t);
+struct merge *merge_create (const struct subcase *, size_t);
 void merge_destroy (struct merge *);
 void merge_append (struct merge *, struct casereader *);
 struct casereader *merge_make_reader (struct merge *);
diff --git a/src/math/np.c b/src/math/np.c
new file mode 100644 (file)
index 0000000..99584a5
--- /dev/null
@@ -0,0 +1,92 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 "np.h"
+#include <math/moments.h>
+#include <gl/xalloc.h>
+#include <stdlib.h>
+#include <math.h>
+#include <gsl/gsl_cdf.h>
+#include <libpspp/compiler.h>
+#include <data/case.h>
+#include <data/casewriter.h>
+
+static void
+destroy (struct statistic *stat)
+{
+  struct order_stats *os = (struct order_stats *) stat;
+  free (os);
+}
+
+
+static void
+acc (struct statistic *s, const struct ccase *cx UNUSED,
+     double c, double cc, double y)
+{
+  struct ccase *cp;
+  struct np *np = (struct np *) s;
+  double rank = np->prev_cc + (c + 1) / 2.0;
+
+  double ns = gsl_cdf_ugaussian_Pinv (rank / ( np->n + 1 ));
+
+  double z = (y - np->mean) / np->stddev;
+
+  double dns = z - ns;
+
+  maximize (&np->ns_max, ns);
+  minimize (&np->ns_min, ns);
+
+  maximize (&np->dns_max, dns);
+  minimize (&np->dns_min, dns);
+
+  maximize (&np->y_max, y);
+  minimize (&np->y_min, y);
+
+  cp = case_create (n_NP_IDX);
+  case_data_rw_idx (cp, NP_IDX_Y)->f = y;
+  case_data_rw_idx (cp, NP_IDX_NS)->f = ns;
+  case_data_rw_idx (cp, NP_IDX_DNS)->f = dns;
+  casewriter_write (np->writer, cp);
+
+  np->prev_cc = cc;
+}
+
+struct order_stats *
+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;
+
+  np->prev_cc = 0;
+
+  moments1_calculate (m, &np->n, &np->mean, &variance, NULL, NULL);
+
+  np->stddev = sqrt (variance);
+
+  np->y_min = np->ns_min = np->dns_min = DBL_MAX;
+  np->y_max = np->ns_max = np->dns_max = -DBL_MAX;
+
+  np->writer = autopaging_writer_create (n_NP_IDX);
+
+  os->k = 0;
+  stat->destroy = destroy;
+  stat->accumulate = acc;
+
+  return os;
+}
diff --git a/src/math/np.h b/src/math/np.h
new file mode 100644 (file)
index 0000000..7db51f7
--- /dev/null
@@ -0,0 +1,59 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 __NP_H__
+#define __NP_H__
+
+#include "order-stats.h"
+
+struct moments1;
+struct casewriter;
+
+enum
+  {
+    NP_IDX_Y = 0,
+    NP_IDX_NS,
+    NP_IDX_DNS,
+    n_NP_IDX
+  };
+
+struct np
+{
+  struct order_stats parent;
+
+  double n;
+  double mean;
+  double stddev;
+
+
+  double prev_cc;
+
+  double ns_min;
+  double ns_max;
+
+  double dns_min;
+  double dns_max;
+
+  double y_min;
+  double y_max;
+
+  struct casewriter *writer;
+};
+
+
+struct order_stats * np_create (const struct moments1 *);
+
+#endif
diff --git a/src/math/order-stats.c b/src/math/order-stats.c
new file mode 100644 (file)
index 0000000..1b6aa13
--- /dev/null
@@ -0,0 +1,157 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 "order-stats.h"
+#include <libpspp/assertion.h>
+#include <data/val-type.h>
+#include <gl/xalloc.h>
+#include <data/variable.h>
+#include <data/casereader.h>
+#include <string.h>
+
+#if 0
+
+#include <stdio.h>
+
+static void
+order_stats_dump_k1 (const struct order_stats *os)
+{
+  struct k *k = &os->k[0];
+  printf ("K1: tc %g; c %g cc %g ccp %g\n",
+         k->tc, k->c, k->cc, k->cc_p1);
+
+}
+
+static void
+order_stats_dump_k2 (const struct order_stats *os)
+{
+  struct k *k = &os->k[1];
+  printf ("K2: tc %g; c %g cc %g ccp %g\n",
+         k->tc, k->c, k->cc, k->cc_p1);
+}
+
+
+void
+order_stats_dump (const struct order_stats *os)
+{
+  order_stats_dump_k1 (os);
+  order_stats_dump_k2 (os);
+}
+
+#endif
+
+static void
+update_k_lower (struct k *kk,
+               double y_i, double c_i, double cc_i)
+{
+  if ( cc_i <= kk->tc )
+    {
+      kk->cc = cc_i;
+      kk->c = c_i;
+      kk->y = y_i;
+    }
+}
+
+
+static void
+update_k_upper (struct k *kk,
+               double y_i, double c_i, double cc_i)
+{
+  if ( cc_i > kk->tc && kk->c_p1 == 0)
+    {
+      kk->cc_p1 = cc_i;
+      kk->c_p1 = c_i;
+      kk->y_p1 = y_i;
+    }
+}
+
+
+static void
+update_k_values (const struct ccase *cx, double y_i, double c_i, double cc_i,
+                struct order_stats **os, size_t n_os)
+{
+  int j;
+
+  for (j = 0 ; j < n_os ; ++j)
+    {
+      int k;
+      struct order_stats *tos = os[j];
+      struct statistic  *stat = (struct statistic *) tos;
+      for (k = 0 ; k < tos->n_k; ++k)
+       {
+         struct k *myk = &tos->k[k];
+         update_k_lower (myk, y_i, c_i, cc_i);
+         update_k_upper (myk, y_i, c_i, cc_i);
+       }
+
+      if ( stat->accumulate )
+       stat->accumulate (stat, cx, c_i, cc_i, y_i);
+
+      tos->cc = cc_i;
+    }
+}
+
+
+void
+order_stats_accumulate (struct order_stats **os, size_t nos,
+                       struct casereader *reader,
+                       const struct variable *wv,
+                       const struct variable *var,
+                       enum mv_class exclude)
+{
+  struct ccase *cx;
+  struct ccase *prev_cx = NULL;
+  double prev_value = -DBL_MAX;
+
+  double cc_i = 0;
+  double c_i = 0;
+
+  for (; (cx = casereader_read (reader)) != NULL; case_unref (cx))
+    {
+      const double weight = wv ? case_data (cx, wv)->f : 1.0;
+      const double this_value = case_data (cx, var)->f;
+
+      /* The casereader MUST be sorted */
+      assert (this_value >= prev_value);
+
+      if ( var_is_value_missing (var, case_data (cx, var), exclude))
+       continue;
+
+      case_unref (prev_cx);
+
+      if ( prev_value == -DBL_MAX || prev_value == this_value)
+       c_i += weight;
+
+      if ( prev_value > -DBL_MAX && this_value > prev_value)
+       {
+         update_k_values (prev_cx, prev_value, c_i, cc_i, os, nos);
+         c_i = weight;
+       }
+
+      cc_i += weight;
+      prev_value = this_value;
+      prev_cx = case_ref (cx);
+    }
+
+  update_k_values (prev_cx, prev_value, c_i, cc_i, os, nos);
+  case_unref (prev_cx);
+
+  casereader_destroy (reader);
+}
+
+
+
diff --git a/src/math/order-stats.h b/src/math/order-stats.h
new file mode 100644 (file)
index 0000000..07da284
--- /dev/null
@@ -0,0 +1,61 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2008 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 __ORDER_STATS_H__
+#define __ORDER_STATS_H__
+
+#include <stddef.h>
+#include <data/missing-values.h>
+#include <math/statistic.h>
+
+struct casereader;
+struct variable;
+
+/*
+  cc <= tc < cc_p1
+*/
+struct k
+{
+  double tc;
+  double cc;
+  double cc_p1;
+  double c;
+  double c_p1;
+  double y;
+  double y_p1;
+};
+
+
+struct order_stats
+{
+  struct statistic parent;
+  int n_k;
+  struct k *k;
+
+  double cc;
+};
+
+enum mv_class;
+
+void order_stats_dump (const struct order_stats *os);
+
+void order_stats_accumulate (struct order_stats **ptl, size_t nos,
+                            struct casereader *reader,
+                            const struct variable *wv,
+                            const struct variable *var,
+                            enum mv_class exclude);
+
+#endif
index aa7eead6c03980d9b1dc2bafca96777341678351..bf99de163ffbaafe2af8711685e9a640a0256784 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2008 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 <assert.h>
-#include <data/val-type.h>
-#include <libpspp/compiler.h>
-#include "factor-stats.h"
 #include "percentiles.h"
-#include <libpspp/misc.h>
+#include <math/order-stats.h>
 
-#include "minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-struct ptile_params
-{
-  double g1, g1_star;
-  double g2, g2_star;
-  int k1, k2;
-};
+#include <libpspp/assertion.h>
+#include <data/val-type.h>
+#include <gl/xalloc.h>
+#include <data/variable.h>
+#include <data/casereader.h>
 
 
 const char *const ptile_alg_desc[] = {
@@ -47,380 +41,151 @@ const char *const ptile_alg_desc[] = {
 
 
 
-
-/* Individual Percentile algorithms */
-
-/* Closest observation to tc1 */
-double ptile_round(const struct weighted_value **wv,
-                  const struct ptile_params *par);
-
-
-/* Weighted average at y_tc2 */
-double ptile_haverage(const struct weighted_value **wv,
-                  const struct ptile_params *par);
-
-
-/* Weighted average at y_tc1 */
-double ptile_waverage(const struct weighted_value **wv,
-                  const struct ptile_params *par);
-
-
-/* Empirical distribution function */
-double ptile_empirical(const struct weighted_value **wv,
-                  const struct ptile_params *par);
-
-
-/* Empirical distribution function with averaging*/
-double ptile_aempirical(const struct weighted_value **wv,
-                  const struct ptile_params *par);
-
-
-
-
-/* Closest observation to tc1 */
 double
-ptile_round(const struct weighted_value **wv,
-           const struct ptile_params *par)
+percentile_calculate (const struct percentile *ptl, enum pc_alg alg)
 {
-  double x;
-  double a=0;
+  struct percentile *mutable = (struct percentile *) ptl;
+  const struct order_stats *os = &ptl->parent;
 
-  if ( par->k1 >= 0 )
-    a = wv[par->k1]->v.f;
+  assert (os->cc == ptl->w);
 
-  if ( wv[par->k1 + 1]->w >= 1 )
-    {
-      if ( par->g1_star < 0.5 )
-       x = a;
-      else
-       x = wv[par->k1 + 1]->v.f;
-    }
-  else
-    {
-      if ( par->g1 < 0.5 )
-       x = a;
-      else
-       x = wv[par->k1 + 1]->v.f;
-
-    }
-
-  return x;
-}
+  if ( ptl->g1 == SYSMIS)
+    mutable->g1 = (os->k[0].tc - os->k[0].cc) / os->k[0].c_p1;
 
-/* Weighted average at y_tc2 */
-double
-ptile_haverage(const struct weighted_value **wv,
-              const struct ptile_params *par)
-{
-
-  double a=0;
+  if ( ptl->g1_star == SYSMIS)
+    mutable->g1_star = os->k[0].tc - os->k[0].cc;
 
-  if ( par->g2_star >= 1.0 )
-      return wv[par->k2 + 1]->v.f ;
-
-  /* Special case  for k2 + 1 >= n_data
-     (actually it's not a special case, but just avoids indexing errors )
-   */
-  if ( par->g2_star == 0 )
+  if ( ptl->g2 == SYSMIS)
     {
-      assert(par->g2 == 0 );
-      return wv[par->k2]->v.f;
-    }
-
-  /* Ditto for k2 < 0 */
-  if ( par->k2 >= 0 )
-    {
-      a = wv[par->k2]->v.f;
+      if ( os->k[1].c == 0 )
+       mutable->g2 = os->k[1].tc / os->k[1].c_p1;
+      else if ( os->k[1].c_p1 == 0 )
+       mutable->g2 = 0;
+      else
+       mutable->g2 = (os->k[1].tc - os->k[1].cc) / os->k[1].c_p1;
     }
 
-  if ( wv[par->k2 + 1]->w >= 1.0 )
-    return ( (1 - par->g2_star) *  a   +
-            par->g2_star * wv[par->k2 + 1]->v.f);
-  else
-    return ( (1 - par->g2) * a +
-            par->g2 * wv[par->k2 + 1]->v.f);
-
-}
-
-
-
-/* Weighted average at y_tc1 */
-double
-ptile_waverage(const struct weighted_value **wv,
-              const struct ptile_params *par)
-{
-  double a=0;
-
-  if ( par->g1_star >= 1.0 )
-      return wv[par->k1 + 1]->v.f ;
-
-  if ( par->k1 >= 0 )
+  if ( ptl->g2_star == SYSMIS)
     {
-      a = wv[par->k1]->v.f;
+      if ( os->k[1].c == 0 )
+       mutable->g2_star = os->k[1].tc;
+      else if ( os->k[1].c_p1 == 0 )
+       mutable->g2_star = 0;
+      else
+       mutable->g2_star = os->k[1].tc - os->k[1].cc;
     }
 
-  if ( wv[par->k1 + 1]->w >= 1.0 )
-    return ( (1 - par->g1_star) * a +
-            par->g1_star * wv[par->k1 + 1]->v.f);
-  else
-    return ( (1 - par->g1) * a +
-            par->g1 * wv[par->k1 + 1]->v.f);
-}
-
-
-/* Empirical distribution function */
-double
-ptile_empirical(const struct weighted_value **wv,
-              const struct ptile_params *par)
-{
-  if ( par->g1_star > 0 )
-    return wv[par->k1 + 1]->v.f;
-  else
-    return wv[par->k1]->v.f;
-}
-
-
-
-/* Empirical distribution function with averageing */
-double
-ptile_aempirical(const struct weighted_value **wv,
-              const struct ptile_params *par)
-{
-  if ( par->g1_star > 0 )
-    return wv[par->k1 + 1]->v.f;
-  else
-    return (wv[par->k1]->v.f + wv[par->k1 + 1]->v.f ) / 2.0 ;
-}
-
-
-
-/* Compute the percentile p */
-double ptile(double p,
-            const struct weighted_value **wv,
-            int n_data,
-            double w,
-            enum pc_alg algorithm);
-
-
-
-double
-ptile(double p,
-      const struct weighted_value **wv,
-      int n_data,
-      double w,
-      enum pc_alg algorithm)
-{
-  int i;
-  double tc1, tc2;
-  double result;
-
-  struct ptile_params pp;
-
-  assert( p <= 1.0);
-
-  tc1 = w * p ;
-  tc2 = (w + 1) * p ;
-
-  pp.k1 = -1;
-  pp.k2 = -1;
-
-  for ( i = 0 ; i < n_data ; ++i )
+  switch (alg)
     {
-      if ( wv[i]->cc <= tc1 )
-       pp.k1 = i;
-
-      if ( wv[i]->cc <= tc2 )
-       pp.k2 = i;
+    case PC_WAVERAGE:
+      if ( ptl->g1_star >= 1.0)
+       return os->k[0].y_p1;
+      else
+       {
+         double a = ( os->k[0].y == SYSMIS ) ? 0 : os->k[0].y;
 
-    }
+         if (os->k[0].c_p1 >= 1.0)
+           return (1 - ptl->g1_star) * a + ptl->g1_star * os->k[0].y_p1;
+         else
+           return (1 - ptl->g1) * a + ptl->g1 * os->k[0].y_p1;
+       }
+      break;
 
+    case PC_ROUND:
+      {
+       double a = ( os->k[0].y == SYSMIS ) ? 0 : os->k[0].y;
 
-  if ( pp.k1 >= 0 )
-    {
-      pp.g1 = ( tc1 - wv[pp.k1]->cc ) / wv[pp.k1 + 1]->w;
-      pp.g1_star = tc1 -  wv[pp.k1]->cc ;
-    }
-  else
-    {
-      pp.g1 = tc1 / wv[pp.k1 + 1]->w;
-      pp.g1_star = tc1 ;
-    }
+       if (os->k[0].c_p1 >= 1.0)
+         return (ptl->g1_star < 0.5) ? a : os->k[0].y_p1;
+       else
+         return (ptl->g1 < 0.5) ? a : os->k[0].y_p1;
+      }
+      break;
 
+    case PC_EMPIRICAL:
+      if ( ptl->g1_star == 0 )
+       return os->k[0].y;
+      else
+       return os->k[0].y_p1;
+      break;
 
-  if ( pp.k2  + 1 >= n_data )
-    {
-      pp.g2 = 0 ;
-      pp.g2_star = 0;
-    }
-  else
-    {
-      if ( pp.k2 >= 0 )
+    case PC_HAVERAGE:
+      if ( ptl->g2_star >= 1.0)
        {
-         pp.g2 = ( tc2 - wv[pp.k2]->cc ) / wv[pp.k2 + 1]->w;
-         pp.g2_star = tc2 -  wv[pp.k2]->cc ;
+         return os->k[1].y_p1;
        }
       else
        {
-         pp.g2 = tc2 / wv[pp.k2 + 1]->w;
-         pp.g2_star = tc2 ;
+         double a = ( os->k[1].y == SYSMIS ) ? 0 : os->k[1].y;
+
+         if ( os->k[1].c_p1 >= 1.0)
+           {
+             if ( ptl->g2_star == 0)
+               return os->k[1].y;
+
+             return (1 - ptl->g2_star) * a + ptl->g2_star * os->k[1].y_p1;
+           }
+         else
+           {
+             return (1 - ptl->g2) * a + ptl->g2 * os->k[1].y_p1;
+           }
        }
-    }
 
-  switch ( algorithm )
-    {
-    case PC_HAVERAGE:
-      result = ptile_haverage(wv, &pp);
-      break;
-    case PC_WAVERAGE:
-      result = ptile_waverage(wv, &pp);
-      break;
-    case PC_ROUND:
-      result = ptile_round(wv, &pp);
-      break;
-    case PC_EMPIRICAL:
-      result = ptile_empirical(wv, &pp);
       break;
+
     case PC_AEMPIRICAL:
-      result = ptile_aempirical(wv, &pp);
+      if ( ptl->g1_star == 0 )
+       return (os->k[0].y + os->k[0].y_p1)/ 2.0;
+      else
+       return os->k[0].y_p1;
       break;
+
     default:
-      result = SYSMIS;
+      NOT_REACHED ();
+      break;
     }
 
-  return result;
+  NOT_REACHED ();
+
+  return SYSMIS;
 }
 
 
-/*
-   Calculate the values of the percentiles in pc_hash.
-   wv is  a sorted array of weighted values of the data set.
-*/
-void
-ptiles(struct hsh_table *pc_hash,
-       const struct weighted_value **wv,
-       int n_data,
-       double w,
-       enum pc_alg algorithm)
+static void
+destroy (struct statistic *stat)
 {
-  struct hsh_iterator hi;
-  struct percentile *p;
-
-  if ( !pc_hash )
-    return ;
-  for ( p = hsh_first(pc_hash, &hi);
-       p != 0 ;
-       p = hsh_next(pc_hash, &hi))
-    {
-      p->v = ptile(p->p/100.0 , wv, n_data, w, algorithm);
-    }
-
+  struct order_stats *os = (struct order_stats *) stat;
+  free (os->k);
+  free (os);
 }
 
 
-/* Calculate Tukey's Hinges */
-void
-tukey_hinges(const struct weighted_value **wv,
-            int n_data,
-            double w,
-            double hinge[3]
-            )
+struct order_stats *
+percentile_create (double p, double W)
 {
-  int i;
-  double c_star = DBL_MAX;
-  double d;
-  double l[3];
-  int h[3];
-  double a, a_star;
-
-  for ( i = 0 ; i < n_data ; ++i )
-    {
-      c_star = MIN(c_star, wv[i]->w);
-    }
-
-  if ( c_star > 1 ) c_star = 1;
-
-  d = floor((w/c_star + 3 ) / 2.0)/ 2.0;
-
-  l[0] = d*c_star;
-  l[1] = w/2.0 + c_star/2.0;
-  l[2] = w + c_star - d*c_star;
-
-  h[0]=-1;
-  h[1]=-1;
-  h[2]=-1;
-
-  for ( i = 0 ; i < n_data ; ++i )
-    {
-      if ( l[0] >= wv[i]->cc ) h[0] = i ;
-      if ( l[1] >= wv[i]->cc ) h[1] = i ;
-      if ( l[2] >= wv[i]->cc ) h[2] = i ;
-    }
-
-  for ( i = 0 ; i < 3 ; i++ )
-    {
-
-      if ( h[i] >= 0 )
-       a_star = l[i] - wv[h[i]]->cc ;
-      else
-       a_star = l[i];
-
-      if ( h[i] + 1 >= n_data )
-      {
-             assert( a_star < 1 ) ;
-             hinge[i] = (1 - a_star) * wv[h[i]]->v.f;
-             continue;
-      }
-      else
-      {
-             a = a_star / ( wv[h[i] + 1]->cc ) ;
-      }
-
-      if ( a_star >= 1.0 )
-       {
-         hinge[i] = wv[h[i] + 1]->v.f ;
-         continue;
-       }
-
-      if ( wv[h[i] + 1]->w >= 1)
-       {
-         hinge[i] = ( 1 - a_star) * wv[h[i]]->v.f
-           + a_star * wv[h[i] + 1]->v.f;
-
-         continue;
-       }
+  struct percentile *ptl = xzalloc (sizeof (*ptl));
+  struct order_stats *os = (struct order_stats *) ptl;
+  struct statistic *stat = (struct statistic *) ptl;
 
-      hinge[i] = (1 - a) * wv[h[i]]->v.f + a * wv[h[i] + 1]->v.f;
+  assert (p >= 0);
+  assert (p <= 1.0);
 
-    }
-
-  assert(hinge[0] <= hinge[1]);
-  assert(hinge[1] <= hinge[2]);
+  ptl->ptile = p;
+  ptl->w = W;
 
-}
+  os->n_k = 2;
+  os->k = xcalloc (sizeof (*os->k), 2);
+  os->k[0].tc = W * p;
+  os->k[1].tc = (W + 1.0) * p;
 
+  ptl->g1 = ptl->g1_star = SYSMIS;
+  ptl->g2 = ptl->g2_star = SYSMIS;
 
-int
-ptile_compare(const struct percentile *p1,
-                  const struct percentile *p2,
-                  void *aux UNUSED)
-{
-
-  int cmp;
+  os->k[1].y_p1 = os->k[1].y = SYSMIS;
+  os->k[0].y_p1 = os->k[0].y = SYSMIS;
 
-  if ( p1->p == p2->p)
-    cmp = 0 ;
-  else if (p1->p < p2->p)
-    cmp = -1 ;
-  else
-    cmp = +1;
+  stat->destroy = destroy;
 
-  return cmp;
+  return os;
 }
 
-unsigned
-ptile_hash(const struct percentile *p, void *aux UNUSED)
-{
-  return hsh_hash_double(p->p);
-}
-
-
index 9e0eb47a46f5275610a51239322c040870d48660..0dd09820945e1bafaee5a017ba3756cb4197783b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008 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 PERCENTILES_H
-#define PERCENTILES_H
+#ifndef __PERCENTILES_H__
+#define __PERCENTILES_H__
 
+#include <stddef.h>
 
-#include <libpspp/hash.h>
-
-struct weighted_value ;
+#include "order-stats.h"
 
 /* The algorithm used to calculate percentiles */
 enum pc_alg {
@@ -32,48 +31,33 @@ enum pc_alg {
   PC_AEMPIRICAL
 } ;
 
-
-
 extern  const char *const ptile_alg_desc[];
 
 
+struct percentile
+{
+  struct order_stats parent;
 
+  double ptile;
+  double w;
 
-struct percentile {
+  /* Mutable */
+  double g1;
+  double g1_star;
 
-  /* The break point of the percentile */
-  double p;
-
-  /* The value of the percentile */
-  double v;
+  double g2;
+  double g2_star;
 };
 
-
-/* Calculate the percentiles of the break points in pc_bp,
-   placing the values in pc_val.
-   wv is  a sorted array of weighted values of the data set.
+/* Create the Pth percentile.
+   W is the total sum of weights in the data set
 */
-void ptiles(struct hsh_table *pc_hash,
-           const struct weighted_value **wv,
-           int n_data,
-           double w,
-           enum pc_alg algorithm);
-
-
-/* Calculate Tukey's Hinges and the Whiskers for the box plot*/
-void tukey_hinges(const struct weighted_value **wv,
-                 int n_data,
-                 double w,
-                 double hinges[3]);
-
-
+struct order_stats *percentile_create (double p, double W);
 
-/* Hash utility functions */
-int ptile_compare(const struct percentile *p1,
-                  const struct percentile *p2,
-                  void *aux);
+/* Return the value of the percentile */
+double percentile_calculate (const struct percentile *ptl, enum pc_alg alg);
 
-unsigned ptile_hash(const struct percentile *p, void *aux);
+void percentile_dump (const struct percentile *ptl);
 
 
 #endif
index 10b8a12cec59d76173a39a15532a53dbec62e306..67aa32d29048fffbb5e7b3c03b371b8db9c88a62 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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 <stdio.h>
 
-#include <data/case-ordering.h>
 #include <data/case.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
 #include <data/casewriter-provider.h>
 #include <data/settings.h>
+#include <data/subcase.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
 #include <math/merge.h>
@@ -42,41 +42,39 @@ int max_buffers = INT_MAX;
 struct sort_writer
   {
     size_t value_cnt;
-    struct case_ordering *ordering;
+    struct subcase ordering;
     struct merge *merge;
     struct pqueue *pqueue;
 
     struct casewriter *run;
     casenumber run_id;
-    struct ccase run_end;
+    struct ccase *run_end;
   };
 
 static struct casewriter_class sort_casewriter_class;
 
-static struct pqueue *pqueue_create (const struct case_ordering *, size_t);
+static struct pqueue *pqueue_create (const struct subcase *, size_t);
 static void pqueue_destroy (struct pqueue *);
 static bool pqueue_is_full (const struct pqueue *);
 static bool pqueue_is_empty (const struct pqueue *);
 static void pqueue_push (struct pqueue *, struct ccase *, casenumber);
-static void pqueue_pop (struct pqueue *, struct ccase *, casenumber *);
+static struct ccase *pqueue_pop (struct pqueue *, casenumber *);
 
 static void output_record (struct sort_writer *);
 
 struct casewriter *
-sort_create_writer (struct case_ordering *ordering, size_t value_cnt)
+sort_create_writer (const struct subcase *ordering, size_t value_cnt)
 {
   struct sort_writer *sort;
 
   sort = xmalloc (sizeof *sort);
   sort->value_cnt = value_cnt;
-  sort->ordering = case_ordering_clone (ordering);
+  subcase_clone (&sort->ordering, ordering);
   sort->merge = merge_create (ordering, value_cnt);
   sort->pqueue = pqueue_create (ordering, value_cnt);
   sort->run = NULL;
   sort->run_id = 0;
-  case_nullify (&sort->run_end);
-
-  case_ordering_destroy (ordering);
+  sort->run_end = NULL;
 
   return casewriter_create (value_cnt, &sort_casewriter_class, sort);
 }
@@ -91,9 +89,9 @@ sort_casewriter_write (struct casewriter *writer UNUSED, void *sort_,
   if (pqueue_is_full (sort->pqueue))
     output_record (sort);
 
-  next_run = (case_is_null (&sort->run_end)
-              || case_ordering_compare_cases (c, &sort->run_end,
-                                              sort->ordering) < 0);
+  next_run = (sort->run_end == NULL
+              || subcase_compare_3way (&sort->ordering, c,
+                                       &sort->ordering, sort->run_end) < 0);
   pqueue_push (sort->pqueue, c, sort->run_id + (next_run ? 1 : 0));
 }
 
@@ -102,11 +100,11 @@ sort_casewriter_destroy (struct casewriter *writer UNUSED, void *sort_)
 {
   struct sort_writer *sort = sort_;
 
-  case_ordering_destroy (sort->ordering);
+  subcase_destroy (&sort->ordering);
   merge_destroy (sort->merge);
   pqueue_destroy (sort->pqueue);
   casewriter_destroy (sort->run);
-  case_destroy (&sort->run_end);
+  case_unref (sort->run_end);
   free (sort);
 }
 
@@ -136,12 +134,12 @@ sort_casewriter_convert_to_reader (struct casewriter *writer, void *sort_)
 static void
 output_record (struct sort_writer *sort)
 {
-  struct ccase min_case;
+  struct ccase *min_case;
   casenumber min_run_id;
 
-  pqueue_pop (sort->pqueue, &min_case, &min_run_id);
+  min_case = pqueue_pop (sort->pqueue, &min_run_id);
 #if 0
-  printf ("\toutput: %f to run %d\n", case_num_idx (&min_case, 0), min_run_id);
+  printf ("\toutput: %f to run %d\n", case_num_idx (min_case, 0), min_run_id);
 #endif
 
   if (sort->run_id != min_run_id && sort->run != NULL)
@@ -155,10 +153,9 @@ output_record (struct sort_writer *sort)
       sort->run_id = min_run_id;
     }
 
-  case_destroy (&sort->run_end);
-  case_clone (&sort->run_end, &min_case);
-
-  casewriter_write (sort->run, &min_case);
+  case_unref (sort->run_end);
+  sort->run_end = case_ref (min_case);
+  casewriter_write (sort->run, min_case);
 }
 
 static struct casewriter_class sort_casewriter_class =
@@ -169,21 +166,34 @@ static struct casewriter_class sort_casewriter_class =
   };
 \f
 /* Reads all the cases from INPUT.  Sorts the cases according to
-   ORDERING.  Returns the sorted cases in a new casereader, or a
-   null pointer if an I/O error occurs.  Both INPUT and ORDERING
-   are destroyed upon return, regardless of success. */
+   ORDERING.  Returns the sorted cases in a new casereader. */
 struct casereader *
-sort_execute (struct casereader *input, struct case_ordering *ordering)
+sort_execute (struct casereader *input, const struct subcase *ordering)
 {
   struct casewriter *output =
     sort_create_writer (ordering, casereader_get_value_cnt (input));
   casereader_transfer (input, output);
   return casewriter_make_reader (output);
 }
+
+/* Reads all the cases from INPUT.  Sorts the cases in ascending
+   order according to VARIABLE.  Returns the sorted cases in a
+   new casereader. */
+struct casereader *
+sort_execute_1var (struct casereader *input, const struct variable *var)
+{
+  struct subcase sc;
+  struct casereader *reader;
+
+  subcase_init_var (&sc, var, SC_ASCEND);
+  reader = sort_execute (input, &sc);
+  subcase_destroy (&sc);
+  return reader;
+}
 \f
 struct pqueue
   {
-    struct case_ordering *ordering;
+    struct subcase ordering;
     struct pqueue_record *records;
     size_t record_cnt;
     size_t record_cap;
@@ -193,7 +203,7 @@ struct pqueue
 struct pqueue_record
   {
     casenumber id;
-    struct ccase c;
+    struct ccase *c;
     casenumber idx;
   };
 
@@ -201,12 +211,12 @@ static int compare_pqueue_records_minheap (const void *a, const void *b,
                                            const void *pq_);
 
 static struct pqueue *
-pqueue_create (const struct case_ordering *ordering, size_t value_cnt)
+pqueue_create (const struct subcase *ordering, size_t value_cnt)
 {
   struct pqueue *pq;
 
   pq = xmalloc (sizeof *pq);
-  pq->ordering = case_ordering_clone (ordering);
+  subcase_clone (&pq->ordering, ordering);
   pq->record_cap
     = settings_get_workspace_cases (value_cnt);
   if (pq->record_cap > max_buffers)
@@ -227,12 +237,11 @@ pqueue_destroy (struct pqueue *pq)
     {
       while (!pqueue_is_empty (pq))
         {
-          struct ccase c;
           casenumber id;
-          pqueue_pop (pq, &c, &id);
-          case_destroy (&c);
+          struct ccase *c = pqueue_pop (pq, &id);
+          case_unref (c);
         }
-      case_ordering_destroy (pq->ordering);
+      subcase_destroy (&pq->ordering);
       free (pq->records);
       free (pq);
     }
@@ -259,15 +268,15 @@ pqueue_push (struct pqueue *pq, struct ccase *c, casenumber id)
 
   r = &pq->records[pq->record_cnt++];
   r->id = id;
-  case_move (&r->c, c);
+  r->c = c;
   r->idx = pq->idx++;
 
   push_heap (pq->records, pq->record_cnt, sizeof *pq->records,
              compare_pqueue_records_minheap, pq);
 }
 
-static void
-pqueue_pop (struct pqueue *pq, struct ccase *c, casenumber *id)
+static struct ccase *
+pqueue_pop (struct pqueue *pq, casenumber *id)
 {
   struct pqueue_record *r;
 
@@ -278,7 +287,7 @@ pqueue_pop (struct pqueue *pq, struct ccase *c, casenumber *id)
 
   r = &pq->records[pq->record_cnt];
   *id = r->id;
-  case_move (c, &r->c);
+  return r->c;
 }
 
 /* Compares record-run tuples A and B on id, then on case data,
@@ -292,7 +301,7 @@ compare_pqueue_records_minheap (const void *a_, const void *b_,
   const struct pqueue *pq = pq_;
   int result = a->id < b->id ? -1 : a->id > b->id;
   if (result == 0)
-    result = case_ordering_compare_cases (&a->c, &b->c, pq->ordering);
+    result = subcase_compare_3way (&pq->ordering, a->c, &pq->ordering, b->c);
   if (result == 0)
     result = a->idx < b->idx ? -1 : a->idx > b->idx;
   return -result;
index ea2c16b2e14a8a8ed1c2fbe0f838bb92bdcd4870..1948d0abea10df18edf47fb81cd736922a99fccb 100644 (file)
 #include <stddef.h>
 #include <stdbool.h>
 
-struct case_ordering;
+struct subcase;
+struct variable;
 
 extern int min_buffers ;
 extern int max_buffers ;
 
-struct casewriter *sort_create_writer (struct case_ordering *, size_t value_cnt);
-struct casereader *sort_execute (struct casereader *, struct case_ordering *);
+struct casewriter *sort_create_writer (const struct subcase *,
+                                       size_t value_cnt);
+struct casereader *sort_execute (struct casereader *, const struct subcase *);
+struct casereader *sort_execute_1var (struct casereader *,
+                                      const struct variable *);
 
 #endif /* math/sort.h */
diff --git a/src/math/statistic.h b/src/math/statistic.h
new file mode 100644 (file)
index 0000000..987264b
--- /dev/null
@@ -0,0 +1,40 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 __STATISTIC_H__
+#define __STATISTIC_H__
+
+#include <stddef.h>
+
+struct ccase ;
+
+struct statistic
+{
+  void (*accumulate) (struct statistic *, const struct ccase *cx, double c, double cc, double y);
+  void (*destroy) (struct statistic *);
+};
+
+static inline void statistic_destroy (struct statistic *s);
+
+
+static inline void
+statistic_destroy (struct statistic *s)
+{
+  if (s) s->destroy (s);
+}
+
+
+#endif
diff --git a/src/math/trimmed-mean.c b/src/math/trimmed-mean.c
new file mode 100644 (file)
index 0000000..da3d424
--- /dev/null
@@ -0,0 +1,94 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 "trimmed-mean.h"
+#include <math/order-stats.h>
+
+#include <gl/xalloc.h>
+#include <libpspp/assertion.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;
+
+  if ( cc > os->k[0].tc && cc < os->k[1].tc)
+      tm->sum += c * y;
+
+  if ( tm->cyk1p1 == SYSMIS && cc >os->k[0].tc)
+      tm->cyk1p1 = c * y;
+}
+
+static void
+destroy (struct statistic *s)
+{
+  struct order_stats *os = (struct order_stats *) s;
+  free (os->k);
+  free (s);
+}
+
+struct statistic *
+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;
+
+  os->n_k = 2;
+  os->k = xcalloc (sizeof (*os->k), 2);
+
+  assert (tail >= 0);
+  assert (tail <= 1);
+
+  os->k[0].tc = tail * W;
+  os->k[1].tc = W * (1 - tail);
+
+  stat->accumulate = acc;
+  stat->destroy = destroy;
+
+  tm->cyk1p1 = SYSMIS;
+  tm->w = W;
+  tm->tail = tail;
+
+  return stat;
+}
+
+
+double
+trimmed_mean_calculate (const struct trimmed_mean *tm)
+{
+  const struct order_stats *os = (const struct order_stats *) tm;
+
+  assert (os->cc == tm->w);
+
+  return
+    (
+     (os->k[0].cc_p1 - os->k[0].tc) * os->k[0].y_p1
+     -
+     (os->k[1].cc - os->k[1].tc) * os->k[1].y_p1
+     +
+     tm->sum
+     -
+     tm->cyk1p1
+     )
+    /
+    ( (1.0 - 2 * tm->tail) * tm->w);
+}
diff --git a/src/math/trimmed-mean.h b/src/math/trimmed-mean.h
new file mode 100644 (file)
index 0000000..9339cab
--- /dev/null
@@ -0,0 +1,42 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 __TRIMMED_MEAN_H__
+#define __TRIMMED_MEAN_H__
+
+#include <stddef.h>
+
+#include "order-stats.h"
+
+
+struct trimmed_mean
+{
+  struct order_stats parent;
+
+  /* The partial sum */
+  double sum;
+
+  /* The product of c_{k_1+1} and y_{k_1 + 1} */
+  double cyk1p1;
+
+  double w;
+  double tail;
+};
+
+struct statistic * trimmed_mean_create (double W, double c_min);
+double trimmed_mean_calculate (const struct trimmed_mean *);
+
+#endif
index 2ca1f959fa22fb19d2d50453081e05ce26b282ee..eecc53a81f68ed388b68cd7459307f1861cbd84f 100644 (file)
@@ -1,8 +1,8 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-noinst_LIBRARIES += src/math/ts/libpspp_ts.a
+noinst_LTLIBRARIES += src/math/ts/libpspp_ts.la
 
-src_math_ts_libpspp_ts_a_SOURCES = \
+src_math_ts_libpspp_ts_la_SOURCES = \
        src/math/ts/innovations.c \
        src/math/ts/innovations.h
 
index 553e20e8f7953a879595c3ddb0dce2e0e715b1dc..b9a7cf223442a42ce597748320647a23a0739939 100644 (file)
@@ -160,7 +160,7 @@ innovations_update_scale (struct innovations_estimate *est, double *theta,
       for (j = 0; j < i; j++)
        {
          k = i - j - 1;
-         result -= theta[k] * theta[k] * est->scale[j];
+         result -= pow2 (theta[k]) * est->scale[j];
        }
       est->scale[i] = result;
     }
diff --git a/src/math/tukey-hinges.c b/src/math/tukey-hinges.c
new file mode 100644 (file)
index 0000000..95a79c1
--- /dev/null
@@ -0,0 +1,101 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 "tukey-hinges.h"
+#include <math/order-stats.h>
+
+#include <gl/xalloc.h>
+#include <libpspp/assertion.h>
+#include <math.h>
+
+void
+tukey_hinges_calculate (const struct tukey_hinges *th, double hinge[3])
+{
+  double a[3];
+  double a_star[3];
+  int i;
+  const struct order_stats *os = &th->parent;
+
+  for (i = 0 ; i < 3 ; ++i)
+    {
+      a_star[i] = os->k[i].tc - os->k[i].cc;
+      a[i] = a_star[i] / os->k[i].c_p1;
+
+      if (a_star[i] < 1)
+       {
+         if (os->k[i].c_p1 >= 1 )
+           {
+             hinge[i] = (1 - a_star[i]) * os->k[i].y
+               + a_star[i] * os->k[i].y_p1;
+           }
+         else
+           {
+             hinge[i] = (1 - a[i]) * os->k[i].y
+               + a[i] * os->k[i].y_p1;
+           }
+       }
+      else
+       {
+         hinge[i] = os->k[i].y_p1;
+       }
+
+    }
+}
+
+static void
+destroy (struct statistic *s)
+{
+  struct order_stats *os = (struct order_stats *) s;
+
+  free (os->k);
+  free (s);
+};
+
+struct statistic *
+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;
+
+  assert (c_min >= 0);
+
+  os->n_k = 3;
+  os->k = xcalloc (sizeof (*os->k), 3);
+
+  if ( c_min >= 1.0)
+    {
+      d = floor ((W + 3) / 2.0) / 2.0;
+
+      os->k[0].tc = d;
+      os->k[1].tc = W/2.0 + 0.5;
+      os->k[2].tc = W + 1 - d;
+    }
+  else
+    {
+      d = floor ((W/c_min + 3.0)/ 2.0) / 2.0 ;
+      os->k[0].tc = d * c_min;
+      os->k[1].tc = (W + c_min) / 2.0;
+      os->k[2].tc = W + c_min * (1 - d);
+    }
+
+
+  stat->destroy = destroy;
+
+  return stat;
+}
diff --git a/src/math/tukey-hinges.h b/src/math/tukey-hinges.h
new file mode 100644 (file)
index 0000000..d87691f
--- /dev/null
@@ -0,0 +1,37 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2008 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 __TUKEY_HINGES_H__
+#define __TUKEY_HINGES_H__
+
+#include <stddef.h>
+
+#include "order-stats.h"
+
+
+struct tukey_hinges
+{
+  struct order_stats parent;
+};
+
+struct statistic * tukey_hinges_create (double W, double c_min);
+
+
+void tukey_hinges_calculate (const struct tukey_hinges *h, double hinge[3]);
+
+
+
+#endif
diff --git a/src/math/wilcoxon-sig.c b/src/math/wilcoxon-sig.c
new file mode 100644 (file)
index 0000000..d9a4bca
--- /dev/null
@@ -0,0 +1,156 @@
+/* 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/>. */
+
+/* Thanks to Rob van Son for writing the original version of this
+   file.  This version has been completely rewritten; only the
+   name of the function has been retained.  In the process of
+   rewriting, it was sped up from O(2**N) to O(N**3). */
+
+#include <config.h>
+#include "wilcoxon-sig.h"
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include "xalloc.h"
+
+/* For integers N and W, with 0 <= N < CHAR_BIT*sizeof(long),
+   calculates and returns the value of the function S(N,W),
+   defined as the number of subsets of 1, 2, 3, ..., N that sum
+   to at least W.  There are 2**N subsets of N items, so S(N,W)
+   is in the range 0...2**N.
+
+   There are a few trivial cases:
+
+           * For W <= 0, S(N,W) = 2**N.
+
+           * For W > N*(N+1)/2, S(N,W) = 0.
+
+           * S(1,1) = 1.
+
+   Notably, these trivial cases include all values of W for N = 1.
+
+   Now consider the remaining, nontrivial cases, that is, N > 1 and
+   1 <= W <= N*(N+1)/2.  In this case, apply the following identity:
+
+           S(N,W) = S(N-1, W) + S(N-1, W-N).
+
+   The first term on the right hand is the number of subsets that do
+   not include N that sum to at least W; the second term is the
+   number of subsets that do include N that sum to at least W.
+
+   Then we repeatedly apply the identity to the result, reducing the
+   value of N by 1 each time until we reach N=1.  Some expansions
+   yield trivial cases, e.g. if W - N <= 0 (in which case we add a
+   2**N term to the final result) or if W is greater than the new N.
+
+   Here is an example:
+
+   S(7,7) = S(6,7) + S(6,0)
+          = S(6,7) + 64
+
+          = (S(5,7) + S(5,1)) + 64
+
+          = (S(4,7) + S(4,2)) + (S(4,1) + S(4,0)) + 64
+          = S(4,7) + S(4,2) + S(4,1) + 80
+
+          = (S(3,7) + S(3,3)) + (S(3,2) + S(3,2)) + (S(3,1) + S(3,0)) + 80
+          = S(3,3) + 2*S(3,2) + S(3,1) + 88
+
+          = (S(2,3) + S(2,0)) + 2*(S(2,2) + S(2,0)) + (S(2,1) + S(2,0)) + 88
+          = S(2,3) + 2*S(2,2) + S(2,1) + 104
+
+          = (S(1,3) + S(1,1)) + 2*(S(1,2) + S(1,0)) + (S(1,1) + S(2,0)) + 104
+          = 2*S(1,1) + 112
+
+          = 114
+
+   This function runs in O(N*W) = O(N**3) time.  It seems very
+   likely that it can be made to run in O(N**2) time or perhaps
+   even better, but N is, practically speaking, quite small.
+   Plus, the return value may be as large as N**2, so N must not
+   be larger than 31 on 32-bit systems anyhow, and even 63**3 is
+   only 250,047.
+*/
+static unsigned long int
+count_sums_to_W (unsigned long int n, unsigned long int w)
+{
+  /* The array contain ints even though everything else is long,
+     but no element in the array can have a value bigger than N,
+     and using int will save some memory on 64-bit systems. */
+  unsigned long int total;
+  unsigned long int max;
+  int *array;
+
+  assert (n < CHAR_BIT * sizeof (unsigned long int));
+  if (n == 0)
+    return 0;
+  else if (w <= 0)
+    return 1 << n;
+  else if (w > n * (n + 1) / 2)
+    return 0;
+  else if (n == 1)
+    return 1;
+
+  array = xcalloc (sizeof *array, w + 1);
+  array[w] = 1;
+
+  max = w;
+  total = 0;
+  for (; n > 1; n--)
+    {
+      unsigned long int max_sum = n * (n + 1) / 2;
+      int i;
+
+      if (max_sum < max)
+        max = max_sum;
+
+      for (i = 1; i <= max; i++)
+        if (array[i] != 0)
+          {
+            int new_w = i - n;
+            if (new_w <= 0)
+               total += array[i] * (1 << (n - 1));
+            else
+              array[new_w] += array[i];
+          }
+    }
+  total += array[1];
+  free (array);
+  return total;
+}
+
+/* Returns the exact, two-tailed level of significance for the
+   Wilcoxon Matched-Pairs Signed-Ranks test, given sum of ranks
+   of positive (or negative samples) W and sample size N.
+
+   Returns -1 if the exact significance level cannot be
+   calculated because W is out of the supported range. */
+double
+LevelOfSignificanceWXMPSR (double w, long int n)
+{
+  unsigned long int max_w;
+
+  /* Limit N to valid range that won't cause integer overflow. */
+  if (n < 0 || n >= CHAR_BIT * sizeof (unsigned long int))
+    return -1;
+
+  max_w = n * (n + 1) / 2;
+  if (w < max_w / 2)
+    w = max_w - w;
+
+  return count_sums_to_W (n, ceil (w)) / (double) (1 << n) * 2;
+}
diff --git a/src/math/wilcoxon-sig.h b/src/math/wilcoxon-sig.h
new file mode 100644 (file)
index 0000000..07eba55
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef WX_MP_SR
+#define WX_MP_SR 1
+
+double LevelOfSignificanceWXMPSR(double w, long int n);
+
+#endif
index d30acf63e50f2b870c05c019fb136e72428a9a1e..ec92c559e044723180977db31ca43708c7f637bd 100644 (file)
@@ -3,7 +3,7 @@
 
 include $(top_srcdir)/src/output/charts/automake.mk
 
-noinst_LIBRARIES += src/output/liboutput.
+noinst_LTLIBRARIES += src/output/liboutput.l
 
 output_sources = \
        src/output/afm.c \
@@ -23,11 +23,11 @@ output_sources = \
 
 
 if WITHCHARTS
-src_output_liboutput_a_SOURCES = $(output_sources) src/output/chart.c
+src_output_liboutput_la_SOURCES = $(output_sources) src/output/chart.c
 
 EXTRA_DIST += src/output/dummy-chart.c
 else
-src_output_liboutput_a_SOURCES = $(output_sources) src/output/dummy-chart.c
+src_output_liboutput_la_SOURCES = $(output_sources) src/output/dummy-chart.c
 
 EXTRA_DIST += src/output/chart.c
 endif
index a480de6da9bc384d040a9bd4dfe8b3ab8ea7adf4..ab0ff51047a4ffeb608770c3b088df8843077204 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-noinst_LIBRARIES += src/output/charts/libcharts.a
+noinst_LTLIBRARIES += src/output/charts/libcharts.la
 
 chart_sources = \
        src/output/charts/barchart.c \
@@ -17,12 +17,12 @@ chart_sources = \
        src/output/charts/plot-hist.h
 
 if WITHCHARTS
-src_output_charts_libcharts_a_SOURCES = \
+src_output_charts_libcharts_la_SOURCES = \
        $(chart_sources)
 
 EXTRA_DIST += src/output/charts/dummy-chart.c
 else
-src_output_charts_libcharts_a_SOURCES =  \
+src_output_charts_libcharts_la_SOURCES =  \
        src/output/charts/dummy-chart.c
 
 EXTRA_DIST += $(chart_sources)
index d4a5ccab6c2fff527e70e8ed4a55cc68f120a64e..c3641580e0511d66a0a293819c8b6f065a53f96d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008 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 <output/chart.h>
 #include <math/chart-geometry.h>
-#include <math/factor-stats.h>
-
-
-
+#include <math/box-whisker.h>
 
 /* Draw a box-and-whiskers plot
 */
 
-/* Draw an outlier on the plot CH
+/* Draw an OUTLIER on the plot CH
  * at CENTRELINE
- * The outlier is in (*wvp)[idx]
- * If EXTREME is non zero, then consider it to be an extreme
- * value
  */
-void
-draw_outlier(struct chart *ch, double centreline,
-            struct weighted_value **wvp,
-            int idx,
-            short extreme);
-
-
-void
-draw_outlier(struct chart *ch, double centreline,
-            struct weighted_value **wvp,
-            int idx,
-            short extreme
-            )
+static void
+draw_case (struct chart *ch, double centreline,
+          const struct outlier *outlier)
 {
-  char label[10];
 
 #define MARKER_CIRCLE 4
 #define MARKER_STAR 3
 
   pl_fmarker_r(ch->lp,
               centreline,
-              ch->data_bottom +
-              (wvp[idx]->v.f - ch->y_min ) * ch->ordinate_scale,
-              extreme?MARKER_STAR:MARKER_CIRCLE,
+              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);
 
-  snprintf(label, 10, "%d", wvp[idx]->case_nos->num);
-
-  pl_alabel_r(ch->lp, 'l', 'c', label);
-
+  pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
 }
 
 
 void
-boxplot_draw_boxplot(struct chart *ch,
-                    double box_centre,
-                    double box_width,
-                    struct metrics *m,
-                    const char *name)
+boxplot_draw_boxplot (struct chart *ch,
+                     double box_centre,
+                     double box_width,
+                     const struct box_whisker *bw,
+                     const char *name)
 {
   double whisker[2];
-  int i;
-
-  const double *hinge = m->hinge;
-  struct weighted_value **wvp = m->wvp;
-  const int n_data = m->n_data;
-
-  const double step = (hinge[2] - hinge[0]) * 1.5;
+  double hinge[3];
+  struct ll *ll;
 
+  const struct ll_list *outliers;
 
   const double box_left = box_centre - box_width / 2.0;
 
   const double box_right = box_centre + box_width / 2.0;
 
+  double box_bottom ;
+  double box_top ;
+  double bottom_whisker ;
+  double top_whisker ;
 
-  const double box_bottom =
-    ch->data_bottom + ( hinge[0] - ch->y_min ) * ch->ordinate_scale;
-
-
-  const double box_top =
-    ch->data_bottom + ( hinge[2] - ch->y_min ) * ch->ordinate_scale;
-
-  assert(m);
+  box_whisker_whiskers (bw, whisker);
+  box_whisker_hinges (bw, hinge);
 
-  /* Can't really draw a boxplot if there's no data */
-  if ( n_data == 0 )
-         return ;
+  box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
 
-  whisker[1] = hinge[2];
-  whisker[0] = wvp[0]->v.f;
+  box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
 
-  for ( i = 0 ; i < n_data ; ++i )
-    {
-      if ( hinge[2] + step >  wvp[i]->v.f)
-       whisker[1] = wvp[i]->v.f;
-
-      if ( hinge[0] - step >  wvp[i]->v.f)
-       whisker[0] = wvp[i]->v.f;
-
-    }
-
-  {
-  const double bottom_whisker =
-    ch->data_bottom + ( whisker[0] - ch->y_min ) * ch->ordinate_scale;
-
-  const double top_whisker =
-    ch->data_bottom + ( whisker[1] - ch->y_min ) * ch->ordinate_scale;
+  bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
+    ch->ordinate_scale;
 
+  top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
 
   pl_savestate_r(ch->lp);
 
-
   /* 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,
+  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);
-
-
+  pl_restorestate_r (ch->lp);
 
   /* Draw the median */
-  pl_savestate_r(ch->lp);
-  pl_linewidth_r(ch->lp,5);
-  pl_fline_r(ch->lp,
+  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,
+            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);
-
+            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
+  pl_restorestate_r (ch->lp);
 
   /* Draw the bottom whisker */
-  pl_fline_r(ch->lp,
+  pl_fline_r (ch->lp,
             box_left,
             bottom_whisker,
             box_right,
             bottom_whisker);
 
   /* Draw top whisker */
-  pl_fline_r(ch->lp,
+  pl_fline_r (ch->lp,
             box_left,
             top_whisker,
             box_right,
             top_whisker);
 
 
-
   /* Draw centre line.
      (bottom half) */
-  pl_fline_r(ch->lp,
+  pl_fline_r (ch->lp,
             box_centre, bottom_whisker,
             box_centre, box_bottom);
 
   /* (top half) */
-  pl_fline_r(ch->lp,
+  pl_fline_r (ch->lp,
             box_centre, top_whisker,
             box_centre, box_top);
-  }
 
-  /* Draw outliers */
-  for ( i = 0 ; i < n_data ; ++i )
+  outliers = box_whisker_outliers (bw);
+  for (ll = ll_head (outliers);
+       ll != ll_null (outliers); ll = ll_next (ll))
     {
-      if ( wvp[i]->v.f >= hinge[2] + step )
-       draw_outlier(ch, box_centre, wvp, i,
-                    ( wvp[i]->v.f > hinge[2] + 2 * step )
-                    );
-
-      if ( wvp[i]->v.f <= hinge[0] - step )
-       draw_outlier(ch, box_centre, wvp, i,
-                    ( wvp[i]->v.f < hinge[0] - 2 * step )
-                    );
+      const struct outlier *outlier = ll_data (ll, struct outlier, ll);
+      draw_case (ch, box_centre, outlier);
     }
 
-
   /* Draw  tick  mark on x axis */
   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
 
   pl_restorestate_r(ch->lp);
-
 }
 
-
-
 void
-boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
+boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
 {
   double y_tick;
   double d;
@@ -223,7 +164,7 @@ boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
   ch->y_max  = y_max;
   ch->y_min  = y_min;
 
-  y_tick = chart_rounded_tick(fabs(ch->y_max - ch->y_min) / 5.0);
+  y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
 
   ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
 
@@ -232,7 +173,6 @@ boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
   ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
     / fabs(ch->y_max - ch->y_min) ;
 
-
   /* Move to data bottom-left */
   pl_move_r(ch->lp,
            ch->data_left, ch->data_bottom);
@@ -241,5 +181,4 @@ boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
     {
       draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
     }
-
 }
index 656d8d49b3cb496277b0a8d49f386b1e6492d41c..7b2c4b8fdc1c2407c86cfe308a1c5ed4b72a31ba 100644 (file)
 #define BOX_WHISKER_H
 
 struct chart ;
-struct weighted_value;
-struct metrics;
+struct box_whisker;
 
-/* Draw an outlier on the plot CH
- * at CENTRELINE
- * The outlier is in (*wvp)[idx]
- * If EXTREME is non zero, then consider it to be an extreme
- * value
- */
-void  draw_outlier(struct chart *ch, double centreline,
-            struct weighted_value **wvp,
-            int idx,
-            short extreme);
+void boxplot_draw_boxplot (struct chart *ch,
+                          double box_centre,
+                          double box_width,
+                          const struct box_whisker *w,
+                          const char *name);
 
 
-void boxplot_draw_boxplot(struct chart *ch,
-                    double box_centre,
-                    double box_width,
-                    struct metrics *m,
-                    const char *name);
-
-
-void boxplot_draw_yscale(struct chart *ch , double y_max, double y_min);
+void boxplot_draw_yscale (struct chart *ch , double y_max, double y_min);
 
 #endif
index 9d4f722b2411ddb3da506459c756c5e46c18e30d..e22f958f7c8e81547c9b103d0de3b37628ce07ee 100644 (file)
 #endif
 
 void
-chart_write_title(struct chart *chart UNUSED, const char *title UNUSED, ...)
+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)
+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)
+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)
+chart_write_xlabel (struct chart *ch UNUSED, const char *label UNUSED)
 {
 }
 
 void
-chart_write_ylabel(struct chart *ch UNUSED, const char *label UNUSED)
+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)
+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)
+chart_datum (struct chart *ch UNUSED, int dataset UNUSED UNUSED,
+            double x UNUSED, double y UNUSED)
 {
 }
 
-struct normal_curve;
+void
+histogram_plot (const struct histogram *hist UNUSED,
+               const char *label UNUSED,
+               const struct moments1 *m UNUSED)
+{
+}
 
 void
-histogram_plot(const gsl_histogram *hist UNUSED,
-              const char *factorname UNUSED,
-              const struct normal_curve *norm UNUSED,
-               short show_normal UNUSED)
+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)
+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,
-                    struct metrics *m UNUSED,
-                    const char *name UNUSED)
+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)
+piechart_plot (const char *title UNUSED,
+              const struct slice *slices UNUSED, int n_slices UNUSED)
 {
 }
index 0f183208a2486ac5c4e07c4fd325b4bbf6a12d6d..4b11618a2f1a409fa3d92a9071a456131ce8485a 100644 (file)
@@ -1,10 +1,10 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   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.
+    (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
 #include <data/variable.h>
 #include <libpspp/hash.h>
 #include <output/chart.h>
+#include <math/histogram.h>
+#include <math/moments.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 /* Write the legend of the chart */
-void
-histogram_write_legend(struct chart *ch, const struct normal_curve *norm)
+static void
+histogram_write_legend (struct chart *ch, double n, double mean, double stddev)
 {
   char buf[100];
-  if ( !ch )
+
+  if (!ch)
     return ;
 
-  pl_savestate_r(ch->lp);
+  pl_savestate_r (ch->lp);
 
-  sprintf(buf,"N = %.2f",norm->N);
-  pl_move_r(ch->lp, ch->legend_left, ch->data_bottom);
-  pl_alabel_r(ch->lp,0,'b',buf);
+  sprintf (buf, "N = %.2f", n);
+  pl_move_r (ch->lp, ch->legend_left, ch->data_bottom);
+  pl_alabel_r (ch->lp, 0, 'b', buf);
 
-  sprintf(buf,"Mean = %.1f",norm->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);
+  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);
 
-  sprintf(buf,"Std. Dev = %.2f",norm->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);
+  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);
 
-  pl_restorestate_r(ch->lp);
+  pl_restorestate_r (ch->lp);
 }
 
-static void hist_draw_bar(struct chart *ch, const gsl_histogram *hist, int bar);
+static void hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar);
 
 
 static void
-hist_draw_bar(struct chart *ch, const gsl_histogram *hist, int bar)
+hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar)
 {
-  if ( !ch )
+  if (!ch)
     return ;
 
-
   {
     double upper;
     double lower;
     double height;
 
-    const size_t bins = gsl_histogram_bins(hist);
+    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 ( 0 == gsl_histogram_get_range(hist, bar, &lower, &upper));
-
-    assert( upper >= lower);
+    assert ( upper >= lower);
 
-    height = gsl_histogram_get(hist, bar) *
-      (ch->data_top - ch->data_bottom) / gsl_histogram_max_val(hist);
+    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_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,
+    pl_fboxrel_r (ch->lp,
                 x_pos, 0,
                 x_pos + width, height);
 
-    pl_restorestate_r(ch->lp);
+    pl_restorestate_r (ch->lp);
 
     {
       char buf[5];
-      snprintf(buf,5,"%g",(upper + lower) / 2.0);
-      draw_tick(ch, TICK_ABSCISSA,
+      snprintf (buf,5,"%g", (upper + lower) / 2.0);
+      draw_tick (ch, TICK_ABSCISSA,
                x_pos + width / 2.0, buf);
     }
   }
@@ -109,73 +110,87 @@ hist_draw_bar(struct chart *ch, const gsl_histogram *hist, int bar)
 
 
 
+void
+histogram_plot (const struct histogram *hist,
+               const char *label,
+               const struct moments1 *m)
+{
+  double mean, var, n;
+
+  moments1_calculate (m, &n, &mean, &var, NULL,  NULL);
+
+  histogram_plot_n (hist, label, n, mean, sqrt(var), m);
+}
+
 
+/* This function is deprecated.  Don't use it in new code */
 void
-histogram_plot(const gsl_histogram *hist,
-              const char *factorname,
-              const struct normal_curve *norm, short show_normal)
+histogram_plot_n (const struct histogram *hist,
+                 const char *label,
+                 double n, double mean, double stddev,
+                 bool show_normal)
 {
   int i;
   int bins;
 
-  struct chart *ch;
+  struct chart *ch = chart_create ();
 
-  ch = chart_create();
-  chart_write_title(ch, _("HISTOGRAM"));
+  chart_write_title (ch, _("HISTOGRAM"));
 
-  chart_write_ylabel(ch, _("Frequency"));
-  chart_write_xlabel(ch, factorname);
+  chart_write_ylabel (ch, _("Frequency"));
+  chart_write_xlabel (ch, label);
 
   if ( ! hist ) /* If this happens, probably all values are SYSMIS */
     {
-      chart_submit(ch);
-      return ;
+      chart_submit (ch);
+      return;
     }
   else
     {
-      bins = gsl_histogram_bins(hist);
+      bins = gsl_histogram_bins (hist->gsl_hist);
     }
 
-  chart_write_yscale(ch, 0, gsl_histogram_max_val(hist), 5);
+  chart_write_yscale (ch, 0, gsl_histogram_max_val (hist->gsl_hist), 5);
 
   for ( i = 0 ; i < bins ; ++i )
-      hist_draw_bar(ch, hist, i);
+    hist_draw_bar (ch, hist, i);
 
-  histogram_write_legend(ch, norm);
+  histogram_write_legend (ch, n, mean, stddev);
 
-  if ( show_normal  )
-  {
-    /* Draw the normal curve */
-
-    double d ;
-    double x_min, x_max, not_used ;
-    double abscissa_scale ;
-    double ordinate_scale ;
-    double range ;
-
-    gsl_histogram_get_range(hist, 0, &x_min, &not_used);
-    range = not_used - x_min;
-    gsl_histogram_get_range(hist, bins - 1, &not_used, &x_max);
-
-    abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
-    ordinate_scale = (ch->data_top - ch->data_bottom) /
-      gsl_histogram_max_val(hist) ;
-
-    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)
-      {
-       const double x = (d - ch->data_left) / abscissa_scale + x_min ;
-       const double y = norm->N * range *
-         gsl_ran_gaussian_pdf(x - norm->mean, norm->stddev);
-
-       pl_fcont_r(ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
-
-      }
-    pl_endpath_r(ch->lp);
+  if (show_normal)
+    {
+      /* Draw the normal curve */
+
+      double d ;
+      double x_min, x_max, not_used ;
+      double abscissa_scale ;
+      double ordinate_scale ;
+      double range ;
+
+      gsl_histogram_get_range (hist->gsl_hist, 0, &x_min, &not_used);
+      range = not_used - x_min;
+      gsl_histogram_get_range (hist->gsl_hist, bins - 1, &not_used, &x_max);
+
+      abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
+      ordinate_scale = (ch->data_top - ch->data_bottom) /
+       gsl_histogram_max_val (hist->gsl_hist) ;
+
+      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)
+       {
+         const double x = (d - ch->data_left) / abscissa_scale + x_min ;
+         const double y = n * range *
+           gsl_ran_gaussian_pdf (x - mean, stddev);
+
+         pl_fcont_r (ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
+
+       }
+      pl_endpath_r (ch->lp);
+    }
 
-  }
-  chart_submit(ch);
+  chart_submit (ch);
 }
 
+
index c808cb4e57fd6fe865cad87f5e9ca573f339b038..606206d5012c47d4e7b239e0a0230bf1788cea8e 100644 (file)
 #ifndef PLOT_HIST_H
 #define PLOT_HIST_H
 
-#include <gsl/gsl_histogram.h>
+#include <stdbool.h>
 
-
-struct normal_curve
-{
-  double N ;
-  double mean ;
-  double stddev ;
-};
 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);
 
-/* Write the legend of the chart */
-void histogram_write_legend(struct chart *ch, const struct normal_curve *norm);
 
-void histogram_plot(const gsl_histogram *hist,
-              const char *factorname,
-              const struct normal_curve *norm, short show_normal);
+/* 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
index 6d4ff9310060800e270b9a76d41cc329169848bd..7758ad709cb1a8f3bc7ff4c9c8f221fb805eefdd 100644 (file)
@@ -38,6 +38,6 @@ chart_init_separate (struct chart *ch UNUSED, const char *type UNUSED,
 }
 
 void
-chart_finalise_separate (struct chart *ch)
+chart_finalise_separate (struct chart *ch UNUSED)
 {
 }
index 044c293c5424509d760667979b7ad2fef357d5a0..c21b681d1f289f980e8bbcd274c2743726c73275 100644 (file)
@@ -6,12 +6,12 @@ include $(top_srcdir)/src/ui/gui/automake.mk
 endif
 
 
-noinst_LIBRARIES += src/ui/libuicommon.a
+noinst_LTLIBRARIES += src/ui/libuicommon.la
 
-src_ui_libuicommon_a_SOURCES = \
-       src/ui/debugger.c \
-       src/ui/debugger.h \
-       src/ui/syntax-gen.c \
-       src/ui/syntax-gen.h
+src_ui_libuicommon_la_SOURCES = \
+       src/ui/command-line.c src/ui/command-line.h \
+       src/ui/debugger.c src/ui/debugger.h \
+       src/ui/source-init-opts.c src/ui/source-init-opts.h \
+       src/ui/syntax-gen.c src/ui/syntax-gen.h
 
 EXTRA_DIST += src/ui/OChangeLog
diff --git a/src/ui/command-line.c b/src/ui/command-line.c
new file mode 100644 (file)
index 0000000..46dddd4
--- /dev/null
@@ -0,0 +1,166 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 "command-line.h"
+#include <argp.h>
+#include <gl/xalloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libpspp/compiler.h>
+#include <assert.h>
+
+
+struct clp_child
+{
+  void *aux;
+};
+
+struct command_line_processor
+{
+  struct argp master_parser;
+
+  struct clp_child *child_lookup_table;
+  struct argp_child *children;
+  int n_children;
+
+  const char *doc;
+  const char *args_doc;
+
+  void *aux;
+};
+
+
+/* Convenience function for use in parsing functions.
+   Returns the object for this parser */
+struct command_line_processor *
+get_subject (struct argp_state *state)
+{
+  const struct argp *root = state->root_argp;
+
+  const struct argp_child *children = root->children;
+
+  return  (struct command_line_processor *) children[0].argp;
+}
+
+
+/* Create a command line processor.
+   DOC is typically the name of the program and short description.
+   ARGS_DOC is a short description of the non option arguments.
+   AUX is an arbitrary pointer.
+ */
+struct command_line_processor *
+command_line_processor_create (const char *doc, const char *args_doc, void *aux)
+{
+  struct command_line_processor *clp = xzalloc (sizeof (*clp));
+
+  clp->children = NULL;
+  clp->child_lookup_table = NULL;
+
+  clp->doc = doc;
+  clp->args_doc = args_doc;
+  clp->aux = aux;
+
+  return clp;
+}
+
+/* Destroy a command line processor */
+void
+command_line_processor_destroy (struct command_line_processor *clp)
+{
+  free (clp->children);
+  free (clp->child_lookup_table);
+  free (clp);
+}
+
+
+/* Add a CHILD to the processor CLP, with the doc string DOC.
+   AUX is an auxilliary pointer, specific to CHILD.
+   If AUX is not known or not needed then it may be set to NULL
+*/
+void
+command_line_processor_add_options (struct command_line_processor *clp, const struct argp *child,
+                              const char *doc, void *aux)
+{
+  clp->n_children++;
+
+  clp->children = xrealloc (clp->children, (clp->n_children + 1) * sizeof (*clp->children));
+  memset (&clp->children[clp->n_children - 1], 0, sizeof (*clp->children));
+
+  clp->child_lookup_table = xrealloc (clp->child_lookup_table,
+                                     clp->n_children * sizeof (*clp->child_lookup_table));
+
+  clp->child_lookup_table [clp->n_children - 1].aux = aux;
+
+  clp->children [clp->n_children - 1].argp = child;
+  clp->children [clp->n_children - 1].header = doc;
+  clp->children [clp->n_children].argp = NULL;
+}
+
+
+/* Set the aux paramter for CHILD in CLP to AUX.
+   Any previous value will be overwritten.
+ */
+void
+command_line_processor_replace_aux (struct command_line_processor *clp, const struct argp *child, void *aux)
+{
+  int i;
+  for (i = 0 ; i < clp->n_children; ++i )
+    {
+      if (child->options == clp->children[i].argp->options)
+       {
+         clp->child_lookup_table[i].aux = aux;
+         break;
+       }
+    }
+  assert (i < clp->n_children);
+}
+
+
+static error_t
+top_level_parser (int key UNUSED, char *arg UNUSED, struct argp_state *state)
+{
+  int i;
+  struct command_line_processor *clp = state->input;
+
+  if ( key == ARGP_KEY_INIT)
+    {
+
+      for (i = 0;  i < clp->n_children ; ++i)
+       {
+         state->child_inputs[i] = clp->child_lookup_table[i].aux;
+       }
+    }
+
+  return ARGP_ERR_UNKNOWN;
+}
+
+
+/* Parse the command line specified by (ARGC, ARGV) using CLP */
+void
+command_line_processor_parse (struct command_line_processor *clp, int argc, char **argv)
+{
+  clp->master_parser.parser = top_level_parser;
+  clp->master_parser.args_doc = clp->args_doc;
+
+  clp->master_parser.doc = clp->doc;
+
+  clp->master_parser.children = clp->children;
+
+  argp_parse (&clp->master_parser, argc, argv, 0, 0, clp);
+}
+
diff --git a/src/ui/command-line.h b/src/ui/command-line.h
new file mode 100644 (file)
index 0000000..98edbea
--- /dev/null
@@ -0,0 +1,36 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 SRC_UI_COMMAND_LINE_H
+#define SRC_UI_COMMAND_LINE_H
+
+#include <argp.h>
+
+struct command_line_processor;
+
+struct command_line_processor * get_subject (struct argp_state *state);
+
+struct command_line_processor *command_line_processor_create (const char *, const char *, void *);
+
+void command_line_processor_add_options (struct command_line_processor *cla, const struct argp *child, const char *doc, void *aux);
+
+void command_line_processor_replace_aux (struct command_line_processor *cla, const struct argp *child, void *aux);
+
+void command_line_processor_destroy (struct command_line_processor *);
+
+void command_line_processor_parse (struct command_line_processor *, int argc, char **argv);
+
+#endif
index 5ecf6695b3231122bc77cb05fa19844907b67bfc..765b529f4382c7d190414dbe643899c76ceb7d34 100644 (file)
@@ -1 +1,2 @@
 psppire
+*.ui
index e743fcf61dbfa7b8128c277b7c59e32dd4c49e9c..c217833cbea5bf140f99c36cc2aa75106a8401b5 100644 (file)
@@ -18,7 +18,6 @@
 #include <config.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <libpspp/copyleft.h>
 #include <libpspp/version.h>
 #include "helper.h"
 
 
-const static gchar *artists[] = { "Patrick Brunier", "Dondi Bogusky", NULL};
+static const gchar *artists[] = { "Patrick Brunier", "Dondi Bogusky", NULL};
 
 void
 about_new (GtkMenuItem *m, GtkWindow *parent)
 {
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   GtkWidget *about =  get_widget_assert (xml, "aboutdialog1");
 
@@ -42,8 +41,7 @@ about_new (GtkMenuItem *m, GtkWindow *parent)
   gtk_about_dialog_set_logo (GTK_ABOUT_DIALOG (about), pb);
 
 
-  gtk_window_set_icon_from_file (GTK_WINDOW (about),
-                                relocate (PKGDATADIR "/psppicon.png"), 0);
+  gtk_window_set_icon_name (GTK_WINDOW (about), "psppicon");
 
   gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (about),
                                "http://www.gnu.org/software/pspp");
index 8dabffbf695e643da35d7e63c5dfcf390ea1b2ab..cd7c34e6cac90d8df925cbd1331a74e204c301dd 100644 (file)
@@ -1,9 +1,13 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
+include $(top_srcdir)/src/ui/gui/sheet/automake.mk
+
 bin_PROGRAMS += src/ui/gui/psppire 
 
-src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) $(GLADE_CFLAGS) -Wall \
-       -DINSTALLDIR=\"$(bindir)\"
+src_ui_gui_psppire_CFLAGS = $(GTK_CFLAGS) -Wall \
+       -DINSTALLDIR=\"$(bindir)\"  \
+       -DDOCDIR=\"$(docdir)\"  \
+       -DGDK_MULTIHEAD_SAFE=1
 
 
 src_ui_gui_psppire_LDFLAGS = \
@@ -11,7 +15,6 @@ src_ui_gui_psppire_LDFLAGS = \
        $(PG_LDFLAGS)
 
 
-
 if RELOCATABLE_VIA_LD
 src_ui_gui_psppire_LDFLAGS += `$(RELOCATABLE_LDFLAGS) $(bindir)`
 else
@@ -19,74 +22,62 @@ src_ui_gui_psppire_LDFLAGS += -rpath $(pkglibdir)
 endif
 
 
-# The library libpsppire contains a single function to register our custom widgets with libglade.
-# This library is dynamically loaded by libglade.   On w32 platforms, dynamic libraries simply 
-# can't be created unless all of the symbols can be resolved at link time.  Thus, all the custom 
-# widgets have to be available.  
-# But they can't appear in the library AND the binary, otherwise glib complains about them already
-# existing (and its a waste of space).  So we have a seperate shared library (statically loaded) 
-# libpsppwidgets which contains our custom widgets.
+src_ui_gui_psppire_LDADD = \
+        src/ui/gui/sheet/libsheet.la \
+       lib/gtk-contrib/libgtksheet.a \
+       src/ui/libuicommon.la \
+       src/libpspp.la \
+       src/libpspp-core.la \
+       $(GTK_LIBS) \
+       @LIBINTL@
 
-pkglib_LTLIBRARIES = src/ui/gui/libpsppwidgets.la src/ui/gui/libpsppire.la 
+src_ui_gui_psppiredir = $(pkgdatadir)
 
-src_ui_gui_libpsppwidgets_la_CFLAGS = $(GTK_CFLAGS)
-src_ui_gui_libpsppwidgets_la_LDFLAGS = -no-undefined
-src_ui_gui_libpsppwidgets_la_LIBADD = $(GTK_LIBS)
 
-src_ui_gui_libpsppwidgets_la_SOURCES = \
-       src/ui/gui/psppire-dialog.c \
-       src/ui/gui/psppire-keypad.c \
-       src/ui/gui/psppire-selector.c \
-       src/ui/gui/psppire-buttonbox.c \
-       src/ui/gui/psppire-hbuttonbox.c \
-       src/ui/gui/psppire-vbuttonbox.c \
-       src/ui/gui/psppire-acr.c 
+themedir = $(DESTDIR)$(datadir)/icons/hicolor
+context = apps
 
 
-src_ui_gui_libpsppire_la_CFLAGS = $(GLADE_CFLAGS) 
-src_ui_gui_libpsppire_la_LDFLAGS = -no-undefined
-src_ui_gui_libpsppire_la_LIBADD = $(GLADE_LIBS) src/ui/gui/libpsppwidgets.la
+install-icons:
+       for size in 16x16 ; do \
+         $(MKDIR_P) $(themedir)/$$size/$(context) ; \
+          $(INSTALL) $(top_srcdir)/src/ui/gui/psppicon.png $(themedir)/$$size/$(context) ; \
+       done 
+       gtk-update-icon-cache --ignore-theme-index $(themedir)
 
-src_ui_gui_libpsppire_la_SOURCES = \
-       src/ui/gui/glade-register.c
+INSTALL_DATA_HOOKS += install-icons
 
-src_ui_gui_psppire_LDADD = \
-       -dlopen src/ui/gui/libpsppire.la \
-       src/ui/gui/libpsppwidgets.la \
-       lib/gtksheet/libgtksheet.a \
-       src/language/liblanguage.a \
-       src/ui/libuicommon.a \
-       src/output/charts/libcharts.a \
-       src/output/liboutput.a \
-       src/math/libpspp_math.a  \
-       lib/linreg/liblinreg.a  \
-       src/data/libdata.a \
-       src/libpspp/libpspp.a \
-       $(GTK_LIBS) \
-       $(GLADE_LIBS) \
-       $(PG_LIBS) \
-       gl/libgl.la \
-       @LIBINTL@ @LIBREADLINE@
+uninstall-icons:
+       for size in 16x16 ; do \
+          $(RM) $(themedir)/$$size/$(context)/psppicon.png ; \
+       done 
+       gtk-update-icon-cache --ignore-theme-index $(themedir)
+
+UNINSTALL_DATA_HOOKS += uninstall-icons
+
+
+nodist_src_ui_gui_psppire_DATA = \
+       $(top_builddir)/src/ui/gui/crosstabs.ui \
+       $(top_builddir)/src/ui/gui/descriptives-dialog.ui \
+       $(top_builddir)/src/ui/gui/data-editor.ui \
+       $(top_builddir)/src/ui/gui/examine.ui \
+       $(top_builddir)/src/ui/gui/frequencies.ui \
+       $(top_builddir)/src/ui/gui/message-dialog.ui \
+       $(top_builddir)/src/ui/gui/psppire.ui \
+       $(top_builddir)/src/ui/gui/oneway.ui \
+       $(top_builddir)/src/ui/gui/output-viewer.ui \
+       $(top_builddir)/src/ui/gui/rank.ui \
+       $(top_builddir)/src/ui/gui/recode.ui \
+       $(top_builddir)/src/ui/gui/regression.ui \
+       $(top_builddir)/src/ui/gui/reliability.ui \
+       $(top_builddir)/src/ui/gui/syntax-editor.ui \
+       $(top_builddir)/src/ui/gui/text-data-import.ui \
+       $(top_builddir)/src/ui/gui/t-test.ui \
+       $(top_builddir)/src/ui/gui/var-sheet-dialogs.ui \
+       $(top_builddir)/src/ui/gui/variable-info-dialog.ui
 
-src_ui_gui_psppiredir = $(pkgdatadir)
 
 dist_src_ui_gui_psppire_DATA = \
-       $(top_srcdir)/src/ui/gui/data-editor.glade \
-       $(top_srcdir)/src/ui/gui/descriptives-dialog.glade \
-       $(top_srcdir)/src/ui/gui/examine.glade \
-       $(top_srcdir)/src/ui/gui/crosstabs.glade \
-       $(top_srcdir)/src/ui/gui/frequencies.glade \
-       $(top_srcdir)/src/ui/gui/message-dialog.glade \
-       $(top_srcdir)/src/ui/gui/oneway.glade \
-       $(top_srcdir)/src/ui/gui/output-viewer.glade \
-       $(top_srcdir)/src/ui/gui/psppire.glade \
-       $(top_srcdir)/src/ui/gui/rank.glade \
-       $(top_srcdir)/src/ui/gui/recode.glade \
-       $(top_srcdir)/src/ui/gui/regression.glade \
-       $(top_srcdir)/src/ui/gui/syntax-editor.glade \
-       $(top_srcdir)/src/ui/gui/text-data-import.glade \
-       $(top_srcdir)/src/ui/gui/t-test.glade \
-       $(top_srcdir)/src/ui/gui/psppicon.png \
        $(top_srcdir)/src/ui/gui/pspplogo.png \
        $(top_srcdir)/src/ui/gui/icons/value-labels.png \
        $(top_srcdir)/src/ui/gui/icons/goto-variable.png\
@@ -105,82 +96,97 @@ dist_src_ui_gui_psppire_DATA = \
 
 
 src_ui_gui_psppire_SOURCES = \
+       src/ui/gui/psppire-dialog.c \
+       src/ui/gui/psppire-keypad.c \
+       src/ui/gui/psppire-selector.c \
+       src/ui/gui/psppire-buttonbox.c \
+       src/ui/gui/psppire-hbuttonbox.c \
+       src/ui/gui/psppire-vbuttonbox.c \
+       src/ui/gui/psppire-acr.c \
        src/ui/gui/about.c \
        src/ui/gui/about.h \
        src/ui/gui/checkbox-treeview.c \
        src/ui/gui/checkbox-treeview.h \
-       src/ui/gui/compute-dialog.c \
-       src/ui/gui/compute-dialog.h \
        src/ui/gui/comments-dialog.c \
        src/ui/gui/comments-dialog.h \
+       src/ui/gui/compute-dialog.c \
+       src/ui/gui/compute-dialog.h \
        src/ui/gui/crosstabs-dialog.c \
        src/ui/gui/crosstabs-dialog.h \
        src/ui/gui/customentry.c \
        src/ui/gui/customentry.h \
-       src/ui/gui/frequencies-dialog.c \
-       src/ui/gui/frequencies-dialog.h \
-       src/ui/gui/goto-case-dialog.c \
-       src/ui/gui/goto-case-dialog.h \
-       src/ui/gui/data-editor.c \
-       src/ui/gui/data-editor.h \
        src/ui/gui/descriptives-dialog.c \
        src/ui/gui/descriptives-dialog.h \
+       src/ui/gui/dialog-common.c \
+       src/ui/gui/dialog-common.h \
+       src/ui/gui/dict-display.h \
+       src/ui/gui/dict-display.c \
        src/ui/gui/examine-dialog.c \
        src/ui/gui/examine-dialog.h \
        src/ui/gui/find-dialog.c \
        src/ui/gui/find-dialog.h \
-       src/ui/gui/dialog-common.c \
-       src/ui/gui/dialog-common.h \
-       src/ui/gui/dict-display.c \
-       src/ui/gui/dict-display.h \
+       src/ui/gui/frequencies-dialog.c \
+       src/ui/gui/frequencies-dialog.h \
+       src/ui/gui/goto-case-dialog.c \
+       src/ui/gui/goto-case-dialog.h \
+       src/ui/gui/helper.c \
+       src/ui/gui/helper.h \
        src/ui/gui/main.c \
        src/ui/gui/message-dialog.c \
        src/ui/gui/message-dialog.h \
-       src/ui/gui/psppire.c \
-       src/ui/gui/psppire.h \
-       src/ui/gui/helper.c \
-       src/ui/gui/helper.h \
        src/ui/gui/missing-val-dialog.c \
        src/ui/gui/missing-val-dialog.h \
         src/ui/gui/oneway-anova-dialog.c \
         src/ui/gui/oneway-anova-dialog.h \
-       src/ui/gui/output-viewer.c \
-       src/ui/gui/output-viewer.h \
+       src/ui/gui/psppire.c \
+       src/ui/gui/psppire.h \
        src/ui/gui/psppire-acr.h \
        src/ui/gui/psppire-buttonbox.h \
-       src/ui/gui/psppire-hbuttonbox.h \
-       src/ui/gui/psppire-vbuttonbox.h \
-       src/ui/gui/psppire-case-file.c \
-       src/ui/gui/psppire-case-file.h \
+       src/ui/gui/psppire-conf.c \
+       src/ui/gui/psppire-conf.h \
        src/ui/gui/psppire-data-editor.c \
        src/ui/gui/psppire-data-editor.h \
        src/ui/gui/psppire-data-store.c \
        src/ui/gui/psppire-data-store.h \
+       src/ui/gui/psppire-data-window.c \
+       src/ui/gui/psppire-data-window.h \
        src/ui/gui/psppire-dialog.h \
        src/ui/gui/psppire-dict.c \
        src/ui/gui/psppire-dict.h \
+       src/ui/gui/psppire-dictview.c \
+       src/ui/gui/psppire-dictview.h \
+       src/ui/gui/psppire-hbuttonbox.h \
        src/ui/gui/psppire-keypad.h \
+       src/ui/gui/psppire-output-window.c \
+       src/ui/gui/psppire-output-window.h \
        src/ui/gui/psppire-selector.h \
+       src/ui/gui/psppire-syntax-window.c \
+       src/ui/gui/psppire-syntax-window.h \
        src/ui/gui/psppire-var-ptr.c \
        src/ui/gui/psppire-var-ptr.h \
        src/ui/gui/psppire-var-sheet.c \
        src/ui/gui/psppire-var-sheet.h \
        src/ui/gui/psppire-var-store.c \
        src/ui/gui/psppire-var-store.h \
+       src/ui/gui/psppire-vbuttonbox.h \
+       src/ui/gui/psppire-window.c \
+       src/ui/gui/psppire-window.h \
+       src/ui/gui/psppire-window-register.c \
+       src/ui/gui/psppire-window-register.h \
        src/ui/gui/rank-dialog.c \
        src/ui/gui/rank-dialog.h \
        src/ui/gui/recode-dialog.c \
        src/ui/gui/recode-dialog.h \
        src/ui/gui/regression-dialog.c \
        src/ui/gui/regression-dialog.h \
+       src/ui/gui/reliability-dialog.c \
+       src/ui/gui/reliability-dialog.h \
        src/ui/gui/select-cases-dialog.c \
        src/ui/gui/select-cases-dialog.h \
        src/ui/gui/sort-cases-dialog.c \
        src/ui/gui/sort-cases-dialog.h \
        src/ui/gui/split-file-dialog.c \
        src/ui/gui/split-file-dialog.h \
-       src/ui/gui/syntax-editor.c \
-       src/ui/gui/syntax-editor.h \
        src/ui/gui/syntax-editor-source.c \
        src/ui/gui/syntax-editor-source.h \
        src/ui/gui/text-data-import-dialog.c \
@@ -199,16 +205,40 @@ src_ui_gui_psppire_SOURCES = \
        src/ui/gui/val-labs-dialog.h \
        src/ui/gui/var-display.c \
        src/ui/gui/var-display.h \
-       src/ui/gui/var-type-dialog.c \
-       src/ui/gui/var-type-dialog.h \
        src/ui/gui/variable-info-dialog.c \
        src/ui/gui/variable-info-dialog.h \
+       src/ui/gui/var-type-dialog.c \
+       src/ui/gui/var-type-dialog.h \
        src/ui/gui/weight-cases-dialog.c \
        src/ui/gui/weight-cases-dialog.h \
        src/ui/gui/widget-io.c \
        src/ui/gui/widget-io.h \
-       src/ui/gui/window-manager.c \
-       src/ui/gui/window-manager.h
+       src/ui/gui/widgets.c \
+       src/ui/gui/widgets.h \
+       src/ui/gui/crosstabs.glade \
+       src/ui/gui/descriptives-dialog.glade \
+       src/ui/gui/data-editor.glade \
+       src/ui/gui/examine.glade \
+       src/ui/gui/frequencies.glade \
+       src/ui/gui/message-dialog.glade \
+       src/ui/gui/psppire.glade \
+       src/ui/gui/oneway.glade \
+       src/ui/gui/output-viewer.glade \
+       src/ui/gui/rank.glade \
+       src/ui/gui/recode.glade \
+       src/ui/gui/regression.glade \
+       src/ui/gui/reliability.glade \
+       src/ui/gui/syntax-editor.glade \
+       src/ui/gui/text-data-import.glade \
+       src/ui/gui/t-test.glade \
+       src/ui/gui/var-sheet-dialogs.glade \
+       src/ui/gui/variable-info-dialog.glade
+
+
+nodist_src_ui_gui_psppire_SOURCES = \
+       src/ui/gui/psppire-marshal.c \
+       src/ui/gui/psppire-marshal.h
+
 
 yelp-check:
        @if ! yelp --version > /dev/null 2>&1 ; then \
@@ -219,6 +249,31 @@ yelp-check:
                echo '    Yelp is available from the GNOME project.  ftp://ftp.gnome.org/pub/gnome/sources/yelp' ; \
                echo ; \
        fi
+
 PHONY += yelp-check
 
-EXTRA_DIST += src/ui/gui/OChangeLog
+AM_CPPFLAGS += -Isrc
+
+src/ui/gui/psppire-marshal.c: src/ui/gui/marshaller-list
+       echo '#include <config.h>' > $@
+       glib-genmarshal --body --prefix=psppire_marshal $< >> $@
+
+src/ui/gui/psppire-marshal.h: src/ui/gui/marshaller-list
+       glib-genmarshal --header --prefix=psppire_marshal $< > $@
+
+.glade.ui:
+       $(top_srcdir)/lib/gtk-contrib/gtk-builder-convert $< $@
+
+desktopdir = $(datadir)/applications
+desktop_DATA = src/ui/gui/pspp.desktop
+
+EXTRA_DIST += src/ui/gui/OChangeLog\
+       src/ui/gui/psppicon.png \
+       src/ui/gui/marshaller-list \
+       $(desktop_DATA)
+
+BUILT_SOURCES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h
+CLEANFILES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h \
+       $(nodist_src_ui_gui_psppire_DATA)
+
+
index cf639e285b84950cfa6d3ba0611b0129429f2bd1..837fcc789550e13b0463dc28987ca5c34c967985 100644 (file)
@@ -85,9 +85,7 @@ treeview_checkbox_populate (GtkTreeView *treeview)
 
   gtk_tree_view_column_add_attribute  (col, renderer, "active", CHECKBOX_COLUMN_SELECTED);
 
-  g_signal_connect (GTK_CELL_RENDERER_TOGGLE (renderer),
-                    "toggled", G_CALLBACK (toggle), treeview);
-
+  g_signal_connect (renderer, "toggled", G_CALLBACK (toggle), treeview);
 
   /* Label column. */
   col = gtk_tree_view_column_new ();
index f1a4936e6de06e9df95abcff13966f09faa8705e..49d143cb6c51f379df11ad5172c789fada53a7f6 100644 (file)
 
 #include "psppire-dialog.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
+#include "psppire-data-editor.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 #include "psppire-var-store.h"
 #include <ui/syntax-gen.h>
 
@@ -29,7 +30,6 @@
 #include "dialog-common.h"
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <gettext.h>
 
@@ -39,7 +39,7 @@
 
 struct comment_dialog
 {
-  GladeXML *xml;
+  GtkBuilder *xml;
   PsppireDict *dict;
 };
 
@@ -95,10 +95,10 @@ comments_dialog (GObject *o, gpointer data)
 {
   GtkTextIter iter;
   gint response ;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
   struct comment_dialog cd;
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   GtkWidget *dialog = get_widget_assert (xml, "comments-dialog");
   GtkWidget *textview = get_widget_assert (xml, "comments-textview1");
@@ -109,7 +109,7 @@ comments_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   {
     PangoContext * context ;
@@ -167,6 +167,7 @@ comments_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&cd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -177,10 +178,7 @@ comments_dialog (GObject *o, gpointer data)
       {
        gchar *syntax = generate_syntax (&cd);
 
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+       paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index 5d40d21639bbe68fd9d06d6ea35dd9f50fba4a40..7703640c4d553e81eb6584f28eb46841a0c3fe21 100644 (file)
 #include "helper.h"
 #include "psppire-dialog.h"
 #include "psppire-keypad.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-var-store.h"
+#include "psppire-selector.h"
 #include "dialog-common.h"
-#include "dict-display.h"
 
 #include <language/expressions/public.h>
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 static void function_list_populate (GtkTreeView *tv);
 
@@ -47,7 +47,7 @@ static void insert_source_row_into_text_view (GtkTreeIter iter,
 
 struct compute_dialog
 {
-  GladeXML *xml;  /* The xml that generated the widgets */
+  GtkBuilder *xml;  /* The xml that generated the widgets */
   PsppireDict *dict;
   gboolean use_type;
 };
@@ -113,7 +113,7 @@ erase_selection (GtkTextBuffer *buffer)
 static void
 on_keypad_button (PsppireKeypad *kp, const gchar *syntax, gpointer data)
 {
-  GladeXML *xml = data;
+  GtkBuilder *xml = data;
 
   GtkWidget *rhs = get_widget_assert (xml, "compute-textview1");
 
@@ -137,7 +137,7 @@ on_keypad_button (PsppireKeypad *kp, const gchar *syntax, gpointer data)
 static void
 erase (PsppireKeypad *kp, gpointer data)
 {
-  GladeXML *xml = data;
+  GtkBuilder *xml = data;
 
   GtkWidget *rhs = get_widget_assert (xml, "compute-textview1");
 
@@ -365,12 +365,12 @@ void
 compute_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = data;
 
   PsppireVarStore *vs = NULL;
   struct compute_dialog scd;
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "compute-variable-dialog");
 
@@ -394,13 +394,12 @@ compute_dialog (GObject *o, gpointer data)
   g_signal_connect (expression, "toggled",
                    G_CALLBACK(on_expression_toggle), &scd);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
-                                vs->dict,
-                                GTK_SELECTION_SINGLE, NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
+  g_object_set (dict_view,
+               "dictionary", vs->dict,
+               "selection-mode", GTK_SELECTION_SINGLE,
+               NULL);
 
   psppire_selector_set_subjects (PSPPIRE_SELECTOR (var_selector),
                                 dict_view, syntax_area,
@@ -447,6 +446,7 @@ compute_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&scd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -457,10 +457,7 @@ compute_dialog (GObject *o, gpointer data)
       {
        gchar *syntax = generate_syntax (&scd);
 
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+       paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index 8f16b5f6c02867662a102b696fc38a20202cce99..3611865e15de360bf1fa8d5688e14bbcbf547a20 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void compute_dialog (GObject *o, gpointer data);
 
index edaa3e42a1f1a5c0185006feef0dfb8199f168f2..2c71ddef4f39bb4b4cf094fd812cffbea8def84e 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
-#include <ui/gui/helper.h>
+#include "helper.h"
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -385,13 +385,14 @@ void
 crosstabs_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
-
   struct crosstabs_dialog cd;
 
-  GladeXML *xml = XML_NEW ("crosstabs.glade");
+  GtkBuilder *xml = builder_new ("crosstabs.ui");
   PsppireVarStore *vs = NULL;
 
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+
+
   GtkWidget *dialog = get_widget_assert   (xml, "crosstabs-dialog");
   GtkWidget *source = get_widget_assert   (xml, "dict-treeview");
   GtkWidget *dest_rows =   get_widget_assert   (xml, "rows");
@@ -419,11 +420,9 @@ crosstabs_dialog (GObject *o, gpointer data)
                                  cells
                                  );
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (source, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (dest_rows), vs->dict);
   set_dest_model (GTK_TREE_VIEW (dest_cols), vs->dict);
@@ -461,9 +460,9 @@ crosstabs_dialog (GObject *o, gpointer data)
   cd.current_opts.table = TRUE;
   cd.current_opts.pivot = TRUE;
 
-  gtk_window_set_transient_for (GTK_WINDOW (cd.format_dialog), de->parent.window);
-  gtk_window_set_transient_for (GTK_WINDOW (cd.cell_dialog), de->parent.window);
-  gtk_window_set_transient_for (GTK_WINDOW (cd.stat_dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (cd.format_dialog), GTK_WINDOW (de));
+  gtk_window_set_transient_for (GTK_WINDOW (cd.cell_dialog), GTK_WINDOW (de));
+  gtk_window_set_transient_for (GTK_WINDOW (cd.stat_dialog), GTK_WINDOW (de));
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &cd);
 
@@ -485,6 +484,7 @@ crosstabs_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&cd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -495,10 +495,7 @@ crosstabs_dialog (GObject *o, gpointer data)
       {
        gchar *syntax = generate_syntax (&cd);
 
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+       paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index 33349b90ba71114336748d770772260214eb40d1..11da9814cfa462a37ab2960c5c16226f99b6b3ed 100644 (file)
@@ -19,7 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 
 void crosstabs_dialog (GObject *o, gpointer data);
index d5907a552946866010a334c13da5175148b0e65a..0409d9e512459125aa878386099f18c1202bafc8 100644 (file)
                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="dict-treeview">
+                  <widget class="PsppireDictView" id="dict-treeview">
                     <property name="visible">True</property>
                     <property name="headers_visible">False</property>
                   </widget>
diff --git a/src/ui/gui/data-editor.c b/src/ui/gui/data-editor.c
deleted file mode 100644 (file)
index f7df21c..0000000
+++ /dev/null
@@ -1,1619 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006, 2007, 2008  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
-   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 <stdlib.h>
-#include <gettext.h>
-
-#include <glade/glade.h>
-#include <gtk/gtk.h>
-
-#include "window-manager.h"
-
-#include "psppire-data-editor.h"
-
-#include "helper.h"
-#include "about.h"
-#include <data/procedure.h>
-#include "psppire-dialog.h"
-#include "psppire-selector.h"
-#include "weight-cases-dialog.h"
-#include "split-file-dialog.h"
-#include "transpose-dialog.h"
-#include "sort-cases-dialog.h"
-#include "select-cases-dialog.h"
-#include "compute-dialog.h"
-#include "goto-case-dialog.h"
-#include "find-dialog.h"
-#include "rank-dialog.h"
-#include "recode-dialog.h"
-#include "comments-dialog.h"
-#include "variable-info-dialog.h"
-#include "descriptives-dialog.h"
-#include "crosstabs-dialog.h"
-#include "frequencies-dialog.h"
-#include "examine-dialog.h"
-#include "dict-display.h"
-#include "regression-dialog.h"
-#include "text-data-import-dialog.h"
-
-#include "oneway-anova-dialog.h"
-#include "t-test-independent-samples-dialog.h"
-#include "t-test-one-sample.h"
-#include "t-test-paired-samples.h"
-
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-#include "data-editor.h"
-#include "syntax-editor.h"
-#include <language/syntax-string-source.h>
-#include <language/command.h>
-#include <ui/syntax-gen.h>
-#include "window-manager.h"
-
-#include "psppire-data-store.h"
-#include "psppire-var-store.h"
-
-static void on_edit_copy (GtkMenuItem *, gpointer);
-static void on_edit_cut (GtkMenuItem *, gpointer);
-static void on_edit_paste (GtkAction *a, gpointer data);
-
-
-static GtkWidget * create_data_sheet_variable_popup_menu (struct data_editor *);
-static GtkWidget * create_data_sheet_cases_popup_menu (struct data_editor *);
-
-static void register_data_editor_actions (struct data_editor *de);
-static void on_insert_variable (GtkAction *, gpointer data);
-static void insert_case (GtkAction *a, gpointer data);
-
-static void toggle_value_labels (GtkToggleAction *a, gpointer data);
-
-/* Callback for when the dictionary changes properties*/
-static void on_weight_change (GObject *, gint, gpointer);
-static void on_filter_change (GObject *, gint, gpointer);
-static void on_split_change (PsppireDict *, gpointer);
-
-static void on_switch_sheet (GtkNotebook *notebook,
-                           GtkNotebookPage *page,
-                           guint page_num,
-                           gpointer user_data);
-
-static void status_bar_activate (GtkCheckMenuItem *, gpointer);
-
-static void grid_lines_activate (GtkCheckMenuItem *, gpointer);
-
-static void data_view_activate (GtkCheckMenuItem *, gpointer);
-
-static void variable_view_activate (GtkCheckMenuItem *, gpointer );
-
-static void fonts_activate (GtkMenuItem *, gpointer);
-
-static void file_quit (GtkCheckMenuItem *, gpointer );
-
-static void
-enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
-{
-  struct data_editor *de = data;
-
-  gtk_action_set_visible (de->delete_cases, case_num != -1);
-}
-
-
-static void
-enable_delete_variables (GtkWidget *w, gint var, gpointer data)
-{
-  struct data_editor *de = data;
-
-  gtk_action_set_visible (de->delete_variables, var != -1);
-}
-
-
-
-/* Run the EXECUTE command. */
-static void
-execute (GtkMenuItem *mi, gpointer data)
-{
-  struct getl_interface *sss = create_syntax_string_source ("EXECUTE.");
-
-  execute_syntax (sss);
-}
-
-static void
-transformation_change_callback (bool transformations_pending,
-                               gpointer data)
-{
-  struct data_editor *de = data;
-  GtkWidget *menuitem =
-    get_widget_assert (de->xml, "transform_run-pending");
-  GtkWidget *status_label  =
-    get_widget_assert (de->xml, "case-counter-area");
-
-  gtk_widget_set_sensitive (menuitem, transformations_pending);
-
-
-  if ( transformations_pending)
-    gtk_label_set_text (GTK_LABEL (status_label),
-                       _("Transformations Pending"));
-  else
-    gtk_label_set_text (GTK_LABEL (status_label), "");
-}
-
-
-static void open_data_file (const gchar *, struct data_editor *);
-
-
-/* Puts FILE_NAME into the recent list.
-   If it's already in the list, it moves it to the top
-*/
-static void
-add_most_recent (const char *file_name)
-{
-#if RECENT_LISTS_AVAILABLE
-
-  GtkRecentManager *manager = gtk_recent_manager_get_default();
-  gchar *uri = g_filename_to_uri (file_name, NULL, NULL);
-
-  gtk_recent_manager_remove_item (manager, uri, NULL);
-
-  if ( ! gtk_recent_manager_add_item (manager, uri))
-    g_warning ("Could not add item %s to recent list\n",uri);
-
-  g_free (uri);
-#endif
-}
-
-
-
-#if RECENT_LISTS_AVAILABLE
-
-static void
-on_recent_data_select (GtkMenuShell *menushell,   gpointer user_data)
-{
-  gchar *file;
-  struct data_editor *de = user_data;
-
-  gchar *uri =
-    gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
-
-  file = g_filename_from_uri (uri, NULL, NULL);
-
-  g_free (uri);
-
-  open_data_file (file, de);
-
-  g_free (file);
-}
-
-static void
-on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
-{
-  gchar *file;
-
-  struct syntax_editor *se ;
-
-  gchar *uri =
-    gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
-
-  file = g_filename_from_uri (uri, NULL, NULL);
-
-  g_free (uri);
-
-  se = (struct syntax_editor *)
-    window_create (WINDOW_SYNTAX, file);
-
-  load_editor_from_file (se, file, NULL);
-
-  g_free (file);
-}
-
-#endif
-
-
-static void
-update_paste_menuitems (GtkWidget *w, gboolean x, gpointer data)
-{
-  struct data_editor *de = data;
-
-  GtkWidget * edit_paste = get_widget_assert (de->xml, "edit_paste");
-
-  gtk_widget_set_sensitive (edit_paste, x);
-}
-
-static void
-update_cut_copy_menuitems (GtkWidget *w, gboolean x, gpointer data)
-{
-  struct data_editor *de = data;
-
-  GtkWidget * edit_copy = get_widget_assert (de->xml, "edit_copy");
-  GtkWidget * edit_cut = get_widget_assert (de->xml, "edit_cut");
-
-  gtk_widget_set_sensitive (edit_copy, x);
-  gtk_widget_set_sensitive (edit_cut, x);
-}
-
-extern PsppireVarStore *the_var_store;
-extern struct dataset *the_dataset;
-extern PsppireDataStore *the_data_store ;
-
-
-/*
-  Create a new data editor.
-*/
-struct data_editor *
-new_data_editor (void)
-{
-  struct data_editor *de ;
-  struct editor_window *e;
-  PsppireVarStore *vs;
-  GtkWidget *vbox ;
-
-  de = g_malloc0 (sizeof (*de));
-
-  e = (struct editor_window *) de;
-
-  de->xml = XML_NEW ("data-editor.glade");
-
-
-  vbox = get_widget_assert (de->xml, "vbox1");
-
-  de->data_editor = PSPPIRE_DATA_EDITOR (psppire_data_editor_new (the_var_store, the_data_store));
-
-  g_signal_connect (de->data_editor, "data-selection-changed",
-                   G_CALLBACK (update_cut_copy_menuitems), de);
-
-  g_signal_connect (de->data_editor, "data-available-changed",
-                   G_CALLBACK (update_paste_menuitems), de);
-
-
-  gtk_widget_show (GTK_WIDGET (de->data_editor));
-
-  gtk_container_add (GTK_CONTAINER (vbox), GTK_WIDGET (de->data_editor));
-  gtk_box_reorder_child (GTK_BOX (vbox) , GTK_WIDGET (de->data_editor), 2);
-  dataset_add_transform_change_callback (the_dataset,
-                                        transformation_change_callback,
-                                        de);
-
-  vs = the_var_store;
-
-  g_assert(vs); /* Traps a possible bug in w32 build */
-
-  g_signal_connect (vs->dict, "weight-changed",
-                   G_CALLBACK (on_weight_change),
-                   de);
-
-  g_signal_connect (vs->dict, "filter-changed",
-                   G_CALLBACK (on_filter_change),
-                   de);
-
-  g_signal_connect (vs->dict, "split-changed",
-                   G_CALLBACK (on_split_change),
-                   de);
-
-  connect_help (de->xml);
-
-
-
-  g_signal_connect (get_widget_assert (de->xml, "edit_copy"),
-                   "activate",
-                   G_CALLBACK (on_edit_copy), de);
-
-  g_signal_connect (get_widget_assert (de->xml, "edit_cut"),
-                   "activate",
-                   G_CALLBACK (on_edit_cut), de);
-
-
-  register_data_editor_actions (de);
-
-  de->toggle_value_labels =
-    gtk_toggle_action_new ("toggle-value-labels",
-                          _("_Labels"),
-                          _("Show/hide value labels"),
-                          "pspp-value-labels");
-
-  g_signal_connect (de->toggle_value_labels, "toggled",
-                   G_CALLBACK (toggle_value_labels), de);
-
-
-  gtk_action_connect_proxy (GTK_ACTION (de->toggle_value_labels),
-                           get_widget_assert (de->xml,
-                                              "togglebutton-value-labels"));
-
-
-  gtk_action_connect_proxy (GTK_ACTION (de->toggle_value_labels),
-                           get_widget_assert (de->xml,
-                                              "view_value-labels"));
-
-  de->delete_cases =
-    gtk_action_new ("clear-cases",
-                   _("Clear"),
-                   _("Delete the cases at the selected position(s)"),
-                   "pspp-clear-cases");
-
-  g_signal_connect_swapped (de->delete_cases, "activate",
-                   G_CALLBACK (psppire_data_editor_delete_cases),
-                   de->data_editor);
-
-  gtk_action_connect_proxy (de->delete_cases,
-                           get_widget_assert (de->xml, "edit_clear-cases"));
-
-  g_signal_connect (get_widget_assert (de->xml, "edit_paste"), "activate",
-                   G_CALLBACK (on_edit_paste),
-                   de);
-
-  gtk_action_set_visible (de->delete_cases, FALSE);
-
-  de->delete_variables =
-    gtk_action_new ("clear-variables",
-                   _("Clear"),
-                   _("Delete the variables at the selected position(s)"),
-                   "pspp-clear-variables");
-
-  g_signal_connect_swapped (de->delete_variables, "activate",
-                           G_CALLBACK (psppire_data_editor_delete_variables),
-                           de->data_editor);
-
-  gtk_action_connect_proxy (de->delete_variables,
-                           get_widget_assert (de->xml, "edit_clear-variables")
-                           );
-
-  gtk_action_set_visible (de->delete_variables, FALSE);
-
-  de->insert_variable =
-    gtk_action_new ("insert-variable",
-                   _("Insert _Variable"),
-                   _("Create a new variable at the current position"),
-                   "pspp-insert-variable");
-
-  g_signal_connect (de->insert_variable, "activate",
-                   G_CALLBACK (on_insert_variable), de->data_editor);
-
-
-  gtk_action_connect_proxy (de->insert_variable,
-                           get_widget_assert (de->xml, "button-insert-variable")
-                           );
-
-  gtk_action_connect_proxy (de->insert_variable,
-                           get_widget_assert (de->xml, "edit_insert-variable")
-                           );
-
-
-  de->insert_case =
-    gtk_action_new ("insert-case",
-                   _("Insert Ca_se"),
-                   _("Create a new case at the current position"),
-                   "pspp-insert-case");
-
-  g_signal_connect (de->insert_case, "activate",
-                   G_CALLBACK (insert_case), de);
-
-
-  gtk_action_connect_proxy (de->insert_case,
-                           get_widget_assert (de->xml, "button-insert-case")
-                           );
-
-
-  gtk_action_connect_proxy (de->insert_case,
-                           get_widget_assert (de->xml, "edit_insert-case")
-                           );
-
-
-
-  de->invoke_goto_dialog =
-    gtk_action_new ("goto-case-dialog",
-                   _("_Goto Case"),
-                   _("Jump to a Case in the Data Sheet"),
-                   "gtk-jump-to");
-
-
-  gtk_action_connect_proxy (de->invoke_goto_dialog,
-                           get_widget_assert (de->xml, "button-goto-case")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_goto_dialog,
-                           get_widget_assert (de->xml, "edit_goto-case")
-                           );
-
-
-  g_signal_connect (de->invoke_goto_dialog, "activate",
-                   G_CALLBACK (goto_case_dialog), de);
-
-
-  de->invoke_weight_cases_dialog =
-    gtk_action_new ("weight-cases-dialog",
-                   _("_Weights"),
-                   _("Weight cases by variable"),
-                   "pspp-weight-cases");
-
-  g_signal_connect (de->invoke_weight_cases_dialog, "activate",
-                   G_CALLBACK (weight_cases_dialog), de);
-
-
-  de->invoke_transpose_dialog =
-    gtk_action_new ("transpose-dialog",
-                   _("_Transpose"),
-                   _("Transpose the cases with the variables"),
-                   NULL);
-
-
-  g_signal_connect (de->invoke_transpose_dialog, "activate",
-                   G_CALLBACK (transpose_dialog), de);
-
-
-
-  de->invoke_split_file_dialog =
-    gtk_action_new ("split-file-dialog",
-                   _("S_plit"),
-                   _("Split the active file"),
-                   "pspp-split-file");
-
-  g_signal_connect (de->invoke_split_file_dialog, "activate",
-                   G_CALLBACK (split_file_dialog), de);
-
-
-
-  de->invoke_sort_cases_dialog =
-    gtk_action_new ("sort-cases-dialog",
-                   _("_Sort"),
-                   _("Sort cases in the active file"),
-                   "pspp-sort-cases");
-
-  g_signal_connect (de->invoke_sort_cases_dialog, "activate",
-                   G_CALLBACK (sort_cases_dialog), de);
-
-  de->invoke_select_cases_dialog =
-    gtk_action_new ("select-cases-dialog",
-                   _("Select _Cases"),
-                   _("Select cases from the active file"),
-                   "pspp-select-cases");
-
-  g_signal_connect (de->invoke_select_cases_dialog, "activate",
-                   G_CALLBACK (select_cases_dialog), de);
-
-
-  de->invoke_compute_dialog =
-    gtk_action_new ("compute-dialog",
-                   _("_Compute"),
-                   _("Compute new values for a variable"),
-                   "pspp-compute");
-
-  g_signal_connect (de->invoke_compute_dialog, "activate",
-                   G_CALLBACK (compute_dialog), de);
-
-  de->invoke_oneway_anova_dialog =
-    gtk_action_new ("oneway-anova",
-                   _("Oneway _ANOVA"),
-                   _("Perform one way analysis of variance"),
-                   NULL);
-
-  g_signal_connect (de->invoke_oneway_anova_dialog, "activate",
-                   G_CALLBACK (oneway_anova_dialog), de);
-
-  de->invoke_t_test_independent_samples_dialog =
-    gtk_action_new ("t-test-independent-samples",
-                   _("_Independent Samples T Test"),
-                   _("Calculate T Test for samples from independent groups"),
-                   NULL);
-
-  g_signal_connect (de->invoke_t_test_independent_samples_dialog, "activate",
-                   G_CALLBACK (t_test_independent_samples_dialog), de);
-
-
-  de->invoke_t_test_paired_samples_dialog =
-    gtk_action_new ("t-test-paired-samples",
-                   _("_Paired Samples T Test"),
-                   _("Calculate T Test for paired samples"),
-                   NULL);
-
-  g_signal_connect (de->invoke_t_test_paired_samples_dialog, "activate",
-                   G_CALLBACK (t_test_paired_samples_dialog), de);
-
-
-  de->invoke_t_test_one_sample_dialog =
-    gtk_action_new ("t-test-one-sample",
-                   _("One _Sample T Test"),
-                   _("Calculate T Test for sample from a single distribution"),
-                   NULL);
-
-  g_signal_connect (de->invoke_t_test_one_sample_dialog, "activate",
-                   G_CALLBACK (t_test_one_sample_dialog), de);
-
-
-  de->invoke_comments_dialog =
-    gtk_action_new ("commments-dialog",
-                   _("Data File _Comments"),
-                   _("Commentary text for the data file"),
-                   NULL);
-
-  g_signal_connect (de->invoke_comments_dialog, "activate",
-                   G_CALLBACK (comments_dialog), de);
-
-  de->invoke_find_dialog  =
-    gtk_action_new ("find-dialog",
-                   _("_Find"),
-                   _("Find Case"),
-                   "gtk-find");
-
-  g_signal_connect (de->invoke_find_dialog, "activate",
-                   G_CALLBACK (find_dialog), de);
-
-
-  de->invoke_rank_dialog  =
-    gtk_action_new ("rank-dialog",
-                   _("Ran_k Cases"),
-                   _("Rank Cases"),
-                   "pspp-rank-cases");
-
-  g_signal_connect (de->invoke_rank_dialog, "activate",
-                   G_CALLBACK (rank_dialog), de);
-
-
-  de->invoke_recode_same_dialog  =
-    gtk_action_new ("recode-same-dialog",
-                   _("Recode into _Same Variables"),
-                   _("Recode values into the same Variables"),
-                   "pspp-recode-same");
-
-  g_signal_connect (de->invoke_recode_same_dialog, "activate",
-                   G_CALLBACK (recode_same_dialog), de);
-
-
-  de->invoke_recode_different_dialog  =
-    gtk_action_new ("recode-different-dialog",
-                   _("Recode into _Different Variables"),
-                   _("Recode values into different Variables"),
-                   "pspp-recode-different");
-
-  g_signal_connect (de->invoke_recode_different_dialog, "activate",
-                   G_CALLBACK (recode_different_dialog), de);
-
-
-  de->invoke_variable_info_dialog  =
-    gtk_action_new ("variable-info-dialog",
-                   _("_Variables"),
-                   _("Jump to Variable"),
-                   "pspp-goto-variable");
-
-  g_signal_connect (de->invoke_variable_info_dialog, "activate",
-                   G_CALLBACK (variable_info_dialog), de);
-
-  de->invoke_descriptives_dialog =
-    gtk_action_new ("descriptives-dialog",
-                   _("_Descriptives"),
-                   _("Calculate descriptive statistics (mean, variance, ...)"),
-                   "pspp-descriptives");
-
-  g_signal_connect (de->invoke_descriptives_dialog, "activate",
-                   G_CALLBACK (descriptives_dialog), de);
-
-
-  de->invoke_frequencies_dialog =
-    gtk_action_new ("frequencies-dialog",
-                   _("_Frequencies"),
-                   _("Generate frequency statistics"),
-                   "pspp-frequencies");
-
-  g_signal_connect (de->invoke_frequencies_dialog, "activate",
-                   G_CALLBACK (frequencies_dialog), de);
-
-  de->invoke_crosstabs_dialog =
-    gtk_action_new ("crosstabs-dialog",
-                   _("_Crosstabs"),
-                   _("Generate crosstabulations"),
-                   "pspp-crosstabs");
-
-  g_signal_connect (de->invoke_crosstabs_dialog, "activate",
-                   G_CALLBACK (crosstabs_dialog), de);
-
-
-  de->invoke_examine_dialog =
-    gtk_action_new ("examine-dialog",
-                   _("_Explore"),
-                   _("Examine Data by Factors"),
-                   "pspp-examine");
-
-  g_signal_connect (de->invoke_examine_dialog, "activate",
-                   G_CALLBACK (examine_dialog), de);
-
-
-  de->invoke_regression_dialog =
-    gtk_action_new ("regression-dialog",
-                   _("Linear _Regression"),
-                   _("Estimate parameters of the linear model"),
-                   "pspp-regression");
-
-  g_signal_connect (de->invoke_regression_dialog, "activate",
-                   G_CALLBACK (regression_dialog), de);
-
-  e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
-
-  g_signal_connect_swapped (get_widget_assert (de->xml,"file_new_data"),
-                           "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->action_data_new);
-
-  g_signal_connect_swapped (get_widget_assert (de->xml,"file_open_data"),
-                           "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->action_data_open);
-
-#if RECENT_LISTS_AVAILABLE
-  {
-    GtkRecentManager *rm = gtk_recent_manager_get_default ();
-    GtkWidget *recent_data = get_widget_assert (de->xml, "file_recent-data");
-    GtkWidget *recent_files = get_widget_assert (de->xml, "file_recent-files");
-    GtkWidget *recent_separator = get_widget_assert (de->xml, "file_separator1");
-
-    GtkWidget *menu = gtk_recent_chooser_menu_new_for_manager (rm);
-
-    GtkRecentFilter *filter = gtk_recent_filter_new ();
-
-    gtk_widget_show (recent_data);
-    gtk_widget_show (recent_files);
-    gtk_widget_show (recent_separator);
-
-    gtk_recent_filter_add_pattern (filter, "*.sav");
-    gtk_recent_filter_add_pattern (filter, "*.SAV");
-
-    gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
-
-    gtk_widget_set_sensitive (recent_data, TRUE);
-    g_signal_connect (menu, "selection-done",
-                     G_CALLBACK (on_recent_data_select), de);
-
-    gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu);
-
-
-    filter = gtk_recent_filter_new ();
-    menu = gtk_recent_chooser_menu_new_for_manager (rm);
-
-    gtk_recent_filter_add_pattern (filter, "*.sps");
-    gtk_recent_filter_add_pattern (filter, "*.SPS");
-
-    gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
-
-    gtk_widget_set_sensitive (recent_files, TRUE);
-    g_signal_connect (menu, "selection-done",
-                     G_CALLBACK (on_recent_files_select), de);
-
-    gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu);
-  }
-#endif
-
-  g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
-                   "activate",
-                   G_CALLBACK (new_syntax_window),
-                   e->window);
-
-  g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
-                   "activate",
-                   G_CALLBACK (open_syntax_window),
-                   e->window);
-
-  g_signal_connect_swapped (get_widget_assert (de->xml,"file_import-text"),
-                           "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->invoke_text_import_assistant);
-
-  g_signal_connect_swapped (get_widget_assert (de->xml,"file_save"),
-                           "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->action_data_save);
-
-  g_signal_connect_swapped (get_widget_assert (de->xml,"file_save_as"),
-                           "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->action_data_save_as);
-
-  gtk_action_connect_proxy (de->invoke_find_dialog,
-                           get_widget_assert (de->xml, "edit_find")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_find_dialog,
-                           get_widget_assert (de->xml, "button-find")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_rank_dialog,
-                           get_widget_assert (de->xml, "transform_rank")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_recode_same_dialog,
-                           get_widget_assert (de->xml,
-                                              "transform_recode-same")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_recode_different_dialog,
-                           get_widget_assert (de->xml,
-                                              "transform_recode-different")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
-                           get_widget_assert (de->xml, "data_weight-cases")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_transpose_dialog,
-                           get_widget_assert (de->xml, "data_transpose")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_split_file_dialog,
-                           get_widget_assert (de->xml, "data_split-file")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_sort_cases_dialog,
-                           get_widget_assert (de->xml, "data_sort-cases")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_select_cases_dialog,
-                           get_widget_assert (de->xml, "data_select-cases")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_compute_dialog,
-                           get_widget_assert (de->xml, "transform_compute")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_t_test_independent_samples_dialog,
-                           get_widget_assert (de->xml,
-                                              "indep-t-test")
-                           );
-
-
-  gtk_action_connect_proxy (de->invoke_t_test_paired_samples_dialog,
-                           get_widget_assert (de->xml,
-                                              "paired-t-test")
-                           );
-
-
-  gtk_action_connect_proxy (de->invoke_t_test_one_sample_dialog,
-                           get_widget_assert (de->xml,
-                                              "one-sample-t-test")
-                           );
-
-
-  gtk_action_connect_proxy (de->invoke_oneway_anova_dialog,
-                           get_widget_assert (de->xml,
-                                              "oneway-anova")
-                           );
-
-
-  gtk_action_connect_proxy (de->invoke_comments_dialog,
-                           get_widget_assert (de->xml, "utilities_comments")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_variable_info_dialog,
-                           get_widget_assert (de->xml, "utilities_variables")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_descriptives_dialog,
-                           get_widget_assert (de->xml, "analyze_descriptives")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_crosstabs_dialog,
-                           get_widget_assert (de->xml, "crosstabs")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_frequencies_dialog,
-                           get_widget_assert (de->xml, "analyze_frequencies")
-                           );
-
-
-  gtk_action_connect_proxy (de->invoke_examine_dialog,
-                           get_widget_assert (de->xml, "analyze_explore")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_regression_dialog,
-                           get_widget_assert (de->xml, "linear-regression")
-                           );
-
-  g_signal_connect (get_widget_assert (de->xml,"help_about"),
-                   "activate",
-                   G_CALLBACK (about_new),
-                   e->window);
-
-
-  g_signal_connect (get_widget_assert (de->xml,"help_reference"),
-                   "activate",
-                   G_CALLBACK (reference_manual),
-                   e->window);
-
-
-  g_signal_connect (de->data_editor,
-                   "cases-selected",
-                   G_CALLBACK (enable_delete_cases),
-                   de);
-
-
-  g_signal_connect (de->data_editor,
-                   "variables-selected",
-                   G_CALLBACK (enable_delete_variables),
-                   de);
-
-
-  g_signal_connect (GTK_NOTEBOOK (de->data_editor),
-                   "switch-page",
-                   G_CALLBACK (on_switch_sheet), de);
-
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
-
-  g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
-                   "activate",
-                   G_CALLBACK (status_bar_activate), de);
-
-
-  g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
-                   "activate",
-                   G_CALLBACK (grid_lines_activate), de);
-
-
-
-  g_signal_connect (get_widget_assert (de->xml, "view_data"),
-                   "activate",
-                   G_CALLBACK (data_view_activate), de);
-
-  g_signal_connect (get_widget_assert (de->xml, "view_variables"),
-                   "activate",
-                   G_CALLBACK (variable_view_activate), de);
-
-
-
-  g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
-                   "activate",
-                   G_CALLBACK (fonts_activate), de);
-
-
-
-
-  gtk_action_connect_proxy (de->action_data_open,
-                           get_widget_assert (de->xml, "button-open")
-                           );
-
-  gtk_action_connect_proxy (de->action_data_save,
-                           get_widget_assert (de->xml, "button-save")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_variable_info_dialog,
-                           get_widget_assert (de->xml, "button-goto-variable")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
-                           get_widget_assert (de->xml, "button-weight-cases")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_split_file_dialog,
-                           get_widget_assert (de->xml, "button-split-file")
-                           );
-
-  gtk_action_connect_proxy (de->invoke_select_cases_dialog,
-                           get_widget_assert (de->xml, "button-select-cases")
-                           );
-
-
-  g_signal_connect (get_widget_assert (de->xml, "file_quit"),
-                   "activate",
-                   G_CALLBACK (file_quit), de);
-
-  g_signal_connect (get_widget_assert (de->xml, "transform_run-pending"),
-                   "activate",
-                   G_CALLBACK (execute), de);
-
-
-  g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
-                   "activate",
-                   G_CALLBACK (minimise_all_windows), NULL);
-
-
-  de->data_sheet_variable_popup_menu =
-    GTK_MENU (create_data_sheet_variable_popup_menu (de));
-
-  de->data_sheet_cases_popup_menu =
-    GTK_MENU (create_data_sheet_cases_popup_menu (de));
-
-
-  g_object_set (de->data_editor,
-               "column-menu", de->data_sheet_variable_popup_menu, NULL);
-
-
-  g_object_set (de->data_editor,
-               "row-menu", de->data_sheet_cases_popup_menu, NULL);
-
-  return de;
-}
-
-
-void
-new_data_window (GtkMenuItem *menuitem, gpointer parent)
-{
-  window_create (WINDOW_DATA, NULL);
-}
-
-/* Callback for when the datasheet/varsheet is selected */
-static void
-on_switch_sheet (GtkNotebook *notebook,
-               GtkNotebookPage *page,
-               guint page_num,
-               gpointer user_data)
-{
-  struct data_editor *de = user_data;
-
-  GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
-  GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
-
-  switch (page_num)
-    {
-    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
-      gtk_widget_hide (view_variables);
-      gtk_widget_show (view_data);
-      gtk_action_set_sensitive (de->insert_variable, TRUE);
-      gtk_action_set_sensitive (de->insert_case, FALSE);
-      gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
-      break;
-    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
-      gtk_widget_show (view_variables);
-      gtk_widget_hide (view_data);
-      gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
-      gtk_action_set_sensitive (de->insert_case, TRUE);
-      break;
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-
-#if 0
-  update_paste_menuitem (de, page_num);
-#endif
-}
-
-
-static void
-status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
-{
-  struct data_editor *de = data;
-  GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
-
-  if ( gtk_check_menu_item_get_active (menuitem) )
-    gtk_widget_show (statusbar);
-  else
-    gtk_widget_hide (statusbar);
-}
-
-
-static void
-grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
-{
-  struct data_editor *de = data;
-  const gboolean grid_visible = gtk_check_menu_item_get_active (menuitem);
-
-  psppire_data_editor_show_grid (de->data_editor, grid_visible);
-}
-
-
-
-static void
-data_view_activate (GtkCheckMenuItem *menuitem, gpointer data)
-{
-  struct data_editor *de = data;
-
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
-}
-
-
-static void
-variable_view_activate (GtkCheckMenuItem *menuitem, gpointer data)
-{
-  struct data_editor *de = data;
-
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
-}
-
-
-static void
-fonts_activate (GtkMenuItem *menuitem, gpointer data)
-{
-  struct data_editor *de = data;
-  GtkWidget *dialog =
-    gtk_font_selection_dialog_new (_("Font Selection"));
-
-  gtk_window_set_transient_for (GTK_WINDOW (dialog),
-                               GTK_WINDOW (get_widget_assert (de->xml,
-                                                              "data_editor")));
-  if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
-    {
-      const gchar *font = gtk_font_selection_dialog_get_font_name
-       (GTK_FONT_SELECTION_DIALOG (dialog));
-
-      PangoFontDescription* font_desc =
-       pango_font_description_from_string (font);
-
-      psppire_data_editor_set_font (de->data_editor, font_desc);
-    }
-
-  gtk_widget_hide (dialog);
-}
-
-
-
-/* Callback for the value labels action */
-static void
-toggle_value_labels (GtkToggleAction *ta, gpointer data)
-{
-  struct data_editor *de = data;
-
-  g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
-}
-
-
-
-static void
-file_quit (GtkCheckMenuItem *menuitem, gpointer data)
-{
-  /* FIXME: Need to be more intelligent here.
-     Give the user the opportunity to save any unsaved data.
-  */
-  g_object_unref (the_data_store);
-  gtk_main_quit ();
-}
-
-
-static void
-insert_case (GtkAction *action, gpointer data)
-{
-  struct data_editor *de = data;
-
-  psppire_data_editor_insert_case (de->data_editor);
-}
-
-static void
-on_insert_variable (GtkAction *action, gpointer data)
-{
-  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
-  psppire_data_editor_insert_variable (de);
-}
-
-
-/* Callback for when the dictionary changes its split variables */
-static void
-on_split_change (PsppireDict *dict, gpointer data)
-{
-  struct data_editor *de = data;
-
-  size_t n_split_vars = dict_get_split_cnt (dict->dict);
-
-  GtkWidget *split_status_area =
-    get_widget_assert (de->xml, "split-file-status-area");
-
-  if ( n_split_vars == 0 )
-    {
-      gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
-    }
-  else
-    {
-      gint i;
-      GString *text;
-      const struct variable *const * split_vars =
-       dict_get_split_vars (dict->dict);
-
-      text = g_string_new (_("Split by "));
-
-      for (i = 0 ; i < n_split_vars - 1; ++i )
-       {
-         g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
-       }
-      g_string_append (text, var_get_name (split_vars[i]));
-
-      gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
-
-      g_string_free (text, TRUE);
-    }
-}
-
-
-/* Callback for when the dictionary changes its filter variable */
-static void
-on_filter_change (GObject *o, gint filter_index, gpointer data)
-{
-  struct data_editor *de = data;
-  GtkWidget *filter_status_area =
-    get_widget_assert (de->xml, "filter-use-status-area");
-
-  if ( filter_index == -1 )
-    {
-      gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
-    }
-  else
-    {
-      PsppireVarStore *vs = NULL;
-      struct variable *var ;
-      gchar *text ;
-
-      g_object_get (de->data_editor, "var-store", &vs, NULL);
-
-      var = psppire_dict_get_variable (vs->dict, filter_index);
-
-      text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
-
-      gtk_label_set_text (GTK_LABEL (filter_status_area), text);
-
-      g_free (text);
-    }
-}
-
-/* Callback for when the dictionary changes its weights */
-static void
-on_weight_change (GObject *o, gint weight_index, gpointer data)
-{
-  struct data_editor *de = data;
-  GtkWidget *weight_status_area =
-    get_widget_assert (de->xml, "weight-status-area");
-
-  if ( weight_index == -1 )
-    {
-      gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
-    }
-  else
-    {
-      struct variable *var ;
-      PsppireVarStore *vs = NULL;
-      gchar *text;
-
-      g_object_get (de->data_editor, "var-store", &vs, NULL);
-
-      var = psppire_dict_get_variable (vs->dict, weight_index);
-
-      text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
-
-      gtk_label_set_text (GTK_LABEL (weight_status_area), text);
-
-      g_free (text);
-    }
-}
-
-
-
-\f
-static void data_save_as_dialog (GtkAction *, struct data_editor *de);
-static void new_file (GtkAction *, struct editor_window *de);
-static void open_data_dialog (GtkAction *, struct data_editor *de);
-static void data_save (GtkAction *action, struct data_editor *e);
-
-
-/* Create the GtkActions and connect to their signals */
-static void
-register_data_editor_actions (struct data_editor *de)
-{
-  de->action_data_open =
-    gtk_action_new ("data-open-dialog",
-                   _("Open"),
-                   _("Open a data file"),
-                   "gtk-open");
-
-  g_signal_connect (de->action_data_open, "activate",
-                   G_CALLBACK (open_data_dialog), de);
-
-
-  de->action_data_save = gtk_action_new ("data-save",
-                                           _("Save"),
-                                           _("Save data to file"),
-                                           "gtk-save");
-
-  g_signal_connect (de->action_data_save, "activate",
-                   G_CALLBACK (data_save), de);
-
-
-
-  de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
-                                           _("Save As"),
-                                           _("Save data to file"),
-                                           "gtk-save");
-
-  g_signal_connect (de->action_data_save_as, "activate",
-                   G_CALLBACK (data_save_as_dialog), de);
-
-  de->action_data_new =
-    gtk_action_new ("data-new",
-                   _("New"),
-                   _("New data file"),
-                   NULL);
-
-  g_signal_connect (de->action_data_new, "activate",
-                   G_CALLBACK (new_file), de);
-
-  de->invoke_text_import_assistant =
-    gtk_action_new ("file_import-text",
-                   _("_Import Text Data"),
-                   _("Import text data file"),
-                   "");
-
-  g_signal_connect (de->invoke_text_import_assistant, "activate",
-                   G_CALLBACK (text_data_import_assistant), de);
-}
-
-/* Returns true if NAME has a suffix which might denote a PSPP file */
-static gboolean
-name_has_suffix (const gchar *name)
-{
-  if ( g_str_has_suffix (name, ".sav"))
-    return TRUE;
-  if ( g_str_has_suffix (name, ".SAV"))
-    return TRUE;
-  if ( g_str_has_suffix (name, ".por"))
-    return TRUE;
-  if ( g_str_has_suffix (name, ".POR"))
-    return TRUE;
-
-  return FALSE;
-}
-
-/* Append SUFFIX to the filename of DE */
-static void
-append_filename_suffix (struct data_editor *de, const gchar *suffix)
-{
-  if ( ! name_has_suffix (de->file_name))
-    {
-      gchar *s = de->file_name;
-      de->file_name = g_strconcat (de->file_name, suffix, NULL);
-      g_free (s);
-    }
-}
-
-/* Save DE to file */
-static void
-save_file (struct data_editor *de)
-{
-  struct getl_interface *sss;
-  struct string file_name ;
-
-  g_assert (de->file_name);
-
-  ds_init_empty (&file_name);
-  syntax_gen_string (&file_name, ss_cstr (de->file_name));
-
-  if ( de->save_as_portable )
-    {
-      append_filename_suffix (de, ".por");
-      sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
-                                        ds_cstr (&file_name));
-    }
-  else
-    {
-      append_filename_suffix (de, ".sav");
-      sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
-                                        ds_cstr (&file_name));
-    }
-
-  ds_destroy (&file_name);
-
-  execute_syntax (sss);
-}
-
-
-/* Callback for data_save action.
-   If there's an existing file name, then just save,
-   otherwise prompt for a file name, then save */
-static void
-data_save (GtkAction *action, struct data_editor *de)
-{
-  if ( de->file_name)
-    save_file (de);
-  else
-    data_save_as_dialog (action, de);
-}
-
-
-/* Callback for data_save_as action. Prompt for a filename and save */
-static void
-data_save_as_dialog (GtkAction *action, struct data_editor *de)
-{
-  struct editor_window *e = (struct editor_window *) de;
-
-  GtkWidget *button_sys;
-  GtkWidget *dialog =
-    gtk_file_chooser_dialog_new (_("Save"),
-                                GTK_WINDOW (e->window),
-                                GTK_FILE_CHOOSER_ACTION_SAVE,
-                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-                                NULL);
-
-  GtkFileFilter *filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
-  gtk_file_filter_add_pattern (filter, "*.sav");
-  gtk_file_filter_add_pattern (filter, "*.SAV");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
-  gtk_file_filter_add_pattern (filter, "*.por");
-  gtk_file_filter_add_pattern (filter, "*.POR");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("All Files"));
-  gtk_file_filter_add_pattern (filter, "*");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  {
-    GtkWidget *button_por;
-    GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
-    button_sys =
-      gtk_radio_button_new_with_label (NULL, _("System File"));
-
-    button_por =
-      gtk_radio_button_new_with_label
-      (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
-       _("Portable File"));
-
-    gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
-    gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
-
-    gtk_widget_show_all (vbox);
-
-    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
-  }
-
-  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
-    {
-    case GTK_RESPONSE_ACCEPT:
-      {
-       g_free (de->file_name);
-
-       de->file_name =
-         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-
-       de->save_as_portable =
-         ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
-
-       if ( de->save_as_portable)
-         append_filename_suffix (de, ".por");
-       else
-         append_filename_suffix (de, ".sav");
-
-       save_file (de);
-
-       window_set_name_from_filename (e, de->file_name);
-      }
-      break;
-    default:
-      break;
-    }
-
-  gtk_widget_destroy (dialog);
-}
-
-
-/* Callback for data_new action.
-   Performs the NEW FILE command */
-static void
-new_file (GtkAction *action, struct editor_window *e)
-{
-  struct data_editor *de = (struct data_editor *) e;
-
-  struct getl_interface *sss =
-    create_syntax_string_source ("NEW FILE.");
-
-  execute_syntax (sss);
-
-  g_free (de->file_name);
-  de->file_name = NULL;
-
-  default_window_name (e);
-}
-
-
-static void
-open_data_file (const gchar *file_name, struct data_editor *de)
-{
-  struct getl_interface *sss;
-  struct string filename;
-
-  ds_init_empty (&filename);
-  syntax_gen_string (&filename, ss_cstr (file_name));
-
-  sss = create_syntax_string_source ("GET FILE=%s.",
-                                    ds_cstr (&filename));
-  ds_destroy (&filename);
-
-  if (execute_syntax (sss) )
-  {
-    window_set_name_from_filename ((struct editor_window *) de, file_name);
-    add_most_recent (file_name);
-  }
-}
-
-
-
-/* Callback for the data_open action.
-   Prompts for a filename and opens it */
-static void
-open_data_dialog (GtkAction *action, struct data_editor *de)
-{
-  struct editor_window *e = (struct editor_window *) de;
-
-  GtkWidget *dialog =
-    gtk_file_chooser_dialog_new (_("Open"),
-                                GTK_WINDOW (e->window),
-                                GTK_FILE_CHOOSER_ACTION_OPEN,
-                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                NULL);
-
-  GtkFileFilter *filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
-  gtk_file_filter_add_pattern (filter, "*.sav");
-  gtk_file_filter_add_pattern (filter, "*.SAV");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
-  gtk_file_filter_add_pattern (filter, "*.por");
-  gtk_file_filter_add_pattern (filter, "*.POR");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("All Files"));
-  gtk_file_filter_add_pattern (filter, "*");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-
-  if ( de->file_name)
-    {
-      gchar *dir_name = g_path_get_dirname (de->file_name);
-      gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
-                                          dir_name);
-      free (dir_name);
-    }
-
-  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
-    {
-    case GTK_RESPONSE_ACCEPT:
-      {
-       g_free (de->file_name);
-       de->file_name =
-         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-
-       open_data_file (de->file_name, de);
-      }
-      break;
-    default:
-      break;
-    }
-
-  gtk_widget_destroy (dialog);
-}
-
-
-static GtkWidget *
-create_data_sheet_variable_popup_menu (struct data_editor *de)
-{
-  GtkWidget *menu = gtk_menu_new ();
-
-  GtkWidget *sort_ascending =
-    gtk_menu_item_new_with_label (_("Sort Ascending"));
-
-  GtkWidget *sort_descending =
-    gtk_menu_item_new_with_label (_("Sort Descending"));
-
-  GtkWidget *insert_variable =
-    gtk_menu_item_new_with_label (_("Insert Variable"));
-
-  GtkWidget *clear_variable =
-    gtk_menu_item_new_with_label (_("Clear"));
-
-
-  gtk_action_connect_proxy (de->delete_variables,
-                           clear_variable );
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable);
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
-                        gtk_separator_menu_item_new ());
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_variable);
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
-                        gtk_separator_menu_item_new ());
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_ascending);
-
-
-  g_signal_connect_swapped (G_OBJECT (sort_ascending), "activate",
-                           G_CALLBACK (psppire_data_editor_sort_ascending),
-                           de->data_editor);
-
-  g_signal_connect_swapped (G_OBJECT (sort_descending), "activate",
-                           G_CALLBACK (psppire_data_editor_sort_descending),
-                           de->data_editor);
-
-  g_signal_connect_swapped (G_OBJECT (insert_variable), "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->insert_variable);
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_descending);
-
-  gtk_widget_show_all (menu);
-
-  return menu;
-}
-
-
-static GtkWidget *
-create_data_sheet_cases_popup_menu (struct data_editor *de)
-{
-  GtkWidget *menu = gtk_menu_new ();
-
-  GtkWidget *insert_case =
-    gtk_menu_item_new_with_label (_("Insert Case"));
-
-  GtkWidget *delete_case =
-    gtk_menu_item_new_with_label (_("Clear"));
-
-
-  gtk_action_connect_proxy (de->delete_cases,
-                           delete_case);
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_case);
-
-  g_signal_connect_swapped (G_OBJECT (insert_case), "activate",
-                           G_CALLBACK (gtk_action_activate),
-                           de->insert_case);
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
-                        gtk_separator_menu_item_new ());
-
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_case);
-
-
-  gtk_widget_show_all (menu);
-
-  return menu;
-}
-
-
-\f
-
-static void
-on_edit_paste (GtkAction *a, gpointer data)
-{
-  struct data_editor *de = data;
-
-  psppire_data_editor_clip_paste (de->data_editor);
-}
-
-static void
-on_edit_copy (GtkMenuItem *m, gpointer data)
-{
-  struct data_editor *de = data;
-
-  psppire_data_editor_clip_copy (de->data_editor);
-}
-
-
-
-static void
-on_edit_cut (GtkMenuItem *m, gpointer data)
-{
-  struct data_editor *de = data;
-
-  psppire_data_editor_clip_cut (de->data_editor);
-}
index cdf6080e2d2aef9c98da2fff7bd6ad67755a4828..06776248738df3cbea663edeca39e2934b910037 100644 (file)
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
 <!--*- mode: xml -*-->
 <glade-interface>
-  <widget class="GtkWindow" id="data_editor">
-    <property name="title">Psppire Data Editor</property>
-    <property name="default_width">975</property>
-    <property name="default_height">480</property>
+  <widget class="GtkMenuBar" id="menubar">
+    <property name="visible">True</property>
     <child>
-      <widget class="GtkVBox" id="vbox1">
+      <widget class="GtkMenuItem" id="file">
         <property name="visible">True</property>
+        <property name="label" translatable="yes">_File</property>
+        <property name="use_underline">True</property>
         <child>
-          <widget class="GtkMenuBar" id="menubar">
-            <property name="visible">True</property>
+          <widget class="GtkMenu" id="menuitem1_menu">
             <child>
-              <widget class="GtkMenuItem" id="file">
+              <widget class="GtkImageMenuItem" id="new1">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_File</property>
+                <property name="label">gtk-new</property>
                 <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
                 <child>
-                  <widget class="GtkMenu" id="menuitem1_menu">
-                    <child>
-                      <widget class="GtkImageMenuItem" id="new1">
-                        <property name="visible">True</property>
-                        <property name="label">gtk-new</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <child>
-                          <widget class="GtkMenu" id="new1_menu">
-                            <child>
-                              <widget class="GtkMenuItem" id="file_new_syntax">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">_Syntax</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="file_new_data">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">_Data</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="open1">
-                        <property name="visible">True</property>
-                        <property name="label">gtk-open</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <child>
-                          <widget class="GtkMenu" id="open1_menu">
-                            <child>
-                              <widget class="GtkMenuItem" id="file_open_syntax">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">_Syntax</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="file_open_data">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">_Data</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="file_import-text">
-                        <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">_Import Delimited Text Data</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="file_separator1">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="file_save">
-                        <property name="visible">True</property>
-                        <property name="label">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="visible">True</property>
-                        <property name="label">gtk-save-as</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
+                  <widget class="GtkMenu" id="new1_menu">
                     <child>
-                      <widget class="GtkSeparatorMenuItem" id="file_separator2">
+                      <widget class="GtkMenuItem" id="file_new_syntax">
                         <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="file_recent-data">
-                        <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">Recently Used Da_ta</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="file_recent-files">
-                        <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">Recently Used _Files</property>
+                        <property name="label" translatable="yes">_Syntax</property>
                         <property name="use_underline">True</property>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkSeparatorMenuItem" id="file_separator3">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="file_quit">
+                      <widget class="GtkMenuItem" id="file_new_data">
                         <property name="visible">True</property>
-                        <property name="label">gtk-quit</property>
+                        <property name="label" translatable="yes">_Data</property>
                         <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
                       </widget>
                     </child>
                   </widget>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="edit">
+              <widget class="GtkImageMenuItem" id="open1">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_Edit</property>
+                <property name="label">gtk-open</property>
                 <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
                 <child>
-                  <widget class="GtkMenu" id="edit_menu">
-                    <child>
-                      <widget class="GtkMenuItem" id="edit_insert-variable">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">Insert Variable</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="edit_insert-case">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">Insert Cases</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="edit_goto-case">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">Go To Case</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator4">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="edit_cut">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label">gtk-cut</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="edit_copy">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label">gtk-copy</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="edit_paste">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label">gtk-paste</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="edit_clear-variables">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">Cl_ear Variables</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
+                  <widget class="GtkMenu" id="open1_menu">
                     <child>
-                      <widget class="GtkMenuItem" id="edit_clear-cases">
+                      <widget class="GtkMenuItem" id="file_open_syntax">
                         <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">_Clear Cases</property>
+                        <property name="label" translatable="yes">_Syntax</property>
                         <property name="use_underline">True</property>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator6">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="edit_find">
+                      <widget class="GtkMenuItem" id="file_open_data">
                         <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">_Find</property>
+                        <property name="label" translatable="yes">_Data</property>
                         <property name="use_underline">True</property>
                       </widget>
                     </child>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="view">
+              <widget class="GtkMenuItem" id="file_import-text">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_View</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">_Import Delimited Text Data</property>
                 <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menuitem3_menu">
-                    <child>
-                      <widget class="GtkCheckMenuItem" id="view_statusbar">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Status Bar</property>
-                        <property name="use_underline">True</property>
-                        <property name="active">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator1">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="view_fonts">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Fonts</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckMenuItem" id="view_gridlines">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Grid Lines</property>
-                        <property name="use_underline">True</property>
-                        <property name="active">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckMenuItem" id="view_value-labels">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Value _Labels</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator3">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="view_data">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Data</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="view_variables">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Variables</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="data">
+              <widget class="GtkSeparatorMenuItem" id="file_separator1">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_Data</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="data_menu">
-                    <child>
-                      <widget class="GtkMenuItem" id="data_sort-cases">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Sort Cases</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="data_transpose">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">_Transpose</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator5">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="data_split-file">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">S_plit File</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="data_select-cases">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">Select _Cases</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="data_weight-cases">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Weight Cases</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="transform">
+              <widget class="GtkImageMenuItem" id="file_save">
                 <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">_Transform</property>
+                <property name="label">gtk-save</property>
                 <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu2">
-                    <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>
-                    <child>
-                      <widget class="GtkMenuItem" id="transform_compute">
-                        <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">_Compute</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="transform_rank">
-                        <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">Ran_k Cases</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator8">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="transform_recode-same">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Recode into _Same Variables</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="transform_recode-different">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Recode into _Different Variables</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separator7">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="transform_run-pending">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                        <property name="label" translatable="yes">_Run Pending Transforms</property>
-                        <property name="use_underline">True</property>
-                        <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
+                <property name="use_stock">True</property>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="analyze">
+              <widget class="GtkImageMenuItem" id="file_save_as">
                 <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">_Analyze</property>
+                <property name="label">gtk-save-as</property>
                 <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu3">
-                    <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>
-                    <child>
-                      <widget class="GtkMenuItem" id="descriptive-statistics">
-                        <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">_Descriptive Statistics</property>
-                        <property name="use_underline">True</property>
-                        <child>
-                          <widget class="GtkMenu" id="menu5">
-                            <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>
-                            <child>
-                              <widget class="GtkMenuItem" id="analyze_frequencies">
-                                <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">_Frequencies</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="analyze_descriptives">
-                                <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">_Descriptives</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="analyze_explore">
-                                <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">_Explore</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="crosstabs">
-                                <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">_Crosstabs</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="compare-means">
-                        <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">Compare _Means</property>
-                        <property name="use_underline">True</property>
-                        <child>
-                          <widget class="GtkMenu" id="menu6">
-                            <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>
-                            <child>
-                              <widget class="GtkMenuItem" id="one-sample-t-test">
-                                <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">_One Sample T Test</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="indep-t-test">
-                                <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">_Independent Samples T Test</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="paired-t-test">
-                                <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">_Paired Samples T Test</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="oneway-anova">
-                                <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">One Way _ANOVA</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="bivariate-correlation">
-                        <property name="visible">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">Bivariate _Correlation</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="linear-regression">
-                        <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">Linear _Regression</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="non-parametrics">
-                        <property name="visible">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">_Non-Parametric Statistics</property>
-                        <property name="use_underline">True</property>
-                        <child>
-                          <widget class="GtkMenu" id="menu4">
-                            <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>
-                            <child>
-                              <widget class="GtkMenuItem" id="chi-square">
-                                <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">_Chi-Square</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkMenuItem" id="binomial">
-                                <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">_Binomial</property>
-                                <property name="use_underline">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
+                <property name="use_stock">True</property>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="utilities">
+              <widget class="GtkSeparatorMenuItem" id="file_separator2">
                 <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">_Utilities</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="file-information">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">D_isplay Data File Information</property>
                 <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkMenu" id="menu1">
+                  <widget class="GtkMenu" id="file-info-menu">
                     <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="tearoff_state">True</property>
                     <child>
-                      <widget class="GtkMenuItem" id="utilities_variables">
+                      <widget class="GtkMenuItem" id="file_information_working-file">
                         <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">_Variables</property>
+                        <property name="label" translatable="yes">Working File</property>
                         <property name="use_underline">True</property>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkMenuItem" id="utilities_comments">
+                      <widget class="GtkMenuItem" id="file_information_external-file">
                         <property name="visible">True</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">Data File _Comments</property>
+                        <property name="label" translatable="yes">External File</property>
                         <property name="use_underline">True</property>
                       </widget>
                     </child>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="windows">
+              <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="file_recent-data">
+                <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">Recently Used Da_ta</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="file_recent-files">
+                <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">Recently Used _Files</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="file_separator3">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_Windows</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="Windows_menu">
-                    <child>
-                      <widget class="GtkMenuItem" id="windows_minimise_all">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Minimize All Windows</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="help">
+              <widget class="GtkImageMenuItem" id="file_quit">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_Help</property>
+                <property name="label">gtk-quit</property>
                 <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menuitem5_menu">
-                    <child>
-                      <widget class="GtkMenuItem" id="help_reference">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_Reference Manual</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkMenuItem" id="help_about">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">_About</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
+                <property name="use_stock">True</property>
               </widget>
             </child>
           </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-          </packing>
         </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="edit">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">_Edit</property>
+        <property name="use_underline">True</property>
         <child>
-          <widget class="GtkHandleBox" id="handlebox1">
-            <property name="visible">True</property>
-            <property name="shadow_type">GTK_SHADOW_OUT</property>
+          <widget class="GtkMenu" id="edit_menu">
             <child>
-              <widget class="GtkToolbar" id="toolbar1">
+              <widget class="GtkMenuItem" id="edit_insert-variable">
                 <property name="visible">True</property>
-                <child>
-                  <widget class="GtkToolButton" id="button-open">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Open</property>
-                    <property name="stock_id">gtk-open</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-save">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Save</property>
-                    <property name="stock_id">gtk-save</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-print">
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Print</property>
-                    <property name="stock_id">gtk-print</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-recent">
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Recall</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSeparatorToolItem" id="separatortoolitem1">
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-undo">
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Undo</property>
-                    <property name="stock_id">gtk-undo</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-redo">
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Redo</property>
-                    <property name="stock_id">gtk-redo</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSeparatorToolItem" id="separatortoolitem2">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-goto-case">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Go To Case</property>
-                    <property name="stock_id">gtk-jump-to</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-goto-variable">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Variables</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSeparatorToolItem" id="separatortoolitem5">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-find">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Find</property>
-                    <property name="stock_id">gtk-find</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSeparatorToolItem" id="separatortoolitem4">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-insert-case">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Insert Case</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-insert-variable">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Insert Variable</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSeparatorToolItem" id="separatortoolitem6">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-split-file">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Split File</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-weight-cases">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Weight Cases</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-select-cases">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Select Cases</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkSeparatorToolItem" id="separatortoolitem7">
-                    <property name="visible">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToggleToolButton" id="togglebutton-value-labels">
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Insert Variable</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="edit_insert-case">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Insert Cases</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_goto-case">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Go To Case</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator4">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_cut">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label">gtk-cut</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_copy">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label">gtk-copy</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_paste">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label">gtk-paste</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="edit_clear-variables">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Cl_ear Variables</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="edit_clear-cases">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">_Clear Cases</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator6">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_find">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">gtk-find</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="view">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">_View</property>
+        <property name="use_underline">True</property>
+        <child>
+          <widget class="GtkMenu" id="menuitem3_menu">
+            <child>
+              <widget class="GtkCheckMenuItem" id="view_statusbar">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Status Bar</property>
+                <property name="use_underline">True</property>
+                <property name="active">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator1">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="view_fonts">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Fonts</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkCheckMenuItem" id="view_gridlines">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Grid Lines</property>
+                <property name="use_underline">True</property>
+                <property name="active">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkCheckMenuItem" id="view_value-labels">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Value _Labels</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator3">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="view_data">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Data</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="view_variables">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Variables</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="data">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">_Data</property>
+        <property name="use_underline">True</property>
+        <child>
+          <widget class="GtkMenu" id="data_menu">
+            <child>
+              <widget class="GtkImageMenuItem" id="data_sort-cases">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Sort Cases</property>
+                <property name="use_underline">True</property>
+                <child internal-child="image">
+                  <widget class="GtkImage" id="menu-item-image1">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Value Labels</property>
-                    <property name="use_underline">True</property>
-                    <property name="stock_id">gtk-missing-image</property>
+                    <property name="stock">gtk-sort-ascending</property>
                   </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkToolButton" id="button-use-sets">
-                    <property name="sensitive">False</property>
-                    <property name="tooltip" translatable="yes">Use Sets</property>
-                    <property name="use_underline">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                  </packing>
                 </child>
               </widget>
             </child>
+            <child>
+              <widget class="GtkMenuItem" id="data_transpose">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">_Transpose</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator5">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="data_split-file">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">S_plit File</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="data_select-cases">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Select _Cases</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="data_weight-cases">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Weight Cases</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="transform">
+        <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">_Transform</property>
+        <property name="use_underline">True</property>
+        <child>
+          <widget class="GtkMenu" id="menu2">
+            <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>
+            <child>
+              <widget class="GtkMenuItem" id="transform_compute">
+                <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">_Compute</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="transform_rank">
+                <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">Ran_k Cases</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator8">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="transform_recode-same">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Recode into _Same Variables</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="transform_recode-different">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Recode into _Different Variables</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkSeparatorMenuItem" id="separator7">
+                <property name="visible">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="transform_run-pending">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">_Run Pending Transforms</property>
+                <property name="use_underline">True</property>
+                <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+              </widget>
+            </child>
           </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
-          <placeholder/>
         </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="analyze">
+        <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">_Analyze</property>
+        <property name="use_underline">True</property>
         <child>
-          <widget class="GtkHBox" id="status-bar">
+          <widget class="GtkMenu" id="menu3">
             <property name="visible">True</property>
-            <property name="border_width">5</property>
-            <property name="spacing">5</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="GtkFrame" id="frame2">
+              <widget class="GtkMenuItem" id="descriptive-statistics">
                 <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</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">_Descriptive Statistics</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkEventBox" id="eventbox1">
+                  <widget class="GtkMenu" id="menu5">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Information Area</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="analyze_frequencies">
+                        <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">_Frequencies</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                    </child>
                     <child>
-                      <widget class="GtkLabel" id="information-area">
+                      <widget class="GtkMenuItem" id="analyze_descriptives">
                         <property name="visible">True</property>
-                        <property name="single_line_mode">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">_Descriptives</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkMenuItem" id="analyze_explore">
+                        <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">_Explore</property>
+                        <property name="use_underline">True</property>
                       </widget>
                     </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkFrame" id="frame3">
-                <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkEventBox" id="eventbox2">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Processor Area</property>
                     <child>
-                      <widget class="GtkLabel" id="processor-area">
+                      <widget class="GtkMenuItem" id="crosstabs">
                         <property name="visible">True</property>
-                        <property name="width_chars">35</property>
-                        <property name="single_line_mode">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">_Crosstabs</property>
+                        <property name="use_underline">True</property>
                       </widget>
                     </child>
                   </widget>
                 </child>
               </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
             </child>
             <child>
-              <widget class="GtkFrame" id="frame5">
+              <widget class="GtkMenuItem" id="compare-means">
                 <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</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">Compare _Means</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkEventBox" id="eventbox4">
+                  <widget class="GtkMenu" id="menu6">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Case Counter Area</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="one-sample-t-test">
+                        <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">_One Sample T Test</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkMenuItem" id="indep-t-test">
+                        <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">_Independent Samples T Test</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkMenuItem" id="paired-t-test">
+                        <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">_Paired Samples T Test</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                    </child>
                     <child>
-                      <widget class="GtkLabel" id="case-counter-area">
+                      <widget class="GtkMenuItem" id="oneway-anova">
                         <property name="visible">True</property>
-                        <property name="width_chars">20</property>
-                        <property name="single_line_mode">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">One Way _ANOVA</property>
+                        <property name="use_underline">True</property>
                       </widget>
                     </child>
                   </widget>
                 </child>
               </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">3</property>
-              </packing>
             </child>
             <child>
-              <widget class="GtkFrame" id="frame6">
+              <widget class="GtkMenuItem" id="reliability">
+                <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">Re_liability</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="linear-regression">
                 <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</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">Linear _Regression</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="non-parametrics">
+                <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">_Non-Parametric Statistics</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkEventBox" id="eventbox5">
+                  <widget class="GtkMenu" id="menu4">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Filter Use Status Area</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="chi-square">
+                        <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">_Chi-Square</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                    </child>
                     <child>
-                      <widget class="GtkLabel" id="filter-use-status-area">
+                      <widget class="GtkMenuItem" id="binomial">
                         <property name="visible">True</property>
-                        <property name="ellipsize">PANGO_ELLIPSIZE_START</property>
-                        <property name="width_chars">10</property>
-                        <property name="single_line_mode">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">_Binomial</property>
+                        <property name="use_underline">True</property>
                       </widget>
                     </child>
                   </widget>
                 </child>
               </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">4</property>
-              </packing>
             </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="utilities">
+        <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">_Utilities</property>
+        <property name="use_underline">True</property>
+        <child>
+          <widget class="GtkMenu" id="menu1">
+            <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>
             <child>
-              <widget class="GtkFrame" id="frame7">
+              <widget class="GtkMenuItem" id="utilities_variables">
                 <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkEventBox" id="eventbox6">
+                <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">_Variables</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="utilities_comments">
+                <property name="visible">True</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">Data File _Comments</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="windows">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">_Windows</property>
+        <property name="use_underline">True</property>
+        <child>
+          <widget class="GtkMenu" id="Windows_menu">
+            <child>
+              <widget class="GtkMenuItem" id="windows_minimise_all">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Minimize All Windows</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkCheckMenuItem" id="windows_split">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Split</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkMenuItem" id="help">
+        <property name="visible">True</property>
+        <property name="label" translatable="yes">_Help</property>
+        <property name="use_underline">True</property>
+        <child>
+          <widget class="GtkMenu" id="menuitem5_menu">
+            <child>
+              <widget class="GtkImageMenuItem" id="help_reference">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Reference Manual</property>
+                <property name="use_underline">True</property>
+                <child internal-child="image">
+                  <widget class="GtkImage" id="menu-item-image2">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Weight Status Area</property>
-                    <child>
-                      <widget class="GtkLabel" id="weight-status-area">
-                        <property name="visible">True</property>
-                        <property name="ellipsize">PANGO_ELLIPSIZE_START</property>
-                        <property name="width_chars">15</property>
-                        <property name="single_line_mode">True</property>
-                      </widget>
-                    </child>
+                    <property name="stock">gtk-help</property>
                   </widget>
                 </child>
               </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">5</property>
-              </packing>
             </child>
             <child>
-              <widget class="GtkFrame" id="frame8">
+              <widget class="GtkImageMenuItem" id="help_about">
                 <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkEventBox" id="eventbox7">
+                <property name="label" translatable="yes">_About</property>
+                <property name="use_underline">True</property>
+                <child internal-child="image">
+                  <widget class="GtkImage" id="menu-item-image3">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Split File Status Area</property>
-                    <child>
-                      <widget class="GtkLabel" id="split-file-status-area">
-                        <property name="visible">True</property>
-                        <property name="ellipsize">PANGO_ELLIPSIZE_START</property>
-                        <property name="width_chars">15</property>
-                        <property name="single_line_mode">True</property>
-                      </widget>
-                    </child>
+                    <property name="stock">gtk-about</property>
                   </widget>
                 </child>
               </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">6</property>
-              </packing>
             </child>
           </widget>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkHandleBox" id="handlebox1">
+    <property name="visible">True</property>
+    <property name="shadow_type">GTK_SHADOW_OUT</property>
+    <child>
+      <widget class="GtkToolbar" id="toolbar1">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkToolButton" id="button-open">
+            <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Open</property>
+            <property name="stock_id">gtk-open</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-save">
+            <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Save</property>
+            <property name="stock_id">gtk-save</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-print">
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Print</property>
+            <property name="stock_id">gtk-print</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-recent">
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Recall</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem1">
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="homogeneous">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-undo">
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Undo</property>
+            <property name="stock_id">gtk-undo</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-redo">
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Redo</property>
+            <property name="stock_id">gtk-redo</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem2">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="homogeneous">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-goto-case">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Go To Case</property>
+            <property name="stock_id">gtk-jump-to</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-goto-variable">
+            <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Variables</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem5">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="homogeneous">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-find">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Find</property>
+            <property name="stock_id">gtk-find</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem4">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="homogeneous">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-insert-case">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Insert Case</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-insert-variable">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Insert Variable</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem6">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="homogeneous">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-split-file">
+            <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Split File</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-weight-cases">
+            <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Weight Cases</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
           <packing>
             <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">4</property>
           </packing>
         </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkWindow" id="var_type_dialog">
-    <property name="border_width">6</property>
-    <property name="title" translatable="yes">Variable Type</property>
-    <property name="resizable">False</property>
-    <property name="modal">True</property>
-    <property name="default_width">485</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="skip_pager_hint">True</property>
-    <child>
-      <widget class="GtkHBox" id="hbox1">
-        <property name="visible">True</property>
-        <property name="border_width">5</property>
-        <property name="spacing">5</property>
         <child>
-          <widget class="GtkVBox" id="vbox2">
+          <widget class="GtkToolButton" id="button-select-cases">
             <property name="visible">True</property>
-            <property name="border_width">13</property>
-            <property name="homogeneous">True</property>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Numeric</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="active">True</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton2">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Comma</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton3">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Dot</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton4">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Scientific notation</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton5">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Date</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton6">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Dollar</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton7">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">Custom currency</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">6</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton8">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">String</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">radiobutton1</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">7</property>
-              </packing>
-            </child>
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Select Cases</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
           </widget>
           <packing>
             <property name="expand">False</property>
-            <property name="fill">False</property>
           </packing>
         </child>
         <child>
-          <widget class="GtkVBox" id="middle_box">
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem7">
             <property name="visible">True</property>
-            <property name="spacing">10</property>
-            <child>
-              <widget class="GtkScrolledWindow" id="scrolledwindow4">
-                <property name="width_request">20</property>
-                <property name="height_request">194</property>
-                <property name="can_focus">True</property>
-                <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkTreeView" id="date_format_list_view">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="headers_visible">False</property>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkHBox" id="custom_currency_hbox">
-                <property name="spacing">15</property>
-                <child>
-                  <widget class="GtkScrolledWindow" id="scrolledwindow5">
-                    <property name="width_request">1</property>
-                    <property name="height_request">120</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
-                    <property name="shadow_type">GTK_SHADOW_IN</property>
-                    <child>
-                      <widget class="GtkTreeView" id="custom_treeview">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="headers_visible">False</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkFrame" id="Sample">
-                    <property name="visible">True</property>
-                    <property name="label_xalign">0</property>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment2">
-                        <property name="visible">True</property>
-                        <property name="left_padding">12</property>
-                        <child>
-                          <widget class="GtkVBox" id="vbox10">
-                            <property name="visible">True</property>
-                            <property name="homogeneous">True</property>
-                            <child>
-                              <widget class="GtkLabel" id="psample_label">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">positive</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkLabel" id="nsample_label">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">negative</property>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label13">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Sample</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="pack_type">GTK_PACK_END</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkScrolledWindow" id="dollar_window">
-                <property name="can_focus">True</property>
-                <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkTreeView" id="dollar_treeview">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="headers_visible">False</property>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="width_decimals">
-                <property name="width_request">100</property>
-                <property name="height_request">50</property>
-                <property name="visible">True</property>
-                <property name="n_rows">2</property>
-                <property name="n_columns">2</property>
-                <property name="column_spacing">2</property>
-                <property name="row_spacing">1</property>
-                <child>
-                  <widget class="GtkHBox" id="hbox2">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkLabel" id="width_label">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Width:</property>
-                        <property name="justify">GTK_JUSTIFY_RIGHT</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="pack_type">GTK_PACK_END</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options">GTK_FILL</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="decimals_entry">
-                    <property name="width_request">25</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="width_entry">
-                    <property name="width_request">25</property>
-                    <property name="can_focus">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="decimals_label">
-                    <property name="visible">True</property>
-                    <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Decimal Places:</property>
-                    <property name="justify">GTK_JUSTIFY_RIGHT</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options">GTK_FILL</property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">3</property>
-              </packing>
-            </child>
           </widget>
           <packing>
-            <property name="fill">False</property>
-            <property name="position">1</property>
+            <property name="expand">False</property>
+            <property name="homogeneous">False</property>
           </packing>
         </child>
         <child>
-          <widget class="GtkVButtonBox" id="vbuttonbox6">
+          <widget class="GtkToggleToolButton" id="togglebutton-value-labels">
             <property name="visible">True</property>
-            <property name="spacing">5</property>
-            <property name="layout_style">GTK_BUTTONBOX_START</property>
-            <child>
-              <widget class="GtkButton" id="var_type_ok">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-ok</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkButton" id="var_type_cancel">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-cancel</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="help_button_variable_type">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-help</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
+            <property name="tooltip" translatable="yes">Value Labels</property>
+            <property name="use_underline">True</property>
+            <property name="stock_id">gtk-missing-image</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkToolButton" id="button-use-sets">
+            <property name="sensitive">False</property>
+            <property name="tooltip" translatable="yes">Use Sets</property>
+            <property name="use_underline">True</property>
           </widget>
           <packing>
-            <property name="position">2</property>
+            <property name="expand">False</property>
           </packing>
         </child>
       </widget>
     </child>
   </widget>
-  <widget class="GtkWindow" id="val_labs_dialog">
-    <property name="title" translatable="yes">Value Labels</property>
-    <property name="resizable">False</property>
-    <property name="modal">True</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="skip_pager_hint">True</property>
+  <widget class="GtkHBox" id="status-bar">
+    <property name="visible">True</property>
+    <property name="spacing">6</property>
     <child>
-      <widget class="GtkHBox" id="hbox3">
+      <widget class="GtkFrame" id="frame2">
         <property name="visible">True</property>
-        <property name="border_width">5</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
         <child>
-          <widget class="GtkFrame" id="frame1">
+          <widget class="GtkEventBox" id="eventbox1">
             <property name="visible">True</property>
-            <property name="label_xalign">0</property>
-            <child>
-              <widget class="GtkAlignment" id="alignment1">
-                <property name="visible">True</property>
-                <property name="border_width">8</property>
-                <property name="left_padding">12</property>
-                <child>
-                  <widget class="GtkTable" id="table3">
-                    <property name="visible">True</property>
-                    <property name="n_rows">2</property>
-                    <property name="n_columns">2</property>
-                    <property name="row_spacing">5</property>
-                    <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow3">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                        <child>
-                          <widget class="GtkTreeView" id="treeview1">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="headers_visible">False</property>
-                            <property name="enable_search">False</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                        <property name="x_options">GTK_FILL</property>
-                        <property name="y_options">GTK_FILL</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkTable" id="table4">
-                        <property name="visible">True</property>
-                        <property name="border_width">5</property>
-                        <property name="n_rows">2</property>
-                        <property name="n_columns">2</property>
-                        <property name="column_spacing">5</property>
-                        <property name="row_spacing">4</property>
-                        <child>
-                          <widget class="GtkHBox" id="hbox4">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkEntry" id="value_entry">
-                                <property name="width_request">85</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="padding">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options">GTK_FILL</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="label_entry">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label6">
-                            <property name="visible">True</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">Value Label:</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label5">
-                            <property name="visible">True</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">Value:</property>
-                          </widget>
-                          <packing>
-                            <property name="x_options">GTK_FILL</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="right_attach">2</property>
-                        <property name="x_options">GTK_FILL</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkVButtonBox" id="vbuttonbox2">
-                        <property name="visible">True</property>
-                        <property name="border_width">5</property>
-                        <child>
-                          <widget class="GtkButton" id="val_labs_add">
-                            <property name="visible">True</property>
-                            <property name="sensitive">False</property>
-                            <property name="can_focus">True</property>
-                            <property name="can_default">True</property>
-                            <property name="label">gtk-add</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="val_labs_change">
-                            <property name="visible">True</property>
-                            <property name="sensitive">False</property>
-                            <property name="can_focus">True</property>
-                            <property name="can_default">True</property>
-                            <property name="label">gtk-apply</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="val_labs_remove">
-                            <property name="visible">True</property>
-                            <property name="sensitive">False</property>
-                            <property name="can_focus">True</property>
-                            <property name="can_default">True</property>
-                            <property name="label">gtk-remove</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                        <property name="x_options">GTK_FILL</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
+            <property name="tooltip" translatable="yes">Information Area</property>
             <child>
-              <widget class="GtkLabel" id="label7">
+              <widget class="GtkLabel" id="information-area">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">Value Labels</property>
-                <property name="use_markup">True</property>
+                <property name="single_line_mode">True</property>
               </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
             </child>
           </widget>
-          <packing>
-            <property name="padding">10</property>
-          </packing>
         </child>
+      </widget>
+    </child>
+    <child>
+      <widget class="GtkFrame" id="frame3">
+        <property name="visible">True</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
         <child>
-          <widget class="GtkVButtonBox" id="vbuttonbox3">
+          <widget class="GtkEventBox" id="eventbox2">
             <property name="visible">True</property>
-            <property name="border_width">5</property>
-            <property name="spacing">5</property>
-            <property name="layout_style">GTK_BUTTONBOX_START</property>
+            <property name="tooltip" translatable="yes">Processor Area</property>
             <child>
-              <widget class="GtkButton" id="val_labs_ok">
+              <widget class="GtkLabel" id="processor-area">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-ok</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkButton" id="val_labs_cancel">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-cancel</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="help_button_value_labels">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-help</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
+                <property name="width_chars">35</property>
+                <property name="single_line_mode">True</property>
               </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
             </child>
           </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-            <property name="position">1</property>
-          </packing>
         </child>
       </widget>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">1</property>
+      </packing>
     </child>
-  </widget>
-  <widget class="GtkWindow" id="missing_values_dialog">
-    <property name="border_width">10</property>
-    <property name="title" translatable="yes">Missing Values</property>
-    <property name="resizable">False</property>
-    <property name="modal">True</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="skip_pager_hint">True</property>
     <child>
-      <widget class="GtkTable" id="table6">
+      <widget class="GtkFrame" id="frame5">
         <property name="visible">True</property>
-        <property name="n_rows">2</property>
-        <property name="n_columns">2</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
         <child>
-          <widget class="GtkVBox" id="vbox7">
+          <widget class="GtkEventBox" id="eventbox4">
             <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Case Counter Area</property>
             <child>
-              <widget class="GtkRadioButton" id="range_missing">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">_Range plus one optional discrete missing value</property>
-                <property name="use_underline">True</property>
-                <property name="focus_on_click">False</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-                <property name="group">no_missing</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkVBox" id="vbox8">
+              <widget class="GtkLabel" id="case-counter-area">
                 <property name="visible">True</property>
-                <property name="spacing">5</property>
-                <child>
-                  <widget class="GtkHBox" id="hbox7">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkHBox" id="hbox8">
-                        <property name="visible">True</property>
-                        <child>
-                          <widget class="GtkLabel" id="label11">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">_Low:</property>
-                            <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">mv-low</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="padding">20</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="mv-low">
-                            <property name="width_request">75</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkHBox" id="hbox9">
-                        <property name="visible">True</property>
-                        <child>
-                          <widget class="GtkLabel" id="label12">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">_High:</property>
-                            <property name="use_underline">True</property>
-                            <property name="mnemonic_widget">mv-high</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="mv-high">
-                            <property name="width_request">75</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </widget>
-                          <packing>
-                            <property name="padding">5</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="fill">False</property>
-                        <property name="padding">20</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkHBox" id="hbox6">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkLabel" id="label10">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Di_screte value:</property>
-                        <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">mv-discrete</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="padding">20</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="mv-discrete">
-                        <property name="width_request">75</property>
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
+                <property name="width_chars">20</property>
+                <property name="single_line_mode">True</property>
               </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
             </child>
           </widget>
-          <packing>
-            <property name="right_attach">2</property>
-            <property name="top_attach">1</property>
-            <property name="bottom_attach">2</property>
-            <property name="x_options">GTK_FILL</property>
-          </packing>
         </child>
+      </widget>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">3</property>
+      </packing>
+    </child>
+    <child>
+      <widget class="GtkFrame" id="frame6">
+        <property name="visible">True</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
         <child>
-          <widget class="GtkVBox" id="vbox5">
+          <widget class="GtkEventBox" id="eventbox5">
             <property name="visible">True</property>
-            <property name="spacing">12</property>
-            <child>
-              <widget class="GtkRadioButton" id="no_missing">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label" translatable="yes">_No missing values</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <property name="active">True</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-              </packing>
-            </child>
+            <property name="tooltip" translatable="yes">Filter Use Status Area</property>
             <child>
-              <widget class="GtkVBox" id="vbox6">
+              <widget class="GtkLabel" id="filter-use-status-area">
                 <property name="visible">True</property>
-                <child>
-                  <widget class="GtkRadioButton" id="discrete_missing">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="label" translatable="yes">_Discrete missing values</property>
-                    <property name="use_underline">True</property>
-                    <property name="focus_on_click">False</property>
-                    <property name="response_id">0</property>
-                    <property name="draw_indicator">True</property>
-                    <property name="group">no_missing</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkHBox" id="hbox10">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkHBox" id="hbox5">
-                        <property name="visible">True</property>
-                        <property name="border_width">5</property>
-                        <property name="spacing">5</property>
-                        <property name="homogeneous">True</property>
-                        <child>
-                          <widget class="GtkEntry" id="mv0">
-                            <property name="width_request">75</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="mv1">
-                            <property name="width_request">75</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="mv2">
-                            <property name="width_request">75</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="padding">20</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
+                <property name="ellipsize">PANGO_ELLIPSIZE_START</property>
+                <property name="width_chars">10</property>
+                <property name="single_line_mode">True</property>
               </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
             </child>
           </widget>
-          <packing>
-            <property name="y_options">GTK_FILL</property>
-          </packing>
         </child>
+      </widget>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">4</property>
+      </packing>
+    </child>
+    <child>
+      <widget class="GtkFrame" id="frame7">
+        <property name="visible">True</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
         <child>
-          <widget class="GtkVButtonBox" id="vbuttonbox5">
+          <widget class="GtkEventBox" id="eventbox6">
             <property name="visible">True</property>
-            <property name="border_width">5</property>
-            <property name="spacing">5</property>
-            <property name="layout_style">GTK_BUTTONBOX_START</property>
-            <child>
-              <widget class="GtkButton" id="missing_val_ok">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-ok</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-            </child>
+            <property name="tooltip" translatable="yes">Weight Status Area</property>
             <child>
-              <widget class="GtkButton" id="missing_val_cancel">
+              <widget class="GtkLabel" id="weight-status-area">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-cancel</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
+                <property name="ellipsize">PANGO_ELLIPSIZE_START</property>
+                <property name="width_chars">15</property>
+                <property name="single_line_mode">True</property>
               </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
             </child>
+          </widget>
+        </child>
+      </widget>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">5</property>
+      </packing>
+    </child>
+    <child>
+      <widget class="GtkFrame" id="frame8">
+        <property name="visible">True</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
+        <child>
+          <widget class="GtkEventBox" id="eventbox7">
+            <property name="visible">True</property>
+            <property name="tooltip" translatable="yes">Split File Status Area</property>
             <child>
-              <widget class="GtkButton" id="help_button_missing_values">
+              <widget class="GtkLabel" id="split-file-status-area">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="label">gtk-help</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
+                <property name="ellipsize">PANGO_ELLIPSIZE_START</property>
+                <property name="width_chars">15</property>
+                <property name="single_line_mode">True</property>
               </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
             </child>
           </widget>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="right_attach">2</property>
-          </packing>
         </child>
       </widget>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">6</property>
+      </packing>
     </child>
   </widget>
 </glade-interface>
diff --git a/src/ui/gui/data-editor.h b/src/ui/gui/data-editor.h
deleted file mode 100644 (file)
index 9ea0e91..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006, 2007  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
-   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 DATA_EDITOR_H
-#define DATA_EDITOR_H
-
-#include <glade/glade.h>
-#include <gtk/gtk.h>
-#include "window-manager.h"
-#include "psppire-data-editor.h"
-
-struct data_editor
-{
-  struct editor_window parent;
-
-  GtkAction *action_data_new;
-  GtkAction *action_data_open;
-  GtkAction *action_data_save_as;
-  GtkAction *action_data_save;
-
-
-  GtkAction *invoke_text_import_assistant;
-
-  /* Actions which invoke dialog boxes */
-  GtkAction *invoke_weight_cases_dialog;
-  GtkAction *invoke_transpose_dialog;
-  GtkAction *invoke_split_file_dialog;
-  GtkAction *invoke_sort_cases_dialog;
-  GtkAction *invoke_compute_dialog;
-  GtkAction *invoke_comments_dialog;
-  GtkAction *invoke_select_cases_dialog;
-  GtkAction *invoke_goto_dialog;
-  GtkAction *invoke_variable_info_dialog;
-  GtkAction *invoke_find_dialog;
-  GtkAction *invoke_rank_dialog;
-  GtkAction *invoke_recode_same_dialog;
-  GtkAction *invoke_recode_different_dialog;
-
-  GtkAction *invoke_crosstabs_dialog;
-  GtkAction *invoke_descriptives_dialog;
-  GtkAction *invoke_frequencies_dialog;
-  GtkAction *invoke_examine_dialog;
-  GtkAction *invoke_regression_dialog;
-
-  GtkAction *invoke_t_test_independent_samples_dialog;
-  GtkAction *invoke_t_test_paired_samples_dialog;
-  GtkAction *invoke_oneway_anova_dialog;
-  GtkAction *invoke_t_test_one_sample_dialog;
-
-
-  /* Actions which do things */
-  GtkAction *insert_variable;
-  GtkAction *insert_case;
-  GtkAction *delete_variables;
-  GtkAction *delete_cases;
-
-  GtkToggleAction *toggle_value_labels;
-
-  GladeXML *xml;
-
-  GtkMenu *data_sheet_variable_popup_menu;
-  GtkMenu *data_sheet_cases_popup_menu;
-
-  PsppireDataEditor *data_editor;
-
-  gboolean save_as_portable;
-
-  /* Name of the file this data is associated with (ie, was loaded from or
-     has been  saved to), in "filename encoding",  or NULL, if it's not
-     associated with any file */
-  gchar *file_name;
-};
-
-
-struct data_editor * new_data_editor (void);
-
-void new_data_window (GtkMenuItem *, gpointer);
-
-void data_editor_select_sheet (struct data_editor *de, gint page);
-
-#endif
index 7b33eae21026e454362cbb238f15c828034b21ff..7b9a958409a8cc8c06c95e9e32dbe8c808325364 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -202,11 +202,11 @@ void
 descriptives_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   struct descriptives_dialog scd;
 
-  GladeXML *xml = XML_NEW ("descriptives-dialog.glade");
+  GtkBuilder *xml = builder_new ("descriptives-dialog.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "descriptives-dialog");
 
@@ -221,14 +221,14 @@ descriptives_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, var_is_numeric);
+  g_object_set (source, "dictionary", vs->dict,
+       "predicate", var_is_numeric, NULL);
 
   set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
 
+
   psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
                                 source,
                                 dest,
@@ -263,6 +263,7 @@ descriptives_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&scd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -272,12 +273,7 @@ descriptives_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&scd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
+       paste_syntax_in_new_window (syntax);
        g_free (syntax);
       }
       break;
index dff7078ae51652c36ebd8bc825ee2c55cbd0f666..3fa67a55a8aa4c7baa1e80a0f88574b4caac9e54 100644 (file)
@@ -13,7 +13,7 @@
         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
         <property name="spacing">2</property>
         <child>
-          <widget class="GtkVBox" id="vbox30">
+          <widget class="GtkVBox" id="vbox29">
             <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>
             <child>
@@ -29,7 +29,7 @@
                     <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                     <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                     <child>
-                      <widget class="GtkTreeView" id="all-variables">
+                      <widget class="PsppireDictView" id="all-variables">
                         <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>
index aa6e39d13b86b66d73f7aff1ecc2c7cf93979294..745663e73139eb35ce2d05026a55fb78a0569dc8 100644 (file)
@@ -19,7 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 
 void descriptives_dialog (GObject *o, gpointer data);
index 356758beeea4100c4f4f1fd3feeaae71ebeb2fb4..2123c3c5cfe7b300b6c2bcaca241f5ae95d5636d 100644 (file)
@@ -19,6 +19,7 @@
 #include <gettext.h>
 #include <gtk/gtk.h>
 
+#include "psppire-conf.h"
 #include "dict-display.h"
 
 #include "psppire-dict.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-
-/* A GtkTreeModelFilterVisibleFunc to filter lines in the treeview */
-static gboolean
-filter_variables (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
-{
-  var_predicate_func *predicate = data;
-  struct variable *var;
-  PsppireDict *dict = PSPPIRE_DICT (model);
-
-  GtkTreePath *path = gtk_tree_model_get_path (model, iter);
-
-  gint *idx = gtk_tree_path_get_indices (path);
-
-  var =  psppire_dict_get_variable (dict, *idx);
-
-  gtk_tree_path_free (path);
-
-  return predicate (var);
-}
-
-/* A GtkTreeCellDataFunc which sets the icon appropriate to the type
-   of variable */
 static void
-var_icon_cell_data_func (GtkTreeViewColumn *col,
-                      GtkCellRenderer *cell,
-                      GtkTreeModel *model,
-                      GtkTreeIter *iter,
-                      gpointer data)
-{
-  struct variable *var;
-  gtk_tree_model_get (model, iter, DICT_TVM_COL_VAR, &var, -1);
-
-  if ( var_is_alpha (var))
-    {
-      g_object_set (cell, "stock-id", "var-string", NULL);
-    }
-  else
-    {
-      const struct fmt_spec *fs = var_get_write_format (var);
-      int cat = fmt_get_category (fs->type);
-      switch ( var_get_measure (var))
-       {
-       case MEASURE_NOMINAL:
-         g_object_set (cell, "stock-id", "var-nominal", NULL);
-         break;
-       case MEASURE_ORDINAL:
-         g_object_set (cell, "stock-id", "var-ordinal", NULL);
-         break;
-       case MEASURE_SCALE:
-         if ( ( FMT_CAT_DATE | FMT_CAT_TIME ) & cat )
-           g_object_set (cell, "stock-id", "var-date-scale", NULL);
-         else
-           g_object_set (cell, "stock-id", "var-scale", NULL);
-         break;
-       default:
-         g_assert_not_reached ();
-       };
-    }
-}
-
-
-void
 get_base_model (GtkTreeModel *top_model, GtkTreeIter *top_iter,
                GtkTreeModel **model, GtkTreeIter *iter
                )
@@ -112,170 +52,6 @@ get_base_model (GtkTreeModel *top_model, GtkTreeIter *top_iter,
   g_assert (PSPPIRE_IS_DICT (*model));
 }
 
-/* A GtkTreeCellDataFunc which renders the name and/or label of the
-   variable */
-static void
-var_description_cell_data_func (GtkTreeViewColumn *col,
-                               GtkCellRenderer *cell,
-                               GtkTreeModel *top_model,
-                               GtkTreeIter *top_iter,
-                               gpointer data)
-{
-  struct variable *var;
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-
-
-  get_base_model (top_model, top_iter, &model, &iter);
-
-  g_assert (PSPPIRE_IS_DICT (model));
-
-
-  gtk_tree_model_get (model,
-                     &iter, DICT_TVM_COL_VAR, &var, -1);
-
-  if ( var_has_label (var))
-    {
-      gchar *text = g_strdup_printf (
-                                    "<span stretch=\"condensed\">%s</span>",
-                                    var_get_label (var));
-
-
-      char *utf8 = pspp_locale_to_utf8 (text, -1, NULL);
-
-      g_free (text);
-      g_object_set (cell, "markup", utf8, NULL);
-      g_free (utf8);
-    }
-  else
-    {
-      g_object_set (cell, "text", var_get_name (var), NULL);
-    }
-}
-
-
-#if GTK_CHECK_VERSION (2, 12, 0)
-/* Sets the tooltip to be the name of the variable under the cursor */
-static gboolean
-set_tooltip_for_variable (GtkTreeView  *treeview,
-                         gint        x,
-                         gint        y,
-                         gboolean    keyboard_mode,
-                         GtkTooltip *tooltip,
-                         gpointer    user_data)
-
-{
-  gint bx, by;
-  GtkTreeIter iter;
-  GtkTreePath *path;
-  GtkTreeModel *tree_model;
-  struct variable *var = NULL;
-  gboolean ok;
-
-
-  gtk_tree_view_convert_widget_to_bin_window_coords (treeview,
-                                                     x, y, &bx, &by);
-
-  if (!gtk_tree_view_get_path_at_pos (treeview, bx, by,
-                                      &path, NULL, NULL, NULL))
-    return FALSE;
-
-  tree_model = gtk_tree_view_get_model (treeview);
-
-
-  gtk_tree_view_set_tooltip_row (treeview, tooltip, path);
-
-  ok = gtk_tree_model_get_iter (tree_model, &iter, path);
-
-  gtk_tree_path_free (path);
-  if (!ok)
-    return FALSE;
-
-
-  gtk_tree_model_get (tree_model, &iter, DICT_TVM_COL_VAR,  &var, -1);
-
-  if ( ! var_has_label (var))
-    return FALSE;
-
-  gtk_tooltip_set_text (tooltip, var_get_name (var));
-
-  return TRUE;
-}
-#endif
-
-   /* Sets up TREEVIEW to display the variables of DICT.
-   MODE is the selection mode for TREEVIEW.
-   PREDICATE determines which variables should be visible, or NULL if
-   all are to be visible.
- */
-void
-attach_dictionary_to_treeview (GtkTreeView *treeview, PsppireDict *dict,
-                              GtkSelectionMode mode,
-                              var_predicate_func *predicate
-                              )
-{
-  GtkTreeViewColumn *col;
-
-  GtkTreeSelection *selection =
-    gtk_tree_view_get_selection (treeview);
-
-  GtkCellRenderer *renderer;
-
-  GtkTreeModel *model ;
-
-  if ( predicate )
-    {
-      model = gtk_tree_model_filter_new (GTK_TREE_MODEL (dict),
-                                         NULL);
-
-      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
-                                             filter_variables,
-                                             predicate,
-                                             NULL);
-    }
-  else
-    {
-      model = GTK_TREE_MODEL (dict);
-    }
-
-  gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), model);
-
-
-  col = gtk_tree_view_column_new ();
-  gtk_tree_view_column_set_title (col, _("Variable"));
-
-  renderer = gtk_cell_renderer_pixbuf_new ();
-  gtk_tree_view_column_pack_start (col, renderer, FALSE);
-
-  gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                          var_icon_cell_data_func,
-                                          NULL, NULL);
-
-
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_tree_view_column_pack_start (col, renderer, TRUE);
-  gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                          var_description_cell_data_func,
-                                          NULL, NULL);
-
-  g_object_set (renderer, "ellipsize-set", TRUE, NULL);
-  g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
-
-  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
-
-  /* FIXME: make this a value in terms of character widths */
-  gtk_tree_view_column_set_min_width (col, 150);
-
-  gtk_tree_view_append_column (treeview, col);
-
-  gtk_tree_selection_set_mode (selection, mode);
-
-  g_object_set (treeview, "has-tooltip", TRUE, NULL);
-
-#if GTK_CHECK_VERSION (2, 12, 0)
-  g_signal_connect (treeview, "query-tooltip", G_CALLBACK (set_tooltip_for_variable), NULL);
-#endif
-}
 
 
 void
@@ -374,3 +150,4 @@ is_currently_in_entry (GtkTreeModel *model, GtkTreeIter *iter,
 }
 
 
+
index a9481c6d85534439490d672accbf7d1a56f06f46..2c8df4407655e7d48cd4aedca7bd880d3130227a 100644 (file)
 #include <gtk/gtk.h>
 
 #include "psppire-selector.h"
-#include "psppire-dict.h"
-#include <data/variable.h>
-
-/* Sets up TREEVIEW to display the variables of DICT.
-   MODE is the selection mode for TREEVIEW.
-   PREDICATE determines which variables should be visible, or NULL if
-   all are to be visible.
- */
-void attach_dictionary_to_treeview (GtkTreeView *treeview, PsppireDict *dict,
-                                   GtkSelectionMode mode,
-                                   var_predicate_func *predicate
-                                   );
 
 
 /* A SelectItemsFunc function for GtkTreeView widgets */
@@ -58,6 +46,3 @@ gboolean is_currently_in_entry (GtkTreeModel *model, GtkTreeIter *iter,
                                PsppireSelector *selector);
 
 
-void get_base_model (GtkTreeModel *top_model, GtkTreeIter *top_iter,
-                    GtkTreeModel **model, GtkTreeIter *iter
-                    );
index 6813b4b1d451ee5d5756abe05f6d34a933f7ab42..1d8944754c5ec3bc268dda14858da4bfb60a5a04 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -236,13 +236,11 @@ void
 examine_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   struct examine_dialog ex_d;
 
-
-  GladeXML *xml = XML_NEW ("examine.glade");
-
+  GtkBuilder *xml = builder_new ("examine.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "examine-dialog");
   GtkWidget *source = get_widget_assert   (xml, "treeview1");
@@ -277,14 +275,11 @@ examine_dialog (GObject *o, gpointer data)
   ex_d.percentiles_button = GTK_TOGGLE_BUTTON
     (get_widget_assert (xml, "percentiles-button"));
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-  gtk_window_set_transient_for (GTK_WINDOW (ex_d.stats_dialog), de->parent.window);
-  gtk_window_set_transient_for (GTK_WINDOW (ex_d.opts_dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
+  gtk_window_set_transient_for (GTK_WINDOW (ex_d.stats_dialog), GTK_WINDOW (de));
+  gtk_window_set_transient_for (GTK_WINDOW (ex_d.opts_dialog), GTK_WINDOW (de));
 
+  g_object_set (source, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (ex_d.dep_list), vs->dict);
   ex_d.dict = vs->dict;
@@ -346,12 +341,7 @@ examine_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&ex_d);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
+       paste_syntax_in_new_window (syntax);
        g_free (syntax);
       }
       break;
index b8e70272e4802a8f9f6844ca7d40236ce6bdc1c4..d8e3fb6c259cab1560ff88645c1609689803d61e 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void examine_dialog (GObject *o, gpointer data);
 
index 00785397fc498e7f8d44e3940bd6f4aa4a261d78..a6f7ca8a7d57c96156ae7ac97c87d0abcd2c2c76 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Tue Feb 12 20:44:51 2008 by john@marilyn-->
+<!--Generated with glade3 3.2.2 on Mon Dec 15 07:18:17 2008 by john@marilyn-->
 <glade-interface>
   <requires lib="psppire"/>
   <widget class="PsppireDialog" id="examine-dialog">
                 <property name="n_rows">3</property>
                 <property name="n_columns">3</property>
                 <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="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                    <child>
-                      <widget class="GtkTreeView" id="treeview1">
-                        <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="headers_visible">False</property>
-                        <property name="headers_clickable">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="bottom_attach">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="psppire-selector1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">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="border_width">5</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="psppire-selector2">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">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="border_width">5</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="psppire-selector3">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">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="border_width">5</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkFrame" id="frame1">
+                  <widget class="GtkFrame" id="frame3">
                     <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="shadow_type">GTK_SHADOW_NONE</property>
                     <child>
-                      <widget class="GtkAlignment" id="alignment1">
+                      <widget class="GtkAlignment" id="alignment3">
                         <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="left_padding">12</property>
                         <child>
-                          <widget class="GtkScrolledWindow" id="scrolledwindow2">
+                          <widget class="GtkEntry" id="entry1">
                             <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_NEVER</property>
-                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                            <child>
-                              <widget class="GtkTreeView" id="treeview2">
-                                <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="headers_visible">False</property>
-                                <property name="headers_clickable">True</property>
-                              </widget>
-                            </child>
                           </widget>
                         </child>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label1">
+                      <widget class="GtkLabel" id="label3">
                         <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">Dependent List:</property>
+                        <property name="label" translatable="yes">Label Cases by:</property>
                         <property name="use_markup">True</property>
                       </widget>
                       <packing>
                   <packing>
                     <property name="left_attach">2</property>
                     <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
                   </packing>
                 </child>
                 <child>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkFrame" id="frame3">
+                  <widget class="GtkFrame" id="frame1">
                     <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="shadow_type">GTK_SHADOW_NONE</property>
                     <child>
-                      <widget class="GtkAlignment" id="alignment3">
+                      <widget class="GtkAlignment" id="alignment1">
                         <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="left_padding">12</property>
                         <child>
-                          <widget class="GtkEntry" id="entry1">
+                          <widget class="GtkScrolledWindow" id="scrolledwindow2">
                             <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_NEVER</property>
+                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                            <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                            <child>
+                              <widget class="GtkTreeView" id="treeview2">
+                                <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="headers_visible">False</property>
+                                <property name="headers_clickable">True</property>
+                              </widget>
+                            </child>
                           </widget>
                         </child>
                       </widget>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label3">
+                      <widget class="GtkLabel" id="label1">
                         <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">Label Cases by:</property>
+                        <property name="label" translatable="yes">Dependent List:</property>
                         <property name="use_markup">True</property>
                       </widget>
                       <packing>
                   <packing>
                     <property name="left_attach">2</property>
                     <property name="right_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="PsppireSelector" id="psppire-selector3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="border_width">5</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
                     <property name="top_attach">2</property>
                     <property name="bottom_attach">3</property>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="PsppireSelector" id="psppire-selector2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="border_width">5</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="PsppireSelector" id="psppire-selector1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="border_width">5</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="x_options"></property>
+                    <property name="y_options"></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="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                    <child>
+                      <widget class="PsppireDictView" id="treeview1">
+                        <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="headers_visible">False</property>
+                        <property name="headers_clickable">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="bottom_attach">3</property>
                   </packing>
                 </child>
               </widget>
                     <property name="receives_default">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">Statistics...</property>
+                    <property name="response_id">0</property>
                   </widget>
                 </child>
                 <child>
                     <property name="receives_default">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">Options...</property>
+                    <property name="response_id">0</property>
                   </widget>
                   <packing>
                     <property name="position">1</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="label" translatable="yes">Descriptives</property>
+                <property name="response_id">0</property>
                 <property name="draw_indicator">True</property>
               </widget>
             </child>
                 <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="label" translatable="yes">Extremes</property>
+                <property name="response_id">0</property>
                 <property name="draw_indicator">True</property>
               </widget>
               <packing>
                 <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="label" translatable="yes">Percentiles</property>
+                <property name="response_id">0</property>
                 <property name="draw_indicator">True</property>
               </widget>
               <packing>
                         <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="label" translatable="yes">Exclude cases listwise</property>
+                        <property name="response_id">0</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
                       </widget>
                         <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="label" translatable="yes">Exclude cases pairwise</property>
+                        <property name="response_id">0</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
                         <property name="group">radiobutton1</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="label" translatable="yes">Repeat values</property>
+                        <property name="response_id">0</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
                         <property name="group">radiobutton1</property>
index 5d98b4ac91ccc0f22a783643825cc2a66bc444e4..70f2085ca9dd76858596db6216d81aa5f498d0cc 100644 (file)
@@ -24,7 +24,7 @@ which match particular strings */
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "dict-display.h"
 #include <data/value.h>
 #include <data/format.h>
@@ -37,7 +37,6 @@ which match particular strings */
 #include <libpspp/message.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include <stdlib.h>
 
 #include "xalloc.h"
@@ -52,10 +51,10 @@ which match particular strings */
 
 struct find_dialog
 {
-  GladeXML *xml;
+  GtkBuilder *xml;
   PsppireDict *dict;
   struct datasheet *data;
-  struct data_editor *de;
+  PsppireDataWindow *de;
   GtkWidget *variable_entry;
   GtkWidget *value_entry;
   GtkWidget *value_labels_checkbox;
@@ -186,7 +185,7 @@ value_labels_toggled (GtkToggleButton *tb, gpointer data)
 void
 find_dialog (GObject *o, gpointer data)
 {
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   struct find_dialog fd;
 
@@ -200,7 +199,7 @@ find_dialog (GObject *o, gpointer data)
   PsppireVarStore *vs ;
   PsppireDataStore *ds ;
 
-  fd.xml = XML_NEW ("psppire.glade");
+  fd.xml = builder_new ("psppire.ui");
   fd.de = de;
 
   find_button = gtk_button_new_from_stock  (GTK_STOCK_FIND);
@@ -221,7 +220,7 @@ find_dialog (GObject *o, gpointer data)
                NULL);
 
   fd.dict = vs->dict;
-  fd.data = ds->case_file->datasheet;
+  fd.data = ds->datasheet;
 
   fd.variable_entry        = get_widget_assert (fd.xml, "find-variable-entry");
   fd.value_entry           = get_widget_assert (fd.xml, "find-value-entry");
@@ -239,13 +238,12 @@ find_dialog (GObject *o, gpointer data)
 
 
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                fd.dict,
-                                GTK_SELECTION_SINGLE,
-                                NULL);
+  g_object_set (source, "dictionary", fd.dict, 
+       "selection-mode", GTK_SELECTION_SINGLE,
+       NULL);
 
   psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
                                 source,
@@ -464,7 +462,7 @@ value_compare (const struct comparator *cmptr,
               const union value *v)
 {
   const struct value_comparator *vc = (const struct value_comparator *) cmptr;
-  return 0 == compare_values (v, vc->pattern, var_get_width (cmptr->var));
+  return 0 == value_compare_3way (v, vc->pattern, var_get_width (cmptr->var));
 }
 
 
index 802a4664e710b8a2352d9938af4dba1930e2c6d6..c78fa95f1869bd907946af25c879803c605933c2 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -307,11 +307,11 @@ void
 frequencies_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   struct frequencies_dialog fd;
 
-  GladeXML *xml = XML_NEW ("frequencies.glade");
+  GtkBuilder *xml = builder_new ("frequencies.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "frequencies-dialog");
   GtkWidget *source = get_widget_assert   (xml, "dict-treeview");
@@ -331,11 +331,9 @@ frequencies_dialog (GObject *o, gpointer data)
                                  );
 
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (source, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
 
@@ -369,7 +367,7 @@ frequencies_dialog (GObject *o, gpointer data)
   fd.current_opts.limit = 50;
 
 
-  gtk_window_set_transient_for (GTK_WINDOW (fd.format_dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (fd.format_dialog), GTK_WINDOW (de));
 
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &fd);
@@ -393,6 +391,7 @@ frequencies_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&fd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -402,12 +401,7 @@ frequencies_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&fd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
+       paste_syntax_in_new_window (syntax);
        g_free (syntax);
       }
       break;
index 90990a414b29c98a51fa6d8285474553a1aec648..f4d44f35b35f2e565b49fcacd510856949d51504 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void frequencies_dialog (GObject *o, gpointer data);
 
index 5617d40d2c3e2cce6be7219fef1068046827a713..3e5e921349e937fae732c330a96239affe5f9269 100644 (file)
@@ -29,7 +29,7 @@
                     <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                     <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                     <child>
-                      <widget class="GtkTreeView" id="dict-treeview">
+                      <widget class="PsppireDictView" id="dict-treeview">
                         <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>
diff --git a/src/ui/gui/glade-register.c b/src/ui/gui/glade-register.c
deleted file mode 100644 (file)
index 8946b27..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  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
-   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 <string.h>
-
-#include <glade/glade-build.h>
-#include "psppire-dialog.h"
-#include "psppire-selector.h"
-#include "psppire-acr.h"
-#include "psppire-keypad.h"
-#include "psppire-hbuttonbox.h"
-#include "psppire-vbuttonbox.h"
-
-GLADE_MODULE_CHECK_INIT
-
-/* Glade registration functions for PSPPIRE custom widgets */
-
-static GtkWidget *
-dialog_find_internal_child (GladeXML *xml,
-                           GtkWidget *parent,
-                           const gchar *childname)
-{
-  if (!strcmp(childname, "hbox"))
-    return PSPPIRE_DIALOG (parent)->box;
-
-  return NULL;
-}
-
-void
-glade_module_register_widgets (void)
-{
-  glade_register_widget (PSPPIRE_DIALOG_TYPE, NULL,
-                        glade_standard_build_children,
-                        dialog_find_internal_child);
-
-
-  glade_register_widget (PSPPIRE_VBUTTON_BOX_TYPE, NULL,
-                        glade_standard_build_children,
-                        NULL);
-
-  glade_register_widget (PSPPIRE_HBUTTON_BOX_TYPE, NULL,
-                        glade_standard_build_children,
-                        NULL);
-
-  glade_register_widget (PSPPIRE_SELECTOR_TYPE, NULL,
-                        glade_standard_build_children,
-                        NULL);
-
-  glade_register_widget (PSPPIRE_KEYPAD_TYPE, NULL,
-                        glade_standard_build_children,
-                        NULL);
-
-  glade_register_widget (PSPPIRE_ACR_TYPE, NULL,
-                        glade_standard_build_children,
-                        NULL);
-}
-
-
-
index 86e80c5d8018691bb31c09e5f7bf5da97bf79687..9a523943b1c5686e639d4df34b56530d4f1b55a5 100644 (file)
 #include "goto-case-dialog.h"
 #include "helper.h"
 #include "psppire-dialog.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-data-store.h"
 
 
 static void
-refresh (const struct data_editor *de, GladeXML *xml)
+refresh (const PsppireDataWindow *de, GtkBuilder *xml)
 {
   PsppireDataStore *ds = NULL;
   casenumber case_count ;
@@ -43,13 +43,13 @@ void
 goto_case_dialog (GObject *o, gpointer data)
 {
   gint response;
-  GladeXML *xml = XML_NEW ("psppire.glade");
-  struct data_editor *de = data;
+  GtkBuilder *xml = builder_new ("psppire.ui");
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   GtkWidget *dialog = get_widget_assert   (xml, "goto-case-dialog");
 
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   refresh (de, xml);
 
index 536ccb08e8edd86d0aa3d6ca6f5fbacc3997cd42..7b3da66d59969de80f0320405cf57db29d3ba62c 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void goto_case_dialog (GObject *o, gpointer data);
 
index 1007ff967fb50653f21f09782d426f03ee01f62c..9ac9fde6ec43b65ab79765859fb5ccfe9f5ec6ec 100644 (file)
@@ -20,6 +20,8 @@
 */
 #include <config.h>
 
+#include "psppire-syntax-window.h"
+
 #include       <glib-object.h>
 
 #include <glib.h>
@@ -32,6 +34,7 @@
 #include <data/casereader-provider.h>
 #include <libpspp/message.h>
 
+#include <gtk/gtkbuilder.h>
 #include <libpspp/i18n.h>
 
 #include <ctype.h>
@@ -45,7 +48,7 @@
 #include <language/lexer/lexer.h>
 #include "psppire-data-store.h"
 #include <output/manager.h>
-#include "output-viewer.h"
+#include "psppire-output-window.h"
 
 #include "xalloc.h"
 
@@ -100,19 +103,53 @@ text_to_value (const gchar *text, union value *v,
 }
 
 
-GtkWidget *
-get_widget_assert (GladeXML *xml, const gchar *name)
+GtkBuilder *
+builder_new_real (const gchar *name)
 {
-  GtkWidget *w;
-  g_assert (xml);
+  GtkBuilder *builder = gtk_builder_new ();
+
+  GError *err = NULL;
+  if ( ! gtk_builder_add_from_file (builder, name,  &err))
+    {
+      g_critical ("Couldnt open user interface  file %s: %s", name, err->message);
+      g_clear_error (&err);
+    }
+
+  return builder;
+}
+
+
+GObject *
+get_object_assert (GtkBuilder *builder, const gchar *name, GType type)
+{
+  GObject *o = NULL;
   g_assert (name);
 
-  w = glade_xml_get_widget (xml, name);
+  o = gtk_builder_get_object (builder, name);
+
+  if ( !o )
+    g_critical ("Object \"%s\" could not be found\n", name);
+
+  if ( ! g_type_is_a (G_OBJECT_TYPE (o), type))
+   {
+     g_critical ("Object \"%s\" was expected to have type %s, but in fact has type %s", 
+       name, g_type_name (type), G_OBJECT_TYPE_NAME (o));
+   }
+
+  return o;
+}
 
-  if ( !w )
-    g_critical ("Widget \"%s\" could not be found\n", name);
 
-  return w;
+GtkAction *
+get_action_assert (GtkBuilder *builder, const gchar *name)
+{
+  return GTK_ACTION (get_object_assert (builder, name, GTK_TYPE_ACTION));
+}
+
+GtkWidget *
+get_widget_assert (GtkBuilder *builder, const gchar *name)
+{
+  return GTK_WIDGET (get_object_assert (builder, name, GTK_TYPE_WIDGET));
 }
 
 /* Converts a string in the pspp locale to utf-8.
@@ -123,6 +160,38 @@ pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err)
   return recode_string (CONV_PSPP_TO_UTF8, text, len);
 }
 
+gchar *
+utf8_to_pspp_locale (const gchar *text, gssize len, GError **err)
+{
+  return recode_string (CONV_UTF8_TO_PSPP, text, len);
+}
+
+/* This function must be used whenever a filename generated by glib,
+   (eg, from gtk_file_chooser_get_filename) and passed to the C library,
+   (eg through a pspp syntax string).
+*/
+gchar *
+convert_glib_filename_to_system_filename (const gchar *fname, GError **err)
+{
+  gchar *output_name;
+
+#ifdef G_OS_WIN32
+  const gchar *target_encoding;
+  gchar *utf8_name = NULL;
+
+  g_get_charset (&target_encoding);
+
+  output_name = g_convert (fname, -1, target_encoding,
+                       "UTF-8", NULL, NULL, err);
+#else
+  output_name = strdup (fname);
+#endif
+
+  return output_name;
+}
+
+
+
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
@@ -143,27 +212,48 @@ give_help (void)
 }
 
 void
-connect_help (GladeXML *xml)
+connect_help (GtkBuilder *xml)
 {
-  GList *helps = glade_xml_get_widget_prefix (xml, "help_button_");
+  GSList *helps = gtk_builder_get_objects (xml);
 
-  GList *i;
-  for ( i = g_list_first (helps); i ; i = g_list_next (i))
-    g_signal_connect (GTK_WIDGET (i->data), "clicked", give_help, 0);
+  GSList *i;
+  for ( i = helps; i ; i = g_slist_next (i))
+    {
+      GObject *o = i->data;
+      if ( GTK_IS_WIDGET (o) )
+       {
+         gchar *name = NULL;
+         gchar s[12] = {0};
+         g_object_get (o, "name", &name, NULL);
+
+         if ( name)
+           strncpy (s, name, 11);
+         s[11] = '\0';
 
-  g_list_free (helps);
-}
 
+         if ( 0 == strcmp ("help_button", s))
+           {
+           g_signal_connect (o, "clicked", give_help, 0);
+           }
+       }
+    }
+
+  g_slist_free (helps);
+}
 
 
 void
 reference_manual (GtkMenuItem *menu, gpointer data)
 {
   GError *err = NULL;
-  if ( ! g_spawn_command_line_async ("yelp info:pspp", &err) )
+  gchar *cmd = g_strdup_printf ("yelp file://%s", relocate (DOCDIR "/pspp.xml"));
+
+  if ( ! g_spawn_command_line_async (cmd, &err) )
     {
       msg (ME, _("Cannot open reference manual: %s"), err->message);
     }
+
+  g_free (cmd);
   g_clear_error (&err);
 }
 
@@ -242,64 +332,17 @@ execute_syntax (struct getl_interface *sss)
 
   reader = proc_extract_active_file_data (the_dataset);
   if (!lazy_casereader_destroy (reader, lazy_serial))
-    psppire_data_store_set_case_file (the_data_store,
-                                      psppire_case_file_new (reader));
+    psppire_data_store_set_reader (the_data_store, reader);
 
   som_flush ();
 
-  reload_the_viewer ();
+  psppire_output_window_reload ();
 
   return retval;
 }
 
 
 
-#ifdef G_ENABLE_DEBUG
-# define g_marshal_value_peek_int(v)      g_value_get_int (v)
-#else
-# define g_marshal_value_peek_int(v)      (v)->data[0].v_int
-#endif
-
-
-/* VOID:INT,INT,INT */
-void
-marshaller_VOID__INT_INT_INT (GClosure     *closure,
-                        GValue       *return_value,
-                        guint         n_param_values,
-                        const GValue *param_values,
-                        gpointer      invocation_hint,
-                        gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__INT_INT_INT) (gpointer     data1,
-                                                 gint         arg_1,
-                                                 gint         arg_2,
-                                                 gint         arg_3,
-                                                 gpointer     data2);
-  register GMarshalFunc_VOID__INT_INT_INT callback;
-  register GCClosure *cc = (GCClosure*) closure;
-  register gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 4);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_int (param_values + 1),
-            g_marshal_value_peek_int (param_values + 2),
-            g_marshal_value_peek_int (param_values + 3),
-            data2);
-}
-
 /* Create a deep copy of SRC */
 GtkListStore *
 clone_list_store (const GtkListStore *src)
@@ -344,3 +387,12 @@ clone_list_store (const GtkListStore *src)
 }
 
 
+void
+paste_syntax_in_new_window (const gchar *syntax)
+{
+  GtkWidget *se = psppire_syntax_window_new ();
+
+  gtk_text_buffer_insert_at_cursor (PSPPIRE_SYNTAX_WINDOW (se)->buffer, syntax, -1);
+
+  gtk_widget_show (se);
+}
index 3b9f03703ea7c85b367b057f47fa63757f4dc218..30792faf0835f5f8b0f1e2d6da11c469e9633516 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2004  Free Software Foundation
+   Copyright (C) 2004, 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 "relocatable.h"
 
+#include <data/format.h>
 #include <data/value.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
-/*
-   GtkRecentChooserMenu was added in 2.10.0
-   but it didn't support GtkRecentFilters until
-   2.10.2
-*/
-#define RECENT_LISTS_AVAILABLE GTK_CHECK_VERSION (2, 10, 2)
+
+
+void paste_syntax_in_new_window (const gchar *syntax);
 
 struct fmt_spec;
 
@@ -42,29 +39,29 @@ gchar * value_to_text (union value v, struct fmt_spec format);
 gboolean text_to_value (const gchar *text, union value *v,
                       struct fmt_spec format);
 
-GtkWidget * get_widget_assert (GladeXML *xml, const gchar *name);
+GObject *get_object_assert (GtkBuilder *builder, const gchar *name, GType type);
+GtkAction * get_action_assert (GtkBuilder *builder, const gchar *name);
+GtkWidget * get_widget_assert (GtkBuilder *builder, const gchar *name);
 
 /* Converts a string in the pspp locale to utf-8 */
-char * pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err);
+gchar * pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err);
+gchar * utf8_to_pspp_locale (const gchar *text, gssize len, GError **err);
+
+gchar * convert_glib_filename_to_system_filename (const gchar *fname,
+                                                 GError **err);
 
 
-void connect_help (GladeXML *);
+void connect_help (GtkBuilder *);
 
 void reference_manual (GtkMenuItem *, gpointer);
 
 struct getl_interface;
 gboolean execute_syntax (struct getl_interface *sss);
 
-#define XML_NEW(FILE) \
-   glade_xml_new (relocate(PKGDATADIR "/" FILE), NULL, NULL)
 
+#define builder_new(NAME) builder_new_real (relocate (PKGDATADIR "/" NAME))
 
-void marshaller_VOID__INT_INT_INT (GClosure     *closure,
-                                  GValue       *return_value,
-                                  guint         n_param_values,
-                                  const GValue *param_values,
-                                  gpointer      invocation_hint,
-                                  gpointer      marshal_data);
+GtkBuilder *builder_new_real (const gchar *name);
 
 
 /* Create a deep copy of SRC */
index eca3f98f6866d9ddeb1a44133914c427f4bfad14..a36e9410a345557f6c7e9a8ece90b0a15b5aec3d 100644 (file)
 #include "psppire.h"
 #include "progname.h"
 #include <stdlib.h>
-#include <getopt.h>
+#include <argp.h>
 #include <gl/relocatable.h>
+#include <ui/command-line.h>
+#include <ui/source-init-opts.h>
 
 #include <libpspp/version.h>
 #include <libpspp/copyleft.h>
 
-static gboolean parse_command_line (int *argc, char ***argv, gchar **filename,
-                                   gboolean *show_splash, GError **err);
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 
+const char *argp_program_version = version;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
 
+\f
+/* Arguments to be interpreted before the X server gets initialised */
+
+static const struct argp_option startup_options [] =
+  {
+    {"no-splash",  'q',  0,  0,  N_("Don't show the splash screen"), 0 },
+    { 0, 0, 0, 0, 0, 0 }
+  };
+
+static error_t
+parse_startup_opts (int key, char *arg, struct argp_state *state)
+{
+  gboolean *showsplash = state->input;
+
+  switch (key)
+    {
+    case 'q':
+      *showsplash = FALSE;
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+static const struct argp startup_argp = {startup_options, parse_startup_opts, 0, 0, 0, 0, 0};
+
+\f
 
 static GtkWidget *
 create_splash_window (void)
@@ -72,13 +105,22 @@ quit_one_loop (gpointer data)
   return FALSE;
 }
 
+struct initialisation_parameters
+{
+  int argc;
+  char **argv;
+  GtkWidget *splash_window;
+  struct command_line_processor *clp;
+};
+
 
 static gboolean
 run_inner_loop (gpointer data)
 {
-  initialize ();
+  struct initialisation_parameters *ip = data;
+  initialize (ip->clp, ip->argc, ip->argv);
 
-  g_timeout_add (500, hide_splash_window, data);
+  g_timeout_add (500, hide_splash_window, ip->splash_window);
 
   gtk_main ();
 
@@ -92,10 +134,10 @@ run_inner_loop (gpointer data)
 int
 main (int argc, char *argv[])
 {
-  GtkWidget *splash_window;
-  gchar *filename = 0;
+  struct command_line_processor *clp ;
+  struct initialisation_parameters init_p;
   gboolean show_splash = TRUE;
-  GError *err = 0;
+
   const gchar *vers;
 
   set_program_name (argv[0]);
@@ -110,78 +152,32 @@ main (int argc, char *argv[])
                                 GTK_MINOR_VERSION,
                                 GTK_MICRO_VERSION)) )
     {
-      g_critical (vers);
+      g_warning (vers);
     }
 
-  /* Deal with options like --version, --help etc */
-  if ( ! parse_command_line (&argc, &argv, &filename, &show_splash, &err) )
-    {
-      g_clear_error (&err);
-      return 0;
-    }
+  clp = command_line_processor_create (_("PSPPIRE --- A user interface for PSPP"), "[ DATA-FILE ]", 0);
+
+  command_line_processor_add_options (clp, &startup_argp, _("Miscellaneous options:"),  &show_splash);
+  command_line_processor_add_options (clp, &post_init_argp,
+                                     _("Options affecting syntax and behavior:"),  NULL);
+  command_line_processor_add_options (clp, &non_option_argp, NULL, NULL);
+
+  command_line_processor_parse (clp, argc, argv);
 
   gdk_init (&argc, &argv);
 
-  splash_window = create_splash_window ();
+  init_p.splash_window = create_splash_window ();
+  init_p.argc = argc;
+  init_p.argv = argv;
+  init_p.clp = clp;
+
   if ( show_splash )
-    gtk_widget_show (splash_window);
+    gtk_widget_show (init_p.splash_window);
 
   g_idle_add (quit_one_loop, 0);
 
-  gtk_quit_add (0, run_inner_loop, splash_window);
+  gtk_quit_add (0, run_inner_loop, &init_p);
   gtk_main ();
 
-
   return 0;
 }
-
-
-/* Parses the command line specified by ARGC and ARGV as received by
-   main ().  Returns true if normal execution should proceed,
-   false if the command-line indicates that PSPP should exit. */
-static gboolean
-parse_command_line (int *argc, char ***argv, gchar **filename,
-                   gboolean *show_splash, GError **err)
-{
-
-  static struct option long_options[] =
-    {
-      {"help", no_argument, NULL, 'h'},
-      {"version", no_argument, NULL, 'V'},
-      {"no-splash", no_argument, NULL, 'q'},
-      {0, 0, 0, 0},
-    };
-
-  int c;
-
-  for (;;)
-    {
-      c = getopt_long (*argc, *argv, "hVq", long_options, NULL);
-      if (c == -1)
-       break;
-
-      switch (c)
-       {
-       case 'h':
-         g_print ("Usage: psppire {|--help|--version|--no-splash}\n");
-          return FALSE;
-       case 'V':
-         g_print (version);
-         g_print ("\n");
-         g_print (legal);
-         return FALSE;
-       case 'q':
-         *show_splash = FALSE;
-         break;
-       default:
-         return FALSE;
-       }
-    }
-
-  if ( optind < *argc)
-    {
-      *filename = (*argv)[optind];
-    }
-
-  return TRUE;
-}
diff --git a/src/ui/gui/marshaller-list b/src/ui/gui/marshaller-list
new file mode 100644 (file)
index 0000000..cb7c911
--- /dev/null
@@ -0,0 +1,13 @@
+#List of custom marshallers used by psppire
+
+BOOLEAN:BOOLEAN
+BOOLEAN:ENUM
+BOOLEAN:BOXED,POINTER
+BOOLEAN:VOID
+VOID:BOXED,BOXED
+VOID:INT,INT
+VOID:INT,LONG
+VOID:INT,INT,INT
+VOID:INT,INT,INT,INT
+VOID:INT,POINTER
+VOID:OBJECT,OBJECT
index 140cea7e55a4e34f3c25061a669a13c0d2cb7297..866f8c6d91f1065762ba0a636291c255bd05c67a 100644 (file)
@@ -31,7 +31,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include <glib.h>
 
 #include "helper.h"
@@ -49,8 +48,8 @@ static GQueue *late_queue;
 
 static int error_cnt, warning_cnt, note_cnt;
 
-static GladeXML *message_xml;
-static GtkDialog *message_dialog;
+static GtkBuilder *message_xml;
+static GtkWidget *message_dialog;
 
 void
 message_dialog_init (struct source_stream *ss)
@@ -60,9 +59,8 @@ message_dialog_init (struct source_stream *ss)
   late_queue = g_queue_new ();
   error_cnt = warning_cnt = note_cnt = 0;
   msg_init (ss, enqueue_msg);
-  message_xml = XML_NEW ("message-dialog.glade");
-  message_dialog = GTK_DIALOG (get_widget_assert (message_xml,
-                                                  "message-dialog"));
+  message_xml = builder_new ("message-dialog.ui");
+  message_dialog = get_widget_assert (message_xml, "message-dialog");
 }
 
 void
@@ -72,7 +70,7 @@ message_dialog_done (void)
   g_queue_free (early_queue);
   dropped_messages = 0;
   g_queue_free (late_queue);
-  gtk_widget_destroy (GTK_WIDGET (message_dialog));
+  gtk_widget_destroy (message_dialog);
   g_object_unref (message_xml);
 }
 
@@ -183,7 +181,7 @@ enqueue_msg (const struct msg *msg)
     }
 }
 
-gboolean
+static gboolean
 popup_messages (gpointer unused UNUSED)
 {
   GtkTextBuffer *text_buffer;
@@ -194,12 +192,20 @@ popup_messages (gpointer unused UNUSED)
   struct string msg = DS_EMPTY_INITIALIZER;
   int message_cnt;
 
+  /* Set up the dialog. */
+  if (message_xml == NULL || message_dialog == NULL)
+    goto use_fallback;
+
   /* If a pointer grab is in effect, then the combination of that, and
      a modal dialog box, will cause an impossible situation.
      So don't pop it up just yet.
   */
-  if ( gdk_pointer_is_grabbed ())
-    return TRUE;
+  if ( gdk_display_pointer_is_grabbed (gtk_widget_get_display (message_dialog)))
+    {
+      ds_destroy (&lead);
+      ds_destroy (&msg);
+      return TRUE;
+    }
 
   /* Compose the lead-in. */
   message_cnt = error_cnt + warning_cnt + note_cnt;
@@ -239,10 +245,6 @@ popup_messages (gpointer unused UNUSED)
   while (!g_queue_is_empty (late_queue))
     format_message (g_queue_pop_head (late_queue), &msg);
 
-  /* Set up the dialog. */
-  if (message_xml == NULL || message_dialog == NULL)
-    goto use_fallback;
-
   text_buffer = gtk_text_buffer_new (NULL);
   gtk_text_buffer_get_end_iter (text_buffer, &end);
   gtk_text_buffer_insert (text_buffer, &end, ds_data (&msg), ds_length (&msg));
@@ -257,8 +259,9 @@ popup_messages (gpointer unused UNUSED)
     goto use_fallback;
   gtk_text_view_set_buffer (text_view, text_buffer);
 
-  gtk_dialog_run (message_dialog);
-  gtk_widget_hide (GTK_WIDGET (message_dialog));
+  gtk_widget_grab_focus (get_widget_assert (message_xml, "close-button"));
+  gtk_dialog_run ( GTK_DIALOG (message_dialog));
+  gtk_widget_hide (message_dialog);
 
   ds_destroy (&lead);
   ds_destroy (&msg);
index 719c1aadce7e8adb4b01c223a2385cc277bfdf15..1069fc1b09f95186cf0e97cff3ebabb101bb7779 100644 (file)
@@ -1,10 +1,12 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.0 on Sat Feb  9 22:47:30 2008 -->
+<!--Generated with glade3 3.4.5 on Thu Dec 18 20:43:13 2008 -->
 <glade-interface>
   <widget class="GtkDialog" id="message-dialog">
+    <property name="width_request">600</property>
+    <property name="height_request">350</property>
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-    <property name="border_width">5</property>
+    <property name="border_width">12</property>
     <property name="title" translatable="yes">Messages Reported</property>
     <property name="modal">True</property>
     <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
       <widget class="GtkVBox" id="dialog-vbox1">
         <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="spacing">2</property>
+        <property name="spacing">24</property>
         <child>
           <widget class="GtkHBox" id="hbox1">
             <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="spacing">12</property>
             <child>
               <widget class="GtkImage" id="image1">
                 <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="yalign">0</property>
                 <property name="stock">gtk-dialog-info</property>
                 <property name="icon_size">6</property>
               </widget>
@@ -34,6 +38,7 @@
               <widget class="GtkVBox" id="vbox1">
                 <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="spacing">6</property>
                 <child>
                   <widget class="GtkLabel" id="lead-in">
                     <property name="visible">True</property>
@@ -62,6 +67,8 @@
                         <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="wrap_mode">GTK_WRAP_WORD</property>
+                        <property name="left_margin">6</property>
+                        <property name="right_margin">6</property>
                         <property name="cursor_visible">False</property>
                       </widget>
                     </child>
@@ -86,7 +93,7 @@
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
             <property name="layout_style">GTK_BUTTONBOX_END</property>
             <child>
-              <widget class="GtkButton" id="button1">
+              <widget class="GtkButton" id="close-button">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
index 9fc53161a4884c9616cf9fa5a61b566c94ff64f3..45eeca68beffda26b705fb2228f41025c81dd5e6 100644 (file)
@@ -23,6 +23,5 @@ struct source_stream ;
 
 void message_dialog_init (struct source_stream *);
 void message_dialog_done (void);
-void popup_message (const struct msg *m);
 
 #endif
index 4e9682f73544bcdcfae24cf26453b51d3482d8e6..0c9831aec334fd3d759a58f4508427b3538c3d58 100644 (file)
@@ -32,7 +32,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <string.h>
 
@@ -219,20 +218,18 @@ on_delete (GtkWidget *w, GdkEvent *e, gpointer data)
 }
 
 
-/* Creates the dialog structure from the xml */
+/* Creates the dialog structure */
 struct missing_val_dialog *
-missing_val_dialog_create (GladeXML *xml)
+missing_val_dialog_create (GtkWindow *toplevel)
 {
-  struct missing_val_dialog *dialog = g_malloc (sizeof (*dialog));
+  GtkBuilder *xml = builder_new ("var-sheet-dialogs.ui");
 
-  connect_help (xml);
+  struct missing_val_dialog *dialog = g_malloc (sizeof (*dialog));
 
   dialog->window = get_widget_assert (xml, "missing_values_dialog");
 
   gtk_window_set_transient_for
-    (GTK_WINDOW (dialog->window),
-     GTK_WINDOW (get_widget_assert (xml, "data_editor")));
-
+    (GTK_WINDOW (dialog->window), toplevel);
 
   g_signal_connect_swapped (get_widget_assert (xml, "missing_val_cancel"),
                   "clicked", G_CALLBACK (gtk_widget_hide), dialog->window);
@@ -262,12 +259,14 @@ missing_val_dialog_create (GladeXML *xml)
     GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing"));
 
 
-  g_signal_connect (G_OBJECT (dialog->button_discrete), "toggled",
+  g_signal_connect (dialog->button_discrete, "toggled",
                   G_CALLBACK (discrete), dialog);
 
-  g_signal_connect (G_OBJECT (dialog->button_range), "toggled",
+  g_signal_connect (dialog->button_range, "toggled",
                   G_CALLBACK (range), dialog);
 
+  g_object_unref (xml);
+
   return dialog;
 }
 
index b7fab934aed838fe762bed1f3659bb4ced1690fa..7dc079d731d342244ef256759acc9cc6cc23e1c4 100644 (file)
@@ -22,7 +22,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <data/missing-values.h>
 
@@ -48,7 +47,7 @@ struct missing_val_dialog
   GtkWidget *discrete;
 };
 
-struct missing_val_dialog * missing_val_dialog_create (GladeXML *xml);
+struct missing_val_dialog * missing_val_dialog_create (GtkWindow *toplevel);
 
 void missing_val_dialog_show (struct missing_val_dialog *dialog);
 
index 13f3621092a06050fbe5be38e0084efbf1b1bbe5..2fb88268731a3b1a4126a1450fca3d122c9252c6 100644 (file)
 
 
 #include <config.h>
-#include <glade/glade.h>
 #include <gtk/gtk.h>
 #include "oneway-anova-dialog.h"
 #include "psppire-dict.h"
 #include "psppire-var-store.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-dialog.h"
 #include "dialog-common.h"
-#include "dict-display.h"
 #include "psppire-acr.h"
+#include "psppire-selector.h"
+#include "dict-display.h"
 
 
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 
 #include "gettext.h"
@@ -121,58 +121,57 @@ refresh (struct oneway_anova_dialog *ow)
 }
 
 
+
 /* Pops up the dialog box */
 void
 oneway_anova_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   PsppireVarStore *vs = NULL;
 
-  GladeXML *xml = XML_NEW ("oneway.glade");
-
   struct oneway_anova_dialog ow;
 
+  GtkBuilder *builder = builder_new ("oneway.ui");
+
   GtkWidget *dict_view =
-    get_widget_assert (xml, "oneway-anova-treeview1");
+    get_widget_assert (builder, "oneway-anova-treeview1");
 
   GtkWidget *selector2 =
-    get_widget_assert (xml, "oneway-anova-selector2");
+    get_widget_assert (builder, "oneway-anova-selector2");
 
   GtkWidget *selector1 =
-    get_widget_assert (xml, "oneway-anova-selector1");
+    get_widget_assert (builder, "oneway-anova-selector1");
 
   GtkWidget *contrasts_button =
-    get_widget_assert (xml, "contrasts-button");
+    get_widget_assert (builder, "contrasts-button");
 
 
   g_signal_connect_swapped (contrasts_button, "clicked",
                    G_CALLBACK (run_contrasts_dialog), &ow);
 
 
-  ow.factor_entry = get_widget_assert (xml, "oneway-anova-entry");
+  ow.factor_entry = get_widget_assert (builder, "oneway-anova-entry");
   ow.vars_treeview =
-    get_widget_assert (xml, "oneway-anova-treeview2");
+    get_widget_assert (builder, "oneway-anova-treeview2");
 
   ow.descriptives =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "checkbutton1"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "checkbutton1"));
 
   ow.homogeneity =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "checkbutton2"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "checkbutton2"));
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
   ow.dict = vs->dict;
 
   ow.dialog =
-    GTK_WINDOW (get_widget_assert (xml, "oneway-anova-dialog"));
+    GTK_WINDOW (get_widget_assert (builder, "oneway-anova-dialog"));
 
-  gtk_window_set_transient_for (ow.dialog, de->parent.window);
+  gtk_window_set_transient_for (ow.dialog, GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (dict_view, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (ow.vars_treeview), vs->dict);
 
@@ -202,16 +201,16 @@ oneway_anova_dialog (GObject *o, gpointer data)
 
   {
     struct contrasts_subdialog *cd = &ow.contrasts;
-    GtkEntry *entry = GTK_ENTRY (get_widget_assert (xml, "entry1"));
+    GtkEntry *entry = GTK_ENTRY (get_widget_assert (builder, "entry1"));
 
-    cd->acr = PSPPIRE_ACR (get_widget_assert (xml, "psppire-acr1"));
-    cd->contrasts_dialog = get_widget_assert (xml, "contrasts-dialog");
+    cd->acr = PSPPIRE_ACR (get_widget_assert (builder, "psppire-acr1"));
+    cd->contrasts_dialog = get_widget_assert (builder, "contrasts-dialog");
 
-    cd->next = get_widget_assert (xml, "next-button");
-    cd->prev = get_widget_assert (xml, "prev-button");
-    cd->ctotal = get_widget_assert (xml, "entry2");
+    cd->next = get_widget_assert (builder, "next-button");
+    cd->prev = get_widget_assert (builder, "prev-button");
+    cd->ctotal = get_widget_assert (builder, "entry2");
 
-    cd->stack_label = get_widget_assert (xml, "contrast-stack-label");
+    cd->stack_label = get_widget_assert (builder, "contrast-stack-label");
 
     /* Contrasts */
     ow.contrasts_array = g_array_new (FALSE, FALSE, sizeof (GtkListStore *));
@@ -222,7 +221,7 @@ oneway_anova_dialog (GObject *o, gpointer data)
     psppire_acr_set_entry (cd->acr, entry);
 
     gtk_window_set_transient_for (GTK_WINDOW (cd->contrasts_dialog),
-                                 de->parent.window);
+                                 GTK_WINDOW (de));
   }
 
   response = psppire_dialog_run (PSPPIRE_DIALOG (ow.dialog));
@@ -232,6 +231,7 @@ oneway_anova_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&ow);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -241,11 +241,7 @@ oneway_anova_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&ow);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
@@ -256,7 +252,7 @@ oneway_anova_dialog (GObject *o, gpointer data)
 
   g_array_free (ow.contrasts_array, FALSE);
 
-  g_object_unref (xml);
+  g_object_unref (builder);
 }
 
 
index bdca13ddb54e8dec747bf3d4f70c464520518fb2..4b12f68b9b050e31e64591560c8c0843c912452c 100644 (file)
                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="oneway-anova-treeview1">
+                  <widget class="PsppireDictView" id="oneway-anova-treeview1">
                     <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>
diff --git a/src/ui/gui/output-viewer.c b/src/ui/gui/output-viewer.c
deleted file mode 100644 (file)
index 2b0c4af..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007 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 <gtk/gtk.h>
-#include <data/file-name.h>
-#include "window-manager.h"
-#include "output-viewer.h"
-#include "helper.h"
-#include "about.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <glade/glade.h>
-#include <ctype.h>
-
-#include "xalloc.h"
-
-struct output_viewer
-{
-  struct editor_window parent;
-  GtkTextBuffer *buffer;  /* The buffer which contains the text */
-  GtkWidget *textview ;
-  FILE *fp;               /* The file it's viewing */
-};
-
-
-static void
-cancel_urgency (GtkWindow *window,  gpointer data)
-{
-  gtk_window_set_urgency_hint (window, FALSE);
-}
-
-
-static struct output_viewer *the_output_viewer = NULL;
-
-int viewer_length = 16;
-int viewer_width = 59;
-
-/* Callback for the "delete" action (clicking the x on the top right
-   hand corner of the window) */
-static gboolean
-on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
-{
-  struct output_viewer *ov = user_data;
-
-  g_free (ov);
-
-  the_output_viewer = NULL;
-
-  unlink (output_file_name ());
-
-  return 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)
-{
-  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));
-}
-
-
-
-/*
-  Create a new output viewer
-*/
-struct output_viewer *
-new_output_viewer (void)
-{
-  GladeXML *xml = XML_NEW ("output-viewer.glade");
-
-  struct output_viewer *ov ;
-  struct editor_window *e;
-
-  connect_help (xml);
-
-  ov = g_malloc (sizeof (*ov));
-
-  e = (struct editor_window *)ov;
-
-
-  e->window = GTK_WINDOW (get_widget_assert (xml, "output-viewer-window"));
-  ov->textview = get_widget_assert (xml, "output-viewer-textview");
-  ov->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (ov->textview));
-
-  g_signal_connect (e->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 (ov->textview, font_desc);
-    pango_font_description_free (font_desc);
-  }
-
-  g_signal_connect (ov->textview, "size-allocate",
-                   G_CALLBACK (on_textview_resize), NULL);
-
-  ov->fp = NULL;
-
-  g_signal_connect (get_widget_assert (xml,"help_about"),
-                   "activate",
-                   G_CALLBACK (about_new),
-                   e->window);
-
-  g_signal_connect (get_widget_assert (xml,"help_reference"),
-                   "activate",
-                   G_CALLBACK (reference_manual),
-                   NULL);
-
-  g_signal_connect (get_widget_assert (xml,"windows_minimise-all"),
-                   "activate",
-                   G_CALLBACK (minimise_all_windows),
-                   NULL);
-
-  g_object_unref (xml);
-
-
-  g_signal_connect (e->window, "delete-event",
-                   G_CALLBACK (on_delete), ov);
-
-  return ov;
-}
-
-
-void
-reload_the_viewer (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 =
-       (struct output_viewer *) window_create (WINDOW_OUTPUT, NULL);
-    }
-
-  reload_viewer (the_output_viewer);
-}
-
-#define OUTPUT_FILE_NAME "psppire.txt"
-
-void
-reload_viewer (struct output_viewer *ov)
-{
-  GtkTextIter end_iter;
-  GtkTextMark *mark ;
-
-  static char *line = NULL;
-
-  gboolean chars_inserted = FALSE;
-
-  gtk_text_buffer_get_end_iter (ov->buffer, &end_iter);
-
-  line = xrealloc (line, sizeof (char) * (viewer_width + 1));
-
-
-  mark = gtk_text_buffer_create_mark (ov->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_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
-       return;
-      }
-
-    /* Delete all the entire buffer */
-    gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
-    gtk_text_buffer_delete (ov->buffer, &start_iter, &end_iter);
-
-
-    gtk_text_buffer_get_start_iter (ov->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 (ov->buffer, &start_iter, line, -1);
-      }
-
-    fclose (fp);
-  }
-#else
-  {
-    if ( ov->fp == NULL)
-      {
-       ov->fp = fopen (output_file_name (), "r");
-       if ( ov->fp == NULL)
-         {
-           g_print ("Cannot open %s\n", output_file_name ());
-           return;
-         }
-      }
-
-    /* Read in the next lot of text */
-    while (fgets (line, viewer_width + 1, ov->fp) != NULL)
-      {
-       chars_inserted = TRUE;
-       gtk_text_buffer_insert (ov->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 (ov->textview),
-                               mark,
-                               0.1, TRUE, 0.0, 0.0);
-
-
-  if ( chars_inserted )
-    gtk_window_set_urgency_hint ( ((struct editor_window *)ov)->window, TRUE);
-}
-
-
-
-
-const char *
-output_file_name (void)
-{
-  const char *dir = default_output_path ();
-  static char *filename = NULL;
-
-  if ( NULL == filename )
-    filename = xasprintf ("%s%s", dir, OUTPUT_FILE_NAME);
-
-
-  return filename;
-}
index 0bcf5f3e407d8cce0f5207d99c127ebe4136fa7c..0b709ee4cadee9fc91a8cb53bdc5a05324612169 100644 (file)
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="menuitem3">
+              <widget class="GtkMenuItem" id="windows_menuitem">
                 <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">_Windows</property>
                 <property name="use_underline">True</property>
                 <child>
-                  <widget class="GtkMenu" id="menu4">
+                  <widget class="GtkMenu" id="windows_menu">
                     <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>
                     <child>
diff --git a/src/ui/gui/output-viewer.h b/src/ui/gui/output-viewer.h
deleted file mode 100644 (file)
index e5bf5c1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  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
-   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_VIEWER_H
-#define OUTPUT_VIEWER_H
-
-#include <gtk/gtk.h>
-
-#include "window-manager.h"
-
-
-extern int viewer_length ;
-extern int viewer_width ;
-
-struct output_viewer * new_output_viewer (void);
-
-void reload_viewer (struct output_viewer *);
-
-void reload_the_viewer (void);
-
-
-const char * output_file_name (void);
-
-#endif
diff --git a/src/ui/gui/pspp.desktop b/src/ui/gui/pspp.desktop
new file mode 100644 (file)
index 0000000..9796b2d
--- /dev/null
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Name=GNU PSPP
+GenericName=Statistical Software
+GenericName[de]=Statistiksoftware
+Comment=Analyze statistical data with a free alternative to SPSS
+Comment[de]= Statistische Daten mit einer freien Alternative zu SPSS analysieren
+Exec=psppire %F
+TryExec=psppire
+Icon=psppicon.png
+StartupNotify=false
+Terminal=false
+Type=Application
+Categories=GTK;Education;Science;Math;
index 4552210729df4dd15243d30c47fcd497f567eee9..184ebd16df8d8deac3115172710dd451847eb7d4 100644 (file)
@@ -56,7 +56,7 @@ psppire_button_box_get_type (void)
       };
 
       button_box_type = g_type_register_static (GTK_TYPE_BUTTON_BOX,
-                                           "PsppireButtonBox", &button_box_info, 0);
+                                           "PsppireButtonBox", &button_box_info, G_TYPE_FLAG_ABSTRACT);
     }
 
   return button_box_type;
@@ -142,7 +142,7 @@ psppire_button_box_class_init (PsppireButtonBoxClass *class)
     g_param_spec_flags ("buttons",
                        _("Buttons"),
                        _("The mask that decides what buttons appear in the button box"),
-                       G_TYPE_PSPPIRE_BUTTON_MASK,
+                       PSPPIRE_TYPE_BUTTON_MASK,
                        PSPPIRE_BUTTON_OK_MASK |
                        PSPPIRE_BUTTON_CANCEL_MASK |
                        PSPPIRE_BUTTON_RESET_MASK |
index 3f865facd040e33cd937112cddf66aca8c7e6ac5..03f9fe34e6a737e5ae12558a704f80042cd81ce7 100644 (file)
@@ -71,7 +71,7 @@ _psppire_button_box_child_requisition (GtkWidget *widget,
                                       int       *width,
 
 
-#define G_TYPE_PSPPIRE_BUTTON_MASK \
+#define PSPPIRE_TYPE_BUTTON_MASK \
   (psppire_button_flags_get_type())
                                       int       *height);
 
diff --git a/src/ui/gui/psppire-case-file.c b/src/ui/gui/psppire-case-file.c
deleted file mode 100644 (file)
index 2edfec8..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  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
-   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 <string.h>
-#include <stdlib.h>
-
-#include "psppire-case-file.h"
-
-#include <gtksheet/gtkextra-marshal.h>
-
-#include <data/format.h>
-#include <data/case.h>
-#include <data/data-in.h>
-#include <data/datasheet.h>
-#include <data/casereader.h>
-#include <math/sort.h>
-#include <libpspp/misc.h>
-
-#include "xalloc.h"
-#include "xmalloca.h"
-
-/* --- prototypes --- */
-static void psppire_case_file_class_init       (PsppireCaseFileClass   *class);
-static void psppire_case_file_init     (PsppireCaseFile        *case_file);
-static void psppire_case_file_finalize (GObject                *object);
-
-
-/* --- variables --- */
-static GObjectClass     *parent_class = NULL;
-
-enum  {CASE_CHANGED,
-       CASE_INSERTED,
-       CASES_DELETED,
-       n_SIGNALS};
-
-static guint signals [n_SIGNALS];
-
-
-/* --- functions --- */
-/**
- * psppire_case_file_get_type:
- * @returns: the type ID for accelerator groups.
- */
-GType
-psppire_case_file_get_type (void)
-{
-  static GType object_type = 0;
-
-  if (!object_type)
-    {
-      static const GTypeInfo object_info = {
-       sizeof (PsppireCaseFileClass),
-       (GBaseInitFunc) NULL,
-       (GBaseFinalizeFunc) NULL,
-       (GClassInitFunc) psppire_case_file_class_init,
-       NULL,   /* class_finalize */
-       NULL,   /* class_data */
-       sizeof (PsppireCaseFile),
-       0,      /* n_preallocs */
-       (GInstanceInitFunc) psppire_case_file_init,
-      };
-
-      object_type = g_type_register_static (G_TYPE_OBJECT, "PsppireCaseFile",
-                                           &object_info, 0);
-    }
-
-  return object_type;
-}
-
-/* Properties */
-enum
-{
-  PROP_0,
-  PROP_DATASHEET,
-  PROP_READER
-};
-
-
-
-
-static void
-psppire_case_file_set_property (GObject         *object,
-                               guint            prop_id,
-                               const GValue    *value,
-                               GParamSpec      *pspec)
-
-{
-  PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
-
-  switch (prop_id)
-    {
-    case PROP_READER:
-      cf->datasheet = datasheet_create (g_value_get_pointer (value));
-      cf->accessible = TRUE;
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    };
-}
-
-static void
-psppire_case_file_get_property (GObject         *object,
-                               guint            prop_id,
-                               GValue          *value,
-                               GParamSpec      *pspec)
-{
-  PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
-
-  switch (prop_id)
-    {
-    case PROP_DATASHEET:
-      g_value_set_pointer (value, cf->datasheet);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    };
-}
-
-
-static void
-psppire_case_file_class_init (PsppireCaseFileClass *class)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (class);
-  GParamSpec *datasheet_spec ;
-  GParamSpec *reader_spec ;
-
-  parent_class = g_type_class_peek_parent (class);
-
-  object_class->finalize = psppire_case_file_finalize;
-
-  datasheet_spec =
-    g_param_spec_pointer ("datasheet",
-                         "Datasheet",
-                         "A pointer to the datasheet belonging to this object",
-                         G_PARAM_READABLE );
-  reader_spec =
-    g_param_spec_pointer ("casereader",
-                         "CaseReader",
-                         "A pointer to the case reader from which this object is constructed",
-                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE );
-
-  object_class->set_property = psppire_case_file_set_property;
-  object_class->get_property = psppire_case_file_get_property;
-
-  g_object_class_install_property (object_class,
-                                   PROP_DATASHEET,
-                                   datasheet_spec);
-
-  g_object_class_install_property (object_class,
-                                   PROP_READER,
-                                   reader_spec);
-
-  signals [CASE_CHANGED] =
-    g_signal_new ("case-changed",
-                 G_TYPE_FROM_CLASS (class),
-                 G_SIGNAL_RUN_FIRST,
-                 0,
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__INT,
-                 G_TYPE_NONE,
-                 1,
-                 G_TYPE_INT);
-
-
-  signals [CASE_INSERTED] =
-    g_signal_new ("case-inserted",
-                 G_TYPE_FROM_CLASS (class),
-                 G_SIGNAL_RUN_FIRST,
-                 0,
-                 NULL, NULL,
-                 g_cclosure_marshal_VOID__INT,
-                 G_TYPE_NONE,
-                 1,
-                 G_TYPE_INT);
-
-
-  signals [CASES_DELETED] =
-    g_signal_new ("cases-deleted",
-                 G_TYPE_FROM_CLASS (class),
-                 G_SIGNAL_RUN_FIRST,
-                 0,
-                 NULL, NULL,
-                 gtkextra_VOID__INT_INT,
-                 G_TYPE_NONE,
-                 2,
-                 G_TYPE_INT, G_TYPE_INT);
-}
-
-static void
-psppire_case_file_finalize (GObject *object)
-{
-  PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
-
-  if ( cf->accessible)
-    datasheet_destroy (cf->datasheet);
-
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-psppire_case_file_init (PsppireCaseFile *cf)
-{
-  cf->datasheet = NULL;
-  cf->accessible = FALSE;
-}
-
-
-/**
- * psppire_case_file_new:
- * @returns: a new #PsppireCaseFile object
- *
- * Creates a new #PsppireCaseFile.
- */
-PsppireCaseFile*
-psppire_case_file_new (struct casereader *reader)
-{
-  return g_object_new (G_TYPE_PSPPIRE_CASE_FILE,
-                      "casereader", reader,
-                      NULL);
-}
-
-
-gboolean
-psppire_case_file_delete_cases (PsppireCaseFile *cf, casenumber n_cases, casenumber first)
-{
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->datasheet, FALSE);
-  g_return_val_if_fail (cf->accessible, FALSE);
-
-  g_return_val_if_fail (first + n_cases <=
-                       psppire_case_file_get_case_count (cf), FALSE);
-
-  datasheet_delete_rows (cf->datasheet, first, n_cases);
-
-  g_signal_emit (cf, signals [CASES_DELETED], 0, first, n_cases);
-
-  return TRUE;
-}
-
-/* Insert case CC into the case file before POSN */
-gboolean
-psppire_case_file_insert_case (PsppireCaseFile *cf,
-                              struct ccase *cc,
-                              casenumber posn)
-{
-  struct ccase tmp;
-  bool result ;
-
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->datasheet, FALSE);
-  g_return_val_if_fail (cf->accessible, FALSE);
-
-  case_clone (&tmp, cc);
-  result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
-
-  if ( result )
-    g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
-  else
-    g_warning ("Cannot insert case at position %ld\n", posn);
-
-  return result;
-}
-
-
-casenumber
-psppire_case_file_get_case_count (const PsppireCaseFile *cf)
-{
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->accessible, FALSE);
-
-  if ( ! cf->datasheet)
-    return 0;
-
-  return datasheet_get_row_cnt (cf->datasheet);
-}
-
-/* Copies the IDXth value from case CASENUM into VALUE.
-   If VALUE is null, then memory is allocated is allocated with
-   malloc.  Returns the value if successful, NULL on failure. */
-union value *
-psppire_case_file_get_value (const PsppireCaseFile *cf,
-                             casenumber casenum, size_t idx,
-                             union value *value, int width)
-{
-  bool allocated;
-
-  g_return_val_if_fail (cf, false);
-  g_return_val_if_fail (cf->datasheet, false);
-
-  g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
-
-  if (value == NULL)
-    {
-      value = xnmalloc (value_cnt_from_width (width), sizeof *value);
-      allocated = true;
-    }
-  else
-    allocated = false;
-  if (!datasheet_get_value (cf->datasheet, casenum, idx, value, width))
-    {
-      if (allocated)
-        free (value);
-      value = NULL;
-    }
-  return value;
-}
-
-void
-psppire_case_file_clear (PsppireCaseFile *cf)
-{
-  datasheet_destroy (cf->datasheet);
-  cf->datasheet = NULL;
-  g_signal_emit (cf, signals [CASES_DELETED], 0, 0, -1);
-}
-
-/* Set the IDXth value of case C to V.
-   Returns true if successful, false on I/O error. */
-gboolean
-psppire_case_file_set_value (PsppireCaseFile *cf, casenumber casenum, gint idx,
-                           union value *v, gint width)
-{
-  bool ok;
-
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->datasheet, FALSE);
-
-  g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
-
-  ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
-  if (ok)
-    g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
-  return ok;
-}
-
-
-
-/* Set the IDXth value of case C using D_IN */
-gboolean
-psppire_case_file_data_in (PsppireCaseFile *cf, casenumber casenum, gint idx,
-                          struct substring input, const struct fmt_spec *fmt)
-{
-  union value *value = NULL;
-  int width;
-  bool ok;
-
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->datasheet, FALSE);
-
-  g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
-
-  width = fmt_var_width (fmt);
-  value = xmalloca (value_cnt_from_width (width) * sizeof *value);
-  ok = (datasheet_get_value (cf->datasheet, casenum, idx, value, width)
-        && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
-        && datasheet_put_value (cf->datasheet, casenum, idx, value, width));
-
-  if (ok)
-    g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
-
-  freea (value);
-
-  return TRUE;
-}
-
-
-void
-psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering)
-{
-  struct casereader *sorted_data;
-  gint c;
-
-  sorted_data = sort_execute (datasheet_make_reader (cf->datasheet), ordering);
-  cf->datasheet = datasheet_create (sorted_data);
-
-  /* FIXME: Need to have a signal to change a range of cases, instead of
-     calling a signal many times */
-  for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
-    g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
-}
-
-
-/* Resize the cases in the casefile, by inserting N_VALUES into every
-   one of them at the position immediately preceeding WHERE.
-*/
-gboolean
-psppire_case_file_insert_values (PsppireCaseFile *cf,
-                                gint n_values, gint where)
-{
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->accessible, FALSE);
-
-  if ( n_values == 0 )
-    return FALSE;
-
-  g_assert (n_values > 0);
-
-  if ( ! cf->datasheet )
-    cf->datasheet = datasheet_create (NULL);
-
-  {
-    union value *values = xcalloc (n_values, sizeof *values);
-    datasheet_insert_columns (cf->datasheet, values, n_values, where);
-    free (values);
-  }
-
-  return TRUE;
-}
-
-
-/* Fills C with the CASENUMth case.
-   Returns true on success, false otherwise.
- */
-gboolean
-psppire_case_file_get_case (const PsppireCaseFile *cf, casenumber casenum,
-                          struct ccase *c)
-{
-  g_return_val_if_fail (cf, FALSE);
-  g_return_val_if_fail (cf->datasheet, FALSE);
-
-  return datasheet_get_row (cf->datasheet, casenum, c);
-}
-
-
-
-struct casereader *
-psppire_case_file_make_reader (PsppireCaseFile *cf)
-{
-  struct casereader *r = datasheet_make_reader (cf->datasheet);
-  cf->accessible = FALSE;
-  return r;
-}
-
diff --git a/src/ui/gui/psppire-case-file.h b/src/ui/gui/psppire-case-file.h
deleted file mode 100644 (file)
index 13cc08d..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  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
-   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 __CASE_FILE_H__
-#define __CASE_FILE_H__
-
-
-#include <glib-object.h>
-#include <glib.h>
-
-#include <libpspp/str.h>
-#include <data/case.h>
-
-
-
-G_BEGIN_DECLS
-
-
-/* --- type macros --- */
-#define G_TYPE_PSPPIRE_CASE_FILE              (psppire_case_file_get_type ())
-#define PSPPIRE_CASE_FILE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_PSPPIRE_CASE_FILE, PsppireCaseFile))
-#define PSPPIRE_CASE_FILE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_PSPPIRE_CASE_FILE, PsppireCaseFileClass))
-#define G_IS_PSPPIRE_CASE_FILE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_PSPPIRE_CASE_FILE))
-#define G_IS_PSPPIRE_CASE_FILE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_PSPPIRE_CASE_FILE))
-#define PSPPIRE_CASE_FILE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_PSPPIRE_CASE_FILE, PsppireCaseFileClass))
-
-
-
-
-/* --- typedefs & structures --- */
-typedef struct _PsppireCaseFile           PsppireCaseFile;
-typedef struct _PsppireCaseFileClass PsppireCaseFileClass;
-
-struct ccase;
-struct casereader;
-
-struct _PsppireCaseFile
-{
-  GObject             parent;
-
-  /* <private> */
-  struct datasheet *datasheet;
-  gboolean      accessible;
-};
-
-
-struct _PsppireCaseFileClass
-{
-  GObjectClass parent_class;
-};
-
-
-/* -- PsppireCaseFile --- */
-GType          psppire_case_file_get_type (void);
-
-PsppireCaseFile *psppire_case_file_new (struct casereader *);
-
-gboolean psppire_case_file_insert_case (PsppireCaseFile *cf, struct ccase *c, casenumber row);
-
-inline casenumber psppire_case_file_get_case_count (const PsppireCaseFile *cf);
-
-
-union value * psppire_case_file_get_value (const PsppireCaseFile *cf,
-                                           casenumber, size_t idx,
-                                           union value *, int width);
-
-struct fmt_spec;
-
-gboolean psppire_case_file_data_in (PsppireCaseFile *cf, casenumber c, gint idx,
-                                   struct substring input,
-                                   const struct fmt_spec *);
-
-gboolean psppire_case_file_set_value (PsppireCaseFile *cf, casenumber casenum,
-                                    gint idx, union value *v, gint width);
-
-void psppire_case_file_clear (PsppireCaseFile *cf);
-
-
-gboolean psppire_case_file_delete_cases (PsppireCaseFile *cf, casenumber n_rows,
-                                       casenumber first);
-
-gboolean psppire_case_file_insert_values (PsppireCaseFile *cf, gint n_values, gint where);
-
-struct case_ordering;
-
-void psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *);
-
-gboolean psppire_case_file_get_case (const PsppireCaseFile *cf, 
-                                       casenumber casenum,
-                                       struct ccase *c);
-
-
-struct casereader * psppire_case_file_make_reader (PsppireCaseFile *cf);
-
-
-G_END_DECLS
-
-#endif /* __PSPPIRE_CASE_FILE_H__ */
diff --git a/src/ui/gui/psppire-conf.c b/src/ui/gui/psppire-conf.c
new file mode 100644 (file)
index 0000000..4090b74
--- /dev/null
@@ -0,0 +1,297 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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/>. */
+
+/*
+   This module provides an interface for simple user preference config
+   parameters.
+*/
+
+#include <config.h>
+#include <stdio.h>
+
+#include "psppire-conf.h"
+
+static void psppire_conf_init            (PsppireConf      *conf);
+static void psppire_conf_class_init      (PsppireConfClass *class);
+
+static void psppire_conf_finalize        (GObject   *object);
+static void psppire_conf_dispose        (GObject   *object);
+
+static GObjectClass *parent_class = NULL;
+
+
+GType
+psppire_conf_get_type (void)
+{
+  static GType conf_type = 0;
+
+  if (!conf_type)
+    {
+      static const GTypeInfo conf_info =
+      {
+       sizeof (PsppireConfClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) psppire_conf_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (PsppireConf),
+       0,
+        (GInstanceInitFunc) psppire_conf_init,
+      };
+
+      conf_type = g_type_register_static (G_TYPE_OBJECT,
+                                               "PsppireConf",
+                                               &conf_info, 0);
+    }
+
+  return conf_type;
+}
+
+
+static void
+conf_read (PsppireConf *conf)
+{
+  g_key_file_load_from_file (conf->keyfile,
+                            conf->filename,
+                            G_KEY_FILE_KEEP_COMMENTS,
+                            NULL);
+}
+
+static void
+conf_write (PsppireConf *conf)
+{
+  gsize length = 0;
+
+  gchar *kf = g_key_file_to_data  (conf->keyfile, &length, NULL);
+
+  if ( ! g_file_set_contents (conf->filename, kf, length, NULL) )
+    {
+      g_warning ("Cannot open %s for writing", conf->filename);
+    }
+
+  g_free (kf);
+}
+
+static void
+psppire_conf_dispose  (GObject *object)
+{
+}
+
+static void
+psppire_conf_finalize (GObject *object)
+{
+  PsppireConf *conf = PSPPIRE_CONF (object);
+  g_key_file_free (conf->keyfile);
+  g_free (conf->filename);
+}
+
+
+static PsppireConf *the_instance = NULL;
+
+static GObject*
+psppire_conf_construct   (GType                  type,
+                                    guint                  n_construct_params,
+                                    GObjectConstructParam *construct_params)
+{
+  GObject *object;
+
+  if (!the_instance)
+    {
+      object = G_OBJECT_CLASS (parent_class)->constructor (type,
+                                                           n_construct_params,
+                                                           construct_params);
+      the_instance = PSPPIRE_CONF (object);
+    }
+  else
+    object = g_object_ref (G_OBJECT (the_instance));
+
+  return object;
+}
+
+static void
+psppire_conf_class_init (PsppireConfClass *class)
+{
+  GObjectClass *object_class;
+
+  parent_class = g_type_class_peek_parent (class);
+  object_class = (GObjectClass*) class;
+
+  object_class->finalize = psppire_conf_finalize;
+  object_class->dispose = psppire_conf_dispose;
+  object_class->constructor = psppire_conf_construct;
+
+}
+
+
+static void
+psppire_conf_init (PsppireConf *conf)
+{
+  const gchar *dirname = g_get_user_config_dir ();
+
+  conf->filename = g_strdup_printf ("%s/%s", dirname, "psppirerc");
+
+  conf->keyfile = g_key_file_new ();
+
+  conf->dispose_has_run = FALSE;
+}
+
+
+PsppireConf *
+psppire_conf_new (void)
+{
+  return g_object_new (psppire_conf_get_type (), NULL);
+}
+
+
+
+gboolean
+psppire_conf_get_int (PsppireConf *conf, const gchar *base,
+                     const gchar *name, gint *value)
+{
+  gboolean ok;
+  GError *err = NULL;
+  conf_read (conf);
+  *value = g_key_file_get_integer (conf->keyfile,
+                                  base,
+                                  name, &err);
+
+  ok = (err == NULL);
+  if ( err != NULL )
+    g_error_free (err);
+
+  return ok;
+}
+
+gboolean
+psppire_conf_get_boolean (PsppireConf *conf, const gchar *base,
+                         const gchar *name, gboolean *value)
+{
+  gboolean ok;
+  gboolean b;
+  GError *err = NULL;
+  conf_read (conf);
+  b = g_key_file_get_boolean (conf->keyfile,
+                             base,
+                             name, &err);
+
+  ok = (err == NULL);
+  if ( err != NULL )
+    g_error_free (err);
+
+  if (ok)
+    *value = b;
+
+  return ok;
+}
+
+
+void
+psppire_conf_set_int (PsppireConf *conf,
+                     const gchar *base, const gchar *name,
+                     gint value)
+{
+  g_key_file_set_integer (conf->keyfile, base, name, value);
+  conf_write (conf);
+}
+
+void
+psppire_conf_set_boolean (PsppireConf *conf,
+                         const gchar *base, const gchar *name,
+                         gboolean value)
+{
+  g_key_file_set_boolean (conf->keyfile, base, name, value);
+  conf_write (conf);
+}
+
+/*
+  A convenience function to set the geometry of a
+  window from from a saved config
+*/
+void
+psppire_conf_set_window_geometry (PsppireConf *conf,
+                                 const gchar *base,
+                                 GtkWindow *window)
+{
+  gint height, width;
+  gint x, y;
+  gboolean maximize;
+
+  if (psppire_conf_get_int (conf, base, "height", &height)
+      &&
+      psppire_conf_get_int (conf, base, "width", &width) )
+    {
+      gtk_window_set_default_size (window, width, height);
+    }
+
+  if ( psppire_conf_get_int (conf, base, "x", &x)
+       &&
+       psppire_conf_get_int (conf, base, "y", &y) )
+    {
+      gtk_window_move (window, x, y);
+    }
+
+  if ( psppire_conf_get_boolean (conf, base, "maximize", &maximize))
+    {
+      if (maximize)
+       gtk_window_maximize (window);
+      else
+       gtk_window_unmaximize (window);
+    }
+}
+
+
+/*
+   A convenience function to save the window geometry.
+   This should typically be called from a window's
+   "configure-event" and "window-state-event" signal handlers
+ */
+void
+psppire_conf_save_window_geometry (PsppireConf *conf,
+                                  const gchar *base,
+                                  GdkEvent *e)
+{
+  switch (e->type)
+    {
+    case GDK_CONFIGURE:
+      {
+       GdkEventConfigure *event = &e->configure;
+
+       if ( gdk_window_get_state (event->window) &
+            GDK_WINDOW_STATE_MAXIMIZED )
+         return;
+
+       psppire_conf_set_int (conf, base, "height", event->height);
+       psppire_conf_set_int (conf, base, "width", event->width);
+
+       psppire_conf_set_int (conf, base, "x", event->x);
+       psppire_conf_set_int (conf, base, "y", event->y);
+      }
+      break;
+    case GDK_WINDOW_STATE:
+      {
+       GdkEventWindowState *event = &e->window_state;
+
+       psppire_conf_set_boolean (conf, base, "maximize",
+                                 event->new_window_state &
+                                 GDK_WINDOW_STATE_MAXIMIZED );
+      }
+      break;
+    default:
+      break;
+    };
+
+}
diff --git a/src/ui/gui/psppire-conf.h b/src/ui/gui/psppire-conf.h
new file mode 100644 (file)
index 0000000..a7415b2
--- /dev/null
@@ -0,0 +1,104 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 <glib-object.h>
+#include <glib.h>
+
+#include <gtk/gtkwindow.h>
+
+#ifndef __PSPPIRE_CONF_H__
+#define __PSPPIRE_CONF_H__
+
+G_BEGIN_DECLS
+
+
+#define PSPPIRE_TYPE_CONF (psppire_conf_get_type ())
+
+#define PSPPIRE_CONF(obj)      \
+                     (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                   PSPPIRE_TYPE_CONF, PsppireConf))
+
+#define PSPPIRE_CONF_CLASS(klass) \
+                     (G_TYPE_CHECK_CLASS_CAST ((klass), \
+                                PSPPIRE_TYPE_CONF, \
+                                 PsppireConfClass))
+
+
+#define PSPPIRE_IS_CONF(obj) \
+                    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_CONF))
+
+#define PSPPIRE_IS_CONF_CLASS(klass) \
+                     (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_CONF))
+
+
+#define PSPPIRE_CONF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                                  PSPPIRE_TYPE_CONF, \
+                                  PsppireConfClass))
+
+typedef struct _PsppireConf       PsppireConf;
+typedef struct _PsppireConfClass  PsppireConfClass;
+
+
+struct _PsppireConf
+{
+  GObject parent;
+
+  /*< private >*/
+  gboolean dispose_has_run ;
+
+  GKeyFile *keyfile;
+  gchar *filename;
+};
+
+
+struct _PsppireConfClass
+{
+  GObjectClass parent_class;
+};
+
+
+GType psppire_conf_get_type (void) G_GNUC_CONST;
+
+PsppireConf * psppire_conf_new (void);
+
+gboolean psppire_conf_get_int (PsppireConf *,
+                              const gchar *, const gchar *, int *);
+
+gboolean psppire_conf_get_boolean (PsppireConf *,
+                                  const gchar *, const gchar *, gboolean *);
+
+void psppire_conf_set_int (PsppireConf *conf,
+                          const gchar *base, const gchar *name,
+                          gint value);
+
+void psppire_conf_set_boolean (PsppireConf *conf,
+                              const gchar *base, const gchar *name,
+                              gboolean value);
+
+
+void psppire_conf_set_window_geometry (PsppireConf *conf,
+                                      const gchar *base,
+                                      GtkWindow *window);
+
+void psppire_conf_save_window_geometry (PsppireConf *,
+                                       const gchar *,
+                                       GdkEvent *);
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_CONF_H__ */
index 916c8838a05a3fff8b8221b623c291e4c631b420..be911ba6d69817d928264eb13effd969272d029b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyrigght (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 <config.h>
 #include <gtk/gtksignal.h>
 #include <gtk/gtk.h>
-#include <gtksheet/gtksheet.h>
+#include <gtk-contrib/gtkextra-sheet.h>
 #include "psppire-data-editor.h"
 #include "psppire-var-sheet.h"
 
-#include <gtksheet/gsheet-hetero-column.h>
 #include <language/syntax-string-source.h>
 #include "psppire-data-store.h"
+#include <ui/gui/sheet/psppire-axis.h>
 #include "helper.h"
 
+#include <gtk-contrib/gtkxpaned.h>
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
 
+static void psppire_data_editor_remove_split (PsppireDataEditor *de);
+static void psppire_data_editor_set_split (PsppireDataEditor *de);
+
 enum {
   DATA_SELECTION_CHANGED,
   DATA_AVAILABLE_CHANGED,
@@ -104,29 +108,42 @@ psppire_data_editor_finalize (GObject *obj)
 
 
 
-static void popup_variable_menu (GtkSheet *sheet, gint column,
-                                GdkEventButton *event, gpointer data);
+static void popup_variable_column_menu (PsppireSheet *sheet, gint column,
+                                       GdkEventButton *event, gpointer data);
+
+static void popup_variable_row_menu (PsppireSheet *sheet, gint row,
+                                    GdkEventButton *event, gpointer data);
 
-static void popup_cases_menu (GtkSheet *sheet, gint row,
+
+static void popup_cases_menu (PsppireSheet *sheet, gint row,
                              GdkEventButton *event, gpointer data);
 
 
 
 
+
 /* Callback which occurs when the data sheet's column title
    is double clicked */
 static gboolean
 on_data_column_clicked (PsppireDataEditor *de, gint col, gpointer data)
 {
-
+  PsppireSheetRange visible_range;
   gint current_row, current_column;
 
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (de), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
+                                PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
 
-  gtk_sheet_get_active_cell (GTK_SHEET (de->var_sheet),
+  psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->var_sheet),
                             &current_row, &current_column);
 
-  gtk_sheet_set_active_cell (GTK_SHEET (de->var_sheet), col, current_column);
+  psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->var_sheet), col, current_column);
+
+
+  psppire_sheet_get_visible_range (PSPPIRE_SHEET (de->var_sheet), &visible_range);
+
+  if ( col < visible_range.row0 || col > visible_range.rowi)
+    psppire_sheet_moveto (PSPPIRE_SHEET (de->var_sheet), col, current_column, 0.5, 0.5);
+
 
   return FALSE;
 }
@@ -137,63 +154,263 @@ on_data_column_clicked (PsppireDataEditor *de, gint col, gpointer data)
 static gboolean
 on_var_row_clicked (PsppireDataEditor *de, gint row, gpointer data)
 {
-  GtkSheetRange visible_range;
+  PsppireSheetRange visible_range;
 
   gint current_row, current_column;
 
   gtk_notebook_set_current_page (GTK_NOTEBOOK(de), PSPPIRE_DATA_EDITOR_DATA_VIEW);
 
-  gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet),
+  psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]),
                             &current_row, &current_column);
 
-  gtk_sheet_set_active_cell (GTK_SHEET (de->data_sheet), current_row, row);
+  psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), current_row, row);
 
-  gtk_sheet_get_visible_range (GTK_SHEET (de->data_sheet), &visible_range);
+  psppire_sheet_get_visible_range (PSPPIRE_SHEET (de->data_sheet[0]), &visible_range);
 
   if ( row < visible_range.col0 || row > visible_range.coli)
-    {
-      gtk_sheet_moveto (GTK_SHEET (de->data_sheet),
-                       current_row, row, 0, 0);
-    }
+    psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), -1, row, 0.5, 0.5);
 
   return FALSE;
 }
 
 
+/* Moves the focus to a new cell.
+   Returns TRUE iff the move should be disallowed */
+static gboolean
+traverse_cell_callback (PsppireSheet *sheet,
+                       PsppireSheetCell *existing_cell,
+                       PsppireSheetCell *new_cell,
+                       gpointer data)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+  const PsppireDict *dict = de->data_store->dict;
+
+  if ( new_cell->col >= psppire_dict_get_var_cnt (dict))
+    return TRUE;
+
+  return FALSE;
+}
+
 
 enum
   {
     PROP_0,
     PROP_DATA_STORE,
     PROP_VAR_STORE,
-    PROP_COLUMN_MENU,
-    PROP_ROW_MENU,
+    PROP_VS_ROW_MENU,
+    PROP_DS_COLUMN_MENU,
+    PROP_DS_ROW_MENU,
     PROP_VALUE_LABELS,
     PROP_CURRENT_CASE,
     PROP_CURRENT_VAR,
-    PROP_DATA_SELECTED
+    PROP_DATA_SELECTED,
+    PROP_SPLIT_WINDOW
   };
 
+
+#define DEFAULT_ROW_HEIGHT 25
+
+static void
+new_data_callback (PsppireDataStore *ds, gpointer data)
+{
+  gint i;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+
+  casenumber n_cases =  psppire_data_store_get_case_count (ds);
+
+  for (i = 0; i < 2; ++i)
+    {
+      psppire_axis_clear (de->vaxis[i]);
+      psppire_axis_append_n (de->vaxis[i], n_cases, DEFAULT_ROW_HEIGHT);
+    }
+}
+
+static void
+case_inserted_callback (PsppireDataStore *ds, gint before, gpointer data)
+{
+  gint i;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+
+  for (i = 0; i < 2; ++i)
+    psppire_axis_insert (de->vaxis[i], before, DEFAULT_ROW_HEIGHT);
+}
+
+
+static void
+cases_deleted_callback (PsppireDataStore *ds, gint first, gint n_cases, gpointer data)
+{
+  gint i;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+
+  for (i = 0; i < 2; ++i)
+    psppire_axis_delete (de->vaxis[0], first, n_cases);
+}
+
+
+
+/* Return the width (in pixels) of an upper case M when rendered in the
+   current font of W
+*/
+static gint
+width_of_m (GtkWidget *w)
+{
+  PangoRectangle rect;
+  PangoLayout *layout = gtk_widget_create_pango_layout (w, "M");
+
+  pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+  g_object_unref (layout);
+
+  return rect.width;
+}
+
+/* Callback for the axis' resize signal.
+   Changes the variable's display width */
+static void
+rewidth_variable (GtkWidget *w, gint unit, glong size)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (w);
+
+  const PsppireDict *dict = de->data_store->dict;
+  struct variable *var = psppire_dict_get_variable (dict, unit);
+
+  if (NULL == var)
+    return;
+
+  var_set_display_width (var, size / (float) width_of_m (w));
+}
+
+
+static void
+new_variables_callback (PsppireDict *dict, gpointer data)
+{
+  gint v;
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+  gint m_width = width_of_m (GTK_WIDGET (de));
+
+  PsppireAxis *vaxis;
+  g_object_get (de->var_sheet, "vertical-axis", &vaxis, NULL);
+
+  psppire_axis_clear (vaxis);
+  psppire_axis_append_n (vaxis, 1 + psppire_dict_get_var_cnt (dict), DEFAULT_ROW_HEIGHT);
+
+  g_signal_connect_swapped (de->haxis, "resize-unit",
+                           G_CALLBACK (rewidth_variable), de);
+
+  psppire_axis_clear (de->haxis);
+
+  for (v = 0 ; v < psppire_dict_get_var_cnt (dict); ++v)
+    {
+      const struct variable *var = psppire_dict_get_variable (dict, v);
+
+      psppire_axis_append (de->haxis, m_width * var_get_display_width (var));
+    }
+}
+
+static void
+insert_variable_callback (PsppireDict *dict, gint x, gpointer data)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+
+  gint m_width  = width_of_m (GTK_WIDGET (de));
+
+  PsppireAxis *var_vaxis;
+
+  const struct variable *var = psppire_dict_get_variable (dict, x);
+
+  g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
+
+  psppire_axis_insert (var_vaxis, x, DEFAULT_ROW_HEIGHT);
+
+
+  psppire_axis_insert (de->haxis, x, m_width * var_get_display_width (var));
+}
+
+
+static void
+delete_variable_callback (PsppireDict *dict, gint posn,
+                         gint x UNUSED, gint y UNUSED, gpointer data)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+
+  PsppireAxis *var_vaxis;
+  g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
+
+  psppire_axis_delete (var_vaxis, posn, 1);
+
+  psppire_axis_delete (de->haxis, posn, 1);
+}
+
+
+static void
+rewidth_variable_callback (PsppireDict *dict, gint posn, gpointer data)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+  gint m_width = width_of_m (GTK_WIDGET (de));
+
+  const struct variable *var = psppire_dict_get_variable (dict, posn);
+
+  gint var_width = var_get_display_width (var);
+
+  /* Don't allow zero width */
+  if ( var_width < 1 )
+    var_width = 1;
+
+  psppire_axis_resize (de->haxis, posn, m_width * var_width);
+}
+
+
 static void
 psppire_data_editor_set_property (GObject         *object,
                                  guint            prop_id,
                                  const GValue    *value,
                                  GParamSpec      *pspec)
 {
+  int i;
   PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
 
   switch (prop_id)
     {
+    case PROP_SPLIT_WINDOW:
+      psppire_data_editor_split_window (de, g_value_get_boolean (value));
+      break;
     case PROP_DATA_STORE:
       if ( de->data_store) g_object_unref (de->data_store);
       de->data_store = g_value_get_pointer (value);
       g_object_ref (de->data_store);
 
-      g_object_set (de->data_sheet,
-                   "row-geometry", de->data_store,
-                   "column-geometry", de->data_store,
-                   "model", de->data_store,
-                   NULL);
+      for (i = 0 ; i < 4 ; ++i )
+       {
+         g_object_set (de->data_sheet[i],
+                       "model", de->data_store,
+                       NULL);
+
+         g_signal_connect_swapped (de->data_store->dict, "filter-changed",
+                                   G_CALLBACK (gtk_widget_queue_draw),
+                                   de->data_sheet[i]);
+       }
+
+      g_signal_connect (de->data_store->dict, "backend-changed",
+                       G_CALLBACK (new_variables_callback), de);
+
+      g_signal_connect (de->data_store->dict, "variable-inserted",
+                       G_CALLBACK (insert_variable_callback), de);
+
+      g_signal_connect (de->data_store->dict, "variable-deleted",
+                       G_CALLBACK (delete_variable_callback), de);
+
+      g_signal_connect (de->data_store->dict, "variable-display-width-changed",
+                       G_CALLBACK (rewidth_variable_callback), de);
+
+      g_signal_connect (de->data_store, "backend-changed",
+                       G_CALLBACK (new_data_callback), de);
+
+      g_signal_connect (de->data_store, "case-inserted",
+                       G_CALLBACK (case_inserted_callback), de);
+
+      g_signal_connect (de->data_store, "cases-deleted",
+                       G_CALLBACK (cases_deleted_callback), de);
+
       break;
     case PROP_VAR_STORE:
       if ( de->var_store) g_object_unref (de->var_store);
@@ -201,23 +418,30 @@ psppire_data_editor_set_property (GObject         *object,
       g_object_ref (de->var_store);
 
       g_object_set (de->var_sheet,
-                   "row-geometry", de->var_store,
                    "model", de->var_store,
                    NULL);
       break;
-    case PROP_COLUMN_MENU:
+    case PROP_VS_ROW_MENU:
+      {
+       GObject *menu = g_value_get_object (value);
+
+       g_signal_connect (de->var_sheet, "button-event-row",
+                         G_CALLBACK (popup_variable_row_menu), menu);
+      }
+      break;
+    case PROP_DS_COLUMN_MENU:
       {
        GObject *menu = g_value_get_object (value);
 
-       g_signal_connect (de->data_sheet, "button-event-column",
-                         G_CALLBACK (popup_variable_menu), menu);
+       g_signal_connect (de->data_sheet[0], "button-event-column",
+                         G_CALLBACK (popup_variable_column_menu), menu);
       }
       break;
-    case PROP_ROW_MENU:
+    case PROP_DS_ROW_MENU:
       {
        GObject *menu = g_value_get_object (value);
 
-       g_signal_connect (de->data_sheet, "button-event-row",
+       g_signal_connect (de->data_sheet[0], "button-event-row",
                          G_CALLBACK (popup_cases_menu), menu);
       }
       break;
@@ -228,14 +452,14 @@ psppire_data_editor_set_property (GObject         *object,
        switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (object)))
          {
          case PSPPIRE_DATA_EDITOR_DATA_VIEW:
-           gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &col);
-           gtk_sheet_set_active_cell (GTK_SHEET (de->data_sheet), row, var);
-           gtk_sheet_moveto (GTK_SHEET (de->data_sheet), row, var, 0.5, 0.5);
+           psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
+           psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), row, var);
+           psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), -1, var, 0.5, 0.5);
            break;
          case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
-           gtk_sheet_get_active_cell (GTK_SHEET (de->var_sheet), &row, &col);
-           gtk_sheet_set_active_cell (GTK_SHEET (de->var_sheet), var, col);
-           gtk_sheet_moveto (GTK_SHEET (de->var_sheet), var, col,  0.5, 0.5);
+           psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->var_sheet), &row, &col);
+           psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->var_sheet), var, col);
+           psppire_sheet_moveto (PSPPIRE_SHEET (de->var_sheet), var, -1,  0.5, 0.5);
            break;
          default:
            g_assert_not_reached ();
@@ -247,9 +471,9 @@ psppire_data_editor_set_property (GObject         *object,
       {
        gint row, col;
        gint case_num = g_value_get_long (value);
-       gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &col);
-       gtk_sheet_set_active_cell (GTK_SHEET (de->data_sheet), case_num, col);
-       gtk_sheet_moveto (GTK_SHEET (de->data_sheet), case_num, col, 0.5, 0.5);
+       psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
+       psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), case_num, col);
+       psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), case_num, -1, 0.5, 0.5);
       }
       break;
     case PROP_VALUE_LABELS:
@@ -274,6 +498,9 @@ psppire_data_editor_get_property (GObject         *object,
 
   switch (prop_id)
     {
+    case PROP_SPLIT_WINDOW:
+      g_value_set_boolean (value, de->split);
+      break;
     case PROP_DATA_STORE:
       g_value_set_pointer (value, de->data_store);
       break;
@@ -283,14 +510,14 @@ psppire_data_editor_get_property (GObject         *object,
     case PROP_CURRENT_CASE:
       {
        gint row, column;
-       gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &column);
+       psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
        g_value_set_long (value, row);
       }
       break;
     case PROP_CURRENT_VAR:
       {
        gint row, column;
-       gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &column);
+       psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
        g_value_set_long (value, column);
       }
       break;
@@ -310,11 +537,13 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
   GParamSpec *data_store_spec ;
   GParamSpec *var_store_spec ;
   GParamSpec *column_menu_spec;
-  GParamSpec *row_menu_spec;
+  GParamSpec *ds_row_menu_spec;
+  GParamSpec *vs_row_menu_spec;
   GParamSpec *value_labels_spec;
   GParamSpec *current_case_spec;
   GParamSpec *current_var_spec;
   GParamSpec *data_selected_spec;
+  GParamSpec *split_window_spec;
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   parent_class = g_type_class_peek_parent (klass);
@@ -325,6 +554,8 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
   object_class->set_property = psppire_data_editor_set_property;
   object_class->get_property = psppire_data_editor_get_property;
 
+  
+
   data_store_spec =
     g_param_spec_pointer ("data-store",
                          "Data Store",
@@ -346,27 +577,40 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
                                    var_store_spec);
 
   column_menu_spec =
-    g_param_spec_object ("column-menu",
-                        "Column Menu",
-                        "A menu to be displayed when button 3 is pressed in the column title buttons",
+    g_param_spec_object ("datasheet-column-menu",
+                        "Data Sheet Column Menu",
+                        "A menu to be displayed when button 3 is pressed in thedata sheet's column title buttons",
                         GTK_TYPE_MENU,
                         G_PARAM_WRITABLE);
 
   g_object_class_install_property (object_class,
-                                   PROP_COLUMN_MENU,
+                                   PROP_DS_COLUMN_MENU,
                                    column_menu_spec);
 
 
-  row_menu_spec =
-    g_param_spec_object ("row-menu",
-                        "Row Menu",
-                        "A menu to be displayed when button 3 is pressed in the row title buttons",
+  ds_row_menu_spec =
+    g_param_spec_object ("datasheet-row-menu",
+                        "Data Sheet Row Menu",
+                        "A menu to be displayed when button 3 is pressed in the data sheet's row title buttons",
+                        GTK_TYPE_MENU,
+                        G_PARAM_WRITABLE);
+
+  g_object_class_install_property (object_class,
+                                   PROP_DS_ROW_MENU,
+                                   ds_row_menu_spec);
+
+
+  vs_row_menu_spec =
+    g_param_spec_object ("varsheet-row-menu",
+                        "Variable Sheet Row Menu",
+                        "A menu to be displayed when button 3 is pressed in the variable sheet's row title buttons",
                         GTK_TYPE_MENU,
                         G_PARAM_WRITABLE);
 
   g_object_class_install_property (object_class,
-                                   PROP_ROW_MENU,
-                                   row_menu_spec);
+                                   PROP_VS_ROW_MENU,
+                                   vs_row_menu_spec);
+
 
   value_labels_spec =
     g_param_spec_boolean ("value-labels",
@@ -419,6 +663,17 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
 
 
 
+  split_window_spec =
+    g_param_spec_boolean ("split",
+                         "Split Window",
+                         "True iff the data sheet is split",
+                         FALSE,
+                         G_PARAM_READABLE | G_PARAM_WRITABLE);
+
+  g_object_class_install_property (object_class,
+                                   PROP_SPLIT_WINDOW,
+                                   split_window_spec);
+
   data_editor_signals [DATA_SELECTION_CHANGED] =
     g_signal_new ("data-selection-changed",
                  G_TYPE_FROM_CLASS (klass),
@@ -468,12 +723,15 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass)
 
 /* Update the data_ref_entry with the reference of the active cell */
 static gint
-update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data)
+update_data_ref_entry (const PsppireSheet *sheet,
+                      gint row, gint col,
+                      gint old_row, gint old_col,
+                      gpointer data)
 {
   PsppireDataEditor *de = data;
 
   PsppireDataStore *data_store =
-    PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
+    PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
 
   if (data_store)
     {
@@ -533,7 +791,7 @@ datum_entry_activate (GtkEntry *entry, gpointer data)
 
   const gchar *text = gtk_entry_get_text (entry);
 
-  gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &column);
+  psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
 
   if ( row == -1 || column == -1)
     return;
@@ -541,14 +799,12 @@ datum_entry_activate (GtkEntry *entry, gpointer data)
   psppire_data_store_set_string (de->data_store, text, row, column);
 }
 
-
 static void on_activate (PsppireDataEditor *de);
-static void on_deactivate (PsppireDataEditor *de);
 static gboolean on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointer data);
 static void on_select_range (PsppireDataEditor *de);
 
-static void on_select_row (GtkSheet *, gint, PsppireDataEditor *);
-static void on_select_variable (GtkSheet *, gint, PsppireDataEditor *);
+static void on_select_row (PsppireSheet *, gint, PsppireDataEditor *);
+static void on_select_variable (PsppireSheet *, gint, PsppireDataEditor *);
 
 
 static void on_owner_change (GtkClipboard *,
@@ -562,30 +818,108 @@ on_map (GtkWidget *w)
   g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change), w);
 }
 
-static gboolean
-traverse_cell_callback (GtkSheet *sheet,
-                       gint row, gint column,
-                       gint *new_row, gint *new_column,
-                       gpointer data)
+
+static void
+init_sheet (PsppireDataEditor *de, int i,
+           GtkAdjustment *hadj, GtkAdjustment *vadj,
+           PsppireAxis *vaxis,
+           PsppireAxis *haxis
+           )
 {
-  PsppireDataStore *data_store = PSPPIRE_DATA_STORE (data);
+  de->sheet_bin[i] = gtk_scrolled_window_new (hadj, vadj);
 
-  if ( *new_column >= psppire_dict_get_var_cnt (data_store->dict))
-    return FALSE;
+  de->data_sheet[i] = psppire_sheet_new (NULL);
 
-  return TRUE;
+  g_object_set (de->sheet_bin[i],
+               "border-width", 3,
+               "shadow-type",  GTK_SHADOW_ETCHED_IN,
+               NULL);
+
+  g_object_set (haxis, "default-size", 75, NULL);
+  g_object_set (vaxis, "default-size", 25, NULL);
+
+  g_object_set (de->data_sheet[i],
+               "horizontal-axis", haxis,
+               "vertical-axis", vaxis,
+               NULL);
+
+  gtk_container_add (GTK_CONTAINER (de->sheet_bin[i]), de->data_sheet[i]);
+
+  g_signal_connect (de->data_sheet[i], "traverse",
+                   G_CALLBACK (traverse_cell_callback), de);
+
+  gtk_widget_show (de->sheet_bin[i]);
+}
+
+
+static void
+init_data_sheet (PsppireDataEditor *de)
+{
+  GtkAdjustment *vadj0, *hadj0;
+  GtkAdjustment *vadj1, *hadj1;
+  GtkWidget *sheet ;
+
+  de->vaxis[0] = psppire_axis_new ();
+  de->vaxis[1] = psppire_axis_new ();
+
+  /* There's only one horizontal axis, since the
+     column widths are parameters of the variables */
+  de->haxis = psppire_axis_new ();
+
+  de->split = TRUE;
+  de->paned = gtk_xpaned_new ();
+
+  init_sheet (de, 0, NULL, NULL, de->vaxis[0], de->haxis);
+  gtk_widget_show (de->sheet_bin[0]);
+  vadj0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
+  hadj0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
+
+  g_object_set (de->sheet_bin[0], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
+  g_object_set (de->sheet_bin[0], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
+
+  init_sheet (de, 1, NULL, vadj0, de->vaxis[0], de->haxis);
+  gtk_widget_show (de->sheet_bin[1]);
+  sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[1]));
+  psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
+  hadj1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
+  g_object_set (de->sheet_bin[1], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
+  g_object_set (de->sheet_bin[1], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
+
+  init_sheet (de, 2, hadj0, NULL, de->vaxis[1], de->haxis);
+  gtk_widget_show (de->sheet_bin[2]);
+  sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[2]));
+  psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
+  g_object_set (de->sheet_bin[2], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
+  g_object_set (de->sheet_bin[2], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
+  vadj1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
+
+  init_sheet (de, 3, hadj1, vadj1, de->vaxis[1], de->haxis);
+  gtk_widget_show (de->sheet_bin[3]);
+  sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[3]));
+  psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
+  psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
+  g_object_set (de->sheet_bin[3], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
+  g_object_set (de->sheet_bin[3], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
+
+  gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin[0], TRUE, TRUE);
+  gtk_xpaned_pack_top_right (GTK_XPANED (de->paned), de->sheet_bin[1], TRUE, TRUE);
+  gtk_xpaned_pack_bottom_left (GTK_XPANED (de->paned), de->sheet_bin[2], TRUE, TRUE);
+  gtk_xpaned_pack_bottom_right (GTK_XPANED (de->paned), de->sheet_bin[3], TRUE, TRUE);
+
+  gtk_xpaned_set_position_y (GTK_XPANED (de->paned), 150);
+  gtk_xpaned_set_position_x (GTK_XPANED (de->paned), 350);
 }
 
+
 static void
 psppire_data_editor_init (PsppireDataEditor *de)
 {
-  GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
   GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
-  GtkWidget *sw_ds = gtk_scrolled_window_new (NULL, NULL);
   GtkWidget *sw_vs = gtk_scrolled_window_new (NULL, NULL);
 
+  init_data_sheet (de);
 
-  de->data_sheet = gtk_sheet_new (NULL, NULL, NULL);
+  de->data_vbox = gtk_vbox_new (FALSE, 0);
   de->var_sheet = psppire_var_sheet_new ();
 
   g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
@@ -607,19 +941,21 @@ psppire_data_editor_init (PsppireDataEditor *de)
   gtk_widget_show_all (sw_vs);
 
 
-  gtk_container_add (GTK_CONTAINER (sw_ds), de->data_sheet);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (vbox), sw_ds, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (de->data_vbox), hbox, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned, TRUE, TRUE, 0);
+
+
+  psppire_data_editor_remove_split (de);
 
-  gtk_widget_show_all (vbox);
+  gtk_widget_show_all (de->data_vbox);
 
-  gtk_notebook_append_page (GTK_NOTEBOOK (de), vbox,
+  gtk_notebook_append_page (GTK_NOTEBOOK (de), de->data_vbox,
                            gtk_label_new_with_mnemonic (_("Data View")));
 
   gtk_notebook_append_page (GTK_NOTEBOOK (de), sw_vs,
                            gtk_label_new_with_mnemonic (_("Variable View")));
 
-  g_signal_connect (de->data_sheet, "activate",
+  g_signal_connect (de->data_sheet[0], "activate",
                    G_CALLBACK (update_data_ref_entry),
                    de);
 
@@ -628,7 +964,7 @@ psppire_data_editor_init (PsppireDataEditor *de)
                    de);
 
 
-  g_signal_connect_swapped (de->data_sheet,
+  g_signal_connect_swapped (de->data_sheet[0],
                    "double-click-column",
                    G_CALLBACK (on_data_column_clicked),
                    de);
@@ -638,22 +974,18 @@ psppire_data_editor_init (PsppireDataEditor *de)
                    G_CALLBACK (on_var_row_clicked),
                    de);
 
-  g_signal_connect_swapped (de->data_sheet, "activate",
+  g_signal_connect_swapped (de->data_sheet[0], "activate",
                            G_CALLBACK (on_activate),
                            de);
 
-  g_signal_connect_swapped (de->data_sheet, "deactivate",
-                           G_CALLBACK (on_deactivate),
-                           de);
-
-  g_signal_connect_swapped (de->data_sheet, "select-range",
+  g_signal_connect_swapped (de->data_sheet[0], "select-range",
                            G_CALLBACK (on_select_range),
                            de);
 
-  g_signal_connect (de->data_sheet, "select-row",
+  g_signal_connect (de->data_sheet[0], "select-row",
                    G_CALLBACK (on_select_row), de);
 
-  g_signal_connect (de->data_sheet, "select-column",
+  g_signal_connect (de->data_sheet[0], "select-column",
                    G_CALLBACK (on_select_variable), de);
 
 
@@ -665,9 +997,15 @@ psppire_data_editor_init (PsppireDataEditor *de)
                    G_CALLBACK (on_switch_page),
                    NULL);
 
+  g_object_set (de, "can-focus", FALSE, NULL);
 
   g_signal_connect (de, "map", G_CALLBACK (on_map), NULL);
 
+
+  //     psppire_sheet_hide_column_titles (de->var_sheet);
+  //  psppire_sheet_hide_row_titles (de->data_sheet);
+
+
   de->dispose_has_run = FALSE;
 }
 
@@ -676,20 +1014,74 @@ GtkWidget*
 psppire_data_editor_new (PsppireVarStore *var_store,
                         PsppireDataStore *data_store)
 {
-  GtkWidget *widget;
+  return  g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
+                                    "var-store",  var_store,
+                                    "data-store",  data_store,
+                                    NULL);
+}
+
+
+static void
+psppire_data_editor_remove_split (PsppireDataEditor *de)
+{
+  if ( !de->split ) return;
+  de->split = FALSE;
+
+  g_object_ref (de->sheet_bin[0]);
+  gtk_container_remove (GTK_CONTAINER (de->paned), de->sheet_bin[0]);
+
+  g_object_ref (de->paned);
+  gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->paned);
+
+  gtk_box_pack_start (GTK_BOX (de->data_vbox), de->sheet_bin[0],
+                     TRUE, TRUE, 0);
+
+  g_object_unref (de->sheet_bin[0]);
+
+  g_object_set (de->sheet_bin[0], "vscrollbar-policy",
+               GTK_POLICY_ALWAYS, NULL);
+
+  g_object_set (de->sheet_bin[0], "hscrollbar-policy",
+               GTK_POLICY_ALWAYS, NULL);
+}
+
+
+static void
+psppire_data_editor_set_split (PsppireDataEditor *de)
+{
+  if ( de->split ) return;
+  de->split = TRUE;
+
+  g_object_ref (de->sheet_bin[0]);
+  gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->sheet_bin[0]);
+
+  gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin [0],
+                           TRUE, TRUE);
+
+  gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned,
+                     TRUE, TRUE, 0);
 
-  widget =  g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
-                         "var-store",  var_store,
-                         "data-store",  data_store,
-                         NULL);
+  g_object_unref (de->paned);
 
-  g_signal_connect (PSPPIRE_DATA_EDITOR(widget)->data_sheet, "traverse",
-                   G_CALLBACK (traverse_cell_callback), data_store);
+  g_object_set (de->sheet_bin[0], "vscrollbar-policy",
+               GTK_POLICY_NEVER, NULL);
+
+  g_object_set (de->sheet_bin[0], "hscrollbar-policy",
+               GTK_POLICY_NEVER, NULL);
+}
+
+void
+psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
+{
+  if (split )
+    psppire_data_editor_set_split (de);
+  else
+    psppire_data_editor_remove_split (de);
 
-  return widget;
+  gtk_widget_show_all (de->data_vbox);
 }
 
-static void data_sheet_set_clip (GtkSheet *sheet);
+static void data_sheet_set_clip (PsppireSheet *sheet);
 static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
                                                   GtkSelectionData *sd,
                                                   gpointer data);
@@ -698,13 +1090,15 @@ static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
 void
 psppire_data_editor_clip_copy (PsppireDataEditor *de)
 {
-  data_sheet_set_clip (GTK_SHEET (de->data_sheet));
+  data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
 }
 
 void
 psppire_data_editor_clip_paste (PsppireDataEditor *de)
 {
-  GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+  GdkDisplay *display = gtk_widget_get_display ( GTK_WIDGET (de));
+  GtkClipboard *clipboard =
+    gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
 
   gtk_clipboard_request_contents (clipboard,
                                  gdk_atom_intern ("UTF8_STRING", TRUE),
@@ -719,19 +1113,19 @@ psppire_data_editor_clip_cut (PsppireDataEditor *de)
 {
   gint max_rows, max_columns;
   gint r;
-  GtkSheetRange range;
+  PsppireSheetRange range;
   PsppireDataStore *ds = de->data_store;
 
-  data_sheet_set_clip (GTK_SHEET (de->data_sheet));
+  data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
 
   /* Now blank all the cells */
-  gtk_sheet_get_selected_range (GTK_SHEET (de->data_sheet), &range);
+  psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
 
    /* If nothing selected, then use active cell */
   if ( range.row0 < 0 || range.col0 < 0 )
     {
       gint row, col;
-      gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &col);
+      psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
 
       range.row0 = range.rowi = row;
       range.col0 = range.coli = col;
@@ -772,7 +1166,7 @@ psppire_data_editor_clip_cut (PsppireDataEditor *de)
     }
 
   /* and remove the selection */
-  gtk_sheet_unselect_range (GTK_SHEET (de->data_sheet));
+  psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
 }
 
 
@@ -781,20 +1175,43 @@ psppire_data_editor_clip_cut (PsppireDataEditor *de)
 /* Popup menu related stuff */
 
 static void
-popup_variable_menu (GtkSheet *sheet, gint column,
+popup_variable_column_menu (PsppireSheet *sheet, gint column,
                     GdkEventButton *event, gpointer data)
 {
   GtkMenu *menu = GTK_MENU (data);
 
   PsppireDataStore *data_store =
-    PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
+    PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
 
   const struct variable *v =
     psppire_dict_get_variable (data_store->dict, column);
 
   if ( v && event->button == 3)
     {
-      gtk_sheet_select_column (sheet, column);
+      psppire_sheet_select_column (sheet, column);
+
+      gtk_menu_popup (menu,
+                     NULL, NULL, NULL, NULL,
+                     event->button, event->time);
+    }
+}
+
+
+static void
+popup_variable_row_menu (PsppireSheet *sheet, gint row,
+                    GdkEventButton *event, gpointer data)
+{
+  GtkMenu *menu = GTK_MENU (data);
+
+  PsppireVarStore *var_store =
+    PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
+
+  const struct variable *v =
+    psppire_dict_get_variable (var_store->dict, row);
+
+  if ( v && event->button == 3)
+    {
+      psppire_sheet_select_row (sheet, row);
 
       gtk_menu_popup (menu,
                      NULL, NULL, NULL, NULL,
@@ -804,18 +1221,18 @@ popup_variable_menu (GtkSheet *sheet, gint column,
 
 
 static void
-popup_cases_menu (GtkSheet *sheet, gint row,
+popup_cases_menu (PsppireSheet *sheet, gint row,
                  GdkEventButton *event, gpointer data)
 {
   GtkMenu *menu = GTK_MENU (data);
 
   PsppireDataStore *data_store =
-    PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
+    PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
 
   if ( row <= psppire_data_store_get_case_count (data_store) &&
        event->button == 3)
     {
-      gtk_sheet_select_row (sheet, row);
+      psppire_sheet_select_row (sheet, row);
 
       gtk_menu_popup (menu,
                      NULL, NULL, NULL, NULL,
@@ -853,8 +1270,8 @@ do_sort (PsppireDataStore *ds, int var, gboolean descend)
 void
 psppire_data_editor_sort_ascending  (PsppireDataEditor *de)
 {
-  GtkSheetRange range;
-  gtk_sheet_get_selected_range (GTK_SHEET(de->data_sheet), &range);
+  PsppireSheetRange range;
+  psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
 
   do_sort (de->data_store,  range.col0, FALSE);
 }
@@ -865,8 +1282,8 @@ psppire_data_editor_sort_ascending  (PsppireDataEditor *de)
 void
 psppire_data_editor_sort_descending (PsppireDataEditor *de)
 {
-  GtkSheetRange range;
-  gtk_sheet_get_selected_range (GTK_SHEET(de->data_sheet), &range);
+  PsppireSheetRange range;
+  psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
 
   do_sort (de->data_store,  range.col0, TRUE);
 }
@@ -879,14 +1296,26 @@ psppire_data_editor_sort_descending (PsppireDataEditor *de)
 void
 psppire_data_editor_insert_variable (PsppireDataEditor *de)
 {
-  glong posn = -1;
-
-  if ( de->data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
-    posn = GTK_SHEET (de->data_sheet)->range.col0;
-  else
-    posn = GTK_SHEET (de->data_sheet)->active_cell.col;
+  glong posn = 0;
 
-  if ( posn == -1 ) posn = 0;
+  switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
+    {
+    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
+      if ( de->data_sheet[0]->state == PSPPIRE_SHEET_COLUMN_SELECTED )
+       posn = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
+      else
+       posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.col;
+      break;
+    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
+      if ( de->var_sheet->state == PSPPIRE_SHEET_ROW_SELECTED )
+       posn = PSPPIRE_SHEET (de->var_sheet)->range.row0;
+      else
+       posn = PSPPIRE_SHEET (de->var_sheet)->active_cell.row;
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    };
 
   psppire_dict_insert_variable (de->data_store->dict, posn, NULL);
 }
@@ -897,10 +1326,10 @@ psppire_data_editor_insert_case (PsppireDataEditor *de)
 {
   glong posn = -1;
 
-  if ( de->data_sheet->state == GTK_SHEET_ROW_SELECTED )
-    posn = GTK_SHEET (de->data_sheet)->range.row0;
+  if ( de->data_sheet[0]->state == PSPPIRE_SHEET_ROW_SELECTED )
+    posn = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
   else
-    posn = GTK_SHEET (de->data_sheet)->active_cell.row;
+    posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.row;
 
   if ( posn == -1 ) posn = 0;
 
@@ -911,12 +1340,12 @@ psppire_data_editor_insert_case (PsppireDataEditor *de)
 void
 psppire_data_editor_delete_cases    (PsppireDataEditor *de)
 {
-  gint first = GTK_SHEET (de->data_sheet)->range.row0;
-  gint n = GTK_SHEET (de->data_sheet)->range.rowi - first + 1;
+  gint first = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
+  gint n = PSPPIRE_SHEET (de->data_sheet[0])->range.rowi - first + 1;
 
   psppire_data_store_delete_cases (de->data_store, first, n);
 
-  gtk_sheet_unselect_range (GTK_SHEET (de->data_sheet));
+  psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
 }
 
 /* Delete the variables currently selected in the
@@ -929,12 +1358,12 @@ psppire_data_editor_delete_variables (PsppireDataEditor *de)
   switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
     {
     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
-      first = GTK_SHEET (de->data_sheet)->range.col0;
-      n = GTK_SHEET (de->data_sheet)->range.coli - first + 1;
+      first = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
+      n = PSPPIRE_SHEET (de->data_sheet[0])->range.coli - first + 1;
       break;
     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
-      first = GTK_SHEET (de->var_sheet)->range.row0;
-      n = GTK_SHEET (de->var_sheet)->range.rowi - first + 1;
+      first = PSPPIRE_SHEET (de->var_sheet)->range.row0;
+      n = PSPPIRE_SHEET (de->var_sheet)->range.rowi - first + 1;
       break;
     default:
       g_assert_not_reached ();
@@ -943,23 +1372,39 @@ psppire_data_editor_delete_variables (PsppireDataEditor *de)
 
   psppire_dict_delete_variables (de->var_store->dict, first, n);
 
-  gtk_sheet_unselect_range (GTK_SHEET (de->data_sheet));
-  gtk_sheet_unselect_range (GTK_SHEET (de->var_sheet));
+  psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
+  psppire_sheet_unselect_range (PSPPIRE_SHEET (de->var_sheet));
 }
 
 
 void
 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
 {
-  gtk_sheet_show_grid (GTK_SHEET (de->var_sheet), grid_visible);
-  gtk_sheet_show_grid (GTK_SHEET (de->data_sheet), grid_visible);
+  psppire_sheet_show_grid (PSPPIRE_SHEET (de->var_sheet), grid_visible);
+  psppire_sheet_show_grid (PSPPIRE_SHEET (de->data_sheet[0]), grid_visible);
+}
+
+
+static void
+set_font (GtkWidget *w, gpointer data)
+{
+  PangoFontDescription *font_desc = data;
+  GtkRcStyle *style = gtk_widget_get_modifier_style (w);
+
+  pango_font_description_free (style->font_desc);
+  style->font_desc = pango_font_description_copy (font_desc);
+
+  gtk_widget_modify_style (w, style);
+
+  if ( GTK_IS_CONTAINER (w))
+    gtk_container_foreach (GTK_CONTAINER (w), set_font, font_desc);
 }
 
 void
 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
 {
-  psppire_data_store_set_font (de->data_store, font_desc);
-  psppire_var_store_set_font (de->var_store, font_desc);
+  set_font (GTK_WIDGET (de), font_desc);
+  gtk_container_foreach (GTK_CONTAINER (de), set_font, font_desc);
 }
 
 
@@ -974,11 +1419,13 @@ emit_selected_signal (PsppireDataEditor *de)
   g_signal_emit (de, data_editor_signals[DATA_SELECTION_CHANGED], 0, data_selected);
 }
 
+
 static void
 on_activate (PsppireDataEditor *de)
 {
   gint row, col;
-  gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &col);
+  psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
+
 
   if ( row < psppire_data_store_get_case_count (de->data_store)
        &&
@@ -992,18 +1439,12 @@ on_activate (PsppireDataEditor *de)
 }
 
 
-static void
-on_deactivate (PsppireDataEditor *de)
-{
-  emit_selected_signal (de);
-}
-
 static void
 on_select_range (PsppireDataEditor *de)
 {
-  GtkSheetRange range;
+  PsppireSheetRange range;
 
-  gtk_sheet_get_selected_range (GTK_SHEET (de->data_sheet), &range);
+  psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
 
   if ( range.rowi < psppire_data_store_get_case_count (de->data_store)
        &&
@@ -1018,15 +1459,22 @@ on_select_range (PsppireDataEditor *de)
 
 
 static gboolean
-on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointer data)
+on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p,
+               gint pagenum, gpointer data)
 {
-  if ( pagenum != PSPPIRE_DATA_EDITOR_DATA_VIEW )
+  switch (pagenum)
     {
+    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
+      gtk_widget_grab_focus (de->data_vbox);
+      on_select_range (de);
+      break;
+    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
+      gtk_widget_grab_focus (de->var_sheet);
       emit_selected_signal (de);
-      return TRUE;
-    }
-
-  on_select_range (de);
+      break;
+    default:
+      break;
+    };
 
   return TRUE;
 }
@@ -1036,13 +1484,13 @@ on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointe
 static gboolean
 data_is_selected (PsppireDataEditor *de)
 {
-  GtkSheetRange range;
+  PsppireSheetRange range;
   gint row, col;
 
   if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (de)) != PSPPIRE_DATA_EDITOR_DATA_VIEW)
     return FALSE;
 
-  gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet), &row, &col);
+  psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
 
   if ( row >= psppire_data_store_get_case_count (de->data_store)
        ||
@@ -1051,7 +1499,7 @@ data_is_selected (PsppireDataEditor *de)
       return FALSE;
     }
 
-  gtk_sheet_get_selected_range (GTK_SHEET (de->data_sheet), &range);
+  psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
 
   if ( range.rowi >= psppire_data_store_get_case_count (de->data_store)
        ||
@@ -1065,14 +1513,14 @@ data_is_selected (PsppireDataEditor *de)
 
 
 static void
-on_select_row (GtkSheet *sheet, gint row, PsppireDataEditor *de)
+on_select_row (PsppireSheet *sheet, gint row, PsppireDataEditor *de)
 {
   g_signal_emit (de, data_editor_signals[CASES_SELECTED], 0, row);
 }
 
 
 static void
-on_select_variable (GtkSheet *sheet, gint var, PsppireDataEditor *de)
+on_select_variable (PsppireSheet *sheet, gint var, PsppireDataEditor *de)
 {
   g_signal_emit (de, data_editor_signals[VARIABLES_SELECTED], 0, var);
 }
@@ -1095,30 +1543,30 @@ static struct casereader *clip_datasheet = NULL;
 static struct dictionary *clip_dict = NULL;
 
 
-static void data_sheet_update_clipboard (GtkSheet *);
+static void data_sheet_update_clipboard (PsppireSheet *);
 
 /* Set the clip according to the currently
    selected range in the data sheet */
 static void
-data_sheet_set_clip (GtkSheet *sheet)
+data_sheet_set_clip (PsppireSheet *sheet)
 {
   int i;
   struct casewriter *writer ;
-  GtkSheetRange range;
+  PsppireSheetRange range;
   PsppireDataStore *ds;
   struct case_map *map = NULL;
   casenumber max_rows;
   size_t max_columns;
 
-  ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
+  ds = PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
 
-  gtk_sheet_get_selected_range (sheet, &range);
+  psppire_sheet_get_selected_range (sheet, &range);
 
    /* If nothing selected, then use active cell */
   if ( range.row0 < 0 || range.col0 < 0 )
     {
       gint row, col;
-      gtk_sheet_get_active_cell (sheet, &row, &col);
+      psppire_sheet_get_active_cell (sheet, &row, &col);
 
       range.row0 = range.rowi = row;
       range.col0 = range.coli = col;
@@ -1172,16 +1620,9 @@ data_sheet_set_clip (GtkSheet *sheet)
   writer = autopaging_writer_create (dict_get_next_value_idx (clip_dict));
   for (i = range.row0; i <= range.rowi ; ++i )
     {
-      struct ccase old;
-
-      if (psppire_case_file_get_case (ds->case_file, i, &old))
-        {
-          struct ccase new;
-
-          case_map_execute (map, &old, &new);
-          case_destroy (&old);
-          casewriter_write (writer, &new);
-        }
+      struct ccase *old = psppire_data_store_get_case (ds, i);
+      if (old != NULL)
+        casewriter_write (writer, case_map_execute (map, old));
       else
         casewriter_force_error (writer);
     }
@@ -1232,8 +1673,10 @@ clip_to_text (void)
   for (r = 0 ; r < case_cnt ; ++r )
     {
       int c;
-      struct ccase cc;
-      if ( !  casereader_peek (clip_datasheet, r, &cc))
+      struct ccase *cc;
+
+      cc = casereader_peek (clip_datasheet, r);
+      if (cc == NULL)
        {
          g_warning ("Clipboard seems to have inexplicably shrunk");
          break;
@@ -1242,7 +1685,7 @@ clip_to_text (void)
       for (c = 0 ; c < var_cnt ; ++c)
        {
          const struct variable *v = dict_get_var (clip_dict, c);
-         data_out_g_string (string, v, &cc);
+         data_out_g_string (string, v, cc);
          if ( c < val_cnt - 1 )
            g_string_append (string, "\t");
        }
@@ -1250,7 +1693,7 @@ clip_to_text (void)
       if ( r < case_cnt)
        g_string_append (string, "\n");
 
-      case_destroy (&cc);
+      case_unref (cc);
     }
 
   return string;
@@ -1275,8 +1718,8 @@ clip_to_html (void)
   for (r = 0 ; r < case_cnt ; ++r )
     {
       int c;
-      struct ccase cc;
-      if ( !  casereader_peek (clip_datasheet, r, &cc))
+      struct ccase *cc = casereader_peek (clip_datasheet, r);
+      if (cc == NULL)
        {
          g_warning ("Clipboard seems to have inexplicably shrunk");
          break;
@@ -1287,13 +1730,13 @@ clip_to_html (void)
        {
          const struct variable *v = dict_get_var (clip_dict, c);
          g_string_append (string, "<td>");
-         data_out_g_string (string, v, &cc);
+         data_out_g_string (string, v, cc);
          g_string_append (string, "</td>\n");
        }
 
       g_string_append (string, "</tr>\n");
 
-      case_destroy (&cc);
+      case_unref (cc);
     }
   g_string_append (string, "</table>\n");
 
@@ -1354,7 +1797,7 @@ static const GtkTargetEntry targets[] = {
 
 
 static void
-data_sheet_update_clipboard (GtkSheet *sheet)
+data_sheet_update_clipboard (PsppireSheet *sheet)
 {
   GtkClipboard *clipboard =
     gtk_widget_get_clipboard (GTK_WIDGET (sheet),
@@ -1391,7 +1834,7 @@ data_sheet_contents_received_callback (GtkClipboard *clipboard,
   c = (char *) sd->data;
 
   /* Paste text to selected position */
-  gtk_sheet_get_active_cell (GTK_SHEET (data_editor->data_sheet),
+  psppire_sheet_get_active_cell (PSPPIRE_SHEET (data_editor->data_sheet[0]),
                             &row, &column);
 
   g_return_if_fail (row >= 0);
index b896c54578aa97c9685ae7fd126804419097d66f..b5b3f3600a5d490f17bcf606788ec4bee5e738cb 100644 (file)
@@ -22,6 +22,7 @@
 #include <glib-object.h>
 #include <gtk/gtknotebook.h>
 
+#include <ui/gui/sheet/psppire-axis.h>
 #include "psppire-var-store.h"
 #include "psppire-data-store.h"
 
@@ -46,10 +47,23 @@ struct _PsppireDataEditor
   gboolean dispose_has_run;
   GtkWidget *cell_ref_entry;
   GtkWidget *datum_entry;
-  GtkWidget *data_sheet;
   GtkWidget *var_sheet;
   PsppireDataStore *data_store;
   PsppireVarStore *var_store;
+
+  GtkWidget *sheet_bin[4];
+  GtkWidget *data_sheet[4];
+
+  GtkWidget *data_vbox;
+
+  GtkWidget *paned;
+  gboolean split;
+
+  PsppireAxis *vaxis[2];
+
+  /* There's only one horizontal axis, since the
+     column widths are parameters of the variables */
+  PsppireAxis *haxis;
 };
 
 
@@ -73,7 +87,7 @@ void           psppire_data_editor_insert_case     (PsppireDataEditor *);
 void           psppire_data_editor_delete_cases    (PsppireDataEditor *);
 void           psppire_data_editor_set_font        (PsppireDataEditor *, PangoFontDescription *);
 void           psppire_data_editor_delete_cases    (PsppireDataEditor *);
-
+void           psppire_data_editor_split_window    (PsppireDataEditor *, gboolean );
 
 
 G_END_DECLS
index afbe3d2fe31f82fb83b4149a2a9656b902d9bc7a..45104f5cbb8ff519418fee88edf92e496e91652c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  Free Software Foundation
+   Copyright (C) 2006, 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 <data/data-out.h>
 #include <data/variable.h>
 
-#include <gtksheet/gsheetmodel.h>
-#include <gtksheet/gsheet-column-iface.h>
-#include <gtksheet/gsheet-row-iface.h>
+#include <ui/gui/sheet/psppire-sheetmodel.h>
+#include <ui/gui/psppire-marshal.h>
 
 #include <pango/pango-context.h>
 
 #include "psppire-data-store.h"
-#include "psppire-case-file.h"
 #include "helper.h"
 
 #include <data/dictionary.h>
 #include <data/data-in.h>
 #include <data/format.h>
 
+#include <math/sort.h>
+
+#include "xalloc.h"
+#include "xmalloca.h"
+
+
 
 static void psppire_data_store_init            (PsppireDataStore      *data_store);
 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
-static void psppire_data_store_sheet_model_init (GSheetModelIface *iface);
-static void psppire_data_store_sheet_column_init (GSheetColumnIface *iface);
-static void psppire_data_store_sheet_row_init (GSheetRowIface *iface);
+static void psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface);
 
 static void psppire_data_store_finalize        (GObject           *object);
 static void psppire_data_store_dispose        (GObject           *object);
 
-static gboolean psppire_data_store_clear_datum (GSheetModel *model,
+static gboolean psppire_data_store_clear_datum (PsppireSheetModel *model,
                                          glong row, glong column);
 
 
-#define MIN_COLUMNS 10
+static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
+                                               struct ccase *cc,
+                                               casenumber posn);
+
+
+static gboolean psppire_data_store_data_in (PsppireDataStore *ds,
+                                           casenumber casenum, gint idx,
+                                           struct substring input,
+                                           const struct fmt_spec *fmt);
+
 
-#define TRAILING_ROWS 10
 
 static GObjectClass *parent_class = NULL;
 
 
-enum  {FONT_CHANGED,
-       n_SIGNALS};
+enum
+  {
+    BACKEND_CHANGED,
+    CASES_DELETED,
+    CASE_INSERTED,
+    CASE_CHANGED,
+    n_SIGNALS
+  };
 
 static guint signals [n_SIGNALS];
 
@@ -95,35 +111,15 @@ psppire_data_store_get_type (void)
        NULL
       };
 
-      static const GInterfaceInfo sheet_column_info =
-      {
-       (GInterfaceInitFunc) psppire_data_store_sheet_column_init,
-       NULL,
-       NULL
-      };
-
-      static const GInterfaceInfo sheet_row_info =
-      {
-       (GInterfaceInitFunc) psppire_data_store_sheet_row_init,
-       NULL,
-       NULL
-      };
 
-
-      data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore",
+      data_store_type = g_type_register_static (G_TYPE_OBJECT,
+                                               "PsppireDataStore",
                                                &data_store_info, 0);
 
       g_type_add_interface_static (data_store_type,
-                                  G_TYPE_SHEET_MODEL,
+                                  PSPPIRE_TYPE_SHEET_MODEL,
                                   &sheet_model_info);
 
-      g_type_add_interface_static (data_store_type,
-                                  G_TYPE_SHEET_COLUMN,
-                                  &sheet_column_info);
-
-      g_type_add_interface_static (data_store_type,
-                                  G_TYPE_SHEET_ROW,
-                                  &sheet_row_info);
     }
 
   return data_store_type;
@@ -141,8 +137,8 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
   object_class->finalize = psppire_data_store_finalize;
   object_class->dispose = psppire_data_store_dispose;
 
-  signals [FONT_CHANGED] =
-    g_signal_new ("font_changed",
+  signals [BACKEND_CHANGED] =
+    g_signal_new ("backend-changed",
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST,
                  0,
@@ -150,12 +146,64 @@ psppire_data_store_class_init (PsppireDataStoreClass *class)
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE,
                  0);
+
+  signals [CASE_INSERTED] =
+    g_signal_new ("case-inserted",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+
+  signals [CASE_CHANGED] =
+    g_signal_new ("case-changed",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
+  signals [CASES_DELETED] =
+    g_signal_new ("cases-deleted",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 psppire_marshal_VOID__INT_INT,
+                 G_TYPE_NONE,
+                 2,
+                 G_TYPE_INT,
+                 G_TYPE_INT);
 }
 
 
 
+static gboolean
+psppire_data_store_insert_values (PsppireDataStore *ds,
+                                 gint n_values, gint where);
+
+static union value *
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value, int width);
+
+
+static gboolean
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v, gint width);
+
+
+
+
 static glong
-psppire_data_store_get_var_count (const GSheetModel *model)
+psppire_data_store_get_var_count (const PsppireSheetModel *model)
 {
   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
 
@@ -165,7 +213,7 @@ psppire_data_store_get_var_count (const GSheetModel *model)
 casenumber
 psppire_data_store_get_case_count (const PsppireDataStore *store)
 {
-  return psppire_case_file_get_case_count (store->case_file);
+  return datasheet_get_row_cnt (store->datasheet);
 }
 
 size_t
@@ -174,8 +222,8 @@ psppire_data_store_get_value_count (const PsppireDataStore *store)
   return psppire_dict_get_value_cnt (store->dict);
 }
 
-casenumber
-psppire_data_store_get_case_count_wrapper (const GSheetModel *model)
+static casenumber
+psppire_data_store_get_case_count_wrapper (const PsppireSheetModel *model)
 {
   const PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
   return psppire_data_store_get_case_count (store);
@@ -185,22 +233,12 @@ static void
 psppire_data_store_init (PsppireDataStore *data_store)
 {
   data_store->dict = 0;
-  data_store->case_file = NULL;
-  data_store->width_of_m = 10;
+  data_store->datasheet = NULL;
   data_store->dispose_has_run = FALSE;
 }
 
-const PangoFontDescription *
-psppire_data_store_get_font_desc (const GSheetModel *model,
-                             glong row, glong column)
-{
-  PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
-
-  return store->font_desc;
-}
-
 static inline gchar *
-psppire_data_store_get_string_wrapper (const GSheetModel *model, glong row,
+psppire_data_store_get_string_wrapper (const PsppireSheetModel *model, glong row,
                                       glong column)
 {
   return psppire_data_store_get_string (PSPPIRE_DATA_STORE (model), row, column);
@@ -208,7 +246,7 @@ psppire_data_store_get_string_wrapper (const GSheetModel *model, glong row,
 
 
 static inline gboolean
-psppire_data_store_set_string_wrapper (GSheetModel *model,
+psppire_data_store_set_string_wrapper (PsppireSheetModel *model,
                                       const gchar *text,
                                       glong row, glong column)
 {
@@ -218,76 +256,37 @@ psppire_data_store_set_string_wrapper (GSheetModel *model,
 
 
 
+static gchar * get_column_subtitle (const PsppireSheetModel *model, gint col);
+static gchar * get_column_button_label (const PsppireSheetModel *model, gint col);
+static gboolean get_column_sensitivity (const PsppireSheetModel *model, gint col);
+static GtkJustification get_column_justification (const PsppireSheetModel *model, gint col);
+
+static gchar * get_row_button_label (const PsppireSheetModel *model, gint row);
+static gboolean get_row_sensitivity (const PsppireSheetModel *model, gint row);
+static gboolean get_row_overstrike (const PsppireSheetModel *model, gint row);
+
 
 static void
-psppire_data_store_sheet_model_init (GSheetModelIface *iface)
+psppire_data_store_sheet_model_init (PsppireSheetModelIface *iface)
 {
   iface->free_strings = TRUE;
   iface->get_string = psppire_data_store_get_string_wrapper;
   iface->set_string = psppire_data_store_set_string_wrapper;
   iface->clear_datum = psppire_data_store_clear_datum;
   iface->is_editable = NULL;
-  iface->is_visible = NULL;
   iface->get_foreground = NULL;
   iface->get_background = NULL;
-  iface->get_font_desc = psppire_data_store_get_font_desc;
-  iface->get_cell_border = NULL;
   iface->get_column_count = psppire_data_store_get_var_count;
   iface->get_row_count = psppire_data_store_get_case_count_wrapper;
-}
-
-static
-gboolean always_true ()
-{
-  return TRUE;
-}
-
-
-static void
-delete_cases_callback (GtkWidget *w,
-        casenumber first, casenumber n_cases, gpointer data)
-{
-  PsppireDataStore *store  ;
-
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
 
-  g_assert (first >= 0);
+  iface->get_column_subtitle = get_column_subtitle;
+  iface->get_column_title = get_column_button_label;
+  iface->get_column_sensitivity = get_column_sensitivity;
+  iface->get_column_justification = get_column_justification;
 
-  g_sheet_model_rows_deleted (G_SHEET_MODEL (store), first, n_cases);
-}
-
-
-static void
-insert_case_callback (GtkWidget *w, casenumber casenum, gpointer data)
-{
-  PsppireDataStore *store  ;
-
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                              casenum, -1,
-                              psppire_case_file_get_case_count (store->case_file),
-                              -1);
-
-  g_sheet_model_rows_inserted (G_SHEET_MODEL (store), casenum, 1);
-}
-
-
-static void
-changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
-{
-  PsppireDataStore *store  ;
-  g_return_if_fail (data);
-
-  store  = PSPPIRE_DATA_STORE (data);
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                casenum, -1,
-                                casenum, -1);
+  iface->get_row_title = get_row_button_label;
+  iface->get_row_sensitivity = get_row_sensitivity;
+  iface->get_row_overstrike = get_row_overstrike;
 }
 
 
@@ -301,25 +300,29 @@ delete_variable_callback (GObject *obj, gint dict_index,
 {
   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
-  g_sheet_model_columns_deleted (G_SHEET_MODEL (store), dict_index, 1);
 
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
+  psppire_sheet_model_columns_deleted (PSPPIRE_SHEET_MODEL (store), dict_index, 1);
+#if AXIS_TRANSITION
+
+
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
                                   dict_index, -1);
+#endif
 }
 
-
 static void
 variable_changed_callback (GObject *obj, gint var_num, gpointer data)
 {
+#if AXIS_TRANSITION
   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
 
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
                                  var_num, 1);
 
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
                               -1, var_num,
                               -1, var_num);
+#endif
 }
 
 static void
@@ -346,12 +349,15 @@ insert_variable_callback (GObject *obj, gint var_num, gpointer data)
       posn = 0;
     }
 
-  psppire_case_file_insert_values (store->case_file, 1, posn);
+  psppire_data_store_insert_values (store, 1, posn);
 
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
+#if AXIS_TRANSITION
+
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (store),
                                  var_num, 1);
+#endif
 
-  g_sheet_model_columns_inserted (G_SHEET_MODEL (store), var_num, 1);
+  psppire_sheet_model_columns_inserted (PSPPIRE_SHEET_MODEL (store), var_num, 1);
 }
 
 
@@ -366,7 +372,7 @@ dict_size_change_callback (GObject *obj,
   const gint new_val_width = value_cnt_from_width (var_get_width (v));
 
   if ( adjustment > 0 )
-    psppire_case_file_insert_values (store->case_file, adjustment,
+    psppire_data_store_insert_values (store, adjustment,
                                     new_val_width - adjustment +
                                     var_get_case_index(v));
 }
@@ -392,28 +398,20 @@ psppire_data_store_new (PsppireDict *dict)
   return retval;
 }
 
-
 void
-psppire_data_store_set_case_file (PsppireDataStore *ds,
-                                 PsppireCaseFile *cf)
+psppire_data_store_set_reader (PsppireDataStore *ds,
+                              struct casereader *reader)
 {
   gint i;
-  if ( ds->case_file)  g_object_unref (ds->case_file);
 
+  if ( ds->datasheet)
+    datasheet_destroy (ds->datasheet);
 
-  ds->case_file = cf;
+  ds->datasheet = datasheet_create (reader);
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (ds),
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (ds),
                               -1, -1, -1, -1);
 
-  for (i = 0 ; i < n_cf_signals ; ++i )
-    {
-      if ( ds->cf_handler_id [i] > 0 )
-       g_signal_handler_disconnect (ds->case_file,
-                                    ds->cf_handler_id[i]);
-    }
-
-
   if ( ds->dict )
     for (i = 0 ; i < n_dict_signals; ++i )
       {
@@ -424,20 +422,7 @@ psppire_data_store_set_case_file (PsppireDataStore *ds,
          }
       }
 
-  ds->cf_handler_id [CASES_DELETED] =
-    g_signal_connect (ds->case_file, "cases-deleted",
-                     G_CALLBACK (delete_cases_callback),
-                     ds);
-
-  ds->cf_handler_id [CASE_INSERTED] =
-    g_signal_connect (ds->case_file, "case-inserted",
-                     G_CALLBACK (insert_case_callback),
-                     ds);
-
-  ds->cf_handler_id [CASE_CHANGED] =
-    g_signal_connect (ds->case_file, "case-changed",
-                     G_CALLBACK (changed_case_callback),
-                     ds);
+  g_signal_emit (ds, signals[BACKEND_CHANGED], 0);
 }
 
 
@@ -491,10 +476,11 @@ psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *di
 
 
   /* The entire model has changed */
-  g_sheet_model_range_changed (G_SHEET_MODEL (data_store), -1, -1, -1, -1);
-
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (data_store), 0, -1);
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (data_store), -1, -1, -1, -1);
 
+#if AXIS_TRANSITION
+  psppire_sheet_column_columns_changed (PSPPIRE_SHEET_COLUMN (data_store), 0, -1);
+#endif
 
   if ( data_store->dict )
     for (i = 0 ; i < n_dict_signals; ++i )
@@ -524,7 +510,11 @@ psppire_data_store_dispose (GObject *object)
   if (ds->dispose_has_run)
     return;
 
-  if (ds->case_file) g_object_unref (ds->case_file);
+  if (ds->datasheet)
+    {
+      datasheet_destroy (ds->datasheet);
+      ds->datasheet = NULL;
+    }
 
   /* must chain up */
   (* parent_class->dispose) (object);
@@ -533,16 +523,6 @@ psppire_data_store_dispose (GObject *object)
 }
 
 
-gboolean
-psppire_data_store_delete_cases (PsppireDataStore *ds,
-                                casenumber first, casenumber count)
-{
-  g_return_val_if_fail (ds, FALSE);
-
-  return psppire_case_file_delete_cases (ds->case_file, count, first);
-}
-
-
 
 /* Insert a blank case before POSN */
 gboolean
@@ -550,18 +530,18 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
 {
   gboolean result;
   gint val_cnt, v;
-  struct ccase cc;
+  struct ccase *cc;
   g_return_val_if_fail (ds, FALSE);
 
-  val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
+  val_cnt = datasheet_get_column_cnt (ds->datasheet) ;
 
   g_return_val_if_fail (val_cnt > 0, FALSE);
 
   g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
 
-  case_create (&cc, val_cnt);
+  cc = case_create (val_cnt);
 
-  memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
+  memset ( case_data_rw_idx (cc, 0), 0, val_cnt * MAX_SHORT_STRING);
 
   for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
     {
@@ -569,12 +549,12 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
       if ( var_is_alpha (pv))
        continue;
 
-      case_data_rw (&cc, pv)->f = SYSMIS;
+      case_data_rw (cc, pv)->f = SYSMIS;
     }
 
-  result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
+  result = psppire_data_store_insert_case (ds, cc, posn);
 
-  case_destroy (&cc);
+  case_unref (cc);
 
   return result;
 }
@@ -591,12 +571,12 @@ psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
   GString *s;
 
   g_return_val_if_fail (store->dict, NULL);
-  g_return_val_if_fail (store->case_file, NULL);
+  g_return_val_if_fail (store->datasheet, NULL);
 
   if (column >= psppire_dict_get_var_cnt (store->dict))
     return NULL;
 
-  if ( row >= psppire_case_file_get_case_count (store->case_file))
+  if ( row >= psppire_data_store_get_case_count (store))
     return NULL;
 
   pv = psppire_dict_get_variable (store->dict, column);
@@ -607,7 +587,7 @@ psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
 
   g_assert (idx >= 0);
 
-  v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
+  v = psppire_data_store_get_value (store, row, idx, NULL,
                                    var_get_width (pv));
 
   g_return_val_if_fail (v, NULL);
@@ -647,7 +627,7 @@ psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
 
 
 static gboolean
-psppire_data_store_clear_datum (GSheetModel *model,
+psppire_data_store_clear_datum (PsppireSheetModel *model,
                                          glong row, glong col)
 {
   PsppireDataStore *store = PSPPIRE_DATA_STORE (model);
@@ -662,8 +642,10 @@ psppire_data_store_clear_datum (GSheetModel *model,
   else
     memcpy (v.s, "", MAX_SHORT_STRING);
 
-  psppire_case_file_set_value (store->case_file, row, index, &v,
-                             var_get_width (pv));
+  psppire_data_store_set_value (store, row, index, &v,
+                               var_get_width (pv));
+
+  psppire_sheet_model_range_changed (model, row, col, row, col);
 
   return TRUE;
 }
@@ -677,9 +659,11 @@ gboolean
 psppire_data_store_set_string (PsppireDataStore *store,
                               const gchar *text, glong row, glong col)
 {
+  gchar *s;
   glong n_cases;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
-  g_return_val_if_fail (pv, FALSE);
+  if ( NULL == pv)
+    return FALSE;
 
   n_cases = psppire_data_store_get_case_count (store);
 
@@ -689,33 +673,20 @@ psppire_data_store_set_string (PsppireDataStore *store,
   if (row == n_cases)
     psppire_data_store_insert_new_case (store, row);
 
-  psppire_case_file_data_in (store->case_file, row,
-                             var_get_case_index (pv), ss_cstr (text),
-                             var_get_write_format (pv));
-
-  return TRUE;
-}
-
-
-void
-psppire_data_store_set_font (PsppireDataStore *store,
-                           const PangoFontDescription *fd)
-{
-  g_return_if_fail (store);
-  g_return_if_fail (PSPPIRE_IS_DATA_STORE (store));
+  s = utf8_to_pspp_locale (text, -1, NULL);
 
-  store->font_desc = fd;
-#if 0
-  store->width_of_m = calc_m_width (fd);
-#endif
-  g_signal_emit (store, signals [FONT_CHANGED], 0);
+  psppire_data_store_data_in (store, row,
+                             var_get_case_index (pv), ss_cstr (s),
+                             var_get_write_format (pv));
+  free (s);
 
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                -1, -1, -1, -1);
+  return TRUE;
 }
 
 
+
 void
 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
 {
@@ -724,17 +695,20 @@ psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
 
   store->show_labels = show_labels;
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store),
                                 -1, -1, -1, -1);
 }
 
 
 void
-psppire_data_store_clear (PsppireDataStore *data_store)
+psppire_data_store_clear (PsppireDataStore *ds)
 {
-  psppire_case_file_clear (data_store->case_file);
+  datasheet_destroy (ds->datasheet);
+  ds->datasheet = NULL;
 
-  psppire_dict_clear (data_store->dict);
+  psppire_dict_clear (ds->dict);
+
+  g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
 }
 
 
@@ -746,12 +720,6 @@ psppire_data_store_get_reader (PsppireDataStore *ds)
   int i;
   struct casereader *reader ;
 
-  for (i = 0 ; i < n_cf_signals ; ++i )
-    {
-      g_signal_handler_disconnect (ds->case_file, ds->cf_handler_id[i]);
-      ds->cf_handler_id[i] = 0 ;
-    }
-
   if ( ds->dict )
     for (i = 0 ; i < n_dict_signals; ++i )
       {
@@ -759,7 +727,10 @@ psppire_data_store_get_reader (PsppireDataStore *ds)
                                ds->dict_handler_id[i]);
       }
 
-  reader = psppire_case_file_make_reader (ds->case_file);
+  reader = datasheet_make_reader (ds->datasheet);
+
+  /* We must not reference this again */
+  ds->datasheet = NULL;
 
   return reader;
 }
@@ -768,203 +739,300 @@ psppire_data_store_get_reader (PsppireDataStore *ds)
 
 /* Column related funcs */
 
-static glong
-geometry_get_column_count (const GSheetColumn *geom)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
-}
+static const gchar null_var_name[]=N_("var");
 
+\f
 
+/* Row related funcs */
 
-static gint
-geometry_get_width (const GSheetColumn *geom, glong unit)
+static gchar *
+get_row_button_label (const PsppireSheetModel *model, gint unit)
 {
-  const struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
-
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return ds->width_of_m * 8 ;
+  gchar *s = g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  gchar *text =  pspp_locale_to_utf8 (s, -1, 0);
 
-  if ( pv == NULL )
-    return ds->width_of_m * 8 ;
+  g_free (s);
 
-  return ds->width_of_m * var_get_display_width (pv);
+  return text;
 }
 
-static void
-geometry_set_width (GSheetColumn *geom, glong unit, gint width)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
 
-  struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
+static gboolean
+get_row_sensitivity (const PsppireSheetModel *model, gint unit)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-  var_set_display_width (pv, width / ds->width_of_m );
+  return (unit < psppire_data_store_get_case_count (ds));
 }
 
 
+\f
 
-static GtkJustification
-geometry_get_justification (const GSheetColumn *geom, glong unit)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
-  const struct variable *pv ;
+/* Column related stuff */
 
+static gchar *
+get_column_subtitle (const PsppireSheetModel *model, gint col)
+{
+  gchar *text;
+  const struct variable *v ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return GTK_JUSTIFY_LEFT;
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return NULL;
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  v = psppire_dict_get_variable (ds->dict, col);
 
-  return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
-          : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
-          : GTK_JUSTIFY_CENTER);
-}
+  if ( ! var_has_label (v))
+    return NULL;
 
+  text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
 
-static const gchar null_var_name[]=N_("var");
+  return text;
+}
 
 static gchar *
-geometry_get_column_button_label (const GSheetColumn *geom, glong unit)
+get_column_button_label (const PsppireSheetModel *model, gint col)
 {
   gchar *text;
   struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
     return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  pv = psppire_dict_get_variable (ds->dict, col);
 
   text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
 
   return text;
 }
 
-
-static gchar *
-geometry_get_column_subtitle (const GSheetColumn *geom, glong unit)
+static gboolean
+get_column_sensitivity (const PsppireSheetModel *model, gint col)
 {
-  gchar *text;
-  const struct variable *v ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return NULL;
+  return (col < psppire_dict_get_var_cnt (ds->dict));
+}
 
-  v = psppire_dict_get_variable (ds->dict, unit);
 
-  if ( ! var_has_label (v))
-    return NULL;
 
-  text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
+static GtkJustification
+get_column_justification (const PsppireSheetModel *model, gint col)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
+  const struct variable *pv ;
 
-  return text;
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return GTK_JUSTIFY_LEFT;
+
+  pv = psppire_dict_get_variable (ds->dict, col);
+
+  return (var_get_alignment (pv) == ALIGN_LEFT ? GTK_JUSTIFY_LEFT
+          : var_get_alignment (pv) == ALIGN_RIGHT ? GTK_JUSTIFY_RIGHT
+          : GTK_JUSTIFY_CENTER);
 }
 
 
-static gboolean
-geometry_get_sensitivity (const GSheetColumn *geom, glong unit)
+
+\f
+
+
+/* Returns the CASENUMth case, or a null pointer on failure.
+ */
+struct ccase *
+psppire_data_store_get_case (const PsppireDataStore *ds,
+                            casenumber casenum)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
-  return (unit < psppire_dict_get_var_cnt (ds->dict));
+  return datasheet_get_row (ds->datasheet, casenum);
 }
 
 
-static void
-psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
+gboolean
+psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
+                                casenumber n_cases)
 {
-  iface->get_column_count = geometry_get_column_count;
-  iface->get_width = geometry_get_width;
-  iface->set_width = geometry_set_width;
-  iface->get_visibility = always_true;
-  iface->get_sensitivity = geometry_get_sensitivity;
-  iface->get_justification = geometry_get_justification;
-  iface->get_button_label = geometry_get_column_button_label;
-  iface->get_subtitle = geometry_get_column_subtitle;
-}
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
+  g_return_val_if_fail (first + n_cases <=
+                       psppire_data_store_get_case_count (ds), FALSE);
 
-/* Row related funcs */
 
-static glong
-geometry_get_row_count (const GSheetRow *geom)
-{
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  datasheet_delete_rows (ds->datasheet, first, n_cases);
+
+  g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
+  psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
 
-  return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
+  return TRUE;
 }
 
-#define ROW_HEIGHT 25
 
-static gint
-geometry_get_height (const GSheetRow *geom, glong unit)
+
+/* Insert case CC into the case file before POSN */
+static gboolean
+psppire_data_store_insert_case (PsppireDataStore *ds,
+                               struct ccase *cc,
+                               casenumber posn)
 {
-  return ROW_HEIGHT;
+  bool result ;
+
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
+
+  case_ref (cc);
+  result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1);
+
+  if ( result )
+    {
+      g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
+      psppire_sheet_model_rows_inserted (PSPPIRE_SHEET_MODEL (ds), posn, 1);
+    }
+  else
+    g_warning ("Cannot insert case at position %ld\n", posn);
+
+  return result;
 }
 
-static guint
-geometry_get_top_ypixel (const GSheetRow *geo, glong row)
+
+/* Copies the IDXth value from case CASENUM into VALUE.
+   If VALUE is null, then memory is allocated is allocated with
+   malloc.  Returns the value if successful, NULL on failure. */
+static union value *
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value, int width)
 {
-  return row * ROW_HEIGHT;
+  bool allocated;
+
+  g_return_val_if_fail (ds, false);
+  g_return_val_if_fail (ds->datasheet, false);
+
+  g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), false);
+
+  if (value == NULL)
+    {
+      value = xnmalloc (value_cnt_from_width (width), sizeof *value);
+      allocated = true;
+    }
+  else
+    allocated = false;
+  if (!datasheet_get_value (ds->datasheet, casenum, idx, value, width))
+    {
+      if (allocated)
+        free (value);
+      value = NULL;
+    }
+  return value;
 }
 
-static glong
-geometry_pixel_to_row (const GSheetRow *geo, guint pixel)
+
+
+/* Set the IDXth value of case C to V.
+   Returns true if successful, false on I/O error. */
+static gboolean
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v, gint width)
 {
-  glong row  = pixel / ROW_HEIGHT;
+  bool ok;
 
-  if (row >= geometry_get_row_count (geo))
-    row = geometry_get_row_count (geo) - 1;
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
-  return row;
+  g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
+
+  ok = datasheet_put_value (ds->datasheet, casenum, idx, v, width);
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
+
+  return ok;
 }
 
 
+
+
+/* Set the IDXth value of case C using D_IN */
 static gboolean
-geometry_get_row_sensitivity (const GSheetRow *geom, glong unit)
+psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
+                           struct substring input, const struct fmt_spec *fmt)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  union value *value = NULL;
+  int width;
+  bool ok;
 
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
-  return (unit < psppire_case_file_get_case_count (ds->case_file));
-}
+  g_return_val_if_fail (idx < datasheet_get_column_cnt (ds->datasheet), FALSE);
 
+  width = fmt_var_width (fmt);
+  value = xmalloca (value_cnt_from_width (width) * sizeof *value);
+  ok = (datasheet_get_value (ds->datasheet, casenum, idx, value, width)
+        && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
+        && datasheet_put_value (ds->datasheet, casenum, idx, value, width));
 
-static gchar *
-geometry_get_row_button_label (const GSheetRow *geom, glong unit)
+  freea (value);
+
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
+
+  return ok;
+}
+
+/* Resize the cases in the casefile, by inserting N_VALUES into every
+   one of them at the position immediately preceeding WHERE.
+*/
+static gboolean
+psppire_data_store_insert_values (PsppireDataStore *ds,
+                                 gint n_values, gint where)
 {
-  gchar *text;
-  gchar *s;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  g_return_val_if_fail (ds, FALSE);
 
-  if ( unit >
-       TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
-    return 0;
+  if ( n_values == 0 )
+    return FALSE;
 
-  s = g_strdup_printf (_("%ld"), unit + FIRST_CASE_NUMBER);
+  g_assert (n_values > 0);
 
-  text =  pspp_locale_to_utf8 (s, -1, 0);
+  if ( ! ds->datasheet )
+    ds->datasheet = datasheet_create (NULL);
 
-  g_free (s);
+  {
+    union value *values = xcalloc (n_values, sizeof *values);
+    datasheet_insert_columns (ds->datasheet, values, n_values, where);
+    free (values);
+  }
 
-  return text;
+  return TRUE;
 }
 
-static void
-psppire_data_store_sheet_row_init (GSheetRowIface *iface)
+static gboolean
+get_row_overstrike (const PsppireSheetModel *model, gint row)
 {
-  iface->get_row_count = geometry_get_row_count;
+  union value val;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
+
+  const struct dictionary *dict = ds->dict->dict;
+
+  const struct variable *filter = dict_get_filter (dict);
+
+  if ( row < 0 || row >= datasheet_get_row_cnt (ds->datasheet))
+    return FALSE;
+
+  if ( ! filter)
+    return FALSE;
+
+  g_assert (var_is_numeric (filter));
+
+  if ( ! datasheet_get_value (ds->datasheet, row,
+                             var_get_case_index (filter),
+                             &val, 0) )
+    return FALSE;
+
 
-  iface->get_height = geometry_get_height;
-  iface->set_height = 0;
-  iface->get_visibility = always_true;
-  iface->get_sensitivity = geometry_get_row_sensitivity;
-  iface->top_ypixel = geometry_get_top_ypixel;
-  iface->pixel_to_row = geometry_pixel_to_row;
-  iface->get_button_label = geometry_get_row_button_label;
+  return (val.f == 0.0);
 }
index 55dcf0683d7e49e96d699b870487244207367027..bbb44ee20b08a9130d59f84fb8dd90d84cba17ba 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  Free Software Foundation
+   Copyright (C) 2006, 2009  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #ifndef __PSPPIRE_DATA_STORE_H__
 #define __PSPPIRE_DATA_STORE_H__
 
-#include <gtksheet/gsheetmodel.h>
 #include "psppire-dict.h"
-#include "psppire-case-file.h"
 
 #define FIRST_CASE_NUMBER 1
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+G_BEGIN_DECLS
 
 #define GTK_TYPE_DATA_STORE           (psppire_data_store_get_type ())
 
@@ -57,14 +53,6 @@ typedef struct _PsppireDataStoreClass  PsppireDataStoreClass;
 struct dictionary;
 
 
-enum cf_signal_handler {
-  CASES_DELETED,
-  CASE_INSERTED,
-  CASE_CHANGED,
-  n_cf_signals
-};
-
-
 enum dict_signal_handler {
   VARIABLE_INSERTED,
   VARIABLE_CHANGED,
@@ -73,8 +61,9 @@ enum dict_signal_handler {
   n_dict_signals
 };
 
-void do_this_thing (PsppireDict *, struct dictionary *, void *);
 
+struct datasheet;
+struct casereader;
 
 struct _PsppireDataStore
 {
@@ -83,47 +72,32 @@ struct _PsppireDataStore
   /*< private >*/
   gboolean dispose_has_run ;
   PsppireDict *dict;
-  PsppireCaseFile *case_file;
-  const PangoFontDescription *font_desc;
-
-  /* The width of an upper case 'M' rendered in the current font */
-  gint width_of_m ;
+  struct datasheet *datasheet;
 
   gboolean show_labels;
 
-  /* Geometry */
-  gint margin_width;
-
-  gint cf_handler_id [n_cf_signals];
+  //  gint cf_handler_id [n_cf_signals];
   gint dict_handler_id [n_dict_signals];
 };
 
 struct _PsppireDataStoreClass
 {
   GObjectClass parent_class;
-
-  /* Padding for future expansion */
-  void (*_gtk_reserved1) (void);
-  void (*_gtk_reserved2) (void);
-  void (*_gtk_reserved3) (void);
-  void (*_gtk_reserved4) (void);
 };
 
 
-inline GType psppire_data_store_get_type (void) G_GNUC_CONST;
+GType psppire_data_store_get_type (void) G_GNUC_CONST;
 PsppireDataStore *psppire_data_store_new     (PsppireDict *dict);
 
-void psppire_data_store_set_case_file (PsppireDataStore *data_store,
-                                      PsppireCaseFile *cf);
+
+void psppire_data_store_set_reader (PsppireDataStore *ds,
+                                   struct casereader *reader);
 
 void psppire_data_store_set_dictionary (PsppireDataStore *data_store,
                                        PsppireDict *dict);
 
-void psppire_data_store_set_font (PsppireDataStore *store,
-                                const PangoFontDescription *fd);
-
 void psppire_data_store_show_labels (PsppireDataStore *store,
-                                   gboolean show_labels);
+                                    gboolean show_labels);
 
 void psppire_data_store_clear (PsppireDataStore *data_store);
 
@@ -142,12 +116,23 @@ gboolean psppire_data_store_set_string (PsppireDataStore *ds,
                                        const gchar *text,
                                        glong row, glong column);
 
-inline casenumber psppire_data_store_get_case_count (const PsppireDataStore *ds);
+
+gboolean psppire_data_store_filtered (PsppireDataStore *ds,
+                                     glong row);
+
+
+casenumber psppire_data_store_get_case_count (const PsppireDataStore *ds);
 size_t psppire_data_store_get_value_count (const PsppireDataStore *ds);
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
 
+\f
+
+struct ccase *psppire_data_store_get_case (const PsppireDataStore *ds,
+                                           casenumber casenum);
+
+
+
+
+G_END_DECLS
 
 #endif /* __PSPPIRE_DATA_STORE_H__ */
diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c
new file mode 100644 (file)
index 0000000..03ae6e4
--- /dev/null
@@ -0,0 +1,1881 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   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
+   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 <gtk/gtksignal.h>
+#include <gtk/gtkbox.h>
+#include "helper.h"
+
+#include "text-data-import-dialog.h"
+
+
+#include <ui/syntax-gen.h>
+#include <language/syntax-string-source.h>
+#include <libpspp/message.h>
+#include <stdlib.h>
+
+#include <data/procedure.h>
+
+#include "psppire.h"
+#include "psppire-window.h"
+#include "psppire-data-window.h"
+#include "psppire-syntax-window.h"
+
+#include "about.h"
+
+#include "goto-case-dialog.h"
+#include "weight-cases-dialog.h"
+#include "split-file-dialog.h"
+#include "transpose-dialog.h"
+#include "sort-cases-dialog.h"
+#include "select-cases-dialog.h"
+#include "compute-dialog.h"
+#include "find-dialog.h"
+#include "rank-dialog.h"
+#include "recode-dialog.h"
+#include "comments-dialog.h"
+#include "variable-info-dialog.h"
+#include "descriptives-dialog.h"
+#include "crosstabs-dialog.h"
+#include "frequencies-dialog.h"
+#include "examine-dialog.h"
+#include "regression-dialog.h"
+#include "reliability-dialog.h"
+#include "oneway-anova-dialog.h"
+#include "t-test-independent-samples-dialog.h"
+#include "t-test-one-sample.h"
+#include "t-test-paired-samples.h"
+
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+
+static void psppire_data_window_base_finalize (PsppireDataWindowClass *, gpointer);
+static void psppire_data_window_base_init     (PsppireDataWindowClass *class);
+static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
+static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
+
+
+static void psppire_data_window_iface_init (PsppireWindowIface *iface);
+
+
+GType
+psppire_data_window_get_type (void)
+{
+  static GType psppire_data_window_type = 0;
+
+  if (!psppire_data_window_type)
+    {
+      static const GTypeInfo psppire_data_window_info =
+       {
+         sizeof (PsppireDataWindowClass),
+         (GBaseInitFunc) psppire_data_window_base_init,
+         (GBaseFinalizeFunc) psppire_data_window_base_finalize,
+         (GClassInitFunc)psppire_data_window_class_init,
+         (GClassFinalizeFunc) NULL,
+         NULL,
+         sizeof (PsppireDataWindow),
+         0,
+         (GInstanceInitFunc) psppire_data_window_init,
+       };
+
+      static const GInterfaceInfo window_interface_info =
+       {
+         (GInterfaceInitFunc) psppire_data_window_iface_init,
+         NULL,
+         NULL
+       };
+
+      psppire_data_window_type =
+       g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
+                               &psppire_data_window_info, 0);
+
+      
+      g_type_add_interface_static (psppire_data_window_type,
+                                  PSPPIRE_TYPE_WINDOW_MODEL,
+                                  &window_interface_info);
+    }
+
+  return psppire_data_window_type;
+}
+
+static GObjectClass *parent_class ;
+
+static void
+psppire_data_window_finalize (GObject *object)
+{
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (object);
+
+  g_object_unref (de->builder);
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (*G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+static void
+psppire_data_window_class_init (PsppireDataWindowClass *class)
+{
+  parent_class = g_type_class_peek_parent (class);
+}
+
+
+static void
+psppire_data_window_base_init (PsppireDataWindowClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = psppire_data_window_finalize;
+}
+
+
+
+static void
+psppire_data_window_base_finalize (PsppireDataWindowClass *class,
+                                  gpointer class_data)
+{
+}
+
+
+\f
+
+
+extern PsppireVarStore *the_var_store;
+extern struct dataset *the_dataset;
+extern PsppireDataStore *the_data_store ;
+
+extern GtkRecentManager *the_recent_mgr;
+
+static void
+set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
+{
+  GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
+
+  gtk_action_set_sensitive (edit_paste, x);
+}
+
+static void
+set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
+{
+  GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
+  GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
+
+  gtk_action_set_sensitive (edit_copy, x);
+  gtk_action_set_sensitive (edit_cut, x);
+}
+
+/* Run the EXECUTE command. */
+static void
+execute (GtkMenuItem *mi, gpointer data)
+{
+  struct getl_interface *sss = create_syntax_string_source ("EXECUTE.");
+
+  execute_syntax (sss);
+}
+
+static void
+transformation_change_callback (bool transformations_pending,
+                               gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
+
+  GtkWidget *menuitem =
+    gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
+
+  GtkWidget *status_label  =
+    get_widget_assert (de->builder, "case-counter-area");
+
+  gtk_widget_set_sensitive (menuitem, transformations_pending);
+
+
+  if ( transformations_pending)
+    gtk_label_set_text (GTK_LABEL (status_label),
+                       _("Transformations Pending"));
+  else
+    gtk_label_set_text (GTK_LABEL (status_label), "");
+}
+
+/* Callback for when the dictionary changes its filter variable */
+static void
+on_filter_change (GObject *o, gint filter_index, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  GtkWidget *filter_status_area =
+    get_widget_assert (de->builder, "filter-use-status-area");
+
+  if ( filter_index == -1 )
+    {
+      gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
+    }
+  else
+    {
+      PsppireVarStore *vs = NULL;
+      struct variable *var ;
+      gchar *text ;
+
+      g_object_get (de->data_editor, "var-store", &vs, NULL);
+
+      var = psppire_dict_get_variable (vs->dict, filter_index);
+
+      text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
+
+      gtk_label_set_text (GTK_LABEL (filter_status_area), text);
+
+      g_free (text);
+    }
+}
+
+/* Callback for when the dictionary changes its split variables */
+static void
+on_split_change (PsppireDict *dict, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  size_t n_split_vars = dict_get_split_cnt (dict->dict);
+
+  GtkWidget *split_status_area =
+    get_widget_assert (de->builder, "split-file-status-area");
+
+  if ( n_split_vars == 0 )
+    {
+      gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
+    }
+  else
+    {
+      gint i;
+      GString *text;
+      const struct variable *const * split_vars =
+       dict_get_split_vars (dict->dict);
+
+      text = g_string_new (_("Split by "));
+
+      for (i = 0 ; i < n_split_vars - 1; ++i )
+       {
+         g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
+       }
+      g_string_append (text, var_get_name (split_vars[i]));
+
+      gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
+
+      g_string_free (text, TRUE);
+    }
+}
+
+
+
+
+/* Callback for when the dictionary changes its weights */
+static void
+on_weight_change (GObject *o, gint weight_index, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  GtkWidget *weight_status_area =
+    get_widget_assert (de->builder, "weight-status-area");
+
+  if ( weight_index == -1 )
+    {
+      gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
+    }
+  else
+    {
+      struct variable *var ;
+      PsppireVarStore *vs = NULL;
+      gchar *text;
+
+      g_object_get (de->data_editor, "var-store", &vs, NULL);
+
+      var = psppire_dict_get_variable (vs->dict, weight_index);
+
+      text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
+
+      gtk_label_set_text (GTK_LABEL (weight_status_area), text);
+
+      g_free (text);
+    }
+}
+
+#if 0
+static void
+dump_rm (GtkRecentManager *rm)
+{
+  GList *items = gtk_recent_manager_get_items (rm);
+
+  GList *i;
+
+  g_print ("Recent Items:\n");
+  for (i = items; i; i = i->next)
+    {
+      GtkRecentInfo *ri = i->data;
+
+      g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
+              gtk_recent_info_get_short_name (ri),
+              gtk_recent_info_get_mime_type (ri),
+              gtk_recent_info_get_description (ri),
+              gtk_recent_info_get_uri (ri)
+              );
+
+
+      gtk_recent_info_unref (ri);
+    }
+
+  g_list_free (items);
+}
+#endif
+
+
+static gboolean
+load_file (PsppireWindow *de, const gchar *file_name)
+{
+  gchar *native_file_name;
+  struct getl_interface *sss;
+  struct string filename;
+
+  ds_init_empty (&filename);
+
+  native_file_name =
+    convert_glib_filename_to_system_filename (file_name, NULL);
+
+  syntax_gen_string (&filename, ss_cstr (native_file_name));
+
+  g_free (native_file_name);
+
+  sss = create_syntax_string_source ("GET FILE=%s.",
+                                    ds_cstr (&filename));
+
+  ds_destroy (&filename);
+
+  if (execute_syntax (sss) )
+    return TRUE;
+
+  return FALSE;
+}
+
+static GtkWidget *
+sysfile_chooser_dialog (PsppireWindow *toplevel)
+{
+  GtkWidget *dialog =
+    gtk_file_chooser_dialog_new (_("Open"),
+                                GTK_WINDOW (toplevel),
+                                GTK_FILE_CHOOSER_ACTION_OPEN,
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                NULL);
+
+  GtkFileFilter *filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
+  gtk_file_filter_add_pattern (filter, "*.sav");
+  gtk_file_filter_add_pattern (filter, "*.SAV");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
+  gtk_file_filter_add_pattern (filter, "*.por");
+  gtk_file_filter_add_pattern (filter, "*.POR");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  {
+    gchar *dir_name;
+    gchar *filename = NULL;
+    g_object_get (toplevel, "filename", &filename, NULL);
+
+    dir_name = g_path_get_dirname (filename);
+    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
+                                        dir_name);
+    free (dir_name);
+  }
+
+  return dialog;
+}
+
+/* Callback for the data_open action.
+   Prompts for a filename and opens it */
+static void
+open_data_dialog (GtkAction *action, PsppireWindow *de)
+{
+  GtkWidget *dialog = sysfile_chooser_dialog (de);
+
+  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
+    {
+    case GTK_RESPONSE_ACCEPT:
+      {
+       gchar *name =
+         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+       psppire_window_load (de, name);
+
+       g_free (name);
+      }
+      break;
+    default:
+      break;
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+/* Returns true if NAME has a suffix which might denote a PSPP file */
+static gboolean
+name_has_suffix (const gchar *name)
+{
+  if ( g_str_has_suffix (name, ".sav"))
+    return TRUE;
+  if ( g_str_has_suffix (name, ".SAV"))
+    return TRUE;
+  if ( g_str_has_suffix (name, ".por"))
+    return TRUE;
+  if ( g_str_has_suffix (name, ".POR"))
+    return TRUE;
+
+  return FALSE;
+}
+
+
+/* Save DE to file */
+static void
+save_file (PsppireWindow *w)
+{
+  gchar *fn = NULL;
+  GString *fnx;
+  struct getl_interface *sss;
+  struct string file_name ;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
+
+  g_object_get (w, "filename", &fn, NULL);
+
+  fnx = g_string_new (fn);
+
+  if ( ! name_has_suffix (fnx->str))
+    {
+      if ( de->save_as_portable)
+       g_string_append (fnx, ".por");
+      else
+       g_string_append (fnx, ".sav");
+    }
+
+  ds_init_empty (&file_name);
+  syntax_gen_string (&file_name, ss_cstr (fnx->str));
+  g_string_free (fnx, FALSE);
+
+  if ( de->save_as_portable )
+    {
+      sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
+                                        ds_cstr (&file_name));
+    }
+  else
+    {
+      sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
+                                        ds_cstr (&file_name));
+    }
+
+  ds_destroy (&file_name);
+
+  execute_syntax (sss);
+}
+
+
+static void
+insert_case (GtkAction *action, gpointer data)
+{
+  PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (data);
+
+  psppire_data_editor_insert_case (dw->data_editor);
+}
+
+static void
+on_insert_variable (GtkAction *action, gpointer data)
+{
+  PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
+  psppire_data_editor_insert_variable (de);
+}
+
+
+static void
+display_dict (PsppireDataWindow *de)
+{
+
+  struct getl_interface *sss =
+    create_syntax_string_source ("DISPLAY DICTIONARY.");
+
+  execute_syntax (sss);
+}
+
+static void
+sysfile_info (PsppireDataWindow *de)
+{
+  GtkWidget *dialog = sysfile_chooser_dialog (PSPPIRE_WINDOW (de));
+
+  if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
+    {
+      struct string filename;
+      struct getl_interface *sss;
+      gchar *file_name =
+       gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+      gchar *native_file_name =
+       convert_glib_filename_to_system_filename (file_name, NULL);
+
+      ds_init_empty (&filename);
+
+      syntax_gen_string (&filename, ss_cstr (native_file_name));
+
+      g_free (native_file_name);
+
+      sss = create_syntax_string_source ("SYSFILE INFO %s.",
+                                        ds_cstr (&filename));
+      execute_syntax (sss);
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+
+/* Callback for data_save_as action. Prompt for a filename and save */
+static void
+data_save_as_dialog (PsppireDataWindow *de)
+{
+  GtkWidget *button_sys;
+  GtkWidget *dialog =
+    gtk_file_chooser_dialog_new (_("Save"),
+                                GTK_WINDOW (de),
+                                GTK_FILE_CHOOSER_ACTION_SAVE,
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                NULL);
+
+  GtkFileFilter *filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
+  gtk_file_filter_add_pattern (filter, "*.sav");
+  gtk_file_filter_add_pattern (filter, "*.SAV");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
+  gtk_file_filter_add_pattern (filter, "*.por");
+  gtk_file_filter_add_pattern (filter, "*.POR");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  {
+    GtkWidget *button_por;
+    GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
+    button_sys =
+      gtk_radio_button_new_with_label (NULL, _("System File"));
+
+    button_por =
+      gtk_radio_button_new_with_label
+      (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
+       _("Portable File"));
+
+    gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
+    gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
+
+    gtk_widget_show_all (vbox);
+
+    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
+  }
+
+  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
+    {
+    case GTK_RESPONSE_ACCEPT:
+      {
+       GString *filename =
+         g_string_new
+         (
+          gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
+          );
+
+       de->save_as_portable =
+         ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
+
+       if ( ! name_has_suffix (filename->str))
+         {
+           if ( de->save_as_portable)
+             g_string_append (filename, ".por");
+           else
+             g_string_append (filename, ".sav");
+         }
+
+       psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
+
+       save_file (PSPPIRE_WINDOW (de));
+
+       g_string_free (filename, TRUE);
+      }
+      break;
+    default:
+      break;
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+
+/* Callback for data_save action.
+ */
+static void
+data_save (PsppireWindow *de)
+{
+  const gchar *fn = psppire_window_get_filename (de);
+
+  if ( NULL != fn)
+    psppire_window_save (de);
+  else
+    data_save_as_dialog (PSPPIRE_DATA_WINDOW (de));
+}
+
+
+/* Callback for data_new action.
+   Performs the NEW FILE command */
+static void
+new_file (GtkAction *action, PsppireDataWindow *de)
+{
+  struct getl_interface *sss =
+    create_syntax_string_source ("NEW FILE.");
+
+  execute_syntax (sss);
+
+  psppire_window_set_filename (PSPPIRE_WINDOW (de), NULL);
+}
+
+
+
+static void
+on_edit_paste (GtkAction *a, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  psppire_data_editor_clip_paste (de->data_editor);
+}
+
+static void
+on_edit_copy (GtkMenuItem *m, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  psppire_data_editor_clip_copy (de->data_editor);
+}
+
+
+
+static void
+on_edit_cut (GtkMenuItem *m, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  psppire_data_editor_clip_cut (de->data_editor);
+}
+
+
+static void
+status_bar_activate (GtkToggleAction *action, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+  GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
+
+  if ( gtk_toggle_action_get_active (action) )
+    gtk_widget_show (statusbar);
+  else
+    gtk_widget_hide (statusbar);
+}
+
+
+static void
+grid_lines_activate (GtkToggleAction *action, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+  const gboolean grid_visible = gtk_toggle_action_get_active (action);
+
+  psppire_data_editor_show_grid (de->data_editor, grid_visible);
+}
+
+static void
+data_view_activate (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
+}
+
+
+static void
+variable_view_activate (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
+}
+
+
+static void
+fonts_activate (GtkMenuItem *menuitem, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
+  PangoFontDescription *current_font;
+  gchar *font_name;
+  GtkWidget *dialog =
+    gtk_font_selection_dialog_new (_("Font Selection"));
+
+
+  current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
+  font_name = pango_font_description_to_string (current_font);
+
+  gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
+
+  g_free (font_name);
+
+  gtk_window_set_transient_for (GTK_WINDOW (dialog),
+                               GTK_WINDOW (toplevel));
+
+  if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
+    {
+      const gchar *font = gtk_font_selection_dialog_get_font_name
+       (GTK_FONT_SELECTION_DIALOG (dialog));
+
+      PangoFontDescription* font_desc =
+       pango_font_description_from_string (font);
+
+      psppire_data_editor_set_font (de->data_editor, font_desc);
+    }
+
+  gtk_widget_hide (dialog);
+}
+
+
+
+/* Callback for the value labels action */
+static void
+toggle_value_labels (GtkToggleAction *ta, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
+}
+
+static void
+toggle_split_window (GtkToggleAction *ta, gpointer data)
+{
+  PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
+
+  psppire_data_editor_split_window (de->data_editor,
+                                   gtk_toggle_action_get_active (ta));
+}
+
+
+static void
+file_quit (GtkCheckMenuItem *menuitem, gpointer data)
+{
+  /* FIXME: Need to be more intelligent here.
+     Give the user the opportunity to save any unsaved data.
+  */
+  g_object_unref (the_data_store);
+
+  psppire_quit ();
+}
+
+
+
+static GtkWidget *
+create_data_sheet_variable_popup_menu (PsppireDataWindow *de)
+{
+  GtkWidget *menu = gtk_menu_new ();
+
+  GtkWidget *sort_ascending =
+    gtk_action_create_menu_item (gtk_action_new ("sort-up",
+                                                _("Sort Ascending"),
+                                                NULL,
+                                                "gtk-sort-ascending"));
+
+  GtkWidget *sort_descending =
+    gtk_action_create_menu_item (gtk_action_new ("sort-down",
+                                                _("Sort Descending"),
+                                                NULL,
+                                                "gtk-sort-descending"));
+
+  GtkWidget *insert_variable =
+    gtk_menu_item_new_with_label (_("Insert Variable"));
+
+  GtkWidget *clear_variable =
+    gtk_menu_item_new_with_label (_("Clear"));
+
+
+  gtk_action_connect_proxy (de->delete_variables,
+                           clear_variable );
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+                        gtk_separator_menu_item_new ());
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_variable);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+                        gtk_separator_menu_item_new ());
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_ascending);
+
+
+  g_signal_connect_swapped (sort_ascending, "activate",
+                           G_CALLBACK (psppire_data_editor_sort_ascending),
+                           de->data_editor);
+
+  g_signal_connect_swapped (sort_descending, "activate",
+                           G_CALLBACK (psppire_data_editor_sort_descending),
+                           de->data_editor);
+
+  g_signal_connect_swapped (insert_variable, "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->insert_variable);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_descending);
+
+  gtk_widget_show_all (menu);
+
+  return menu;
+}
+
+
+static GtkWidget *
+create_data_sheet_cases_popup_menu (PsppireDataWindow *de)
+{
+  GtkWidget *menu = gtk_menu_new ();
+
+  GtkWidget *insert_case =
+    gtk_menu_item_new_with_label (_("Insert Case"));
+
+  GtkWidget *delete_case =
+    gtk_menu_item_new_with_label (_("Clear"));
+
+
+  gtk_action_connect_proxy (de->delete_cases,
+                           delete_case);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_case);
+
+  g_signal_connect_swapped (insert_case, "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->insert_case);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+                        gtk_separator_menu_item_new ());
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_case);
+
+
+  gtk_widget_show_all (menu);
+
+  return menu;
+}
+
+
+static GtkWidget *
+create_var_sheet_variable_popup_menu (PsppireDataWindow *de)
+{
+  GtkWidget *menu = gtk_menu_new ();
+
+  GtkWidget *insert_variable =
+    gtk_menu_item_new_with_label (_("Insert Variable"));
+
+  GtkWidget *delete_variable =
+    gtk_menu_item_new_with_label (_("Clear"));
+
+
+  gtk_action_connect_proxy (de->delete_variables,
+                           delete_variable);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable);
+
+  g_signal_connect_swapped (insert_variable, "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->insert_variable);
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+                        gtk_separator_menu_item_new ());
+
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_variable);
+
+
+  gtk_widget_show_all (menu);
+
+  return menu;
+}
+
+
+static void
+on_recent_data_select (GtkMenuShell *menushell,
+                      PsppireWindow *window)
+{
+  gchar *file;
+
+  gchar *uri =
+    gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
+
+  file = g_filename_from_uri (uri, NULL, NULL);
+
+  g_free (uri);
+
+  psppire_window_load (window, file);
+
+  g_free (file);
+}
+
+static void
+on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
+{
+  gchar *file;
+
+  GtkWidget *se ;
+
+  gchar *uri =
+    gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
+
+  file = g_filename_from_uri (uri, NULL, NULL);
+
+  g_free (uri);
+
+  se = psppire_syntax_window_new ();
+
+  if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
+    gtk_widget_show (se);
+  else
+    gtk_widget_destroy (se);
+
+  g_free (file);
+}
+
+
+static void
+enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
+{
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+
+  gtk_action_set_visible (de->delete_cases, case_num != -1);
+}
+
+
+static void
+enable_delete_variables (GtkWidget *w, gint var, gpointer data)
+{
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+
+  gtk_action_set_visible (de->delete_variables, var != -1);
+}
+
+/* Callback for when the datasheet/varsheet is selected */
+static void
+on_switch_sheet (GtkNotebook *notebook,
+                GtkNotebookPage *page,
+                guint page_num,
+                gpointer user_data)
+{
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
+
+  GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
+
+  GtkWidget *view_data =
+    gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
+
+  GtkWidget *view_variables =
+    gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
+
+  switch (page_num)
+    {
+    case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
+      gtk_widget_hide (view_variables);
+      gtk_widget_show (view_data);
+      gtk_action_set_sensitive (de->insert_variable, TRUE);
+      gtk_action_set_sensitive (de->insert_case, FALSE);
+      gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
+      break;
+    case PSPPIRE_DATA_EDITOR_DATA_VIEW:
+      gtk_widget_show (view_variables);
+      gtk_widget_hide (view_data);
+      gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
+      gtk_action_set_sensitive (de->insert_case, TRUE);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+#if 0
+  update_paste_menuitem (de, page_num);
+#endif
+}
+
+
+static GtkAction *
+resolve_action (GtkBuilder *builder, const gchar *action, const gchar *proxy)
+{
+  GtkWidget *pr = NULL;
+  GtkAction *act = get_action_assert (builder, action);
+  g_assert (GTK_IS_ACTION (act));
+
+  if ( proxy )
+    pr = get_widget_assert (builder, proxy);
+
+  if ( pr )
+    gtk_action_connect_proxy (act, pr);
+
+  return act;
+}
+
+
+static void
+set_unsaved (gpointer w)
+{
+  psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
+}
+
+static void
+psppire_data_window_init (PsppireDataWindow *de)
+{
+  PsppireVarStore *vs;
+
+  GtkWidget *menubar;
+  GtkWidget *hb ;
+  GtkWidget *sb ;
+
+  GtkWidget *box = gtk_vbox_new (FALSE, 0);
+  de->builder = builder_new ("data-editor.ui");
+
+  menubar = get_widget_assert (de->builder, "menubar");
+  hb = get_widget_assert (de->builder, "handlebox1");
+  sb = get_widget_assert (de->builder, "status-bar");
+
+  de->data_editor =
+    PSPPIRE_DATA_EDITOR (psppire_data_editor_new (the_var_store, the_data_store));
+
+  g_signal_connect_swapped (the_data_store, "case-changed",
+                           G_CALLBACK (set_unsaved), de);
+
+  g_signal_connect_swapped (the_data_store, "case-inserted",
+                           G_CALLBACK (set_unsaved), de);
+
+  g_signal_connect_swapped (the_data_store, "cases-deleted",
+                           G_CALLBACK (set_unsaved), de);
+
+  dataset_set_callback (the_dataset, set_unsaved, de);
+
+  connect_help (de->builder);
+
+  gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
+
+  gtk_container_add (GTK_CONTAINER (de), box);
+
+  set_cut_copy_menuitem_sensitivity (de, FALSE);
+
+  g_signal_connect_swapped (de->data_editor, "data-selection-changed",
+                   G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
+
+
+  set_paste_menuitem_sensitivity (de, FALSE);
+
+  g_signal_connect_swapped (de->data_editor, "data-available-changed",
+                   G_CALLBACK (set_paste_menuitem_sensitivity), de);
+
+  dataset_add_transform_change_callback (the_dataset,
+                                        transformation_change_callback,
+                                        de);
+
+
+  vs = the_var_store;
+
+  g_assert(vs); /* Traps a possible bug in w32 build */
+
+  g_signal_connect (vs->dict, "weight-changed",
+                   G_CALLBACK (on_weight_change),
+                   de);
+
+  g_signal_connect (vs->dict, "filter-changed",
+                   G_CALLBACK (on_filter_change),
+                   de);
+
+  g_signal_connect (vs->dict, "split-changed",
+                   G_CALLBACK (on_split_change),
+                   de);
+
+
+  g_signal_connect (get_action_assert (de->builder, "edit_copy"),
+                   "activate",
+                   G_CALLBACK (on_edit_copy), de);
+
+  g_signal_connect (get_action_assert (de->builder, "edit_cut"),
+                   "activate",
+                   G_CALLBACK (on_edit_cut), de);
+
+
+
+  {
+    GtkWidget *toolbarbutton = get_widget_assert (de->builder, "button-open");
+
+    GtkAction *action_data_open =
+      resolve_action (de->builder, "file_open_data", NULL);
+
+    g_object_set (action_data_open,
+                 "tooltip",  _("Open a data file"),
+                 "stock-id", "gtk-open",
+                 NULL);
+
+    g_signal_connect (action_data_open, "activate",
+                     G_CALLBACK (open_data_dialog), de);
+
+    g_signal_connect_swapped (toolbarbutton, "clicked",
+                     G_CALLBACK (gtk_action_activate), action_data_open);
+  }
+
+
+
+  {
+    GtkAction *action_data_new =
+      resolve_action (de->builder, "file_new_data", NULL);
+
+    g_object_set (action_data_new,
+                 "tooltip", _("New data file"),
+                 "stock-id", "gtk-new",
+                 NULL);
+
+    g_signal_connect (action_data_new, "activate",
+                     G_CALLBACK (new_file), de);
+  }
+
+
+
+  {
+    GtkAction *invoke_text_import_assistant =
+      resolve_action (de->builder, "file_import-text", NULL);
+
+    g_object_set (invoke_text_import_assistant,
+                 "tooltip",  _("Import text data file"),
+                 "stock-id", "gtk-convert",
+                 NULL);
+
+    g_signal_connect (invoke_text_import_assistant, "activate",
+                     G_CALLBACK (text_data_import_assistant), de);
+  }
+
+
+
+  {
+    GtkAction *action_data_save =
+      resolve_action (de->builder, "file_save", "button-save");
+
+
+    g_object_set (action_data_save,
+                 "tooltip", _("Save data to file"),
+                 "stock-id", "gtk-save",
+                 NULL);
+
+    g_signal_connect_swapped (action_data_save, "activate",
+                             G_CALLBACK (data_save), de);
+  }
+
+
+
+
+  {
+    GtkAction *action_data_save_as =
+      resolve_action (de->builder, "file_save_as", NULL);
+
+    g_object_set (action_data_save_as,
+                 "label", _("Save As"),
+                 "tooltip", _("Save data to file"),
+                 "stock-id", "gtk-save-as",
+                 NULL);
+
+    g_signal_connect_swapped (action_data_save_as, "activate",
+                     G_CALLBACK (data_save_as_dialog), de);
+  }
+
+
+  {
+    GtkAction *action_info_working_file =
+      resolve_action (de->builder,
+                     "file_information_working-file", NULL);
+
+
+    g_signal_connect_swapped (action_info_working_file, "activate",
+                     G_CALLBACK (display_dict), de);
+  }
+
+
+  {
+    GtkAction *action_info_external_file =
+      resolve_action (de->builder,
+                     "file_information_external-file", NULL);
+
+
+    g_signal_connect_swapped (action_info_external_file, "activate",
+                     G_CALLBACK (sysfile_info), de);
+  }
+
+
+
+  {
+    GtkAction *value_labels_action =
+      resolve_action (de->builder,
+                     "view_value-labels", "togglebutton-value-labels");
+
+    g_object_set (value_labels_action,
+                 "tooltip",  _("Show/hide value labels"),
+                 "stock-id", "pspp-value-labels",
+                 NULL);
+
+    g_signal_connect (value_labels_action, "toggled",
+                     G_CALLBACK (toggle_value_labels), de);
+  }
+
+
+  g_signal_connect (get_action_assert (de->builder, "edit_paste"), "activate",
+                   G_CALLBACK (on_edit_paste),
+                   de);
+
+  {
+    de->delete_cases =
+      resolve_action (de->builder, "edit_clear-cases", NULL);
+
+
+    g_object_set (de->delete_cases,
+                 "label", _("Clear"),
+                 "tooltip", _("Delete the cases at the selected position(s)"),
+                 "stock-id", "gtk-clear",
+                 NULL);
+
+    g_signal_connect_swapped (de->delete_cases, "activate",
+                             G_CALLBACK (psppire_data_editor_delete_cases),
+                             de->data_editor);
+
+    gtk_action_set_visible (de->delete_cases, FALSE);
+  }
+
+
+  {
+    de->delete_variables =
+      resolve_action (de->builder, "edit_clear-variables", NULL);
+
+    g_object_set (de->delete_variables,
+                 "label", _("Clear"),
+                 "tooltip", _("Delete the variables at the selected position(s)"),
+                 "stock-id", "gtk-clear",
+                 NULL);
+
+
+    g_signal_connect_swapped (de->delete_variables, "activate",
+                             G_CALLBACK (psppire_data_editor_delete_variables),
+                             de->data_editor);
+
+    gtk_action_set_visible (de->delete_variables, FALSE);
+  }
+
+
+  de->insert_variable =
+    resolve_action (de->builder, "edit_insert-variable",
+                   "button-insert-variable");
+
+  g_object_set (de->insert_variable,
+               "tooltip", _("Create a new variable at the current position"),
+               "stock-id", "pspp-insert-variable",
+               NULL);
+
+  g_signal_connect (de->insert_variable, "activate",
+                   G_CALLBACK (on_insert_variable), de->data_editor);
+
+
+
+
+
+  de->insert_case =
+    resolve_action (de->builder, "edit_insert-case", "button-insert-case");
+
+  g_object_set (de->insert_case,
+               "tooltip", _("Create a new case at the current position"),
+               "stock-id", "pspp-insert-case",
+               NULL);
+
+  g_signal_connect (de->insert_case, "activate",
+                   G_CALLBACK (insert_case), de);
+
+
+
+
+
+  de->invoke_goto_dialog =
+    resolve_action (de->builder, "edit_goto-case", "button-goto-case");
+
+
+  g_object_set (de->invoke_goto_dialog,
+               "tooltip", _("Jump to a Case in the Data Sheet"),
+               "stock-id", "gtk-jump-to",
+               NULL);
+
+  g_signal_connect (de->invoke_goto_dialog, "activate",
+                   G_CALLBACK (goto_case_dialog), de);
+
+
+
+  {
+    GtkAction *invoke_weight_cases_dialog =
+      resolve_action (de->builder, "data_weight-cases", "button-weight-cases");
+
+
+    g_object_set (invoke_weight_cases_dialog,
+                 "stock-id", "pspp-weight-cases",
+                 "tooltip", _("Weight cases by variable"),
+                 NULL);
+
+    g_signal_connect (invoke_weight_cases_dialog, "activate",
+                     G_CALLBACK (weight_cases_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_transpose_dialog =
+      resolve_action (de->builder, "data_transpose", NULL);
+
+
+    g_object_set (invoke_transpose_dialog,
+                 "tooltip", _("Transpose the cases with the variables"),
+                 "stock-id", "pspp-transpose",
+                 NULL);
+
+    g_signal_connect (invoke_transpose_dialog, "activate",
+                     G_CALLBACK (transpose_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_split_file_dialog =
+      resolve_action (de->builder, "data_split-file", "button-split-file");
+
+    g_object_set (invoke_split_file_dialog,
+                 "tooltip", _("Split the active file"),
+                 "stock-id", "pspp-split-file",
+                 NULL);
+
+    g_signal_connect (invoke_split_file_dialog, "activate",
+                     G_CALLBACK (split_file_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_sort_cases_dialog =
+      resolve_action (de->builder, "data_sort-cases", NULL);
+
+
+    g_object_set (invoke_sort_cases_dialog,
+                 "tooltip", _("Sort cases in the active file"),
+                 "stock-id", "gtk-sort-ascending",
+                 NULL);
+
+    g_signal_connect (invoke_sort_cases_dialog, "activate",
+                     G_CALLBACK (sort_cases_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_select_cases_dialog =
+      resolve_action (de->builder, "data_select-cases", "button-select-cases");
+
+    g_object_set (invoke_select_cases_dialog,
+                 "tooltip", _("Select cases from the active file"),
+                 "stock-id", "pspp-select-cases",
+                 NULL);
+
+    g_signal_connect (invoke_select_cases_dialog, "activate",
+                     G_CALLBACK (select_cases_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_compute_dialog =
+      resolve_action (de->builder, "transform_compute", NULL);
+
+    g_object_set (invoke_compute_dialog,
+                 "tooltip", _("Compute new values for a variable"),
+                 "stock-id", "pspp-compute",
+                 NULL);
+
+    g_signal_connect (invoke_compute_dialog, "activate",
+                     G_CALLBACK (compute_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_oneway_anova_dialog =
+      resolve_action (de->builder, "oneway-anova", NULL);
+
+    g_object_set (invoke_oneway_anova_dialog,
+                 "tooltip", _("Perform one way analysis of variance"),
+                 NULL);
+
+    g_signal_connect (invoke_oneway_anova_dialog, "activate",
+                     G_CALLBACK (oneway_anova_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_t_test_independent_samples_dialog =
+      resolve_action (de->builder, "indep-t-test", NULL);
+
+
+    g_object_set (invoke_t_test_independent_samples_dialog,
+                 "tooltip",
+                 _("Calculate T Test for samples from independent groups"),
+                 NULL);
+
+    g_signal_connect (invoke_t_test_independent_samples_dialog, "activate",
+                     G_CALLBACK (t_test_independent_samples_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_t_test_paired_samples_dialog =
+      resolve_action (de->builder, "paired-t-test", NULL);
+
+    g_object_set (invoke_t_test_paired_samples_dialog,
+                 "tooltip",
+                 _("Calculate T Test for paired samples"),
+                 NULL);
+
+    g_signal_connect (invoke_t_test_paired_samples_dialog, "activate",
+                     G_CALLBACK (t_test_paired_samples_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_t_test_one_sample_dialog =
+      resolve_action (de->builder, "one-sample-t-test", NULL);
+
+    g_object_set (invoke_t_test_one_sample_dialog,
+                 "tooltip",
+                 _("Calculate T Test for sample from a single distribution"),
+                 NULL);
+
+    g_signal_connect (invoke_t_test_one_sample_dialog, "activate",
+                     G_CALLBACK (t_test_one_sample_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_comments_dialog =
+      resolve_action (de->builder, "utilities_comments", NULL);
+
+
+    g_object_set (invoke_comments_dialog,
+                 "tooltip",
+                 _("Commentary text for the data file"),
+                 NULL);
+
+    g_signal_connect (invoke_comments_dialog, "activate",
+                     G_CALLBACK (comments_dialog), de);
+  }
+
+
+
+  {
+    GtkAction *invoke_find_dialog =
+      resolve_action (de->builder, "edit_find", "button-find");
+
+    g_object_set (invoke_find_dialog, "stock-id", "gtk-find", NULL);
+
+    g_signal_connect (invoke_find_dialog, "activate",
+                     G_CALLBACK (find_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_rank_dialog =
+      resolve_action (de->builder, "transform_rank", NULL);
+
+    g_object_set (invoke_rank_dialog,
+                 "stock-id", "pspp-rank-cases",
+                 "tooltip", _("Rank Cases"),
+                 NULL);
+
+    g_signal_connect (invoke_rank_dialog, "activate",
+                     G_CALLBACK (rank_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_recode_same_dialog =
+      resolve_action (de->builder, "transform_recode-same", NULL);
+
+    g_object_set (invoke_recode_same_dialog,
+                 "stock-id", "pspp-recode-same",
+                 "tooltip", _("Recode values into the same variables"),
+                 NULL);
+
+    g_signal_connect (invoke_recode_same_dialog, "activate",
+                     G_CALLBACK (recode_same_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_recode_different_dialog  =
+      resolve_action (de->builder, "transform_recode-different", NULL);
+
+    g_object_set (invoke_recode_different_dialog,
+                 "stock-id", "pspp-recode-different",
+                 "tooltip", _("Recode values into different variables"),
+                 NULL);
+
+    g_signal_connect (invoke_recode_different_dialog, "activate",
+                     G_CALLBACK (recode_different_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_variable_info_dialog  =
+      resolve_action (de->builder, "utilities_variables", "button-goto-variable");
+
+    g_object_set (invoke_variable_info_dialog,
+                 "stock-id", "pspp-goto-variable",
+                 "tooltip", _("Jump to variable"),
+                 NULL);
+
+    g_signal_connect (invoke_variable_info_dialog, "activate",
+                     G_CALLBACK (variable_info_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_descriptives_dialog =
+      resolve_action (de->builder,  "analyze_descriptives", NULL);
+
+    g_object_set (invoke_descriptives_dialog,
+                 "tooltip", _("Calculate descriptive statistics (mean, variance, ...)"),
+                 "stock-id", "pspp-descriptives",
+                 NULL);
+
+    g_signal_connect (invoke_descriptives_dialog, "activate",
+                     G_CALLBACK (descriptives_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_frequencies_dialog =
+      resolve_action (de->builder,  "analyze_frequencies", NULL);
+
+    g_object_set (invoke_frequencies_dialog,
+                 "tooltip", _("Generate frequency statistics"),
+                 "stock-id", "pspp-frequencies",
+                 NULL);
+
+    g_signal_connect (invoke_frequencies_dialog, "activate",
+                     G_CALLBACK (frequencies_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_crosstabs_dialog =
+      resolve_action (de->builder, "crosstabs", NULL);
+
+    g_object_set (invoke_crosstabs_dialog,
+                 "tooltip", _("Generate crosstabulations"),
+                 "stock-id", "pspp-crosstabs",
+                 NULL);
+
+    g_signal_connect (invoke_crosstabs_dialog, "activate",
+                     G_CALLBACK (crosstabs_dialog), de);
+  }
+
+
+
+  {
+    GtkAction *invoke_examine_dialog =
+      resolve_action (de->builder, "analyze_explore", NULL);
+
+    g_object_set (invoke_examine_dialog,
+                 "tooltip", _("Examine Data by Factors"),
+                 "stock-id", "pspp-examine",
+                 NULL);
+
+    g_signal_connect (invoke_examine_dialog, "activate",
+                     G_CALLBACK (examine_dialog), de);
+  }
+
+
+  {
+    GtkAction *invoke_regression_dialog =
+      resolve_action (de->builder, "linear-regression", NULL);
+
+    g_object_set (invoke_regression_dialog,
+                 "tooltip", _("Estimate parameters of the linear model"),
+                 "stock-id", "pspp-regression",
+                 NULL
+                 );
+
+    g_signal_connect (invoke_regression_dialog, "activate",
+                     G_CALLBACK (regression_dialog), de);
+  }
+
+  {
+    GtkAction *invoke_reliability_dialog =
+      resolve_action (de->builder, "reliability", NULL);
+
+    g_object_set (invoke_reliability_dialog,
+                 "tooltip", _("Reliability Analysis"),
+                 "stock-id", "pspp-reliability",
+                 NULL
+                 );
+
+    g_signal_connect (invoke_reliability_dialog, "activate",
+                     G_CALLBACK (reliability_dialog), de);
+  }
+
+
+  {
+    GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
+
+    GtkWidget *recent_data =
+      gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
+
+    GtkWidget *recent_files =
+      gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
+
+
+    GtkWidget *menu_data =
+      gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
+
+    GtkWidget *menu_files =
+      gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
+
+    {
+      GtkRecentFilter *filter = gtk_recent_filter_new ();
+
+      gtk_recent_filter_add_pattern (filter, "*.sav");
+      gtk_recent_filter_add_pattern (filter, "*.SAV");
+      gtk_recent_filter_add_pattern (filter, "*.por");
+      gtk_recent_filter_add_pattern (filter, "*.POR");
+
+      gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
+
+      gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
+    }
+
+    gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
+
+
+    g_signal_connect (menu_data, "selection-done",
+                     G_CALLBACK (on_recent_data_select),
+                     de);
+
+    {
+      GtkRecentFilter *filter = gtk_recent_filter_new ();
+
+      gtk_recent_filter_add_pattern (filter, "*.sps");
+      gtk_recent_filter_add_pattern (filter, "*.SPS");
+
+      gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
+
+      gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
+    }
+
+    gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
+
+    g_signal_connect (menu_files, "selection-done",
+                     G_CALLBACK (on_recent_files_select),
+                     de);
+
+  }
+
+  g_signal_connect (get_action_assert (de->builder,"file_new_syntax"),
+                   "activate",
+                   G_CALLBACK (create_syntax_window),
+                   NULL);
+
+  g_signal_connect (get_action_assert (de->builder,"file_open_syntax"),
+                   "activate",
+                   G_CALLBACK (open_syntax_window),
+                   de);
+
+  {
+    GtkAction *abt = get_action_assert (de->builder, "help_about");
+    g_object_set (abt, "stock-id", "gtk-about", NULL);
+    g_signal_connect (abt,
+                     "activate",
+                     G_CALLBACK (about_new),
+                     de);
+  }
+
+
+  g_signal_connect (get_action_assert (de->builder,"help_reference"),
+                   "activate",
+                   G_CALLBACK (reference_manual),
+                   de);
+
+
+  g_signal_connect (de->data_editor,
+                   "cases-selected",
+                   G_CALLBACK (enable_delete_cases),
+                   de);
+
+  g_signal_connect (de->data_editor,
+                   "variables-selected",
+                   G_CALLBACK (enable_delete_variables),
+                   de);
+
+
+  g_signal_connect (de->data_editor,
+                   "switch-page",
+                   G_CALLBACK (on_switch_sheet), de);
+
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
+
+  g_signal_connect (get_action_assert (de->builder, "view_statusbar"),
+                   "activate",
+                   G_CALLBACK (status_bar_activate), de);
+
+
+  g_signal_connect (get_action_assert (de->builder, "view_gridlines"),
+                   "activate",
+                   G_CALLBACK (grid_lines_activate), de);
+
+
+
+  g_signal_connect (get_action_assert (de->builder, "view_data"),
+                   "activate",
+                   G_CALLBACK (data_view_activate), de);
+
+  g_signal_connect (get_action_assert (de->builder, "view_variables"),
+                   "activate",
+                   G_CALLBACK (variable_view_activate), de);
+
+
+  {
+    GtkAction *font_action =
+      resolve_action (de->builder, "view_fonts", NULL);
+
+    g_object_set (font_action,
+                 "stock-id", "gtk-select-font",
+                 NULL);
+
+    g_signal_connect (font_action,
+                     "activate",
+                     G_CALLBACK (fonts_activate), de);
+  }
+
+
+
+  g_signal_connect (get_action_assert (de->builder, "file_quit"),
+                   "activate",
+                   G_CALLBACK (file_quit), de);
+
+  g_signal_connect (get_action_assert (de->builder, "transform_run-pending"),
+                   "activate",
+                   G_CALLBACK (execute), de);
+
+
+  g_signal_connect (get_action_assert (de->builder, "windows_minimise_all"),
+                   "activate",
+                   G_CALLBACK (psppire_window_minimise_all), NULL);
+
+
+  {
+    GtkAction *split_window_action =
+      resolve_action (de->builder, "windows_split", NULL);
+
+    g_object_set (split_window_action,
+                 "tooltip", _("Split the window vertically and horizontally"),
+                 "stock-id", "pspp-split-window",
+                 NULL);
+
+    g_signal_connect (split_window_action, "toggled",
+                     G_CALLBACK (toggle_split_window),
+                     de);
+  }
+
+  de->data_sheet_variable_popup_menu =
+    GTK_MENU (create_data_sheet_variable_popup_menu (de));
+
+  de->var_sheet_variable_popup_menu =
+    GTK_MENU (create_var_sheet_variable_popup_menu (de));
+
+  de->data_sheet_cases_popup_menu =
+    GTK_MENU (create_data_sheet_cases_popup_menu (de));
+
+  {
+  GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
+
+  PSPPIRE_WINDOW (de)->menu =
+    GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
+  }
+
+
+  g_object_set (de->data_editor,
+               "datasheet-column-menu", de->data_sheet_variable_popup_menu,
+               "datasheet-row-menu", de->data_sheet_cases_popup_menu,
+               "varsheet-row-menu", de->var_sheet_variable_popup_menu,
+               NULL);
+
+  gtk_widget_show (GTK_WIDGET (de->data_editor));
+  gtk_widget_show (box);
+}
+
+
+GtkWidget*
+psppire_data_window_new (void)
+{
+  return GTK_WIDGET (g_object_new (psppire_data_window_get_type (),
+                                  "description", _("Data Editor"),
+                                  NULL));
+}
+
+
+
+
+\f
+
+static void
+psppire_data_window_iface_init (PsppireWindowIface *iface)
+{
+  iface->save = save_file;
+  iface->load = load_file;
+}
diff --git a/src/ui/gui/psppire-data-window.h b/src/ui/gui/psppire-data-window.h
new file mode 100644 (file)
index 0000000..1b6deab
--- /dev/null
@@ -0,0 +1,80 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 __PSPPIRE_DATA_WINDOW_H__
+#define __PSPPIRE_DATA_WINDOW_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkaction.h>
+#include "psppire-window.h"
+#include "psppire-data-editor.h"
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PSPPIRE_DATA_WINDOW_TYPE            (psppire_data_window_get_type ())
+#define PSPPIRE_DATA_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_DATA_WINDOW_TYPE, PsppireDataWindow))
+#define PSPPIRE_DATA_WINDOW_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), \
+    PSPPIRE_DATA_WINDOW_TYPE, PsppireData_WindowClass))
+#define PSPPIRE_IS_DATA_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+    PSPPIRE_DATA_WINDOW_TYPE))
+#define PSPPIRE_IS_DATA_WINDOW_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+    PSPPIRE_DATA_WINDOW_TYPE))
+
+
+typedef struct _PsppireDataWindow       PsppireDataWindow;
+typedef struct _PsppireDataWindowClass  PsppireDataWindowClass;
+
+
+struct _PsppireDataWindow
+{
+  PsppireWindow parent;
+
+  /* <private> */
+  PsppireDataEditor *data_editor;
+  GtkBuilder *builder;
+
+
+  GtkAction *invoke_goto_dialog;
+
+  GtkAction *insert_variable;
+  GtkAction *insert_case;
+  GtkAction *delete_variables;
+  GtkAction *delete_cases;
+
+
+  GtkMenu *data_sheet_variable_popup_menu;
+  GtkMenu *data_sheet_cases_popup_menu;
+  GtkMenu *var_sheet_variable_popup_menu;
+
+
+  gboolean save_as_portable;
+};
+
+struct _PsppireDataWindowClass
+{
+  PsppireWindowClass parent_class;
+};
+
+GType      psppire_data_window_get_type        (void);
+GtkWidget* psppire_data_window_new             (void);
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_DATA_WINDOW_H__ */
index 24d9089698fddeeb2ca098b1d146c844ecec413d..dbe4612f026a160f29592553894be93caff3e3ca 100644 (file)
 
 #include <gtk/gtk.h>
 #include <gtk/gtksignal.h>
+#include <gtk/gtkbuildable.h>
 #include "psppire-dialog.h"
 #include "psppire-buttonbox.h"
 #include "psppire-selector.h"
+#include "psppire-conf.h"
+#include <string.h>
 
 static void psppire_dialog_class_init          (PsppireDialogClass *);
 static void psppire_dialog_init                (PsppireDialog      *);
@@ -34,6 +37,9 @@ enum  {DIALOG_REFRESH,
 static guint signals [n_SIGNALS];
 
 
+static void psppire_dialog_buildable_init (GtkBuildableIface *iface);
+
+
 GType
 psppire_dialog_get_type (void)
 {
@@ -54,8 +60,19 @@ psppire_dialog_get_type (void)
        (GInstanceInitFunc) psppire_dialog_init,
       };
 
+      static const GInterfaceInfo buildable_info =
+      {
+       (GInterfaceInitFunc) psppire_dialog_buildable_init,
+       NULL,
+       NULL
+      };
+
       dialog_type = g_type_register_static (GTK_TYPE_WINDOW,
                                            "PsppireDialog", &dialog_info, 0);
+
+      g_type_add_interface_static (dialog_type,
+                                  GTK_TYPE_BUILDABLE,
+                                  &buildable_info);
     }
 
   return dialog_type;
@@ -86,7 +103,8 @@ psppire_dialog_finalize (GObject *object)
 enum
 {
   PROP_0,
-  PROP_ORIENTATION
+  PROP_ORIENTATION,
+  PROP_SLIDING
 };
 
 
@@ -102,14 +120,17 @@ psppire_dialog_get_property (GObject         *object,
     {
     case PROP_ORIENTATION:
       {
-       if ( GTK_IS_VBOX (dialog->box) )
+       if ( GTK_IS_VBOX (dialog->box) || GTK_VPANED (dialog->box))
          g_value_set_enum (value, PSPPIRE_VERTICAL);
-       else if ( GTK_IS_HBOX (dialog->box))
+       else if ( GTK_IS_HBOX (dialog->box) || GTK_HPANED (dialog->box))
          g_value_set_enum (value, PSPPIRE_HORIZONTAL);
        else if ( GTK_IS_TABLE (dialog->box))
          g_value_set_enum (value, PSPPIRE_TABULAR);
       }
       break;
+    case PROP_SLIDING:
+      g_value_set_boolean (value, dialog->slidable);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -118,22 +139,26 @@ psppire_dialog_get_property (GObject         *object,
 
 
 static void
-dialog_set_orientation (PsppireDialog *dialog, const GValue *orval)
+dialog_set_container (PsppireDialog *dialog)
 {
-  PsppireOrientation orientation = g_value_get_enum (orval);
-
   if ( dialog->box != NULL)
     {
       gtk_container_remove (GTK_CONTAINER (dialog), dialog->box);
     }
 
-  switch ( orientation )
+  switch (dialog->orientation)
     {
     case PSPPIRE_HORIZONTAL:
-      dialog->box = gtk_hbox_new (FALSE, 5);
+      if ( dialog->slidable)
+       dialog->box = gtk_hpaned_new();
+      else
+       dialog->box = gtk_hbox_new (FALSE, 5);
       break;
     case PSPPIRE_VERTICAL:
-      dialog->box = gtk_vbox_new (FALSE, 5);
+      if ( dialog->slidable)
+       dialog->box = gtk_vpaned_new();
+      else
+       dialog->box = gtk_vbox_new (FALSE, 5);
       break;
     case PSPPIRE_TABULAR:
       dialog->box = gtk_table_new (2, 3, FALSE);
@@ -144,6 +169,7 @@ dialog_set_orientation (PsppireDialog *dialog, const GValue *orval)
       break;
     }
 
+  gtk_widget_show_all (dialog->box);
   gtk_container_add (GTK_CONTAINER (dialog), dialog->box);
 }
 
@@ -159,13 +185,18 @@ psppire_dialog_set_property (GObject         *object,
 
   switch (prop_id)
     {
+    case PROP_SLIDING:
+      dialog->slidable = g_value_get_boolean (value);
+      break;
     case PROP_ORIENTATION:
-      dialog_set_orientation (dialog, value);
+      dialog->orientation = g_value_get_enum (value);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     };
+
+  dialog_set_container (dialog);
 }
 
 
@@ -176,15 +207,24 @@ psppire_dialog_class_init (PsppireDialogClass *class)
 {
   GObjectClass *object_class = (GObjectClass *) class;
 
+  GParamSpec *sliding_spec ;
 
   orientation_spec =
     g_param_spec_enum ("orientation",
                       "Orientation",
                       "Which way widgets are packed",
-                      G_TYPE_PSPPIRE_ORIENTATION,
+                      PSPPIRE_TYPE_ORIENTATION,
                       PSPPIRE_HORIZONTAL /* default value */,
                       G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
 
+  sliding_spec =
+    g_param_spec_boolean ("slidable",
+                         "Slidable",
+                         "Can the container be sized by the user",
+                         FALSE,
+                         G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
+
+
 
   object_class->set_property = psppire_dialog_set_property;
   object_class->get_property = psppire_dialog_get_property;
@@ -194,6 +234,11 @@ psppire_dialog_class_init (PsppireDialogClass *class)
                                    orientation_spec);
 
 
+  g_object_class_install_property (object_class,
+                                   PROP_SLIDING,
+                                   sliding_spec);
+
+
 
   signals [DIALOG_REFRESH] =
     g_signal_new ("refresh",
@@ -247,6 +292,39 @@ delete_event_callback (GtkWidget *w, GdkEvent *e, gpointer data)
 }
 
 
+static gboolean
+configure_event_callback (GtkDialog *dialog,
+                         GdkEvent *event, gpointer data)
+{
+  gchar *base = NULL;
+
+  PsppireConf *conf = psppire_conf_new ();
+
+  if ( ! GTK_WIDGET_MAPPED (dialog))
+    return FALSE;
+
+  g_object_get (dialog, "name", &base, NULL);
+
+  psppire_conf_save_window_geometry (conf, base, event);
+
+  return FALSE;
+}
+
+
+static void
+on_realize (GtkWindow *dialog, gpointer data)
+{
+  PsppireConf *conf = psppire_conf_new ();
+
+  const gchar *base = NULL;
+
+  g_object_get (dialog, "name", &base, NULL);
+
+  psppire_conf_set_window_geometry (conf, base, dialog);
+}
+
+
+
 static void
 psppire_dialog_init (PsppireDialog *dialog)
 {
@@ -254,6 +332,7 @@ psppire_dialog_init (PsppireDialog *dialog)
   dialog->box = NULL;
   dialog->contents_are_valid = NULL;
   dialog->validity_data = NULL;
+  dialog->slidable = FALSE;
 
   g_value_init (&value, orientation_spec->value_type);
   g_param_value_set_default (orientation_spec, &value);
@@ -261,18 +340,25 @@ psppire_dialog_init (PsppireDialog *dialog)
   gtk_window_set_type_hint (GTK_WINDOW (dialog),
        GDK_WINDOW_TYPE_HINT_DIALOG);
 
-  dialog_set_orientation (dialog, &value);
-
   g_value_unset (&value);
 
-  g_signal_connect (G_OBJECT (dialog), "delete-event",
+  g_signal_connect (dialog, "delete-event",
                    G_CALLBACK (delete_event_callback),
                    dialog);
 
+  g_signal_connect (dialog, "configure-event",
+                   G_CALLBACK (configure_event_callback),
+                   dialog);
+
+  g_signal_connect (dialog, "realize",
+                   G_CALLBACK (on_realize),
+                   dialog);
+
+
   gtk_window_set_type_hint (GTK_WINDOW (dialog),
        GDK_WINDOW_TYPE_HINT_DIALOG);
 
-  gtk_widget_show_all (dialog->box);
+  g_object_set (dialog, "icon-name", "psppicon", NULL);
 }
 
 
@@ -281,7 +367,8 @@ psppire_dialog_new (void)
 {
   PsppireDialog *dialog ;
 
-  dialog = g_object_new (psppire_dialog_get_type (), NULL);
+  dialog = g_object_new (psppire_dialog_get_type (),
+                        NULL);
 
   return GTK_WIDGET (dialog) ;
 }
@@ -475,3 +562,26 @@ psppire_dialog_set_valid_predicate (PsppireDialog *dialog,
 }
 
 
+
+
+
+static GObject *
+get_internal_child    (GtkBuildable *buildable,
+                      GtkBuilder *builder,
+                      const gchar *childname)
+{
+  PsppireDialog *dialog = PSPPIRE_DIALOG (buildable);
+
+  if ( 0 == strcmp (childname, "hbox"))
+    return G_OBJECT (dialog->box);
+
+  return NULL;
+}
+
+
+
+static void
+psppire_dialog_buildable_init (GtkBuildableIface *iface)
+{
+  iface->get_internal_child = get_internal_child;
+}
index 05215ce984e2b14bd332aad5bb02f86ffd25d2ef..6c175cfb8a690d0de4632f5daabacfc12bf754e5 100644 (file)
@@ -42,6 +42,14 @@ typedef struct _PsppireDialogClass  PsppireDialogClass;
 
 typedef gboolean (*ContentsAreValid) (gpointer);
 
+typedef enum
+  {
+    PSPPIRE_HORIZONTAL,
+    PSPPIRE_VERTICAL,
+    PSPPIRE_TABULAR
+  } PsppireOrientation;
+
+
 
 struct _PsppireDialog
 {
@@ -54,6 +62,8 @@ struct _PsppireDialog
 
   ContentsAreValid contents_are_valid;
   gpointer validity_data;
+  gboolean slidable;
+  PsppireOrientation orientation;
 };
 
 struct _PsppireDialogClass
@@ -77,14 +87,7 @@ void           psppire_dialog_notify_change (PsppireDialog *);
 GType psppire_orientation_get_type (void);
 
 
-typedef enum
-  {
-    PSPPIRE_HORIZONTAL,
-    PSPPIRE_VERTICAL,
-    PSPPIRE_TABULAR
-  } PsppireOrientation;
-
-#define G_TYPE_PSPPIRE_ORIENTATION (psppire_orientation_get_type ())
+#define PSPPIRE_TYPE_ORIENTATION (psppire_orientation_get_type ())
 
 
 G_END_DECLS
index e6f1373a54d969ed76de022e0135924ace674c8c..564257f46c8cdb74c6adef8ce6d4547f0bd70764 100644 (file)
@@ -19,7 +19,7 @@
 #include <stdlib.h>
 
 #include <gtk/gtk.h>
-#include <gtksheet/gtkextra-marshal.h>
+#include <ui/gui/psppire-marshal.h>
 
 #include "psppire-dict.h"
 #include <data/dictionary.h>
 #include "helper.h"
 #include "message-dialog.h"
 
-/* --- prototypes --- */
-static void psppire_dict_class_init    (PsppireDictClass       *class);
-static void psppire_dict_init  (PsppireDict            *dict);
-static void psppire_dict_finalize      (GObject                *object);
-
-static void dictionary_tree_model_init (GtkTreeModelIface *iface);
-
-
-/* --- variables --- */
-static GObjectClass     *parent_class = NULL;
 
 enum  {
   BACKEND_CHANGED,
@@ -48,6 +38,7 @@ enum  {
   VARIABLE_RESIZED,
   VARIABLE_INSERTED,
   VARIABLE_DELETED,
+  VARIABLE_DISPLAY_WIDTH_CHANGED,
 
   WEIGHT_CHANGED,
   FILTER_CHANGED,
@@ -55,6 +46,18 @@ enum  {
   n_SIGNALS
 };
 
+
+/* --- prototypes --- */
+static void psppire_dict_class_init    (PsppireDictClass       *class);
+static void psppire_dict_init  (PsppireDict            *dict);
+static void psppire_dict_finalize      (GObject                *object);
+
+static void dictionary_tree_model_init (GtkTreeModelIface *iface);
+
+
+/* --- variables --- */
+static GObjectClass     *parent_class = NULL;
+
 static guint signals [n_SIGNALS];
 
 /* --- functions --- */
@@ -93,8 +96,6 @@ psppire_dict_get_type (void)
 
       g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
                                   &tree_model_info);
-
-
     }
 
   return object_type;
@@ -152,7 +153,7 @@ psppire_dict_class_init (PsppireDictClass *class)
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL,
-                 marshaller_VOID__INT_INT_INT,
+                 psppire_marshal_VOID__INT_INT_INT,
                  G_TYPE_NONE,
                  3,
                  G_TYPE_INT,
@@ -166,12 +167,23 @@ psppire_dict_class_init (PsppireDictClass *class)
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL,
-                 gtkextra_VOID__INT_INT,
+                 psppire_marshal_VOID__INT_INT,
                  G_TYPE_NONE,
                  2,
                  G_TYPE_INT,
                  G_TYPE_INT);
 
+  signals [VARIABLE_DISPLAY_WIDTH_CHANGED] =
+    g_signal_new ("variable-display-width-changed",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__INT,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_INT);
+
 
   signals [WEIGHT_CHANGED] =
     g_signal_new ("weight-changed",
@@ -223,7 +235,10 @@ psppire_dict_finalize (GObject *object)
 static void
 addcb (struct dictionary *d, int idx, void *pd)
 {
-  g_signal_emit (pd, signals [VARIABLE_INSERTED], 0, idx);
+  PsppireDict *dict = PSPPIRE_DICT (pd);
+
+  if ( ! dict->disable_insert_signal)
+    g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx);
 }
 
 static void
@@ -264,6 +279,13 @@ split_changed_callback (struct dictionary *d, void *pd)
   g_signal_emit (pd, signals [SPLIT_CHANGED], 0);
 }
 
+static void
+variable_display_width_callback (struct dictionary *d, int idx, void *pd)
+{
+  g_signal_emit (pd, signals [VARIABLE_DISPLAY_WIDTH_CHANGED], 0, idx);
+}
+
+
 
 static const struct dict_callbacks gui_callbacks =
   {
@@ -273,13 +295,15 @@ static const struct dict_callbacks gui_callbacks =
     resize_cb,
     weight_changed_callback,
     filter_changed_callback,
-    split_changed_callback
+    split_changed_callback,
+    variable_display_width_callback
   };
 
 static void
 psppire_dict_init (PsppireDict *psppire_dict)
 {
   psppire_dict->stamp = g_random_int ();
+  psppire_dict->disable_insert_signal = FALSE;
 }
 
 /**
@@ -291,7 +315,7 @@ psppire_dict_init (PsppireDict *psppire_dict)
 PsppireDict*
 psppire_dict_new_from_dict (struct dictionary *d)
 {
-  PsppireDict *new_dict = g_object_new (G_TYPE_PSPPIRE_DICT, NULL);
+  PsppireDict *new_dict = g_object_new (PSPPIRE_TYPE_DICT, NULL);
   new_dict->dict = d;
 
   dict_set_callbacks (new_dict->dict, &gui_callbacks, new_dict);
@@ -349,9 +373,15 @@ psppire_dict_insert_variable (PsppireDict *d, gint idx, const gchar *name)
   if ( ! name )
     name = auto_generate_var_name (d);
 
+  d->disable_insert_signal = TRUE;
+
   var = dict_create_var (d->dict, name, 0);
 
   dict_reorder_var (d->dict, var, idx);
+
+  d->disable_insert_signal = FALSE;
+
+  g_signal_emit (d, signals[VARIABLE_INSERTED], 0, idx);
 }
 
 /* Delete N variables beginning at FIRST */
@@ -404,7 +434,9 @@ psppire_dict_set_name (PsppireDict* d, gint idx, const gchar *name)
 
 
 
-/* Return the IDXth variable */
+/* Return the IDXth variable.
+   Will return NULL if IDX  exceeds the number of variables in the dictionary.
+ */
 struct variable *
 psppire_dict_get_variable (const PsppireDict *d, gint idx)
 {
index f645b355a6b0d5b90100fc60606e96d8240b1968..3fd73f9a6dc0bd9f21e9016cc7037f495081dcb6 100644 (file)
@@ -30,26 +30,26 @@ G_BEGIN_DECLS
 
 
 /* --- type macros --- */
-#define G_TYPE_PSPPIRE_DICT              (psppire_dict_get_type ())
-#define PSPPIRE_DICT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_PSPPIRE_DICT, PsppireDict))
-#define PSPPIRE_DICT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_PSPPIRE_DICT, PsppireDictClass))
-#define PSPPIRE_IS_DICT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_PSPPIRE_DICT))
-#define PSPPIRE_IS_DICT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_PSPPIRE_DICT))
-#define PSPPIRE_DICT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_PSPPIRE_DICT, PsppireDictClass))
-
+#define PSPPIRE_TYPE_DICT              (psppire_dict_get_type ())
+#define PSPPIRE_DICT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), PSPPIRE_TYPE_DICT, PsppireDict))
+#define PSPPIRE_DICT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), PSPPIRE_TYPE_DICT, PsppireDictClass))
+#define PSPPIRE_IS_DICT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), PSPPIRE_TYPE_DICT))
+#define PSPPIRE_IS_DICT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DICT))
+#define PSPPIRE_DICT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), PSPPIRE_TYPE_DICT, PsppireDictClass))
 
 
 /* --- typedefs & structures --- */
 typedef struct _PsppireDict       PsppireDict;
 typedef struct _PsppireDictClass PsppireDictClass;
 
-enum {DICT_TVM_COL_NAME=0, DICT_TVM_COL_VAR, n_DICT_COLS} ;
+enum {DICT_TVM_COL_NAME=0, DICT_TVM_COL_VAR, DICT_TVM_COL_LABEL, n_DICT_COLS} ;
 
 struct _PsppireDict
 {
   GObject             parent;
   struct dictionary *dict;
 
+  gboolean disable_insert_signal;
   /* For GtkTreeModelIface */
   gint stamp;
 };
@@ -96,7 +96,7 @@ void psppire_dict_resize_variable (PsppireDict *,
 gboolean psppire_dict_check_name (const PsppireDict *dict,
                              const gchar *name, gboolean report);
 
-inline gint psppire_dict_get_next_value_idx (const PsppireDict *dict);
+gint psppire_dict_get_next_value_idx (const PsppireDict *dict);
 
 gboolean psppire_dict_rename_var (PsppireDict *dict, struct variable *v,
                              const gchar *text);
diff --git a/src/ui/gui/psppire-dictview.c b/src/ui/gui/psppire-dictview.c
new file mode 100644 (file)
index 0000000..a2cc7df
--- /dev/null
@@ -0,0 +1,580 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 <gtk/gtktreeview.h>
+#include "psppire-dictview.h"
+#include "psppire-dict.h"
+#include "psppire-conf.h"
+#include <data/format.h>
+#include "helper.h"
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+static void psppire_dict_view_base_finalize (PsppireDictViewClass *, gpointer);
+static void psppire_dict_view_base_init     (PsppireDictViewClass *class);
+static void psppire_dict_view_class_init    (PsppireDictViewClass *class);
+static void psppire_dict_view_init          (PsppireDictView      *dict_view);
+
+
+GType
+psppire_dict_view_get_type (void)
+{
+  static GType psppire_dict_view_type = 0;
+
+  if (!psppire_dict_view_type)
+    {
+      static const GTypeInfo psppire_dict_view_info =
+      {
+       sizeof (PsppireDictViewClass),
+       (GBaseInitFunc) psppire_dict_view_base_init,
+        (GBaseFinalizeFunc) psppire_dict_view_base_finalize,
+       (GClassInitFunc)psppire_dict_view_class_init,
+       (GClassFinalizeFunc) NULL,
+       NULL,
+        sizeof (PsppireDictView),
+       0,
+       (GInstanceInitFunc) psppire_dict_view_init,
+      };
+
+      psppire_dict_view_type =
+       g_type_register_static (GTK_TYPE_TREE_VIEW, "PsppireDictView",
+                               &psppire_dict_view_info, 0);
+    }
+
+  return psppire_dict_view_type;
+}
+
+
+static void
+psppire_dict_view_finalize (GObject *object)
+{
+  PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
+
+  g_object_unref (dict_view->menu);
+}
+
+/* Properties */
+enum
+{
+  PROP_0,
+  PROP_MODEL,
+  PROP_DICTIONARY,
+  PROP_PREDICATE,
+  PROP_SELECTION_MODE
+};
+
+
+/* A GtkTreeModelFilterVisibleFunc to filter lines in the treeview */
+static gboolean
+filter_variables (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+  var_predicate_func *predicate = data;
+  struct variable *var;
+  PsppireDict *dict = PSPPIRE_DICT (model);
+
+  GtkTreePath *path = gtk_tree_model_get_path (model, iter);
+
+  gint *idx = gtk_tree_path_get_indices (path);
+
+  var =  psppire_dict_get_variable (dict, *idx);
+
+  gtk_tree_path_free (path);
+
+  return predicate (var);
+}
+
+static void
+set_model (PsppireDictView *dict_view)
+{
+  GtkTreeModel *model ;
+
+  if ( dict_view->predicate )
+    {
+      model = gtk_tree_model_filter_new (GTK_TREE_MODEL (dict_view->dict),
+                                        NULL);
+
+      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
+                                             filter_variables,
+                                             dict_view->predicate,
+                                             NULL);
+    }
+  else
+    {
+      model = GTK_TREE_MODEL (dict_view->dict);
+    }
+
+  gtk_tree_view_set_model (GTK_TREE_VIEW (dict_view), model);
+}
+
+static void
+psppire_dict_view_set_property (GObject         *object,
+                              guint            prop_id,
+                              const GValue    *value,
+                              GParamSpec      *pspec)
+{
+  PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_DICTIONARY:
+      dict_view->dict = g_value_get_object (value);
+      break;
+    case PROP_MODEL:
+      g_critical ("Don't set the \"model\" property on %s. "
+                 "Use the \"dictionary\" property instead.",
+                 G_OBJECT_TYPE_NAME (dict_view));
+      break;
+    case PROP_PREDICATE:
+      dict_view->predicate = g_value_get_pointer (value);
+      break;
+    case PROP_SELECTION_MODE:
+      {
+       GtkTreeSelection *selection =
+         gtk_tree_view_get_selection (GTK_TREE_VIEW (dict_view));
+
+       GtkSelectionMode mode = g_value_get_enum (value);
+
+       gtk_tree_selection_set_mode (selection, mode);
+      }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+
+
+  set_model (dict_view);
+}
+
+
+static void
+psppire_dict_view_get_property (GObject         *object,
+                              guint            prop_id,
+                              GValue          *value,
+                              GParamSpec      *pspec)
+{
+  PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_DICTIONARY:
+      g_value_set_object (value, dict_view->dict);
+      break;
+    case PROP_PREDICATE:
+      g_value_set_pointer (value, dict_view->predicate);
+      break;
+    case PROP_SELECTION_MODE:
+      {
+       GtkTreeSelection *selection =
+         gtk_tree_view_get_selection (GTK_TREE_VIEW (dict_view));
+
+       g_value_set_enum (value, gtk_tree_selection_get_mode (selection));
+      }
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+
+static void
+psppire_dict_view_class_init (PsppireDictViewClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  GParamSpec *dictionary_spec =
+    g_param_spec_object ("dictionary",
+                        "Dictionary",
+                        _("The dictionary to be displayed by this widget"),
+                        PSPPIRE_TYPE_DICT,
+                        G_PARAM_READABLE | G_PARAM_WRITABLE);
+
+  GParamSpec *predicate_spec =
+    g_param_spec_pointer ("predicate",
+                         "Predicate",
+                         _("A predicate function"),
+                         G_PARAM_READABLE | G_PARAM_WRITABLE);
+
+
+  GParamSpec *selection_mode_spec =
+    g_param_spec_enum ("selection-mode",
+                      "Selection Mode",
+                      _("How many things can be selected"),
+                      GTK_TYPE_SELECTION_MODE,
+                      GTK_SELECTION_MULTIPLE,
+                      G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
+
+
+  GParamSpec *dummy_spec =
+    g_param_spec_pointer ("model",
+                         "Model",
+                         "Don't set the property",
+                         G_PARAM_WRITABLE);
+
+  object_class->set_property = psppire_dict_view_set_property;
+  object_class->get_property = psppire_dict_view_get_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_MODEL,
+                                   dummy_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_DICTIONARY,
+                                   dictionary_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_PREDICATE,
+                                   predicate_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_SELECTION_MODE,
+                                   selection_mode_spec);
+}
+
+
+static void
+psppire_dict_view_base_init (PsppireDictViewClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = psppire_dict_view_finalize;
+}
+
+
+
+static void
+psppire_dict_view_base_finalize (PsppireDictViewClass *class,
+                                gpointer class_data)
+{
+
+}
+
+
+static void
+dv_get_base_model (GtkTreeModel *top_model, GtkTreeIter *top_iter,
+               GtkTreeModel **model, GtkTreeIter *iter
+               )
+{
+  *model = top_model;
+  *iter = *top_iter;
+
+  while ( ! PSPPIRE_IS_DICT (*model))
+    {
+      GtkTreeIter parent_iter = *iter;
+
+      if ( GTK_IS_TREE_MODEL_FILTER (*model))
+       {
+         GtkTreeModelFilter *parent_model = GTK_TREE_MODEL_FILTER (*model);
+
+         *model = gtk_tree_model_filter_get_model (parent_model);
+
+         gtk_tree_model_filter_convert_iter_to_child_iter (parent_model,
+                                                           iter,
+                                                           &parent_iter);
+       }
+      else if (GTK_IS_TREE_MODEL_SORT (*model))
+       {
+         GtkTreeModelSort *parent_model = GTK_TREE_MODEL_SORT (*model);
+
+         *model = gtk_tree_model_sort_get_model (parent_model);
+
+         gtk_tree_model_sort_convert_iter_to_child_iter (parent_model,
+                                                         iter,
+                                                         &parent_iter);
+       }
+    }
+}
+
+
+
+/* A GtkTreeCellDataFunc which renders the name and/or label of the
+   variable */
+static void
+var_description_cell_data_func (GtkTreeViewColumn *col,
+                               GtkCellRenderer *cell,
+                               GtkTreeModel *top_model,
+                               GtkTreeIter *top_iter,
+                               gpointer data)
+{
+  PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
+  struct variable *var;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+
+
+  dv_get_base_model (top_model, top_iter, &model, &iter);
+
+  g_assert (PSPPIRE_IS_DICT (model));
+
+  gtk_tree_model_get (model,
+                     &iter, DICT_TVM_COL_VAR, &var, -1);
+
+  if ( var_has_label (var) && dv->prefer_labels)
+    {
+      gchar *text = g_strdup_printf (
+                                    "<span stretch=\"condensed\">%s</span>",
+                                    var_get_label (var));
+
+      char *utf8 = pspp_locale_to_utf8 (text, -1, NULL);
+
+      g_free (text);
+      g_object_set (cell, "markup", utf8, NULL);
+      g_free (utf8);
+    }
+  else
+    {
+      char *name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
+      g_object_set (cell, "text", name, NULL);
+      g_free (name);
+    }
+}
+
+
+
+/* A GtkTreeCellDataFunc which sets the icon appropriate to the type
+   of variable */
+static void
+var_icon_cell_data_func (GtkTreeViewColumn *col,
+                      GtkCellRenderer *cell,
+                      GtkTreeModel *model,
+                      GtkTreeIter *iter,
+                      gpointer data)
+{
+  struct variable *var;
+  gtk_tree_model_get (model, iter, DICT_TVM_COL_VAR, &var, -1);
+
+  if ( var_is_alpha (var))
+    {
+      g_object_set (cell, "stock-id", "var-string", NULL);
+    }
+  else
+    {
+      const struct fmt_spec *fs = var_get_write_format (var);
+      int cat = fmt_get_category (fs->type);
+      switch ( var_get_measure (var))
+       {
+       case MEASURE_NOMINAL:
+         g_object_set (cell, "stock-id", "var-nominal", NULL);
+         break;
+       case MEASURE_ORDINAL:
+         g_object_set (cell, "stock-id", "var-ordinal", NULL);
+         break;
+       case MEASURE_SCALE:
+         if ( ( FMT_CAT_DATE | FMT_CAT_TIME ) & cat )
+           g_object_set (cell, "stock-id", "var-date-scale", NULL);
+         else
+           g_object_set (cell, "stock-id", "var-scale", NULL);
+         break;
+       default:
+         g_assert_not_reached ();
+       };
+    }
+}
+
+
+/* Sets the tooltip to be the name of the variable under the cursor */
+static gboolean
+set_tooltip_for_variable (GtkTreeView  *treeview,
+                         gint        x,
+                         gint        y,
+                         gboolean    keyboard_mode,
+                         GtkTooltip *tooltip,
+                         gpointer    user_data)
+{
+  gint bx, by;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  GtkTreeModel *tree_model;
+  struct variable *var = NULL;
+  gboolean ok;
+
+
+  gtk_tree_view_convert_widget_to_bin_window_coords (treeview,
+                                                     x, y, &bx, &by);
+
+  if (!gtk_tree_view_get_path_at_pos (treeview, bx, by,
+                                      &path, NULL, NULL, NULL))
+    return FALSE;
+
+  tree_model = gtk_tree_view_get_model (treeview);
+
+
+  gtk_tree_view_set_tooltip_row (treeview, tooltip, path);
+
+  ok = gtk_tree_model_get_iter (tree_model, &iter, path);
+
+  gtk_tree_path_free (path);
+  if (!ok)
+    return FALSE;
+
+
+  gtk_tree_model_get (tree_model, &iter, DICT_TVM_COL_VAR,  &var, -1);
+
+  if ( ! var_has_label (var))
+    return FALSE;
+
+  {
+    gchar *tip ;
+
+    if ( PSPPIRE_DICT_VIEW (treeview)->prefer_labels )
+      tip = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
+    else
+      tip = pspp_locale_to_utf8 (var_get_label (var), -1, NULL);
+
+    gtk_tooltip_set_text (tooltip, tip);
+
+    g_free (tip);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+show_menu (PsppireDictView *dv, GdkEventButton *event, gpointer data)
+{
+  if (event->button != 3)
+    return FALSE;
+
+  gtk_menu_popup (GTK_MENU (dv->menu), NULL, NULL, NULL, NULL,
+                 event->button, event->time);
+
+  return TRUE;
+}
+
+static void
+toggle_label_preference (GtkCheckMenuItem *checkbox, gpointer data)
+{
+  PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
+
+  dv->prefer_labels = gtk_check_menu_item_get_active (checkbox);
+
+  gtk_widget_queue_draw (GTK_WIDGET (dv));
+}
+
+
+
+static void
+psppire_dict_view_init (PsppireDictView *dict_view)
+{
+  GtkTreeViewColumn *col = gtk_tree_view_column_new ();
+
+  GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new ();
+
+  dict_view->prefer_labels = TRUE;
+
+  psppire_conf_get_boolean (psppire_conf_new (),
+                           G_OBJECT_TYPE_NAME (dict_view),
+                           "prefer-labels",
+                           &dict_view->prefer_labels);
+
+  gtk_tree_view_column_set_title (col, _("Variable"));
+
+  gtk_tree_view_column_pack_start (col, renderer, FALSE);
+
+  gtk_tree_view_column_set_cell_data_func (col, renderer,
+                                          var_icon_cell_data_func,
+                                          NULL, NULL);
+
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (col, renderer, TRUE);
+  gtk_tree_view_column_set_cell_data_func (col, renderer,
+                                          var_description_cell_data_func,
+                                          dict_view, NULL);
+
+  g_object_set (renderer, "ellipsize-set", TRUE, NULL);
+  g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
+
+  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
+
+  /* FIXME: make this a value in terms of character widths */
+  gtk_tree_view_column_set_min_width (col, 150);
+
+  gtk_tree_view_append_column (GTK_TREE_VIEW (dict_view), col);
+
+  g_object_set (dict_view,
+               "has-tooltip", TRUE,
+               "headers-visible", FALSE,
+               NULL);
+
+  g_signal_connect (dict_view, "query-tooltip",
+                   G_CALLBACK (set_tooltip_for_variable), NULL);
+
+  dict_view->menu = gtk_menu_new ();
+
+
+  {
+    GtkWidget *checkbox =
+      gtk_check_menu_item_new_with_label  (_("Prefer variable labels"));
+
+    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (checkbox),
+                                   dict_view->prefer_labels);
+
+    g_signal_connect (checkbox, "toggled",
+                     G_CALLBACK (toggle_label_preference), dict_view);
+
+
+    gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), checkbox);
+
+  }
+
+  gtk_widget_show_all (dict_view->menu);
+
+  g_signal_connect (dict_view, "button-press-event",
+                   G_CALLBACK (show_menu), NULL);
+}
+
+
+GtkWidget*
+psppire_dict_view_new (void)
+{
+  return GTK_WIDGET (g_object_new (psppire_dict_view_get_type (), NULL));
+}
+
+
+
+struct variable *
+psppire_dict_view_get_selected_variable (PsppireDictView *treeview)
+{
+  struct variable *var;
+  GtkTreeModel *top_model;
+  GtkTreeIter top_iter;
+
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  GtkTreeSelection *selection =
+    gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+
+  if (! gtk_tree_selection_get_selected (selection,
+                                        &top_model, &top_iter))
+    return NULL;
+
+  dv_get_base_model (top_model, &top_iter, &model, &iter);
+
+  g_assert (PSPPIRE_IS_DICT (model));
+
+  gtk_tree_model_get (model,
+                     &iter, DICT_TVM_COL_VAR, &var, -1);
+
+  return var;
+}
+
+
diff --git a/src/ui/gui/psppire-dictview.h b/src/ui/gui/psppire-dictview.h
new file mode 100644 (file)
index 0000000..dd08d78
--- /dev/null
@@ -0,0 +1,68 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 __PSPPIRE_DICT_VIEW_H__
+#define __PSPPIRE_DICT_VIEW_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtktreeview.h>
+
+#include "psppire-dict.h"
+#include "dict-display.h"
+
+G_BEGIN_DECLS
+
+#define PSPPIRE_DICT_VIEW_TYPE            (psppire_dict_view_get_type ())
+#define PSPPIRE_DICT_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_DICT_VIEW_TYPE, PsppireDictView))
+#define PSPPIRE_DICT_VIEW_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), \
+    PSPPIRE_DICT_VIEW_TYPE, PsppireDictViewClass))
+#define PSPPIRE_IS_DICT_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+    PSPPIRE_DICT_VIEW_TYPE))
+#define PSPPIRE_IS_DICT_VIEW_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+    PSPPIRE_DICT_VIEW_TYPE))
+
+
+typedef struct _PsppireDictView       PsppireDictView;
+typedef struct _PsppireDictViewClass  PsppireDictViewClass;
+
+
+
+struct _PsppireDictView
+{
+  GtkTreeView parent;
+
+  PsppireDict *dict;
+  var_predicate_func *predicate;
+  GtkWidget *menu;
+  gboolean prefer_labels;
+};
+
+struct _PsppireDictViewClass
+{
+  GtkTreeViewClass parent_class;
+
+};
+
+GType      psppire_dict_view_get_type        (void);
+struct variable * psppire_dict_view_get_selected_variable (PsppireDictView *);
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_DICT_VIEW_H__ */
diff --git a/src/ui/gui/psppire-output-window.c b/src/ui/gui/psppire-output-window.c
new file mode 100644 (file)
index 0000000..106b4ad
--- /dev/null
@@ -0,0 +1,376 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 <gtk/gtksignal.h>
+#include <gtk/gtkbox.h>
+#include "helper.h"
+
+#include <libpspp/message.h>
+#include <stdlib.h>
+
+#include "about.h"
+
+#include "psppire-output-window.h"
+
+
+#include "xalloc.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+
+static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
+static void psppire_output_window_base_init     (PsppireOutputWindowClass *class);
+static void psppire_output_window_class_init    (PsppireOutputWindowClass *class);
+static void psppire_output_window_init          (PsppireOutputWindow      *window);
+
+
+GType
+psppire_output_window_get_type (void)
+{
+  static GType psppire_output_window_type = 0;
+
+  if (!psppire_output_window_type)
+    {
+      static const GTypeInfo psppire_output_window_info =
+      {
+       sizeof (PsppireOutputWindowClass),
+       (GBaseInitFunc) psppire_output_window_base_init,
+        (GBaseFinalizeFunc) psppire_output_window_base_finalize,
+       (GClassInitFunc)psppire_output_window_class_init,
+       (GClassFinalizeFunc) NULL,
+       NULL,
+        sizeof (PsppireOutputWindow),
+       0,
+       (GInstanceInitFunc) psppire_output_window_init,
+      };
+
+      psppire_output_window_type =
+       g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
+                               &psppire_output_window_info, 0);
+    }
+
+  return psppire_output_window_type;
+}
+
+static GObjectClass *parent_class;
+
+static void
+psppire_output_window_finalize (GObject *object)
+{
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (*G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+static void
+psppire_output_window_class_init (PsppireOutputWindowClass *class)
+{
+  parent_class = g_type_class_peek_parent (class);
+}
+
+
+static void
+psppire_output_window_base_init (PsppireOutputWindowClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = psppire_output_window_finalize;
+}
+
+
+
+static void
+psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
+                                    gpointer class_data)
+{
+}
+
+
+\f
+
+static PsppireOutputWindow *the_output_viewer = NULL;
+
+
+int viewer_length = 16;
+int viewer_width = 59;
+
+/* Callback for the "delete" action (clicking the x on the top right
+   hand corner of the window) */
+static gboolean
+on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
+{
+  PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
+
+  gtk_widget_destroy (GTK_WIDGET (ow));
+
+  the_output_viewer = NULL;
+
+  unlink (OUTPUT_FILE_NAME);
+
+  return FALSE;
+}
+
+
+
+static void
+cancel_urgency (GtkWindow *window,  gpointer data)
+{
+  gtk_window_set_urgency_hint (window, FALSE);
+}
+/* Sets width and length according to the new size
+   of the output window */
+static void
+on_textview_resize (GtkWidget     *widget,
+                   GtkAllocation *allocation,
+                   gpointer       user_data)
+{
+  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));
+}
+
+
+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");
+
+
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+
+  g_object_ref (menubar);
+  gtk_widget_unparent (menubar);
+
+  g_object_ref (sw);
+  gtk_widget_unparent (sw);
+
+
+  gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
+
+
+  gtk_widget_show_all (box);
+
+  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),
+                   window);
+
+  g_signal_connect (get_action_assert (xml,"help_reference"),
+                   "activate",
+                   G_CALLBACK (reference_manual),
+                   NULL);
+
+  g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
+                   "activate",
+                   G_CALLBACK (psppire_window_minimise_all),
+                   NULL);
+
+  {
+    GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
+
+    PSPPIRE_WINDOW (window)->menu =
+      GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar1/windows_menuitem/windows_minimise-all")->parent);
+  }
+
+  g_object_unref (xml);
+
+  g_signal_connect (window, "delete-event",
+                   G_CALLBACK (on_delete), window);
+}
+
+
+GtkWidget*
+psppire_output_window_new (void)
+{
+  return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
+                                  "filename", "Output",
+                                  "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_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
+       return;
+      }
+
+    /* Delete all the entire buffer */
+    gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
+    gtk_text_buffer_delete (ov->buffer, &start_iter, &end_iter);
+
+
+    gtk_text_buffer_get_start_iter (ov->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 (ov->buffer, &start_iter, line, -1);
+      }
+
+    fclose (fp);
+  }
+#else
+  {
+    if ( ow->fp == NULL)
+      {
+       ow->fp = fopen (OUTPUT_FILE_NAME, "r");
+       if ( ow->fp == NULL)
+         {
+           g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
+           return;
+         }
+      }
+
+    /* Read in the next lot of text */
+    while (fgets (line, viewer_width + 1, ow->fp) != NULL)
+      {
+       chars_inserted = TRUE;
+       gtk_text_buffer_insert (ow->buffer, &end_iter, line, -1);
+      }
+  }
+#endif
+
+  /* Scroll to where the start of this lot of text begins */
+  gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ow->textview),
+                               mark,
+                               0.1, TRUE, 0.0, 0.0);
+
+
+  if ( chars_inserted )
+    gtk_window_set_urgency_hint ( GTK_WINDOW (ow), TRUE);
+}
+
+
+
diff --git a/src/ui/gui/psppire-output-window.h b/src/ui/gui/psppire-output-window.h
new file mode 100644 (file)
index 0000000..621a9e2
--- /dev/null
@@ -0,0 +1,78 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 __PSPPIRE_OUTPUT_WINDOW_H__
+#define __PSPPIRE_OUTPUT_WINDOW_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkaction.h>
+#include <gtk/gtktextbuffer.h>
+#include "psppire-window.h"
+#include <gtk/gtk.h>
+
+extern int viewer_length;
+extern int viewer_width ;
+
+
+#define OUTPUT_FILE_NAME "psppire.txt"
+
+
+
+G_BEGIN_DECLS
+
+#define PSPPIRE_OUTPUT_WINDOW_TYPE            (psppire_output_window_get_type ())
+#define PSPPIRE_OUTPUT_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_OUTPUT_WINDOW_TYPE, PsppireOutputWindow))
+#define PSPPIRE_OUTPUT_WINDOW_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), \
+    PSPPIRE_OUTPUT_WINDOW_TYPE, PsppireOutput_WindowClass))
+#define PSPPIRE_IS_OUTPUT_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+    PSPPIRE_OUTPUT_WINDOW_TYPE))
+#define PSPPIRE_IS_OUTPUT_WINDOW_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+    PSPPIRE_OUTPUT_WINDOW_TYPE))
+
+
+typedef struct _PsppireOutputWindow       PsppireOutputWindow;
+typedef struct _PsppireOutputWindowClass  PsppireOutputWindowClass;
+
+
+struct _PsppireOutputWindow
+{
+  PsppireWindow parent;
+
+  /* <private> */
+  GtkTextBuffer *buffer;  /* The buffer which contains the text */
+  GtkWidget *textview ;
+  FILE *fp;               /* The file it's viewing */
+};
+
+struct _PsppireOutputWindowClass
+{
+  PsppireWindowClass parent_class;
+
+};
+
+GType      psppire_output_window_get_type        (void);
+GtkWidget* psppire_output_window_new             (void);
+
+
+void psppire_output_window_reload (void);
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_OUTPUT_WINDOW_H__ */
index 2f9dbe06a890cbe5903ed74891b2574c1b702cea..d6ea3375257186d0566aa8a7706da717030db17c 100644 (file)
@@ -179,7 +179,7 @@ psppire_selector_class_init (PsppireSelectorClass *class)
     g_param_spec_enum ("orientation",
                       "Orientation",
                       "Where the selector is relative to its subjects",
-                      G_TYPE_PSPPIRE_SELECTOR_ORIENTATION,
+                      PSPPIRE_TYPE_SELECTOR_ORIENTATION,
                       PSPPIRE_SELECT_SOURCE_BEFORE_DEST /* default value */,
                       G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
 
index bdd358ebab30ebdb1dba610a9f58b0767dca1c1f..6fff5d4269da20286062e0f8c36def77374f1fa5 100644 (file)
@@ -130,7 +130,7 @@ typedef enum {
   PSPPIRE_SELECT_SOURCE_BELOW_DEST
 } PsppireSelectorOrientation;
 
-#define G_TYPE_PSPPIRE_SELECTOR_ORIENTATION \
+#define PSPPIRE_TYPE_SELECTOR_ORIENTATION \
   (psppire_selector_orientation_get_type())
 
 
diff --git a/src/ui/gui/psppire-syntax-window.c b/src/ui/gui/psppire-syntax-window.c
new file mode 100644 (file)
index 0000000..053dc22
--- /dev/null
@@ -0,0 +1,615 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 <gtk/gtksignal.h>
+#include <gtk/gtkbox.h>
+#include "helper.h"
+
+#include <libpspp/message.h>
+#include <stdlib.h>
+
+#include "psppire.h"
+#include "psppire-syntax-window.h"
+
+#include "psppire-data-window.h"
+#include "psppire-window-register.h"
+#include "psppire.h"
+#include "about.h"
+#include "psppire-syntax-window.h"
+#include "syntax-editor-source.h"
+#include <language/lexer/lexer.h>
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+static void psppire_syntax_window_base_finalize (PsppireSyntaxWindowClass *, gpointer);
+static void psppire_syntax_window_base_init     (PsppireSyntaxWindowClass *class);
+static void psppire_syntax_window_class_init    (PsppireSyntaxWindowClass *class);
+static void psppire_syntax_window_init          (PsppireSyntaxWindow      *syntax_editor);
+
+
+static void psppire_syntax_window_iface_init (PsppireWindowIface *iface);
+
+
+GType
+psppire_syntax_window_get_type (void)
+{
+  static GType psppire_syntax_window_type = 0;
+
+  if (!psppire_syntax_window_type)
+    {
+      static const GTypeInfo psppire_syntax_window_info =
+      {
+       sizeof (PsppireSyntaxWindowClass),
+       (GBaseInitFunc) psppire_syntax_window_base_init,
+        (GBaseFinalizeFunc) psppire_syntax_window_base_finalize,
+       (GClassInitFunc)psppire_syntax_window_class_init,
+       (GClassFinalizeFunc) NULL,
+       NULL,
+        sizeof (PsppireSyntaxWindow),
+       0,
+       (GInstanceInitFunc) psppire_syntax_window_init,
+      };
+
+      static const GInterfaceInfo window_interface_info =
+       {
+         (GInterfaceInitFunc) psppire_syntax_window_iface_init,
+         NULL,
+         NULL
+       };
+
+      psppire_syntax_window_type =
+       g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireSyntaxWindow",
+                               &psppire_syntax_window_info, 0);
+
+      g_type_add_interface_static (psppire_syntax_window_type,
+                                  PSPPIRE_TYPE_WINDOW_MODEL,
+                                  &window_interface_info);
+    }
+
+  return psppire_syntax_window_type;
+}
+
+static GObjectClass *parent_class ;
+
+static void
+psppire_syntax_window_finalize (GObject *object)
+{
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (*G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+static void
+psppire_syntax_window_class_init (PsppireSyntaxWindowClass *class)
+{
+  parent_class = g_type_class_peek_parent (class);
+}
+
+
+static void
+psppire_syntax_window_base_init (PsppireSyntaxWindowClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = psppire_syntax_window_finalize;
+}
+
+
+
+static void
+psppire_syntax_window_base_finalize (PsppireSyntaxWindowClass *class,
+                                    gpointer class_data)
+{
+}
+
+
+static void
+editor_execute_syntax (const PsppireSyntaxWindow *sw, GtkTextIter start,
+                      GtkTextIter stop)
+{
+  PsppireWindow *win = PSPPIRE_WINDOW (sw);
+  const gchar *name = psppire_window_get_filename (win);
+  execute_syntax (create_syntax_editor_source (sw->buffer, start, stop, name));
+}
+
+
+/* Parse and execute all the text in the buffer */
+static void
+on_run_all (GtkMenuItem *menuitem, gpointer user_data)
+{
+  GtkTextIter begin, end;
+  PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
+
+  gtk_text_buffer_get_iter_at_offset (se->buffer, &begin, 0);
+  gtk_text_buffer_get_iter_at_offset (se->buffer, &end, -1);
+
+  editor_execute_syntax (se, begin, end);
+}
+
+/* Parse and execute the currently selected text */
+static void
+on_run_selection (GtkMenuItem *menuitem, gpointer user_data)
+{
+  GtkTextIter begin, end;
+  PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
+
+  if ( gtk_text_buffer_get_selection_bounds (se->buffer, &begin, &end) )
+    editor_execute_syntax (se, begin, end);
+}
+
+
+/* Parse and execute the from the current line, to the end of the
+   buffer */
+static void
+on_run_to_end (GtkMenuItem *menuitem, gpointer user_data)
+{
+  GtkTextIter begin, end;
+  GtkTextIter here;
+  gint line;
+
+  PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
+
+  /* Get the current line */
+  gtk_text_buffer_get_iter_at_mark (se->buffer,
+                                   &here,
+                                   gtk_text_buffer_get_insert (se->buffer)
+                                   );
+
+  line = gtk_text_iter_get_line (&here) ;
+
+  /* Now set begin and end to the start of this line, and end of buffer
+     respectively */
+  gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
+  gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
+
+  editor_execute_syntax (se, begin, end);
+}
+
+
+
+/* Parse and execute the current line */
+static void
+on_run_current_line (GtkMenuItem *menuitem, gpointer user_data)
+{
+  GtkTextIter begin, end;
+  GtkTextIter here;
+  gint line;
+
+  PsppireSyntaxWindow *se = PSPPIRE_SYNTAX_WINDOW (user_data);
+
+  /* Get the current line */
+  gtk_text_buffer_get_iter_at_mark (se->buffer,
+                                   &here,
+                                   gtk_text_buffer_get_insert (se->buffer)
+                                   );
+
+  line = gtk_text_iter_get_line (&here) ;
+
+  /* Now set begin and end to the start of this line, and start of
+     following line respectively */
+  gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
+  gtk_text_buffer_get_iter_at_line (se->buffer, &end, line + 1);
+
+  editor_execute_syntax (se, begin, end);
+}
+
+
+
+/* Append ".sps" to FILENAME if necessary.
+   The returned result must be freed when no longer required.
+ */
+static gchar *
+append_suffix (const gchar *filename)
+{
+  if ( ! g_str_has_suffix (filename, ".sps" ) &&
+       ! g_str_has_suffix (filename, ".SPS" ) )
+    {
+      return g_strdup_printf ("%s.sps", filename);
+    }
+
+  return strdup (filename);
+}
+
+/*
+  Save BUFFER to the file called FILENAME.
+  If successful, clears the buffer's modified flag
+*/
+static gboolean
+save_editor_to_file (PsppireSyntaxWindow *se,
+                    const gchar *filename,
+                    GError **err)
+{
+  GtkTextBuffer *buffer = se->buffer;
+  gboolean result ;
+  GtkTextIter start, stop;
+  gchar *text;
+
+  gchar *suffixedname;
+  gchar *glibfilename;
+  g_assert (filename);
+
+  suffixedname = append_suffix (filename);
+
+  glibfilename = g_filename_from_utf8 (suffixedname, -1, 0, 0, err);
+
+  g_free ( suffixedname);
+
+  if ( ! glibfilename )
+    return FALSE;
+
+  gtk_text_buffer_get_iter_at_line (buffer, &start, 0);
+  gtk_text_buffer_get_iter_at_offset (buffer, &stop, -1);
+
+  text = gtk_text_buffer_get_text (buffer, &start, &stop, FALSE);
+
+  result =  g_file_set_contents (glibfilename, text, -1, err);
+
+  if ( result )
+    {
+      gchar *msg = g_strdup_printf (_("Saved file \"%s\""), filename);
+      gtk_statusbar_push (GTK_STATUSBAR (se->sb), se->text_context, msg);
+      gtk_text_buffer_set_modified (buffer, FALSE);
+      g_free (msg);
+    }
+
+  return result;
+}
+
+
+/* Callback for the File->SaveAs menuitem */
+static void
+syntax_save_as (PsppireWindow *se)
+{
+  GtkFileFilter *filter;
+  gint response;
+
+  GtkWidget *dialog =
+    gtk_file_chooser_dialog_new (_("Save Syntax"),
+                                GTK_WINDOW (se),
+                                GTK_FILE_CHOOSER_ACTION_SAVE,
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
+                                NULL);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
+  gtk_file_filter_add_pattern (filter, "*.sps");
+  gtk_file_filter_add_pattern (filter, "*.SPS");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+                                                 TRUE);
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  if ( response == GTK_RESPONSE_ACCEPT )
+    {
+      GError *err = NULL;
+      char *filename =
+       gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
+
+      if ( ! save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err) )
+       {
+         msg ( ME, err->message );
+         g_error_free (err);
+       }
+
+      free (filename);
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+
+/* Callback for the File->Save menuitem */
+static void
+syntax_save (PsppireWindow *se)
+{
+  const gchar *filename = psppire_window_get_filename (se);
+
+  if ( filename == NULL )
+    syntax_save_as (se);
+  else
+    {
+      GError *err = NULL;
+      save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err);
+      if ( err )
+       {
+         msg (ME, err->message);
+         g_error_free (err);
+       }
+    }
+}
+
+
+/* Callback for the File->Quit menuitem */
+static gboolean
+on_quit (GtkMenuItem *menuitem, gpointer    user_data)
+{
+  psppire_quit ();
+
+  return FALSE;
+}
+
+
+void
+create_syntax_window (void)
+{
+  GtkWidget *w = psppire_syntax_window_new ();
+  gtk_widget_show (w);
+}
+
+/* Callback for the File->Open->Syntax menuitem */
+void
+open_syntax_window (GtkMenuItem *menuitem, gpointer parent)
+{
+  GtkFileFilter *filter;
+  gint response;
+
+  GtkWidget *dialog =
+    gtk_file_chooser_dialog_new (_("Open Syntax"),
+                                GTK_WINDOW (parent),
+                                GTK_FILE_CHOOSER_ACTION_OPEN,
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_OPEN,   GTK_RESPONSE_ACCEPT,
+                                NULL);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
+  gtk_file_filter_add_pattern (filter, "*.sps");
+  gtk_file_filter_add_pattern (filter, "*.SPS");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  if (response == GTK_RESPONSE_ACCEPT)
+    {
+      const char *file_name = gtk_file_chooser_get_filename
+       (GTK_FILE_CHOOSER (dialog));
+
+      GtkWidget *se = psppire_syntax_window_new ();
+
+      if ( psppire_window_load (PSPPIRE_WINDOW (se), file_name) ) 
+       gtk_widget_show (se);
+      else
+       gtk_widget_destroy (se);
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+
+static void
+on_text_changed (GtkTextBuffer *buffer, PsppireSyntaxWindow *window)
+{
+  gtk_statusbar_pop (GTK_STATUSBAR (window->sb), window->text_context);
+}
+
+static void
+on_modified_changed (GtkTextBuffer *buffer, PsppireWindow *window)
+{
+  if (gtk_text_buffer_get_modified (buffer))
+    psppire_window_set_unsaved (window);
+}
+
+extern struct source_stream *the_source_stream ;
+
+static void
+psppire_syntax_window_init (PsppireSyntaxWindow *window)
+{
+  GtkBuilder *xml = builder_new ("syntax-editor.ui");
+  GtkWidget *box = gtk_vbox_new (FALSE, 0);
+
+  GtkWidget *menubar = get_widget_assert (xml, "menubar2");
+  GtkWidget *sw = get_widget_assert (xml, "scrolledwindow8");
+
+
+  GtkWidget *text_view = get_widget_assert (xml, "syntax_text_view");
+  window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+  window->lexer = lex_create (the_source_stream);
+
+  window->sb = get_widget_assert (xml, "statusbar2");
+  window->text_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->sb), "Text Context");
+
+  g_signal_connect (window->buffer, "changed", G_CALLBACK (on_text_changed), window);
+
+  g_signal_connect (window->buffer, "modified-changed",
+                   G_CALLBACK (on_modified_changed), window);
+
+  connect_help (xml);
+
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+  g_object_ref (menubar);
+
+  g_object_ref (sw);
+
+  g_object_ref (window->sb);
+
+
+  gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), window->sb, FALSE, TRUE, 0);
+
+  gtk_widget_show_all (box);
+
+  g_signal_connect (get_action_assert (xml,"file_new_syntax"),
+                   "activate",
+                   G_CALLBACK (create_syntax_window),
+                   NULL);
+
+  g_signal_connect (get_action_assert (xml,"file_open_syntax"),
+                   "activate",
+                   G_CALLBACK (open_syntax_window),
+                   window);
+
+#if 0
+  g_signal_connect (get_action_assert (xml,"file_new_data"),
+                   "activate",
+                   G_CALLBACK (create_data_window),
+                   window);
+#endif
+
+  {
+    GtkAction *abt = get_action_assert (xml, "help_about");
+    g_object_set (abt, "stock-id", "gtk-about", NULL);
+
+    g_signal_connect (abt,
+                     "activate",
+                     G_CALLBACK (about_new),
+                     window);
+  }
+
+  g_signal_connect (get_action_assert (xml,"help_reference"),
+                   "activate",
+                   G_CALLBACK (reference_manual),
+                   NULL);
+
+  g_signal_connect_swapped (get_action_assert (xml, "file_save"),
+                   "activate",
+                   G_CALLBACK (syntax_save),
+                   window);
+
+  g_signal_connect_swapped (get_action_assert (xml, "file_save_as"),
+                   "activate",
+                   G_CALLBACK (syntax_save_as),
+                   window);
+
+  g_signal_connect (get_action_assert (xml,"file_quit"),
+                   "activate",
+                   G_CALLBACK (on_quit),
+                   window);
+
+  g_signal_connect (get_action_assert (xml,"run_all"),
+                   "activate",
+                   G_CALLBACK (on_run_all),
+                   window);
+
+
+  g_signal_connect (get_action_assert (xml,"run_selection"),
+                   "activate",
+                   G_CALLBACK (on_run_selection),
+                   window);
+
+  g_signal_connect (get_action_assert (xml,"run_current_line"),
+                   "activate",
+                   G_CALLBACK (on_run_current_line),
+                   window);
+
+  g_signal_connect (get_action_assert (xml,"run_to_end"),
+                   "activate",
+                   G_CALLBACK (on_run_to_end),
+                   window);
+
+  g_signal_connect (get_action_assert (xml,"windows_minimise_all"),
+                   "activate",
+                   G_CALLBACK (psppire_window_minimise_all), NULL);
+
+
+  {
+  GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
+
+  PSPPIRE_WINDOW (window)->menu =
+    GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar2/windows/windows_minimise_all")->parent);
+  }
+
+  g_object_unref (xml);
+}
+
+
+GtkWidget*
+psppire_syntax_window_new (void)
+{
+  return GTK_WIDGET (g_object_new (psppire_syntax_window_get_type (),
+                                  "filename", "Syntax",
+                                  "description", _("Syntax Editor"),
+                                  NULL));
+}
+
+static void
+error_dialog (GtkWindow *w, const gchar *filename,  GError *err)
+{
+  gchar *fn = g_filename_display_basename (filename);
+
+  GtkWidget *dialog =
+    gtk_message_dialog_new (w,
+                           GTK_DIALOG_DESTROY_WITH_PARENT,
+                           GTK_MESSAGE_ERROR,
+                           GTK_BUTTONS_CLOSE,
+                           _("Cannot load syntax file '%s'"),
+                           fn);
+
+  g_free (fn);
+
+  g_object_set (dialog, "icon-name", "psppicon", NULL);
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                           err->message);
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+
+  gtk_widget_destroy (dialog);
+}
+
+/*
+  Loads the buffer from the file called FILENAME
+*/
+static gboolean
+syntax_load (PsppireWindow *window, const gchar *filename)
+{
+  GError *err = NULL;
+  gchar *text;
+  GtkTextIter iter;
+  PsppireSyntaxWindow *sw = PSPPIRE_SYNTAX_WINDOW (window);
+
+  /* FIXME: What if it's a very big file ? */
+  if ( ! g_file_get_contents (filename, &text, NULL, &err) )
+    {
+      error_dialog (GTK_WINDOW (window), filename, err);
+      g_clear_error (&err);
+      return FALSE;
+    }
+
+  gtk_text_buffer_get_iter_at_line (sw->buffer, &iter, 0);
+
+  gtk_text_buffer_insert (sw->buffer, &iter, text, -1);
+
+  gtk_text_buffer_set_modified (sw->buffer, FALSE);
+
+  return TRUE;
+}
+
+\f
+
+static void
+psppire_syntax_window_iface_init (PsppireWindowIface *iface)
+{
+  iface->save = syntax_save;
+  iface->load = syntax_load;
+}
diff --git a/src/ui/gui/psppire-syntax-window.h b/src/ui/gui/psppire-syntax-window.h
new file mode 100644 (file)
index 0000000..c863a92
--- /dev/null
@@ -0,0 +1,71 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 __PSPPIRE_SYNTAX_WINDOW_H__
+#define __PSPPIRE_SYNTAX_WINDOW_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkaction.h>
+#include <gtk/gtktextbuffer.h>
+#include "psppire-window.h"
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PSPPIRE_SYNTAX_WINDOW_TYPE            (psppire_syntax_window_get_type ())
+#define PSPPIRE_SYNTAX_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_SYNTAX_WINDOW_TYPE, PsppireSyntaxWindow))
+#define PSPPIRE_SYNTAX_WINDOW_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), \
+    PSPPIRE_SYNTAX_WINDOW_TYPE, PsppireSyntax_WindowClass))
+#define PSPPIRE_IS_SYNTAX_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+    PSPPIRE_SYNTAX_WINDOW_TYPE))
+#define PSPPIRE_IS_SYNTAX_WINDOW_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+    PSPPIRE_SYNTAX_WINDOW_TYPE))
+
+
+typedef struct _PsppireSyntaxWindow       PsppireSyntaxWindow;
+typedef struct _PsppireSyntaxWindowClass  PsppireSyntaxWindowClass;
+
+
+struct _PsppireSyntaxWindow
+{
+  PsppireWindow parent;
+
+  /* <private> */
+
+  GtkTextBuffer *buffer;  /* The buffer which contains the text */
+  struct lexer *lexer;    /* Lexer to parse syntax */
+  GtkWidget *sb;
+  guint text_context;
+};
+
+struct _PsppireSyntaxWindowClass
+{
+  PsppireWindowClass parent_class;
+
+};
+
+GType      psppire_syntax_window_get_type        (void);
+GtkWidget* psppire_syntax_window_new             (void);
+
+void open_syntax_window (GtkMenuItem *menuitem, gpointer parent);
+void create_syntax_window (void);
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_SYNTAX_WINDOW_H__ */
index 256f3c93930a66dcbaf849ed2234f306414ad81b..696bab2150bab788b72f78a858261a77e2be0221 100644 (file)
@@ -1,4 +1,3 @@
-
 /* PSPPIRE - a graphical user interface for PSPP.
    Copyright (C) 2008 Free Software Foundation, Inc.
 
 
 #include <config.h>
 #include "psppire-var-sheet.h"
+#include <ui/gui/sheet/psppire-axis.h>
 
-#include <glade/glade.h>
 #include "helper.h"
-#include <gtksheet/gsheet-hetero-column.h>
+
 #include "customentry.h"
 #include <data/variable.h>
 #include "psppire-var-store.h"
 
 static void psppire_var_sheet_class_init  (PsppireVarSheetClass *klass);
 static void psppire_var_sheet_init        (PsppireVarSheet      *vs);
+static void psppire_var_sheet_realize     (GtkWidget *w);
+static void psppire_var_sheet_unrealize   (GtkWidget *w);
+
 
-enum 
+enum
   {
     PSPPIRE_VAR_SHEET_MAY_CREATE_VARS = 1
   };
@@ -58,7 +60,7 @@ psppire_var_sheet_get_type (void)
        (GInstanceInitFunc) psppire_var_sheet_init,
       };
 
-      vs_type = g_type_register_static (GTK_TYPE_SHEET, "PsppireVarSheet",
+      vs_type = g_type_register_static (PSPPIRE_TYPE_SHEET, "PsppireVarSheet",
                                        &vs_info, 0);
     }
 
@@ -96,20 +98,6 @@ struct column_parameters
   gint width ;
 };
 
-static const struct column_parameters column_def[] = {
-  { N_("Name"),    80},
-  { N_("Type"),    100},
-  { N_("Width"),   57},
-  { N_("Decimals"),91},
-  { N_("Label"),   95},
-  { N_("Values"),  103},
-  { N_("Missing"), 95},
-  { N_("Columns"), 80},
-  { N_("Align"),   69},
-  { N_("Measure"), 99},
-};
-
-
 #define n_ALIGNMENTS 3
 
 const gchar *const alignments[n_ALIGNMENTS + 1]={
@@ -136,9 +124,7 @@ create_label_list (const gchar *const *labels)
   gint i = 0;
   GtkTreeIter iter;
 
-  GtkListStore *list_store;
-  list_store = gtk_list_store_new (1, G_TYPE_STRING);
-
+  GtkListStore *list_store = gtk_list_store_new (1, G_TYPE_STRING);
 
   while ( (s = labels[i++]))
     {
@@ -193,17 +179,19 @@ psppire_var_sheet_get_property (GObject      *object,
 }
 
 
-
 static void
 psppire_var_sheet_class_init (PsppireVarSheetClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GParamSpec *pspec;
 
   parent_class = g_type_class_peek_parent (klass);
 
   object_class->dispose = psppire_var_sheet_dispose;
   object_class->finalize = psppire_var_sheet_finalize;
+  widget_class->realize = psppire_var_sheet_realize;
+  widget_class->unrealize = psppire_var_sheet_unrealize;
   object_class->set_property = psppire_var_sheet_set_property;
   object_class->get_property = psppire_var_sheet_get_property;
 
@@ -251,90 +239,77 @@ change_measure (GtkComboBox *cb,
 }
 
 
-
+/* Moves the focus to a new cell.
+   Returns TRUE iff the move should be disallowed */
 static gboolean
-traverse_cell_callback (GtkSheet *sheet,
-                       gint row, gint column,
-                       gint *new_row, gint *new_column
-                       )
+traverse_cell_callback (PsppireSheet *sheet,
+                       const PsppireSheetCell *existing_cell,
+                       PsppireSheetCell *new_cell)
 {
   PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet);
-  PsppireVarStore *var_store = PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
+  PsppireVarStore *var_store = PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
 
   gint n_vars = psppire_var_store_get_var_cnt (var_store);
 
-  if (*new_row >= n_vars && !var_sheet->may_create_vars)
-    return FALSE;
+  if (new_cell->row >= n_vars && !var_sheet->may_create_vars)
+    return TRUE;
 
-  if ( row == n_vars && *new_row >= n_vars)
+  if ( existing_cell->row == n_vars && new_cell->row >= n_vars)
     {
-      GtkEntry *entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
+      GtkEntry *entry = psppire_sheet_get_entry (sheet);
 
       const gchar *name = gtk_entry_get_text (entry);
 
       if (! psppire_dict_check_name (var_store->dict, name, TRUE))
-       return FALSE;
+       return TRUE;
 
-      psppire_dict_insert_variable (var_store->dict, row, name);
+      psppire_dict_insert_variable (var_store->dict, existing_cell->row, name);
 
-      return TRUE;
+      return FALSE;
     }
 
+
   /* If the destination cell is outside the current  variables, then
      automatically create variables for the new rows.
   */
-  if ( ((*new_row > n_vars) ||
-        (*new_row == n_vars && *new_column != PSPPIRE_VAR_STORE_COL_NAME)) )
+  if ( ((new_cell->row > n_vars) ||
+        (new_cell->row == n_vars &&
+        new_cell->col != PSPPIRE_VAR_STORE_COL_NAME)) )
     {
       gint i;
-      for ( i = n_vars ; i <= *new_row; ++i )
+      for ( i = n_vars ; i <= new_cell->row; ++i )
        psppire_dict_insert_variable (var_store->dict, i, NULL);
     }
 
-  return TRUE;
+  return FALSE;
 }
 
 
 
-
-/*
-   Callback whenever the pointer leaves a cell on the var sheet.
-*/
-static gboolean
-var_sheet_cell_entry_leave (GtkSheet * sheet, gint row, gint column,
-                           gpointer data)
-{
-  gtk_sheet_change_entry (sheet, GTK_TYPE_ENTRY);
-  return TRUE;
-}
-
-
 /*
-   Callback whenever the pointer enters a cell on the var sheet.
+   Callback whenever the active cell changes on the var sheet.
 */
-static gboolean
-var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
-                           gpointer data)
+static void
+var_sheet_change_active_cell (PsppireVarSheet *vs,
+                             gint row, gint column,
+                             gint oldrow, gint oldcolumn,
+                             gpointer data)
 {
-  GtkSheetCellAttr attributes;
-  PsppireVarStore *var_store ;
+  PsppireVarStore *var_store;
   PsppireVarSheetClass *vs_class =
     PSPPIRE_VAR_SHEET_CLASS(G_OBJECT_GET_CLASS (vs));
 
   struct variable *var ;
-  GtkSheet *sheet = GTK_SHEET (vs);
+  PsppireSheet *sheet = PSPPIRE_SHEET (vs);
 
-  g_return_val_if_fail (sheet != NULL, FALSE);
+  g_return_if_fail (sheet != NULL);
 
-  var_store = PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
+  var_store = PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
 
   g_assert (var_store);
 
-  if ( row >= psppire_var_store_get_var_cnt (var_store))
-    return TRUE;
-
-  gtk_sheet_get_attributes (sheet, row, column, &attributes);
-
+  g_return_if_fail (oldcolumn == PSPPIRE_VAR_STORE_COL_NAME ||
+                   row < psppire_var_store_get_var_cnt (var_store));
 
   var = psppire_var_store_get_var (var_store, row);
 
@@ -342,12 +317,12 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
     {
     case PSPPIRE_VAR_STORE_COL_ALIGN:
       {
+       GtkEntry *entry;
        static GtkListStore *list_store = NULL;
        GtkComboBoxEntry *cbe;
-       gtk_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
-       cbe =
-         GTK_COMBO_BOX_ENTRY (gtk_sheet_get_entry (sheet)->parent);
-
+       psppire_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
+       entry = psppire_sheet_get_entry (sheet);
+       cbe = GTK_COMBO_BOX_ENTRY (GTK_WIDGET (entry)->parent);
 
        if ( ! list_store) list_store = create_label_list (alignments);
 
@@ -356,26 +331,25 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
 
        gtk_combo_box_entry_set_text_column (cbe, 0);
 
-       g_signal_connect (G_OBJECT (cbe),"changed",
+       g_signal_connect (cbe, "changed",
                         G_CALLBACK (change_alignment), var);
       }
       break;
 
     case PSPPIRE_VAR_STORE_COL_MEASURE:
       {
+       GtkEntry *entry;
        GtkComboBoxEntry *cbe;
-       gtk_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
-       cbe =
-         GTK_COMBO_BOX_ENTRY (gtk_sheet_get_entry (sheet)->parent);
-
-
+       psppire_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
+       entry = psppire_sheet_get_entry (sheet);
+       cbe = GTK_COMBO_BOX_ENTRY (GTK_WIDGET (entry)->parent);
 
        gtk_combo_box_set_model (GTK_COMBO_BOX (cbe),
                                GTK_TREE_MODEL (vs_class->measure_list));
 
        gtk_combo_box_entry_set_text_column (cbe, 0);
 
-       g_signal_connect (G_OBJECT (cbe),"changed",
+       g_signal_connect (cbe, "changed",
                          G_CALLBACK (change_measure), var);
       }
       break;
@@ -384,10 +358,10 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
       {
        PsppireCustomEntry *customEntry;
 
-       gtk_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
+       psppire_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
 
        customEntry =
-         PSPPIRE_CUSTOM_ENTRY (gtk_sheet_get_entry (sheet));
+         PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
 
        if ( var_is_long_string (var))
          g_object_set (customEntry,
@@ -407,10 +381,10 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
       {
        PsppireCustomEntry *customEntry;
 
-       gtk_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
+       psppire_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
 
        customEntry =
-         PSPPIRE_CUSTOM_ENTRY (gtk_sheet_get_entry (sheet));
+         PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
 
        if ( var_is_long_string (var))
          g_object_set (customEntry,
@@ -432,10 +406,10 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
       {
        PsppireCustomEntry *customEntry;
 
-       gtk_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
+       psppire_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
 
        customEntry =
-         PSPPIRE_CUSTOM_ENTRY (gtk_sheet_get_entry (sheet));
+         PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
 
 
        /* Popup the Variable Type dialog box */
@@ -452,11 +426,12 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
     case PSPPIRE_VAR_STORE_COL_DECIMALS:
     case PSPPIRE_VAR_STORE_COL_COLUMNS:
       {
-       if ( attributes.is_editable)
+       if ( psppire_sheet_model_is_editable (PSPPIRE_SHEET_MODEL(var_store),
+                                             row, column))
          {
            gint r_min, r_max;
 
-           const gchar *s = gtk_sheet_cell_get_text (sheet, row, column);
+           const gchar *s = psppire_sheet_cell_get_text (sheet, row, column);
 
            if (s)
              {
@@ -488,10 +463,10 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
                                         1.0, 1.0, 1.0 /* steps */
                                         );
 
-               gtk_sheet_change_entry (sheet, GTK_TYPE_SPIN_BUTTON);
+               psppire_sheet_change_entry (sheet, GTK_TYPE_SPIN_BUTTON);
 
                spinButton =
-                 GTK_SPIN_BUTTON (gtk_sheet_get_entry (sheet));
+                 GTK_SPIN_BUTTON (psppire_sheet_get_entry (sheet));
 
                gtk_spin_button_set_adjustment (spinButton, GTK_ADJUSTMENT (adj));
                gtk_spin_button_set_digits (spinButton, 0);
@@ -501,51 +476,56 @@ var_sheet_cell_entry_enter (PsppireVarSheet *vs, gint row, gint column,
       break;
 
     default:
-      gtk_sheet_change_entry (sheet, GTK_TYPE_ENTRY);
+      psppire_sheet_change_entry (sheet, GTK_TYPE_ENTRY);
       break;
     }
+}
 
 
-  return TRUE;
-}
+static void
+psppire_var_sheet_realize (GtkWidget *w)
+{
+  PsppireVarSheet *vs = PSPPIRE_VAR_SHEET (w);
 
+  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (vs));
 
+  vs->val_labs_dialog = val_labs_dialog_create (GTK_WINDOW (toplevel));
+  vs->missing_val_dialog = missing_val_dialog_create (GTK_WINDOW (toplevel));
+  vs->var_type_dialog = var_type_dialog_create (GTK_WINDOW (toplevel));
 
+  /* Chain up to the parent class */
+  GTK_WIDGET_CLASS (parent_class)->realize (w);
+}
 
 static void
-psppire_var_sheet_init (PsppireVarSheet *vs)
+psppire_var_sheet_unrealize (GtkWidget *w)
 {
-  gint i;
-  GObject *geo = g_sheet_hetero_column_new (75, PSPPIRE_VAR_STORE_n_COLS);
-  GladeXML *xml = XML_NEW ("data-editor.glade");
+  PsppireVarSheet *vs = PSPPIRE_VAR_SHEET (w);
 
-  vs->val_labs_dialog = val_labs_dialog_create (xml);
-  vs->missing_val_dialog = missing_val_dialog_create (xml);
-  vs->var_type_dialog = var_type_dialog_create (xml);
+  g_free (vs->val_labs_dialog);
+  g_free (vs->missing_val_dialog);
+  g_free (vs->var_type_dialog);
 
-  g_object_unref (xml);
+  /* Chain up to the parent class */
+  GTK_WIDGET_CLASS (parent_class)->unrealize (w);
+}
 
-  vs->dispose_has_run = FALSE;
-  vs->may_create_vars = TRUE;
 
-  for (i = 0 ; i < PSPPIRE_VAR_STORE_n_COLS ; ++i )
-    {
-      g_sheet_hetero_column_set_button_label (G_SHEET_HETERO_COLUMN (geo), i,
-                                             gettext (column_def[i].label));
 
-      g_sheet_hetero_column_set_width (G_SHEET_HETERO_COLUMN (geo), i,
-                                      column_def[i].width);
-    }
+static void
+psppire_var_sheet_init (PsppireVarSheet *vs)
+{
+  GtkBuilder *builder = builder_new ("data-editor.ui");
 
-  g_object_set (vs, "column-geometry", geo, NULL);
+  connect_help (builder);
 
+  g_object_unref (builder);
 
-  g_signal_connect (vs, "activate",
-                   G_CALLBACK (var_sheet_cell_entry_enter),
-                   NULL);
+  vs->dispose_has_run = FALSE;
+  vs->may_create_vars = TRUE;
 
-  g_signal_connect (vs, "deactivate",
-                   G_CALLBACK (var_sheet_cell_entry_leave),
+  g_signal_connect (vs, "activate",
+                   G_CALLBACK (var_sheet_change_active_cell),
                    NULL);
 
   g_signal_connect (vs, "traverse",
@@ -553,8 +533,42 @@ psppire_var_sheet_init (PsppireVarSheet *vs)
 }
 
 
+static const struct column_parameters column_def[] = {
+  { N_("Name"),    80},
+  { N_("Type"),    100},
+  { N_("Width"),   57},
+  { N_("Decimals"),91},
+  { N_("Label"),   95},
+  { N_("Values"),  103},
+  { N_("Missing"), 95},
+  { N_("Columns"), 80},
+  { N_("Align"),   69},
+  { N_("Measure"), 99},
+};
+
 GtkWidget*
 psppire_var_sheet_new (void)
 {
-  return GTK_WIDGET (g_object_new (psppire_var_sheet_get_type (), NULL));
+  gint i;
+  PsppireAxis *ha = psppire_axis_new ();
+  PsppireAxis *va = psppire_axis_new ();
+
+  GtkWidget *w = g_object_new (psppire_var_sheet_get_type (), NULL);
+
+  for (i = 0 ; i < 10 ; ++i)
+    psppire_axis_append (ha, column_def[i].width);
+
+  g_object_set (va,
+               "default-size", 25,
+               NULL);
+
+  g_object_set (ha, "minimum-extent", 0,
+               NULL);
+
+  g_object_set (w,
+               "horizontal-axis", ha,
+               "vertical-axis", va,
+               NULL);
+
+  return w;
 }
index e705ef01ca7f4d3221e92e20cc784eb1f412efdb..b996275c70404e94ea09e4c79a046d2783796dcb 100644 (file)
@@ -20,7 +20,7 @@
 
 #include <glib.h>
 #include <glib-object.h>
-#include <gtksheet/gtksheet.h>
+#include <gtk-contrib/psppire-sheet.h>
 #include "val-labs-dialog.h"
 #include "missing-val-dialog.h"
 #include "var-type-dialog.h"
@@ -40,7 +40,7 @@ typedef struct _PsppireVarSheetClass  PsppireVarSheetClass;
 
 struct _PsppireVarSheet
 {
-  GtkSheet parent;
+  PsppireSheet parent;
 
   gboolean dispose_has_run;
   gboolean may_create_vars;
@@ -53,7 +53,7 @@ struct _PsppireVarSheet
 
 struct _PsppireVarSheetClass
 {
-  GtkSheetClass parent_class;
+  PsppireSheetClass parent_class;
 
   GtkListStore *alignment_list;
   GtkListStore *measure_list;
index fa356b812d60dc47e3b160e8079b3c6caf8cfc70..1d6f465d437e5c2605bb8001a7ddce6fd720eb3c 100644 (file)
 
 #include <gobject/gvaluecollector.h>
 
-#include <gtksheet/gsheetmodel.h>
+#include <ui/gui/sheet/psppire-sheetmodel.h>
 
 #include "psppire-var-store.h"
-#include <gtksheet/gsheet-row-iface.h>
 #include "helper.h"
 
 #include <data/dictionary.h>
 
 enum
   {
-    PSPPIRE_VAR_STORE_TRAILING_ROWS = 1,
+    PROP_0,
     PSPPIRE_VAR_STORE_FORMAT_TYPE
   };
 
 static void         psppire_var_store_init            (PsppireVarStore      *var_store);
 static void         psppire_var_store_class_init      (PsppireVarStoreClass *class);
-static void         psppire_var_store_sheet_model_init (GSheetModelIface *iface);
+static void         psppire_var_store_sheet_model_init (PsppireSheetModelIface *iface);
 static void         psppire_var_store_finalize        (GObject           *object);
 
 
 gchar * missing_values_to_string (const struct variable *pv, GError **err);
 
 
-static gchar *psppire_var_store_get_string (const GSheetModel *sheet_model, glong row, glong column);
+static gchar *psppire_var_store_get_string (const PsppireSheetModel *sheet_model, glong row, glong column);
 
-static gboolean  psppire_var_store_clear (GSheetModel *model,  glong row, glong col);
+static gboolean  psppire_var_store_clear (PsppireSheetModel *model,  glong row, glong col);
 
 
-static gboolean psppire_var_store_set_string (GSheetModel *model,
+static gboolean psppire_var_store_set_string (PsppireSheetModel *model,
                                          const gchar *text, glong row, glong column);
 
-static glong psppire_var_store_get_row_count (const GSheetModel * model);
-static glong psppire_var_store_get_column_count (const GSheetModel * model);
+static glong psppire_var_store_get_row_count (const PsppireSheetModel * model);
+static glong psppire_var_store_get_column_count (const PsppireSheetModel * model);
 
 static gchar *text_for_column (const struct variable *pv, gint c, GError **err);
 
 
-static void psppire_var_store_sheet_row_init (GSheetRowIface *iface);
-
-
-
 static GObjectClass *parent_class = NULL;
 
 GType
@@ -128,24 +123,11 @@ psppire_var_store_get_type (void)
        NULL
       };
 
-      static const GInterfaceInfo sheet_row_info =
-      {
-       (GInterfaceInitFunc) psppire_var_store_sheet_row_init,
-       NULL,
-       NULL
-      };
-
       var_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireVarStore", &var_store_info, 0);
 
       g_type_add_interface_static (var_store_type,
-                                  G_TYPE_SHEET_MODEL,
+                                  PSPPIRE_TYPE_SHEET_MODEL,
                                   &sheet_model_info);
-
-      g_type_add_interface_static (var_store_type,
-                                  G_TYPE_SHEET_ROW,
-                                  &sheet_row_info);
-
-
     }
 
   return var_store_type;
@@ -161,10 +143,6 @@ psppire_var_store_set_property (GObject      *object,
 
   switch (property_id)
     {
-    case PSPPIRE_VAR_STORE_TRAILING_ROWS:
-      self->trailing_rows = g_value_get_int (value);
-      break;
-
     case PSPPIRE_VAR_STORE_FORMAT_TYPE:
       self->format_type = g_value_get_enum (value);
       break;
@@ -185,10 +163,6 @@ psppire_var_store_get_property (GObject      *object,
 
   switch (property_id)
     {
-    case PSPPIRE_VAR_STORE_TRAILING_ROWS:
-      g_value_set_int (value, self->trailing_rows);
-      break;
-
     case PSPPIRE_VAR_STORE_FORMAT_TYPE:
       g_value_set_enum (value, self->format_type);
       break;
@@ -213,43 +187,28 @@ psppire_var_store_class_init (PsppireVarStoreClass *class)
   object_class->set_property = psppire_var_store_set_property;
   object_class->get_property = psppire_var_store_get_property;
 
-  /* The minimum value for trailing-rows is 1 to prevent the
-     var-store from ever having 0 rows, which breaks invariants
-     in gtksheet. */
-  pspec = g_param_spec_int ("trailing-rows",
-                            "Trailing rows",
-                            "Number of rows displayed after last variable",
-                            1  /* minimum value */,
-                            100 /* maximum value */,
-                            40  /* default value */,
-                            G_PARAM_READWRITE);
-  g_object_class_install_property (object_class,
-                                   PSPPIRE_VAR_STORE_TRAILING_ROWS,
-                                   pspec);
-
   pspec = g_param_spec_enum ("format-type",
                              "Variable format type",
                              ("Whether variables have input or output "
                               "formats"),
-                             G_TYPE_PSPPIRE_VAR_STORE_FORMAT_TYPE,
+                             PSPPIRE_TYPE_VAR_STORE_FORMAT_TYPE,
                              PSPPIRE_VAR_STORE_OUTPUT_FORMATS,
                              G_PARAM_READWRITE);
+
   g_object_class_install_property (object_class,
                                    PSPPIRE_VAR_STORE_FORMAT_TYPE,
                                    pspec);
 }
 
+#define DISABLED_COLOR "gray"
+
 static void
 psppire_var_store_init (PsppireVarStore *var_store)
 {
-  GdkColormap *colormap = gdk_colormap_get_system ();
-
-  g_assert (gdk_color_parse ("gray", &var_store->disabled));
-
-  gdk_colormap_alloc_color (colormap, &var_store->disabled, FALSE, TRUE);
+  if ( ! gdk_color_parse (DISABLED_COLOR, &var_store->disabled))
+       g_critical ("Could not parse color \"%s\"", DISABLED_COLOR);
 
   var_store->dict = 0;
-  var_store->trailing_rows = 40;
   var_store->format_type = PSPPIRE_VAR_STORE_OUTPUT_FORMATS;
 }
 
@@ -301,15 +260,15 @@ psppire_var_store_get_var (PsppireVarStore *store, glong row)
 }
 
 static gboolean
-psppire_var_store_is_editable (const GSheetModel *model, glong row, glong column)
+psppire_var_store_is_editable (const PsppireSheetModel *model, glong row, glong column)
 {
   PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
   return psppire_var_store_item_editable (store, row, column);
 }
 
 
-static const GdkColor *
-psppire_var_store_get_foreground (const GSheetModel *model, glong row, glong column)
+static GdkColor *
+psppire_var_store_get_foreground (const PsppireSheetModel *model, glong row, glong column)
 {
   PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
 
@@ -320,20 +279,12 @@ psppire_var_store_get_foreground (const GSheetModel *model, glong row, glong col
 }
 
 
-const PangoFontDescription *
-psppire_var_store_get_font_desc (const GSheetModel *model,
-                             glong row, glong column)
-{
-  PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
-
-  return store->font_desc;
-}
-
-
-
+static gchar *get_column_title (const PsppireSheetModel *model, gint col);
+static gchar *get_row_title (const PsppireSheetModel *model, gint row);
+static gboolean get_row_sensitivity (const PsppireSheetModel *model, gint row);
 
 static void
-psppire_var_store_sheet_model_init (GSheetModelIface *iface)
+psppire_var_store_sheet_model_init (PsppireSheetModelIface *iface)
 {
   iface->get_row_count = psppire_var_store_get_row_count;
   iface->get_column_count = psppire_var_store_get_column_count;
@@ -342,14 +293,17 @@ psppire_var_store_sheet_model_init (GSheetModelIface *iface)
   iface->set_string = psppire_var_store_set_string;
   iface->clear_datum = psppire_var_store_clear;
   iface->is_editable = psppire_var_store_is_editable;
-  iface->is_visible = NULL;
   iface->get_foreground = psppire_var_store_get_foreground;
   iface->get_background = NULL;
-  iface->get_font_desc = psppire_var_store_get_font_desc;
-  iface->get_cell_border = NULL;
-}
+  iface->get_justification = NULL;
 
+  iface->get_column_title = get_column_title;
 
+  iface->get_row_title = get_row_title;
+  iface->get_row_sensitivity = get_row_sensitivity;
+
+  iface->get_row_overstrike = NULL;
+}
 
 /**
  * psppire_var_store_new:
@@ -373,9 +327,9 @@ psppire_var_store_new (PsppireDict *dict)
 static void
 var_change_callback (GtkWidget *w, gint n, gpointer data)
 {
-  GSheetModel *model = G_SHEET_MODEL (data);
+  PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
 
-  g_sheet_model_range_changed (model,
+  psppire_sheet_model_range_changed (model,
                                 n, 0, n, PSPPIRE_VAR_STORE_n_COLS);
 }
 
@@ -383,9 +337,9 @@ var_change_callback (GtkWidget *w, gint n, gpointer data)
 static void
 var_delete_callback (GtkWidget *w, gint dict_idx, gint case_idx, gint val_cnt, gpointer data)
 {
-  GSheetModel *model = G_SHEET_MODEL (data);
+  PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
 
-  g_sheet_model_rows_deleted (model, dict_idx, 1);
+  psppire_sheet_model_rows_deleted (model, dict_idx, 1);
 }
 
 
@@ -393,9 +347,9 @@ var_delete_callback (GtkWidget *w, gint dict_idx, gint case_idx, gint val_cnt, g
 static void
 var_insert_callback (GtkWidget *w, glong row, gpointer data)
 {
-  GSheetModel *model = G_SHEET_MODEL (data);
+  PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
 
-  g_sheet_model_rows_inserted (model, row, 1);
+  psppire_sheet_model_rows_inserted (model, row, 1);
 }
 
 static void
@@ -403,7 +357,7 @@ refresh (PsppireDict  *d, gpointer data)
 {
   PsppireVarStore *vs = data;
 
-  g_sheet_model_range_changed (G_SHEET_MODEL (vs), -1, -1, -1, -1);
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (vs), -1, -1, -1, -1);
 }
 
 /**
@@ -434,7 +388,7 @@ psppire_var_store_set_dictionary (PsppireVarStore *var_store, PsppireDict *dict)
                    var_store);
 
   /* The entire model has changed */
-  g_sheet_model_range_changed (G_SHEET_MODEL (var_store), -1, -1, -1, -1);
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (var_store), -1, -1, -1, -1);
 }
 
 static void
@@ -445,7 +399,7 @@ psppire_var_store_finalize (GObject *object)
 }
 
 static gchar *
-psppire_var_store_get_string (const GSheetModel *model, glong row, glong column)
+psppire_var_store_get_string (const PsppireSheetModel *model, glong row, glong column)
 {
   PsppireVarStore *store = PSPPIRE_VAR_STORE (model);
 
@@ -465,7 +419,7 @@ psppire_var_store_get_string (const GSheetModel *model, glong row, glong column)
    Returns true if anything was updated, false otherwise.
 */
 static gboolean
-psppire_var_store_clear (GSheetModel *model,  glong row, glong col)
+psppire_var_store_clear (PsppireSheetModel *model,  glong row, glong col)
 {
   struct variable *pv ;
 
@@ -495,7 +449,7 @@ psppire_var_store_clear (GSheetModel *model,  glong row, glong col)
    Returns true if anything was updated, false otherwise.
 */
 static gboolean
-psppire_var_store_set_string (GSheetModel *model,
+psppire_var_store_set_string (PsppireSheetModel *model,
                          const gchar *text, glong row, glong col)
 {
   struct variable *pv ;
@@ -513,8 +467,16 @@ psppire_var_store_set_string (GSheetModel *model,
   switch (col)
     {
     case PSPPIRE_VAR_STORE_COL_NAME:
-      return psppire_dict_rename_var (var_store->dict, pv, text);
-      break;
+      {
+       int i;
+       /* Until non-ascii in variable names is better managed,
+          simply refuse to allow them to be entered. */
+       for (i = 0 ; i < strlen (text) ; ++i )
+         if (!g_ascii_isprint (text[i]))
+           return FALSE;
+       return psppire_dict_rename_var (var_store->dict, pv, text);
+       break;
+      }
     case PSPPIRE_VAR_STORE_COL_COLUMNS:
       if ( ! text) return FALSE;
       var_set_display_width (pv, atoi (text));
@@ -568,8 +530,12 @@ psppire_var_store_set_string (GSheetModel *model,
       }
       break;
     case PSPPIRE_VAR_STORE_COL_LABEL:
-      var_set_label (pv, text);
-      return TRUE;
+      {
+       gchar *s = utf8_to_pspp_locale (text, -1, NULL);
+       var_set_label (pv, s);
+       free (s);
+       return TRUE;
+      }
       break;
     case PSPPIRE_VAR_STORE_COL_TYPE:
     case PSPPIRE_VAR_STORE_COL_VALUES:
@@ -588,7 +554,7 @@ psppire_var_store_set_string (GSheetModel *model,
 }
 
 
-const static gchar none[] = N_("None");
+static const gchar none[] = N_("None");
 
 static  gchar *
 text_for_column (const struct variable *pv, gint c, GError **err)
@@ -763,20 +729,8 @@ psppire_var_store_get_var_cnt (PsppireVarStore  *store)
 }
 
 
-void
-psppire_var_store_set_font (PsppireVarStore *store, const PangoFontDescription *fd)
-{
-  g_return_if_fail (store);
-  g_return_if_fail (PSPPIRE_IS_VAR_STORE (store));
-
-  store->font_desc = fd;
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store), -1, -1, -1, -1);
-}
-
-
 static glong
-psppire_var_store_get_row_count (const GSheetModel * model)
+psppire_var_store_get_row_count (const PsppireSheetModel * model)
 {
   gint rows = 0;
   PsppireVarStore *vs = PSPPIRE_VAR_STORE (model);
@@ -788,38 +742,20 @@ psppire_var_store_get_row_count (const GSheetModel * model)
 }
 
 static glong
-psppire_var_store_get_column_count (const GSheetModel * model)
+psppire_var_store_get_column_count (const PsppireSheetModel * model)
 {
   return PSPPIRE_VAR_STORE_n_COLS ;
 }
 
+\f
 
 /* Row related funcs */
 
-static glong
-geometry_get_row_count (const GSheetRow *geom)
-{
-  gint rows = 0;
-  PsppireVarStore *vs = PSPPIRE_VAR_STORE (geom);
-
-  if (vs->dict)
-    rows =  psppire_dict_get_var_cnt (vs->dict);
-
-  return rows + vs->trailing_rows;
-}
-
-
-static gint
-geometry_get_height (const GSheetRow *geom, glong row)
-{
-  return 25;
-}
-
 
 static gboolean
-geometry_is_sensitive (const GSheetRow *geom, glong row)
+get_row_sensitivity (const PsppireSheetModel *model, gint row)
 {
-  PsppireVarStore *vs = PSPPIRE_VAR_STORE (geom);
+  PsppireVarStore *vs = PSPPIRE_VAR_STORE (model);
 
   if ( ! vs->dict)
     return FALSE;
@@ -827,32 +763,34 @@ geometry_is_sensitive (const GSheetRow *geom, glong row)
   return  row < psppire_dict_get_var_cnt (vs->dict);
 }
 
-static
-gboolean always_true ()
-{
-  return TRUE;
-}
-
 
 static gchar *
-geometry_get_button_label (const GSheetRow *geom, glong unit)
+get_row_title (const PsppireSheetModel *model, gint unit)
 {
-  gchar *label = g_strdup_printf (_("%ld"), unit + 1);
-
-  return label;
+  return g_strdup_printf (_("%d"), unit + 1);
 }
 
-static void
-psppire_var_store_sheet_row_init (GSheetRowIface *iface)
-{
-  iface->get_row_count =     geometry_get_row_count;
-  iface->get_height =        geometry_get_height;
-  iface->set_height =        0;
-  iface->get_visibility =    always_true;
-  iface->get_sensitivity =   geometry_is_sensitive;
 
-  iface->get_button_label = geometry_get_button_label;
-}
+\f
 
+static const gchar *column_titles[] = {
+  N_("Name"),
+  N_("Type"),
+  N_("Width"),
+  N_("Decimals"),
+  N_("Label"),
+  N_("Values"),
+  N_("Missing"),
+  N_("Columns"),
+  N_("Align"),
+  N_("Measure"),
+};
 
 
+static gchar *
+get_column_title (const PsppireSheetModel *model, gint col)
+{
+  if ( col >= 10)
+    return NULL;
+  return g_strdup (gettext (column_titles[col]));
+}
index a4782efceb04e009126e7408eb10289ed1f85438..4cda0159bf0e776f4601f1e1b126b3ed211d11f2 100644 (file)
@@ -17,7 +17,6 @@
 #ifndef __PSPPIRE_VAR_STORE_H__
 #define __PSPPIRE_VAR_STORE_H__
 
-#include <gtksheet/gsheetmodel.h>
 #include "psppire-dict.h"
 #include <gdk/gdk.h>
 
@@ -35,7 +34,7 @@ typedef enum
   }
 PsppireVarStoreFormatType;
 
-#define G_TYPE_PSPPIRE_VAR_STORE_FORMAT_TYPE \
+#define PSPPIRE_TYPE_VAR_STORE_FORMAT_TYPE \
         (psppire_var_store_format_type_get_type ())
 
 /* PSPPIRE variable store. */
@@ -66,8 +65,6 @@ struct _PsppireVarStore
   /*< private >*/
   PsppireDict *dict;
   GdkColor disabled;
-  const PangoFontDescription *font_desc;
-  gint trailing_rows;
   PsppireVarStoreFormatType format_type;
 };
 
diff --git a/src/ui/gui/psppire-window-register.c b/src/ui/gui/psppire-window-register.c
new file mode 100644 (file)
index 0000000..2052adc
--- /dev/null
@@ -0,0 +1,197 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 "psppire-window-register.h"
+
+static void psppire_window_register_init            (PsppireWindowRegister      *window_register);
+static void psppire_window_register_class_init      (PsppireWindowRegisterClass *class);
+
+static void psppire_window_register_finalize        (GObject   *object);
+static void psppire_window_register_dispose        (GObject   *object);
+
+static GObjectClass *parent_class = NULL;
+
+
+enum  {
+  INSERTED,
+  REMOVED,
+  n_SIGNALS
+};
+
+static guint signals [n_SIGNALS];
+
+GType
+psppire_window_register_get_type (void)
+{
+  static GType window_register_type = 0;
+
+  if (!window_register_type)
+    {
+      static const GTypeInfo window_register_info =
+      {
+       sizeof (PsppireWindowRegisterClass),
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+        (GClassInitFunc) psppire_window_register_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+        sizeof (PsppireWindowRegister),
+       0,
+        (GInstanceInitFunc) psppire_window_register_init,
+      };
+
+      window_register_type = g_type_register_static (G_TYPE_OBJECT,
+                                               "PsppireWindowRegister",
+                                               &window_register_info, 0);
+    }
+
+  return window_register_type;
+}
+
+
+static void
+psppire_window_register_finalize (GObject *object)
+{
+}
+
+static void
+psppire_window_register_dispose  (GObject *object)
+{
+}
+
+static PsppireWindowRegister *the_instance = NULL;
+
+static GObject*
+psppire_window_register_construct   (GType                  type,
+                                    guint                  n_construct_params,
+                                    GObjectConstructParam *construct_params)
+{
+  GObject *object;
+  
+  if (!the_instance)
+    {
+      object = G_OBJECT_CLASS (parent_class)->constructor (type,
+                                                           n_construct_params,
+                                                           construct_params);
+      the_instance = PSPPIRE_WINDOW_REGISTER (object);
+    }
+  else
+    object = g_object_ref (G_OBJECT (the_instance));
+
+  return object;
+}
+
+static void
+psppire_window_register_class_init (PsppireWindowRegisterClass *class)
+{
+  GObjectClass *object_class;
+
+  parent_class = g_type_class_peek_parent (class);
+  object_class = (GObjectClass*) class;
+
+  object_class->finalize = psppire_window_register_finalize;
+  object_class->dispose = psppire_window_register_dispose;
+  object_class->constructor = psppire_window_register_construct;
+
+  signals [INSERTED] =
+    g_signal_new ("inserted",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__POINTER,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_POINTER);
+
+  signals [REMOVED] =
+    g_signal_new ("removed",
+                 G_TYPE_FROM_CLASS (class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__POINTER,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_POINTER);
+}
+
+static void
+psppire_window_register_init (PsppireWindowRegister *window_register)
+{
+  window_register->dispose_has_run = FALSE;
+  window_register->name_table = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+void
+psppire_window_register_insert (PsppireWindowRegister *wr, PsppireWindow *window, const gchar *name)
+{
+  g_hash_table_insert (wr->name_table, (gpointer) name, window);
+
+  g_signal_emit (wr, signals[INSERTED], 0, name);
+}
+
+
+void
+psppire_window_register_remove (PsppireWindowRegister *wr, const gchar *name)
+{
+  g_signal_emit (wr, signals[REMOVED], 0, name);
+
+  g_hash_table_remove (wr->name_table, (gpointer) name);
+}
+
+PsppireWindow *
+psppire_window_register_lookup (PsppireWindowRegister *wr, const gchar *name)
+{
+  return g_hash_table_lookup (wr->name_table, name);
+}
+
+void
+psppire_window_register_foreach (PsppireWindowRegister *wr,
+                                GHFunc func, gpointer data)
+{
+  g_hash_table_foreach (wr->name_table, func, data);
+}
+
+static void
+minimise_window (gpointer key, gpointer value, gpointer data)
+{
+  gtk_window_iconify (GTK_WINDOW (value));
+}
+
+
+void
+psppire_window_register_minimise_all (PsppireWindowRegister *wr)
+{
+  g_hash_table_foreach (wr->name_table, minimise_window, wr);
+}
+
+
+
+PsppireWindowRegister *
+psppire_window_register_new (void)
+{
+  return g_object_new (psppire_window_register_get_type (), NULL);
+}
+
+
+gint
+psppire_window_register_n_items (PsppireWindowRegister *wr)
+{
+  return g_hash_table_size (wr->name_table);
+}
diff --git a/src/ui/gui/psppire-window-register.h b/src/ui/gui/psppire-window-register.h
new file mode 100644 (file)
index 0000000..940d724
--- /dev/null
@@ -0,0 +1,95 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 <glib-object.h>
+#include <glib.h>
+
+#include "psppire-window.h"
+
+#ifndef __PSPPIRE_WINDOW_REGISTER_H__
+#define __PSPPIRE_WINDOW_REGISTER_H__
+
+G_BEGIN_DECLS
+
+
+#define PSPPIRE_TYPE_WINDOW_REGISTER (psppire_window_register_get_type ())
+
+#define PSPPIRE_WINDOW_REGISTER(obj)   \
+                     (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                   PSPPIRE_TYPE_WINDOW_REGISTER, PsppireWindowRegister))
+
+#define PSPPIRE_WINDOW_REGISTER_CLASS(klass) \
+                     (G_TYPE_CHECK_CLASS_CAST ((klass), \
+                                PSPPIRE_TYPE_WINDOW_REGISTER, \
+                                 PsppireWindowRegisterClass))
+
+
+#define PSPPIRE_IS_WINDOW_REGISTER(obj) \
+                    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_WINDOW_REGISTER))
+
+#define PSPPIRE_IS_WINDOW_REGISTER_CLASS(klass) \
+                     (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_WINDOW_REGISTER))
+
+
+#define PSPPIRE_WINDOW_REGISTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                                  PSPPIRE_TYPE_WINDOW_REGISTER, \
+                                  PsppireWindowRegisterClass))
+
+typedef struct _PsppireWindowRegister       PsppireWindowRegister;
+typedef struct _PsppireWindowRegisterClass  PsppireWindowRegisterClass;
+
+
+struct _PsppireWindowRegister
+{
+  GObject parent;
+
+  /*< private >*/
+  gboolean dispose_has_run ;
+  GHashTable *name_table;
+};
+
+
+struct _PsppireWindowRegisterClass
+{
+  GObjectClass parent_class;
+};
+
+
+GType psppire_window_register_get_type (void) G_GNUC_CONST;
+
+PsppireWindowRegister * psppire_window_register_new (void);
+
+void psppire_window_register_insert (PsppireWindowRegister *wr, PsppireWindow *window,
+                                    const gchar *name);
+
+void psppire_window_register_remove (PsppireWindowRegister *wr, const gchar *name);
+
+
+PsppireWindow *psppire_window_register_lookup (PsppireWindowRegister *wr, const gchar *name);
+
+
+void psppire_window_register_foreach (PsppireWindowRegister *wr, GHFunc func,
+                                     gpointer);
+
+void psppire_window_register_minimise_all (PsppireWindowRegister *wr);
+
+gint psppire_window_register_n_items (PsppireWindowRegister *wr);
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_WINDOW_REGISTER_H__ */
diff --git a/src/ui/gui/psppire-window.c b/src/ui/gui/psppire-window.c
new file mode 100644 (file)
index 0000000..a02edb2
--- /dev/null
@@ -0,0 +1,677 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 <gtk/gtkstock.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkcheckmenuitem.h>
+#include <gtk/gtkmain.h>
+
+#include <stdlib.h>
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+#include "psppire-window.h"
+#include "psppire-window-register.h"
+#include "psppire-conf.h"
+
+static void psppire_window_base_finalize (PsppireWindowClass *, gpointer);
+static void psppire_window_base_init     (PsppireWindowClass *class);
+static void psppire_window_class_init    (PsppireWindowClass *class);
+static void psppire_window_init          (PsppireWindow      *window);
+
+
+static GObjectClass *parent_class;
+
+GType
+psppire_window_get_type (void)
+{
+  static GType psppire_window_type = 0;
+
+  if (!psppire_window_type)
+    {
+      static const GTypeInfo psppire_window_info =
+      {
+       sizeof (PsppireWindowClass),
+       (GBaseInitFunc) psppire_window_base_init,
+        (GBaseFinalizeFunc) psppire_window_base_finalize,
+       (GClassInitFunc) psppire_window_class_init,
+       (GClassFinalizeFunc) NULL,
+       NULL,
+        sizeof (PsppireWindow),
+       0,
+       (GInstanceInitFunc) psppire_window_init,
+      };
+
+      psppire_window_type =
+       g_type_register_static (GTK_TYPE_WINDOW, "PsppireWindow",
+                               &psppire_window_info, G_TYPE_FLAG_ABSTRACT);
+    }
+
+  return psppire_window_type;
+}
+
+
+/* Properties */
+enum
+{
+  PROP_0,
+  PROP_FILENAME,
+  PROP_DESCRIPTION
+};
+
+
+gchar *
+uniquify (const gchar *str, int *x)
+{
+  return g_strdup_printf ("%s%d", str, (*x)++);
+}
+
+static gchar mdash[6] = {0,0,0,0,0,0};
+
+static void
+psppire_window_set_title (PsppireWindow *window)
+{
+  GString *title = g_string_sized_new (80);
+
+  g_string_printf (title, _("%s %s PSPPIRE %s"),
+                  window->basename ? window->basename : "",
+                  mdash, window->description);
+
+  if (window->dirty)
+    g_string_prepend_c (title, '*');
+
+  gtk_window_set_title (GTK_WINDOW (window), title->str);
+
+  g_string_free (title, TRUE);
+}
+
+static void
+psppire_window_set_property (GObject         *object,
+                            guint            prop_id,
+                            const GValue    *value,
+                            GParamSpec      *pspec)
+{
+  PsppireWindow *window = PSPPIRE_WINDOW (object);
+
+  switch (prop_id)
+    {
+    case PROP_DESCRIPTION:
+      window->description = g_value_dup_string (value);
+      psppire_window_set_title (window);
+      break;
+    case PROP_FILENAME:
+      {
+       PsppireWindowRegister *reg = psppire_window_register_new ();
+
+       gchar *candidate_name ;
+
+       {
+         const gchar *name = g_value_get_string (value);
+         int x = 0;
+         GValue def = {0};
+         g_value_init (&def, pspec->value_type);
+
+         if ( NULL == name)
+           {
+             g_param_value_set_default (pspec, &def);
+             name = g_value_get_string (&def);
+           }
+
+         candidate_name = strdup (name);
+
+         while ( psppire_window_register_lookup (reg, candidate_name))
+           {
+             free (candidate_name);
+             candidate_name = uniquify (name, &x);
+           }
+
+         window->basename = g_filename_display_basename (candidate_name);
+
+         g_value_unset (&def);
+       }
+
+       psppire_window_set_title (window);
+
+       if ( window->name)
+         psppire_window_register_remove (reg, window->name);
+
+       free (window->name);
+       window->name = candidate_name;
+
+       psppire_window_register_insert (reg, window, window->name);
+      }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+static void
+psppire_window_get_property (GObject         *object,
+                            guint            prop_id,
+                            GValue          *value,
+                            GParamSpec      *pspec)
+{
+  PsppireWindow *window = PSPPIRE_WINDOW (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILENAME:
+      g_value_set_string (value, window->name);
+      break;
+    case PROP_DESCRIPTION:
+      g_value_set_string (value, window->description);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+static void
+on_realize (GtkWindow *window, gpointer data)
+{
+  PsppireConf *conf = psppire_conf_new ();
+
+  const gchar *base = G_OBJECT_TYPE_NAME (window);
+
+  psppire_conf_set_window_geometry (conf, base, window);
+}
+
+
+static gboolean
+save_geometry (GtkWidget *window, GdkEvent *event, gpointer data)
+{
+  const gchar *base = G_OBJECT_TYPE_NAME (window);
+
+  PsppireConf *conf = psppire_conf_new ();
+
+  psppire_conf_save_window_geometry (conf, base, event);
+
+  return FALSE;
+}
+
+
+static void
+psppire_window_finalize (GObject *object)
+{
+  PsppireWindow *window = PSPPIRE_WINDOW (object);
+
+  PsppireWindowRegister *reg = psppire_window_register_new ();
+
+  psppire_window_register_remove (reg, window->name);
+  free (window->name);
+  free (window->description);
+
+  g_signal_handler_disconnect (psppire_window_register_new (),
+                              window->remove_handler);
+
+  g_signal_handler_disconnect (psppire_window_register_new (),
+                              window->insert_handler);
+
+  g_hash_table_destroy (window->menuitem_table);
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+psppire_window_class_init (PsppireWindowClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  GParamSpec *description_spec =
+    g_param_spec_string ("description",
+                      "Description",
+                      "A string describing the usage of the window",
+                        "??????", /*Should be overridden by derived classes */
+                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+  GParamSpec *filename_spec =
+    g_param_spec_string ("filename",
+                      "File name",
+                      "The name of the file associated with this window, if any",
+                        "Untitled",
+                        G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
+
+  g_unichar_to_utf8 (0x2014, mdash);
+
+  object_class->set_property = psppire_window_set_property;
+  object_class->get_property = psppire_window_get_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_DESCRIPTION,
+                                   description_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_FILENAME,
+                                   filename_spec);
+
+  parent_class = g_type_class_peek_parent (class);
+}
+
+
+static void
+psppire_window_base_init (PsppireWindowClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = psppire_window_finalize;
+}
+
+
+
+static void
+psppire_window_base_finalize (PsppireWindowClass *class,
+                               gpointer class_data)
+{
+}
+
+static void
+menu_toggled (GtkCheckMenuItem *mi, gpointer data)
+{
+  /* Prohibit changes to the state */
+  mi->active = !mi->active;
+}
+
+
+/* Look up the window associated with this menuitem and present it to the user */
+static void
+menu_activate (GtkMenuItem *mi, gpointer data)
+{
+  const gchar *key = data;
+
+  PsppireWindowRegister *reg = psppire_window_register_new ();
+
+  PsppireWindow *window = psppire_window_register_lookup (reg, key);
+
+  gtk_window_present (GTK_WINDOW (window));
+}
+
+static void
+insert_menuitem_into_menu (PsppireWindow *window, gpointer key)
+{
+  gchar *filename = g_filename_display_name (key);
+  GtkWidget *item = gtk_check_menu_item_new_with_label (filename);
+
+  g_free (filename);
+
+  g_signal_connect (item, "toggled", G_CALLBACK (menu_toggled), NULL);
+  g_signal_connect (item, "activate", G_CALLBACK (menu_activate), key);
+
+  gtk_widget_show (item);
+
+  gtk_menu_shell_append (window->menu, item);
+
+  /* Set the state without emitting a signal */
+  GTK_CHECK_MENU_ITEM (item)->active =
+   (psppire_window_register_lookup (psppire_window_register_new (), key) == window);
+
+  g_hash_table_insert (window->menuitem_table, key, item);
+}
+
+static void
+insert_item (gpointer key, gpointer value, gpointer data)
+{
+  PsppireWindow *window = PSPPIRE_WINDOW (data);
+
+  if ( NULL != g_hash_table_lookup (window->menuitem_table, key))
+    return;
+
+  insert_menuitem_into_menu (window, key);
+}
+
+/* Insert a new item into the window menu */
+static void
+insert_menuitem (GObject *reg, const gchar *key, gpointer data)
+{
+  PsppireWindow *window = PSPPIRE_WINDOW (data);
+  
+  insert_menuitem_into_menu (window, (gpointer) key);
+}
+
+
+static void
+remove_menuitem (PsppireWindowRegister *reg, const gchar *key, gpointer data)
+{
+  PsppireWindow *window = PSPPIRE_WINDOW (data);
+  GtkWidget *item ;
+
+  item = g_hash_table_lookup (window->menuitem_table, key);
+
+  g_hash_table_remove (window->menuitem_table, key);
+
+  if (GTK_IS_CONTAINER (window->menu))
+    gtk_container_remove (GTK_CONTAINER (window->menu), item);
+}
+
+static void
+insert_existing_items (PsppireWindow *window)
+{
+  psppire_window_register_foreach (psppire_window_register_new (), insert_item, window);
+}
+
+
+static gboolean
+on_delete (PsppireWindow *w, GdkEvent *event, gpointer user_data)
+{
+  PsppireWindowRegister *reg = psppire_window_register_new ();
+
+  if ( w->dirty )
+    {
+      gint response = psppire_window_query_save (w);
+
+      switch (response)
+       {
+       default:
+       case GTK_RESPONSE_CANCEL:
+         return TRUE;
+         break;
+       case GTK_RESPONSE_APPLY:
+         psppire_window_save (w);
+         break;
+       case GTK_RESPONSE_REJECT:
+         break;
+       }
+    }
+
+  if ( 1 == psppire_window_register_n_items (reg))
+    gtk_main_quit ();
+
+  return FALSE;
+}
+
+
+static void
+psppire_window_init (PsppireWindow *window)
+{
+  window->name = NULL;
+  window->menu = NULL;
+  window->description = strdup ("");
+
+  window->menuitem_table  = g_hash_table_new (g_str_hash, g_str_equal);
+
+
+  g_signal_connect (window,  "realize", G_CALLBACK (insert_existing_items), NULL);
+
+  window->insert_handler = g_signal_connect (psppire_window_register_new (),
+                                            "inserted",
+                                            G_CALLBACK (insert_menuitem),
+                                            window);
+
+  window->remove_handler = g_signal_connect (psppire_window_register_new (),
+                                            "removed",
+                                            G_CALLBACK (remove_menuitem),
+                                            window);
+
+  window->dirty = FALSE;
+
+  g_signal_connect_swapped (window, "delete-event", G_CALLBACK (on_delete), window);
+
+  g_object_set (window, "icon-name", "psppicon", NULL);
+
+  g_signal_connect (window, "configure-event",
+                   G_CALLBACK (save_geometry), window);
+
+  g_signal_connect (window, "window-state-event",
+                   G_CALLBACK (save_geometry), window);
+
+  g_signal_connect (window, "realize",
+                   G_CALLBACK (on_realize), window);
+
+}
+
+/*
+   Ask the user if the buffer should be saved.
+   Return the response.
+*/
+gint
+psppire_window_query_save (PsppireWindow *se)
+{
+  gchar *fn;
+  gint response;
+  GtkWidget *dialog;
+  GtkWidget *cancel_button;
+
+  const gchar *description;
+  const gchar *filename = psppire_window_get_filename (se);
+
+  GTimeVal time;
+
+  g_get_current_time (&time);
+
+  g_object_get (se, "description", &description, NULL);
+
+  g_return_val_if_fail (filename != NULL, GTK_RESPONSE_NONE);
+
+
+  fn = g_filename_display_basename (filename);
+
+  dialog =
+    gtk_message_dialog_new (GTK_WINDOW (se),
+                           GTK_DIALOG_MODAL,
+                           GTK_MESSAGE_WARNING,
+                           GTK_BUTTONS_NONE,
+                           _("Save the changes to \"%s\" before closing?"),
+                           fn);
+  g_free (fn);
+
+  g_object_set (dialog, "icon-name", "psppicon", NULL);
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                           _("If you don't save, changes from the last %ld seconds will be permanently lost."),
+                                           time.tv_sec - se->savetime.tv_sec);
+
+  gtk_dialog_add_button  (GTK_DIALOG (dialog),
+                         _("Close _without saving"),
+                         GTK_RESPONSE_REJECT);
+
+  cancel_button = gtk_dialog_add_button  (GTK_DIALOG (dialog),
+                                         GTK_STOCK_CANCEL,
+                                         GTK_RESPONSE_CANCEL);
+
+  gtk_dialog_add_button  (GTK_DIALOG (dialog),
+                         GTK_STOCK_SAVE,
+                         GTK_RESPONSE_APPLY);
+
+  gtk_widget_grab_focus (cancel_button);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  gtk_widget_destroy (dialog);
+
+  return response;
+}
+
+
+
+const gchar *
+psppire_window_get_filename (PsppireWindow *w)
+{
+  const gchar *name = NULL;
+  g_object_get (w, "filename", &name, NULL);
+  return name;
+}
+
+
+void
+psppire_window_set_filename (PsppireWindow *w, const gchar *filename)
+{
+  g_object_set (w, "filename", filename, NULL);
+}
+
+void
+psppire_window_set_unsaved (PsppireWindow *w)
+{
+  if ( w->dirty == FALSE)
+    g_get_current_time (&w->savetime);
+
+  w->dirty = TRUE;
+
+  psppire_window_set_title (w);
+}
+
+gboolean
+psppire_window_get_unsaved (PsppireWindow *w)
+{
+  return w->dirty;
+}
+
+
+\f
+
+
+static void
+minimise_window (gpointer key, gpointer value, gpointer data)
+{
+  gtk_window_iconify (GTK_WINDOW (value));
+}
+
+
+void
+psppire_window_minimise_all (void)
+{
+  PsppireWindowRegister *reg = psppire_window_register_new ();
+
+  g_hash_table_foreach (reg->name_table, minimise_window, NULL);
+}
+
+
+\f
+
+GType
+psppire_window_model_get_type (void)
+{
+  static GType window_model_type = 0;
+
+  if (! window_model_type)
+    {
+      static const GTypeInfo window_model_info =
+      {
+        sizeof (PsppireWindowIface), /* class_size */
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       NULL,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      window_model_type =
+       g_type_register_static (G_TYPE_INTERFACE, "PsppireWindowModel",
+                               &window_model_info, 0);
+
+      g_type_interface_add_prerequisite (window_model_type, G_TYPE_OBJECT);
+    }
+
+  return window_model_type;
+}
+
+
+void
+psppire_window_save (PsppireWindow *w)
+{
+  PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
+
+  g_assert (PSPPIRE_IS_WINDOW_MODEL (w));
+
+  g_assert (i);
+
+  g_return_if_fail (i->save);
+
+  i->save (w);
+
+  w->dirty = FALSE;
+  psppire_window_set_title (w);
+}
+
+extern GtkRecentManager *the_recent_mgr;
+
+static void add_most_recent (const char *file_name, GtkRecentManager *rm);
+static void delete_recent (const char *file_name, GtkRecentManager *rm);
+
+gboolean
+psppire_window_load (PsppireWindow *w, const gchar *file)
+{
+  gboolean ok;
+  PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
+
+  g_assert (PSPPIRE_IS_WINDOW_MODEL (w));
+
+  g_assert (i);
+
+  g_return_val_if_fail (i->load, FALSE);
+
+  ok = i->load (w, file);
+
+  if ( ok )
+    {
+      psppire_window_set_filename (w, file);
+      add_most_recent (file, the_recent_mgr);
+      w->dirty = FALSE;
+    }
+  else
+    delete_recent (file, the_recent_mgr);
+
+  psppire_window_set_title (w);
+
+  return ok;
+}
+
+
+/* Puts FILE_NAME into the recent list.
+   If it's already in the list, it moves it to the top
+*/
+static void
+add_most_recent (const char *file_name, GtkRecentManager *rm)
+{
+  gchar *uri = g_filename_to_uri  (file_name, NULL, NULL);
+
+  if ( uri )
+    gtk_recent_manager_add_item (rm, uri);
+
+  g_free (uri);
+}
+
+
+
+/*
+   If FILE_NAME exists in the recent list, then  delete it.
+ */
+static void
+delete_recent (const char *file_name, GtkRecentManager *rm)
+{
+  gchar *uri = g_filename_to_uri  (file_name, NULL, NULL);
+
+  if ( uri )
+    gtk_recent_manager_remove_item (rm, uri, NULL);
+
+  g_free (uri);
+}
+
diff --git a/src/ui/gui/psppire-window.h b/src/ui/gui/psppire-window.h
new file mode 100644 (file)
index 0000000..441e12a
--- /dev/null
@@ -0,0 +1,117 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   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
+   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 __PSPPIRE_WINDOW_H__
+#define __PSPPIRE_WINDOW_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkaction.h>
+#include <gtk/gtkmenushell.h>
+#include <gtk/gtkrecentmanager.h>
+
+G_BEGIN_DECLS
+
+
+#define PSPPIRE_TYPE_WINDOW            (psppire_window_get_type ())
+
+#define PSPPIRE_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+    PSPPIRE_TYPE_WINDOW, PsppireWindow))
+
+#define PSPPIRE_WINDOW_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), \
+    PSPPIRE_TYPE_WINDOW, PsppireWindowClass))
+
+#define PSPPIRE_IS_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+    PSPPIRE_TYPE_WINDOW))
+
+#define PSPPIRE_IS_WINDOW_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+    PSPPIRE_TYPE_WINDOW))
+
+
+
+#define PSPPIRE_TYPE_WINDOW_MODEL            (psppire_window_model_get_type ())
+
+#define PSPPIRE_IS_WINDOW_MODEL(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_WINDOW_MODEL))
+
+#define PSPPIRE_WINDOW_MODEL_GET_IFACE(obj) \
+   (G_TYPE_INSTANCE_GET_INTERFACE ((obj), PSPPIRE_TYPE_WINDOW_MODEL, PsppireWindowIface))
+
+
+typedef struct _PsppireWindow       PsppireWindow;
+typedef struct _PsppireWindowClass  PsppireWindowClass;
+typedef struct _PsppireWindowIface  PsppireWindowIface;
+
+
+struct _PsppireWindow
+{
+  GtkWindow parent;
+
+  /* <private> */
+  gchar *name;
+  gchar *description;
+  gchar *basename;
+
+  GHashTable *menuitem_table;
+  GtkMenuShell *menu;
+
+  guint insert_handler;
+  guint remove_handler;
+
+  gboolean dirty;
+  GTimeVal savetime;
+};
+
+
+struct _PsppireWindowClass
+{
+  GtkWindowClass parent_class;
+};
+
+
+struct _PsppireWindowIface
+{
+  GTypeInterface g_iface;
+
+  void (*save) (PsppireWindow *w);
+  gboolean (*load) (PsppireWindow *w, const gchar *);
+};
+
+
+GType      psppire_window_get_type        (void);
+GType      psppire_window_model_get_type        (void);
+
+const gchar * psppire_window_get_filename (PsppireWindow *);
+
+void psppire_window_set_filename (PsppireWindow *w, const gchar *filename);
+
+void psppire_window_minimise_all (void);
+
+void psppire_window_set_unsaved (PsppireWindow *);
+
+gboolean psppire_window_get_unsaved (PsppireWindow *);
+
+gint psppire_window_query_save (PsppireWindow *);
+
+void psppire_window_save (PsppireWindow *w);
+gboolean psppire_window_load (PsppireWindow *w, const gchar *file);
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_WINDOW_H__ */
index 974d18b7e0d70f9cff244fbff3d5ee600ab126ba..599d8108ddb7d8b91897eb6f16bf0cf27003ec1b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2004, 2005, 2006  Free Software Foundation
+   Copyright (C) 2004, 2005, 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 <libintl.h>
 #include <gsl/gsl_errno.h>
 
+#include <argp.h>
+#include <ui/command-line.h>
 #include "relocatable.h"
 
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire.h"
+#include "widgets.h"
 
+#include <libpspp/getl.h>
 #include <unistd.h>
 #include <data/casereader.h>
 #include <data/datasheet.h>
 #include <libpspp/version.h>
 #include <output/output.h>
 #include <output/journal.h>
+#include <language/syntax-string-source.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include "psppire-dict.h"
 #include "psppire-var-store.h"
 #include "psppire-data-store.h"
 #include "helper.h"
 #include "message-dialog.h"
+#include <ui/syntax-gen.h>
+
+#include "psppire-window-register.h"
+#include "psppire-output-window.h"
+
+#include <data/sys-file-reader.h>
+#include <data/por-file-reader.h>
 
-#include "output-viewer.h"
+#include <ui/source-init-opts.h>
 
+GtkRecentManager *the_recent_mgr = 0;
 PsppireDataStore *the_data_store = 0;
 PsppireVarStore *the_var_store = 0;
 
@@ -57,21 +69,23 @@ static void create_icon_factory (void);
 struct source_stream *the_source_stream ;
 struct dataset * the_dataset = NULL;
 
+static GtkWidget *the_data_window;
 
 static void
 replace_casereader (struct casereader *s)
 {
-  PsppireCaseFile *pcf = psppire_case_file_new (s);
-
-  psppire_data_store_set_case_file (the_data_store, pcf);
+  psppire_data_store_set_reader (the_data_store, s);
 }
 
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
 
+const char * output_file_name (void);
+
+
 void
-initialize (void)
+initialize (struct command_line_processor *clp, int argc, char **argv)
 {
   PsppireDict *dictionary = 0;
 
@@ -82,7 +96,7 @@ initialize (void)
   bindtextdomain (PACKAGE, locale_dir);
 
 
-  glade_init ();
+  preregister_widgets ();
 
   gsl_set_error_handler_off ();
   fn_init ();
@@ -134,7 +148,19 @@ initialize (void)
   journal_enable ();
   textdomain (PACKAGE);
 
-  new_data_window (NULL, NULL);
+
+  the_recent_mgr = gtk_recent_manager_get_default ();
+
+  the_data_window = psppire_data_window_new ();
+
+  command_line_processor_replace_aux (clp, &post_init_argp, the_source_stream);
+  command_line_processor_replace_aux (clp, &non_option_argp, the_source_stream);
+
+  command_line_processor_parse (clp, argc, argv);
+
+  execute_syntax (create_syntax_string_source (""));
+
+  gtk_widget_show (the_data_window);
 }
 
 
@@ -148,6 +174,24 @@ de_initialize (void)
 }
 
 
+static void
+func (gpointer key, gpointer value, gpointer data)
+{
+  gboolean rv;
+  PsppireWindow *window = PSPPIRE_WINDOW (value);
+
+  g_signal_emit_by_name (window, "delete-event", 0, &rv);
+}
+
+void
+psppire_quit (void)
+{
+  PsppireWindowRegister *reg = psppire_window_register_new ();
+  psppire_window_register_foreach (reg, func, NULL);
+
+  gtk_main_quit ();
+}
+
 
 struct icon_info
 {
@@ -209,11 +253,11 @@ create_icon_factory (void)
 
 
     gtk_stock_add (items, 2);
-    gtk_icon_factory_add (factory, "pspp-stock-reset", 
+    gtk_icon_factory_add (factory, "pspp-stock-reset",
                          gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
                          );
 
-    gtk_icon_factory_add (factory, "pspp-stock-select", 
+    gtk_icon_factory_add (factory, "pspp-stock-select",
                          gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
                          );
   }
@@ -221,3 +265,93 @@ create_icon_factory (void)
   gtk_icon_factory_add_default (factory);
 }
 
+\f
+
+static error_t
+parse_non_options (int key, char *arg, struct argp_state *state)
+{
+  struct source_stream *ss = state->input;
+
+  if ( NULL == ss )
+    return 0;
+
+  switch (key)
+    {
+    case ARGP_KEY_ARG:
+      {
+       gchar *filename = NULL;
+       gchar *utf8 = NULL;
+       const gchar *local_encoding = NULL;
+       gsize written = -1;
+       const gboolean local_is_utf8 = g_get_charset (&local_encoding);
+
+       /* There seems to be no Glib function to convert from local encoding
+          to filename encoding.  Therefore it has to be done in two steps:
+          the intermediate encoding is UTF8.
+
+          Either step could fail.  However, in many cases the file can still
+          be loaded even if the conversion fails. So in those cases, after showing
+          a warning, we simply copy the locally encoded filename to the destination
+          and hope for the best.
+       */
+
+       if ( local_is_utf8)
+         {
+           utf8 = strdup (arg);
+         }
+       else
+         {
+           GError *err = NULL;
+           utf8 = g_locale_to_utf8 (arg, -1, NULL, &written, &err);
+           if ( NULL == utf8)
+             {
+               g_warning ("Cannot convert filename from local encoding \"%s\" to UTF-8: %s",
+                          local_encoding,
+                          err->message);
+               g_clear_error (&err);
+             }
+         }
+
+       if ( NULL != utf8)
+         {
+           GError *err = NULL;
+           filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
+           if ( NULL == filename)
+             {
+               g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
+                          err->message);
+               g_clear_error (&err);
+             }
+         }
+
+       g_free (utf8);
+
+       if ( filename == NULL)
+         filename = strdup (arg);
+
+       psppire_window_load (PSPPIRE_WINDOW (the_data_window), filename);
+
+       g_free (filename);
+       break;
+      }
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+
+const struct argp non_option_argp = {NULL, parse_non_options, 0, 0, 0, 0, 0};
+
+
+const char *
+output_file_name (void)
+{
+  const char *dir = default_output_path ();
+  static char *filename = NULL;
+
+  if ( NULL == filename )
+    filename = xasprintf ("%s%s", dir, OUTPUT_FILE_NAME);
+
+  return filename;
+}
index bffb34e48d572a282266cb0c8ef4becc6eecd663..ad08cb2b13a3e30543582e02eb9ebe1349816791 100644 (file)
@@ -6,7 +6,6 @@
   <widget class="GtkAboutDialog" id="aboutdialog1">
     <property name="modal">True</property>
     <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
-    <property name="name">PSPPire</property>
     <property name="copyright">Free Software Foundation</property>
     <property name="comments" translatable="yes">This is beta status software.  Please report bugs to bug-gnu-pspp@gnu.org</property>
     <property name="authors"></property>
             <child>
               <widget class="GtkScrolledWindow" id="scrolledwindow1">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="weight-cases-treeview">
+                  <widget class="PsppireDictView" id="weight-cases-treeview">
                     <property name="visible">True</property>
                     <property name="headers_visible">False</property>
                     <property name="fixed_height_mode">True</property>
@@ -62,7 +62,6 @@
                   <widget class="GtkFrame" id="frame1">
                     <property name="visible">True</property>
                     <property name="border_width">5</property>
-                    <property name="label_xalign">0</property>
                     <child>
                       <widget class="GtkVBox" id="vbox2">
                         <property name="visible">True</property>
@@ -72,6 +71,7 @@
                             <property name="visible">True</property>
                             <property name="label" translatable="yes">Do not weight cases</property>
                             <property name="focus_on_click">False</property>
+                            <property name="response_id">0</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
                           </widget>
@@ -82,6 +82,7 @@
                             <property name="sensitive">False</property>
                             <property name="label" translatable="yes">Weight cases by</property>
                             <property name="focus_on_click">False</property>
+                            <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                             <property name="group">weight-cases-radiobutton1</property>
                           </widget>
             <child>
               <widget class="GtkFrame" id="frame3">
                 <property name="visible">True</property>
-                <property name="label_xalign">0</property>
                 <property name="shadow_type">GTK_SHADOW_IN</property>
                 <child>
                   <widget class="GtkScrolledWindow" id="scrolledwindow1">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
                     <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                     <child>
-                      <widget class="GtkTreeView" id="source-treeview">
+                      <widget class="PsppireDictView" id="source-treeview">
                         <property name="visible">True</property>
                         <property name="headers_visible">False</property>
                       </widget>
                 <property name="column_spacing">5</property>
                 <property name="row_spacing">5</property>
                 <child>
-                  <widget class="PsppireSelector" id="psppire-selector2">
-                    <property name="visible">True</property>
-                    <property name="border_width">5</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="psppire-selector3">
+                  <widget class="GtkVBox" id="vbox5">
                     <property name="visible">True</property>
-                    <property name="border_width">5</property>
+                    <child>
+                      <widget class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Name Variable:</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="new-name-entry">
+                        <property name="visible">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
                   </widget>
                   <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
                     <property name="top_attach">1</property>
                     <property name="bottom_attach">2</property>
-                    <property name="x_options"></property>
-                    <property name="y_options">GTK_FILL</property>
+                    <property name="y_options"></property>
                   </packing>
                 </child>
                 <child>
                     <child>
                       <widget class="GtkFrame" id="frame2">
                         <property name="visible">True</property>
-                        <property name="label_xalign">0</property>
                         <property name="shadow_type">GTK_SHADOW_IN</property>
                         <child>
                           <widget class="GtkScrolledWindow" id="scrolledwindow2">
                             <property name="visible">True</property>
+                            <property name="can_focus">False</property>
                             <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
                             <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                             <child>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkVBox" id="vbox5">
+                  <widget class="PsppireSelector" id="psppire-selector3">
                     <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkLabel" id="label4">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Name Variable:</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="new-name-entry">
-                        <property name="visible">True</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
+                    <property name="border_width">5</property>
                   </widget>
                   <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
                     <property name="top_attach">1</property>
                     <property name="bottom_attach">2</property>
+                    <property name="x_options"></property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="PsppireSelector" id="psppire-selector2">
+                    <property name="visible">True</property>
+                    <property name="border_width">5</property>
+                  </widget>
+                  <packing>
+                    <property name="x_options"></property>
                     <property name="y_options"></property>
                   </packing>
                 </child>
                   <widget class="GtkFrame" id="frame5">
                     <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_xalign">0</property>
                     <property name="shadow_type">GTK_SHADOW_IN</property>
                     <child>
                       <widget class="GtkScrolledWindow" id="scrolledwindow3">
                         <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
                         <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                         <child>
-                          <widget class="GtkTreeView" id="split-file-dict-treeview">
+                          <widget class="PsppireDictView" id="split-file-dict-treeview">
                             <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="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="label" translatable="yes">Analyze all cases.  Do not create groups.</property>
+                            <property name="response_id">0</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                             <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="label" translatable="yes">Compare groups.</property>
+                            <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                             <property name="group">split-radiobutton0</property>
                           </widget>
                             <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="label" translatable="yes">Organize output by groups.</property>
+                            <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                             <property name="group">split-radiobutton0</property>
                           </widget>
                               <widget class="GtkFrame" id="frame4">
                                 <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_xalign">0</property>
                                 <property name="label_yalign">0</property>
                                 <property name="shadow_type">GTK_SHADOW_IN</property>
                                 <child>
                             <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">Sort the file by grouping variables.</property>
                             <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                             <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 is already sorted.</property>
                             <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                             <property name="group">split-radiobutton3</property>
                           </widget>
                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="sort-cases-treeview1">
+                  <widget class="PsppireDictView" id="sort-cases-treeview1">
                     <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>
                   <widget class="GtkFrame" id="frame9">
                     <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_xalign">0</property>
                     <child>
                       <widget class="GtkAlignment" id="alignment5">
                         <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="label" translatable="yes">Ascending</property>
+                                <property name="response_id">0</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                               </widget>
                                 <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="label" translatable="yes">Descending</property>
+                                <property name="response_id">0</property>
                                 <property name="draw_indicator">True</property>
                                 <property name="group">sort-cases-radiobutton0</property>
                               </widget>
                         <property name="receives_default">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">Type &amp; Label</property>
+                        <property name="response_id">0</property>
                       </widget>
                       <packing>
                         <property name="expand">False</property>
                         <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                         <property name="shadow_type">GTK_SHADOW_IN</property>
                         <child>
-                          <widget class="GtkTreeView" id="compute-treeview1">
+                          <widget class="PsppireDictView" id="compute-treeview1">
                             <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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <child>
                   <widget class="GtkButton" id="button4">
-                    <property name="visible">False</property>
                     <property name="sensitive">False</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">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">If...</property>
+                    <property name="response_id">0</property>
                   </widget>
                   <packing>
                     <property name="expand">False</property>
           <widget class="GtkAlignment" id="alignment2">
             <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="yscale">0</property>
             <property name="top_padding">5</property>
             <property name="left_padding">5</property>
             <property name="right_padding">5</property>
                     <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                     <property name="shadow_type">GTK_SHADOW_IN</property>
                     <child>
-                      <widget class="GtkTreeView" id="select-cases-treeview">
+                      <widget class="PsppireDictView" id="select-cases-treeview">
                         <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>
                       <widget class="GtkFrame" id="Select5">
                         <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_xalign">0</property>
                         <child>
                           <widget class="GtkAlignment" id="alignment11">
                             <property name="visible">True</property>
                                 <property name="n_columns">2</property>
                                 <property name="row_spacing">5</property>
                                 <child>
-                                  <widget class="GtkLabel" id="label10">
+                                  <widget class="GtkRadioButton" id="radiobutton-all">
+                                    <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="response_id">0</property>
+                                    <property name="active">True</property>
+                                    <property name="draw_indicator">True</property>
+                                  </widget>
+                                </child>
+                                <child>
+                                  <widget class="GtkAlignment" id="alignment18">
                                     <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="xalign">0</property>
-                                    <property name="label" translatable="yes">All Cases</property>
+                                    <property name="yalign">0</property>
+                                    <property name="xscale">0</property>
+                                    <property name="yscale">0</property>
+                                    <child>
+                                      <widget class="GtkRadioButton" id="radiobutton-filter-variable">
+                                        <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="response_id">0</property>
+                                        <property name="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
+                                      </widget>
+                                    </child>
                                   </widget>
                                   <packing>
-                                    <property name="left_attach">1</property>
-                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">4</property>
+                                    <property name="bottom_attach">5</property>
+                                    <property name="x_options"></property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <widget class="GtkVBox" id="vbox14">
-                                    <property name="visible">False</property>
+                                  <widget class="GtkAlignment" id="alignment17">
+                                    <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="xalign">0</property>
+                                    <property name="yalign">0</property>
+                                    <property name="xscale">0</property>
+                                    <property name="yscale">0</property>
                                     <child>
-                                      <widget class="GtkLabel" id="label11">
+                                      <widget class="GtkRadioButton" id="radiobutton-range">
                                         <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="xalign">0</property>
-                                        <property name="label" translatable="yes">If condition is satisfied</property>
+                                        <property name="response_id">0</property>
+                                        <property name="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
                                       </widget>
                                     </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="top_attach">3</property>
+                                    <property name="bottom_attach">4</property>
+                                    <property name="x_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkAlignment" id="alignment16">
+                                    <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="xalign">0</property>
+                                    <property name="yalign">0</property>
+                                    <property name="xscale">0</property>
+                                    <property name="yscale">0</property>
                                     <child>
-                                      <widget class="GtkHBox" id="hbox9">
+                                      <widget class="GtkRadioButton" id="radiobutton-sample">
                                         <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>
-                                        <child>
-                                          <widget class="GtkButton" id="button-if">
-                                            <property name="visible">True</property>
-                                            <property name="sensitive">False</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">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">If...</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="fill">False</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="label26">
-                                            <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>
-                                          <packing>
-                                            <property name="position">1</property>
-                                          </packing>
-                                        </child>
+                                        <property name="response_id">0</property>
+                                        <property name="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="top_attach">2</property>
+                                    <property name="bottom_attach">3</property>
+                                    <property name="x_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkAlignment" id="alignment13">
+                                    <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="xalign">0</property>
+                                    <property name="yalign">0</property>
+                                    <property name="xscale">0</property>
+                                    <property name="yscale">0</property>
+                                    <child>
+                                      <widget class="GtkRadioButton" id="radiobutton-if">
+                                        <property name="sensitive">False</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="response_id">0</property>
+                                        <property name="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
                                       </widget>
-                                      <packing>
-                                        <property name="position">1</property>
-                                      </packing>
                                     </child>
                                   </widget>
                                   <packing>
-                                    <property name="left_attach">1</property>
-                                    <property name="right_attach">2</property>
                                     <property name="top_attach">1</property>
                                     <property name="bottom_attach">2</property>
+                                    <property name="x_options"></property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <widget class="GtkVBox" id="vbox24">
+                                  <widget class="GtkVBox" id="vbox26">
                                     <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>
                                     <child>
-                                      <widget class="GtkLabel" id="label13">
+                                      <widget class="GtkLabel" id="label25">
                                         <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="xalign">0</property>
-                                        <property name="label" translatable="yes">Random sample of cases</property>
+                                        <property name="label" translatable="yes">Use filter variable</property>
                                       </widget>
                                     </child>
                                     <child>
-                                      <widget class="GtkHBox" id="hbox11">
+                                      <widget class="GtkHBox" id="hbox19">
                                         <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="spacing">5</property>
                                         <child>
-                                          <widget class="GtkButton" id="button-sample">
+                                          <widget class="PsppireSelector" id="psppire-selector-filter">
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
                                             <property name="receives_default">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">Sample...</property>
+                                            <property name="border_width">5</property>
                                           </widget>
                                           <packing>
                                             <property name="expand">False</property>
                                           </packing>
                                         </child>
                                         <child>
-                                          <widget class="GtkLabel" id="random-sample-label">
+                                          <widget class="GtkEntry" id="filter-variable-entry">
                                             <property name="visible">True</property>
+                                            <property name="sensitive">False</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>
                                           </widget>
                                           <packing>
                                   <packing>
                                     <property name="left_attach">1</property>
                                     <property name="right_attach">2</property>
-                                    <property name="top_attach">2</property>
-                                    <property name="bottom_attach">3</property>
+                                    <property name="top_attach">4</property>
+                                    <property name="bottom_attach">5</property>
                                   </packing>
                                 </child>
                                 <child>
                                             <property name="receives_default">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">Range...</property>
+                                            <property name="response_id">0</property>
                                           </widget>
                                           <packing>
                                             <property name="expand">False</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <widget class="GtkVBox" id="vbox26">
+                                  <widget class="GtkVBox" id="vbox24">
                                     <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>
                                     <child>
-                                      <widget class="GtkLabel" id="label25">
+                                      <widget class="GtkLabel" id="label13">
                                         <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="xalign">0</property>
-                                        <property name="label" translatable="yes">Use filter variable</property>
+                                        <property name="label" translatable="yes">Random sample of cases</property>
                                       </widget>
                                     </child>
                                     <child>
-                                      <widget class="GtkHBox" id="hbox19">
+                                      <widget class="GtkHBox" id="hbox11">
                                         <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="spacing">5</property>
                                         <child>
-                                          <widget class="PsppireSelector" id="psppire-selector-filter">
+                                          <widget class="GtkButton" id="button-sample">
                                             <property name="visible">True</property>
                                             <property name="can_focus">True</property>
                                             <property name="receives_default">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="border_width">5</property>
+                                            <property name="label" translatable="yes">Sample...</property>
+                                            <property name="response_id">0</property>
                                           </widget>
                                           <packing>
                                             <property name="expand">False</property>
                                           </packing>
                                         </child>
                                         <child>
-                                          <widget class="GtkEntry" id="filter-variable-entry">
+                                          <widget class="GtkLabel" id="random-sample-label">
                                             <property name="visible">True</property>
-                                            <property name="sensitive">False</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>
                                           </widget>
                                           <packing>
                                   <packing>
                                     <property name="left_attach">1</property>
                                     <property name="right_attach">2</property>
-                                    <property name="top_attach">4</property>
-                                    <property name="bottom_attach">5</property>
+                                    <property name="top_attach">2</property>
+                                    <property name="bottom_attach">3</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <widget class="GtkAlignment" id="alignment13">
-                                    <property name="visible">True</property>
+                                  <widget class="GtkVBox" id="vbox14">
                                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="xalign">0</property>
-                                    <property name="yalign">0</property>
-                                    <property name="xscale">0</property>
-                                    <property name="yscale">0</property>
                                     <child>
-                                      <widget class="GtkRadioButton" id="radiobutton-if">
-                                        <property name="visible">False</property>
-                                        <property name="sensitive">False</property>
-                                        <property name="can_focus">True</property>
+                                      <widget class="GtkLabel" id="label11">
+                                        <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="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">radiobutton-all</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">If condition is satisfied</property>
                                       </widget>
                                     </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="top_attach">1</property>
-                                    <property name="bottom_attach">2</property>
-                                    <property name="x_options"></property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkAlignment" id="alignment16">
-                                    <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="xalign">0</property>
-                                    <property name="yalign">0</property>
-                                    <property name="xscale">0</property>
-                                    <property name="yscale">0</property>
-                                    <child>
-                                      <widget class="GtkRadioButton" id="radiobutton-sample">
-                                        <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="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">radiobutton-all</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="top_attach">2</property>
-                                    <property name="bottom_attach">3</property>
-                                    <property name="x_options"></property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkAlignment" id="alignment17">
-                                    <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="xalign">0</property>
-                                    <property name="yalign">0</property>
-                                    <property name="xscale">0</property>
-                                    <property name="yscale">0</property>
                                     <child>
-                                      <widget class="GtkRadioButton" id="radiobutton-range">
+                                      <widget class="GtkHBox" id="hbox9">
                                         <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="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">radiobutton-all</property>
+                                        <child>
+                                          <widget class="GtkButton" id="button-if">
+                                            <property name="visible">True</property>
+                                            <property name="sensitive">False</property>
+                                            <property name="can_focus">True</property>
+                                            <property name="receives_default">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">If...</property>
+                                            <property name="response_id">0</property>
+                                          </widget>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkLabel" id="label26">
+                                            <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>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
                                       </widget>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
                                     </child>
                                   </widget>
                                   <packing>
-                                    <property name="top_attach">3</property>
-                                    <property name="bottom_attach">4</property>
-                                    <property name="x_options"></property>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <widget class="GtkAlignment" id="alignment18">
+                                  <widget class="GtkLabel" id="label10">
                                     <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="xalign">0</property>
-                                    <property name="yalign">0</property>
-                                    <property name="xscale">0</property>
-                                    <property name="yscale">0</property>
-                                    <child>
-                                      <widget class="GtkRadioButton" id="radiobutton-filter-variable">
-                                        <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="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">radiobutton-all</property>
-                                      </widget>
-                                    </child>
+                                    <property name="label" translatable="yes">All Cases</property>
                                   </widget>
                                   <packing>
-                                    <property name="top_attach">4</property>
-                                    <property name="bottom_attach">5</property>
-                                    <property name="x_options"></property>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
                                   </packing>
                                 </child>
-                                <child>
-                                  <widget class="GtkRadioButton" id="radiobutton-all">
-                                    <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="active">True</property>
-                                    <property name="draw_indicator">True</property>
-                                  </widget>
-                                </child>
                               </widget>
                             </child>
                           </widget>
                       <widget class="GtkFrame" id="frame8">
                         <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_xalign">0</property>
                         <child>
                           <widget class="GtkAlignment" id="alignment12">
                             <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="label" translatable="yes">Filtered</property>
+                                    <property name="response_id">0</property>
                                     <property name="active">True</property>
                                     <property name="draw_indicator">True</property>
                                   </widget>
                                     <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="label" translatable="yes">Deleted</property>
+                                    <property name="response_id">0</property>
                                     <property name="active">True</property>
                                     <property name="draw_indicator">True</property>
                                     <property name="group">radiobutton-filter</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="label" translatable="yes">Display comments in output</property>
+                <property name="response_id">0</property>
                 <property name="draw_indicator">True</property>
               </widget>
               <packing>
       </widget>
     </child>
   </widget>
-  <widget class="PsppireDialog" id="variable-info-dialog">
-    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-    <property name="title">Variables</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox6">
-        <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="spacing">5</property>
-        <child>
-          <widget class="GtkScrolledWindow" id="scrolledwindow11">
-            <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_NEVER</property>
-            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="shadow_type">GTK_SHADOW_IN</property>
-            <child>
-              <widget class="GtkTreeView" id="treeview2">
-                <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="headers_visible">False</property>
-                <property name="reorderable">True</property>
-                <property name="fixed_height_mode">True</property>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkVBox" id="vbox23">
-            <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="spacing">5</property>
-            <child>
-              <widget class="GtkLabel" id="label24">
-                <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="xalign">0</property>
-                <property name="label" translatable="yes">Variable Information:</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="padding">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkScrolledWindow" id="scrolledwindow12">
-                <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_NEVER</property>
-                <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkTextView" id="textview1">
-                    <property name="height_request">200</property>
-                    <property name="visible">True</property>
-                    <property name="events">GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="editable">False</property>
-                    <property name="wrap_mode">GTK_WRAP_WORD_CHAR</property>
-                    <property name="left_margin">3</property>
-                    <property name="cursor_visible">False</property>
-                    <property name="accepts_tab">False</property>
-                    <property name="text" translatable="yes">
-
-
-
-
-
-
-
-
-
-</property>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="padding">5</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireHButtonBox" id="psppire-hbuttonbox3">
-                <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="border_width">5</property>
-                <property name="homogeneous">True</property>
-                <property name="buttons">PSPPIRE_BUTTON_GOTO_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK | PSPPIRE_BUTTON_PASTE_MASK</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="pack_type">GTK_PACK_END</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
   <widget class="PsppireDialog" id="select-cases-range-dialog">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
     <property name="title">Select Cases: Range</property>
             <property name="n_columns">3</property>
             <property name="column_spacing">5</property>
             <child>
-              <widget class="GtkLabel" id="label14">
+              <widget class="GtkSpinButton" id="range-dialog-last">
                 <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="label" translatable="yes">Observation</property>
+                <property name="truncate_multiline">True</property>
+                <property name="adjustment">1 1 100 1 10 10</property>
               </widget>
               <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+                <property name="top_attach">1</property>
                 <property name="bottom_attach">2</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkLabel" id="label12">
+              <widget class="GtkSpinButton" id="range-dialog-first">
                 <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="label" translatable="yes">Last case</property>
+                <property name="truncate_multiline">True</property>
+                <property name="adjustment">0 1 0 1 10 10</property>
               </widget>
               <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="x_options"></property>
-                <property name="y_options"></property>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
               </packing>
             </child>
             <child>
               </packing>
             </child>
             <child>
-              <widget class="GtkSpinButton" id="range-dialog-first">
+              <widget class="GtkLabel" id="label12">
                 <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="truncate_multiline">True</property>
-                <property name="adjustment">1 1 0 1 10 10</property>
+                <property name="label" translatable="yes">Last case</property>
               </widget>
               <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+                <property name="x_options"></property>
+                <property name="y_options"></property>
               </packing>
             </child>
             <child>
-              <widget class="GtkSpinButton" id="range-dialog-last">
+              <widget class="GtkLabel" id="label14">
                 <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="truncate_multiline">True</property>
-                <property name="adjustment">1 1 100 1 10 10</property>
+                <property name="label" translatable="yes">Observation</property>
               </widget>
               <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">1</property>
                 <property name="bottom_attach">2</property>
               </packing>
             </child>
                 <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="border_width">5</property>
-                <property name="label_xalign">0</property>
                 <child>
                   <widget class="GtkAlignment" id="alignment11">
                     <property name="visible">True</property>
                         <property name="n_rows">2</property>
                         <property name="n_columns">2</property>
                         <child>
-                          <widget class="GtkRadioButton" id="radio-button-user-label">
-                            <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="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="x_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radio-button-expression-label">
+                          <widget class="GtkLabel" id="label27">
                             <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="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radio-button-user-label</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Use expression as label</property>
                           </widget>
                           <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
                             <property name="top_attach">1</property>
                             <property name="bottom_attach">2</property>
-                            <property name="x_options"></property>
+                            <property name="y_options"></property>
                           </packing>
                         </child>
                         <child>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkLabel" id="label27">
+                          <widget class="GtkRadioButton" id="radio-button-expression-label">
                             <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="xalign">0</property>
-                            <property name="label" translatable="yes">Use expression as label</property>
+                            <property name="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radio-button-user-label</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
                             <property name="top_attach">1</property>
                             <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
+                            <property name="x_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioButton" id="radio-button-user-label">
+                            <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="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </widget>
+                          <packing>
+                            <property name="x_options"></property>
                           </packing>
                         </child>
                       </widget>
                 <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="border_width">5</property>
-                <property name="label_xalign">0</property>
                 <child>
                   <widget class="GtkAlignment" id="alignment10">
                     <property name="visible">True</property>
                         <property name="border_width">5</property>
                         <property name="n_rows">2</property>
                         <property name="n_columns">2</property>
-                        <child>
-                          <widget class="GtkLabel" id="label28">
-                            <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="xalign">0</property>
-                            <property name="label" translatable="yes">Numeric</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radio-button-numeric">
-                            <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="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radio-button-string</property>
-                          </widget>
-                          <packing>
-                            <property name="x_options"></property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radio-button-string">
-                            <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="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="x_options"></property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
                         <child>
                           <widget class="GtkHBox" id="hbox20">
                             <property name="visible">True</property>
                             <property name="x_options">GTK_FILL</property>
                           </packing>
                         </child>
+                        <child>
+                          <widget class="GtkRadioButton" id="radio-button-string">
+                            <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="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </widget>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options"></property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkRadioButton" id="radio-button-numeric">
+                            <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="response_id">0</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radio-button-string</property>
+                          </widget>
+                          <packing>
+                            <property name="x_options"></property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label28">
+                            <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="xalign">0</property>
+                            <property name="label" translatable="yes">Numeric</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
                       </widget>
                     </child>
                   </widget>
           <widget class="GtkFrame" id="frame6">
             <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_xalign">0</property>
             <child>
               <widget class="GtkAlignment" id="alignment3">
                 <property name="visible">True</property>
                     <property name="column_spacing">5</property>
                     <property name="row_spacing">5</property>
                     <child>
-                      <widget class="GtkAlignment" id="alignment9">
+                      <widget class="GtkRadioButton" id="radiobutton-sample-percent">
                         <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="xalign">0</property>
-                        <property name="xscale">0</property>
-                        <child>
-                          <placeholder/>
-                        </child>
+                        <property name="response_id">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioButton" id="radiobutton-sample-n-cases">
+                        <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="response_id">0</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton-sample-percent</property>
                       </widget>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
                         <property name="top_attach">1</property>
                         <property name="bottom_attach">2</property>
+                        <property name="x_options"></property>
                       </packing>
                     </child>
                     <child>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkRadioButton" id="radiobutton-sample-n-cases">
+                      <widget class="GtkAlignment" id="alignment9">
                         <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="active">True</property>
-                        <property name="draw_indicator">True</property>
-                        <property name="group">radiobutton-sample-percent</property>
+                        <property name="xalign">0</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <placeholder/>
+                        </child>
                       </widget>
                       <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
                         <property name="top_attach">1</property>
                         <property name="bottom_attach">2</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton-sample-percent">
-                        <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="active">True</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="x_options"></property>
                       </packing>
                     </child>
                   </widget>
                         <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="label" translatable="yes">Search value labels</property>
+                        <property name="response_id">0</property>
                         <property name="draw_indicator">True</property>
                       </widget>
                       <packing>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <child>
                       <widget class="GtkCheckButton" id="find-match-regexp-checkbutton">
-                        <property name="sensitive">False</property>
                         <property name="visible">True</property>
+                        <property name="sensitive">False</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="label" translatable="yes">Regular expression Match</property>
+                        <property name="response_id">0</property>
                         <property name="draw_indicator">True</property>
                       </widget>
                     </child>
                     <child>
                       <widget class="GtkCheckButton" id="find-match-substring-checkbutton">
-                        <property name="sensitive">False</property>
                         <property name="visible">True</property>
+                        <property name="sensitive">False</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="label" translatable="yes">Search substrings</property>
+                        <property name="response_id">0</property>
                         <property name="draw_indicator">True</property>
                       </widget>
                       <packing>
                         <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="label" translatable="yes">Wrap around</property>
+                        <property name="response_id">0</property>
                         <property name="draw_indicator">True</property>
                       </widget>
                       <packing>
                         <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="label" translatable="yes">Search backward</property>
+                        <property name="response_id">0</property>
                         <property name="draw_indicator">True</property>
                       </widget>
                       <packing>
index 9c99af02ea98cca2588660d6c5c0e8aa04cb9fd2..408d858ba14b4e65ccfe11c36920a657b06d7c7d 100644 (file)
 #ifndef PSPPIRE_H
 #define PSPPIRE_H
 
+#include <argp.h>
 
-void initialize (void);
+struct command_line_processor ;
+extern const struct argp non_option_argp ;
+
+void initialize (struct command_line_processor *, int argc, char **argv);
 void de_initialize (void);
+void psppire_quit (void);
+
 
 #endif /* PSPPIRE_H */
index 174ac6d04138b9b1dfa9e4914b9ac68ebf7cc8ed..2c4d19a0fde9b6f37ace69dcd82292a9bff7f5e0 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -220,77 +220,77 @@ void
 rank_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   struct rank_dialog rd;
 
-  GladeXML *xml = XML_NEW ("rank.glade");
+  GtkBuilder * builder = builder_new ("rank.ui");
 
-  GtkWidget *vars = get_widget_assert   (xml, "dict-treeview");
-  GtkWidget *selector1 = get_widget_assert (xml, "psppire-selector1");
-  GtkWidget *selector2 = get_widget_assert (xml, "psppire-selector2");
+  GtkWidget *vars = get_widget_assert   (builder, "dict-treeview");
+  GtkWidget *selector1 = get_widget_assert (builder, "psppire-selector1");
+  GtkWidget *selector2 = get_widget_assert (builder, "psppire-selector2");
 
 
-  GtkWidget *types_button = get_widget_assert (xml, "button1");
-  GtkWidget *ties_button = get_widget_assert (xml, "button2");
+  GtkWidget *types_button = get_widget_assert (builder, "button1");
+  GtkWidget *ties_button = get_widget_assert (builder, "button2");
 
   PsppireVarStore *vs = NULL;
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
   rd.dict = vs->dict;
-  rd.rank_vars =   get_widget_assert (xml, "variables-treeview");
-  rd.group_vars =  get_widget_assert (xml, "group-vars-treeview");
-  rd.dialog = get_widget_assert   (xml, "rank-dialog");
+  rd.rank_vars =   get_widget_assert (builder, "variables-treeview");
+  rd.group_vars =  get_widget_assert (builder, "group-vars-treeview");
+  rd.dialog = get_widget_assert   (builder, "rank-dialog");
   rd.ascending_togglebutton =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "radiobutton1"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "radiobutton1"));
 
   rd.summary_togglebutton =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "summary-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "summary-checkbutton"));
 
-  rd.types_dialog = get_widget_assert (xml, "rank-types-dialog");
+  rd.types_dialog = get_widget_assert (builder, "rank-types-dialog");
 
 
-  rd.ntiles_entry  = get_widget_assert (xml, "ntiles-entry");
+  rd.ntiles_entry  = get_widget_assert (builder, "ntiles-entry");
 
   rd.func_button[RANK]    =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "rank-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "rank-checkbutton"));
 
   rd.func_button[SAVAGE]  =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "savage-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "savage-checkbutton"));
 
   rd.func_button[RFRACTION] =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "rfrac-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "rfrac-checkbutton"));
 
   rd.func_button[PERCENT] =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "percent-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "percent-checkbutton"));
 
   rd.func_button[N]       =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "sum-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "sum-checkbutton"));
 
   rd.func_button[NTILES] =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "ntiles-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "ntiles-checkbutton"));
 
   rd.func_button[PROPORTION] =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "prop-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "prop-checkbutton"));
 
   rd.func_button[NORMAL] =
-    GTK_TOGGLE_BUTTON (get_widget_assert (xml, "normal-checkbutton"));
+    GTK_TOGGLE_BUTTON (get_widget_assert (builder, "normal-checkbutton"));
 
-  rd.formula_box = get_widget_assert (xml, "formula-frame");
+  rd.formula_box = get_widget_assert (builder, "formula-frame");
 
-  rd.blom = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "blom-button"));
-  rd.tukey = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "tukey-button"));
-  rd.rankit = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "rankit-button"));
-  rd.vw = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "vw-button"));
+  rd.blom = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "blom-button"));
+  rd.tukey = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "tukey-button"));
+  rd.rankit = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "rankit-button"));
+  rd.vw = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "vw-button"));
 
   /* Ties dialog */
-  rd.ties_dialog = PSPPIRE_DIALOG (get_widget_assert (xml, "ties-dialog"));
+  rd.ties_dialog = PSPPIRE_DIALOG (get_widget_assert (builder, "ties-dialog"));
 
-  rd.mean = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "mean-button"));
-  rd.low = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "low-button"));
-  rd.high = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "high-button"));
-  rd.condense = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "condense-button"));
+  rd.mean = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "mean-button"));
+  rd.low = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "low-button"));
+  rd.high = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "high-button"));
+  rd.condense = GTK_TOGGLE_BUTTON (get_widget_assert (builder, "condense-button"));
 
   g_signal_connect_swapped (rd.func_button[PROPORTION], "toggled",
                            G_CALLBACK (set_sensitivity),
@@ -304,12 +304,9 @@ rank_dialog (GObject *o, gpointer data)
                    G_CALLBACK (on_ntiles_toggle),
                    rd.ntiles_entry);
 
-  gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (vars),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), GTK_WINDOW (de));
 
+  g_object_set (vars, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (rd.rank_vars), vs->dict);
 
@@ -349,6 +346,7 @@ rank_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&rd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -358,12 +356,7 @@ rank_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&rd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
+       paste_syntax_in_new_window (syntax);
        g_free (syntax);
       }
       break;
@@ -371,7 +364,7 @@ rank_dialog (GObject *o, gpointer data)
       break;
     }
 
-  g_object_unref (xml);
+  g_object_unref (builder);
 }
 
 
index a2ddace6e50cb4c8f8a5ec1c86d46776b791f0ba..8a5064f5c7e58bbb5c7d9071a4a3cfab5be82447 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void rank_dialog (GObject *o, gpointer data);
 
index 77c6cfc33d10c6964e790097c56b156ce28d51ca..fd412dfeb5fcdad9a559f003bb8cbfc02b3b5c1a 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.2 on Fri Oct 12 18:52:29 2007 by john@marilyn-->
+<!--Generated with glade3 3.2.2 on Mon Dec 15 06:55:22 2008 by john@marilyn-->
 <glade-interface>
   <requires lib="psppire"/>
   <widget class="PsppireDialog" id="rank-dialog">
                 <property name="n_rows">2</property>
                 <property name="n_columns">3</property>
                 <child>
-                  <widget class="PsppireSelector" id="psppire-selector1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">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="border_width">5</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="psppire-selector2">
+                  <widget class="GtkScrolledWindow" id="scrolledwindow5">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
-                    <property name="receives_default">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="border_width">5</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                    <child>
+                      <widget class="PsppireDictView" id="dict-treeview">
+                        <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="headers_visible">False</property>
+                        <property name="headers_clickable">True</property>
+                      </widget>
+                    </child>
                   </widget>
                   <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
                     <property name="bottom_attach">2</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkVBox" id="vbox2">
+                  <widget class="GtkVBox" id="vbox4">
                     <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>
                     <child>
-                      <widget class="GtkLabel" id="label1">
+                      <widget class="GtkLabel" id="label3">
                         <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="xalign">0</property>
-                        <property name="label" translatable="yes">Variable(s):</property>
+                        <property name="label" translatable="yes">By:</property>
                       </widget>
                       <packing>
                         <property name="expand">False</property>
@@ -72,7 +62,7 @@
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                      <widget class="GtkScrolledWindow" id="scrolledwindow3">
                         <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>
@@ -80,7 +70,7 @@
                         <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                         <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                         <child>
-                          <widget class="GtkTreeView" id="variables-treeview">
+                          <widget class="GtkTreeView" id="group-vars-treeview">
                             <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>
                   <packing>
                     <property name="left_attach">2</property>
                     <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkVBox" id="vbox4">
+                  <widget class="GtkVBox" id="vbox2">
                     <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>
                     <child>
-                      <widget class="GtkLabel" id="label3">
+                      <widget class="GtkLabel" id="label1">
                         <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="xalign">0</property>
-                        <property name="label" translatable="yes">By:</property>
+                        <property name="label" translatable="yes">Variable(s):</property>
                       </widget>
                       <packing>
                         <property name="expand">False</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow3">
+                      <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="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                         <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                         <child>
-                          <widget class="GtkTreeView" id="group-vars-treeview">
+                          <widget class="GtkTreeView" id="variables-treeview">
                             <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>
                   <packing>
                     <property name="left_attach">2</property>
                     <property name="right_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="PsppireSelector" id="psppire-selector2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="border_width">5</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
                     <property name="top_attach">1</property>
                     <property name="bottom_attach">2</property>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkScrolledWindow" id="scrolledwindow5">
+                  <widget class="PsppireSelector" id="psppire-selector1">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
+                    <property name="receives_default">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_NEVER</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                    <child>
-                      <widget class="GtkTreeView" id="dict-treeview">
-                        <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="headers_visible">False</property>
-                        <property name="headers_clickable">True</property>
-                      </widget>
-                    </child>
+                    <property name="border_width">5</property>
                   </widget>
                   <packing>
-                    <property name="bottom_attach">2</property>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
                   </packing>
                 </child>
               </widget>
                   <widget class="GtkFrame" id="frame1">
                     <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_xalign">0</property>
                     <child>
                       <widget class="GtkAlignment" id="alignment1">
                         <property name="visible">True</property>
                 </child>
               </widget>
               <packing>
+                <property name="expand">False</property>
                 <property name="position">1</property>
               </packing>
             </child>
                 <property name="n_rows">3</property>
                 <property name="n_columns">2</property>
                 <child>
-                  <widget class="GtkHBox" id="hbox2">
+                  <widget class="GtkCheckButton" id="sum-checkbutton">
                     <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>
-                    <child>
-                      <widget class="GtkCheckButton" id="ntiles-checkbutton">
-                        <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="label" translatable="yes">Ntiles</property>
-                        <property name="response_id">0</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkSpinButton" id="ntiles-entry">
-                        <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="adjustment">0 0 100 1 10 10</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
+                    <property name="label" translatable="yes">Sum of case weights</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
                   </widget>
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkCheckButton" id="rank-checkbutton">
-                    <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="label" translatable="yes">Rank</property>
-                    <property name="response_id">0</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="savage-checkbutton">
+                  <widget class="GtkCheckButton" id="percent-checkbutton">
                     <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="label" translatable="yes">Savage score</property>
+                    <property name="label" translatable="yes">Fractional rank as %</property>
                     <property name="response_id">0</property>
                     <property name="draw_indicator">True</property>
                   </widget>
                   <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
                   </packing>
                 </child>
                 <child>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkCheckButton" id="percent-checkbutton">
+                  <widget class="GtkCheckButton" id="savage-checkbutton">
                     <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="label" translatable="yes">Fractional rank as %</property>
+                    <property name="label" translatable="yes">Savage score</property>
                     <property name="response_id">0</property>
                     <property name="draw_indicator">True</property>
                   </widget>
                   <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkCheckButton" id="sum-checkbutton">
+                  <widget class="GtkCheckButton" id="rank-checkbutton">
                     <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="label" translatable="yes">Sum of case weights</property>
+                    <property name="label" translatable="yes">Rank</property>
                     <property name="response_id">0</property>
                     <property name="draw_indicator">True</property>
                   </widget>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox2">
+                    <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>
+                    <child>
+                      <widget class="GtkCheckButton" id="ntiles-checkbutton">
+                        <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="label" translatable="yes">Ntiles</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkSpinButton" id="ntiles-entry">
+                        <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="adjustment">0 0 100 1 10 10</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
                   <packing>
                     <property name="left_attach">1</property>
                     <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
                   </packing>
                 </child>
               </widget>
               <widget class="GtkFrame" id="formula-frame">
                 <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_xalign">0</property>
                 <child>
                   <widget class="GtkAlignment" id="alignment2">
                     <property name="visible">True</property>
           <widget class="GtkFrame" id="frame2">
             <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_xalign">0</property>
             <child>
               <widget class="GtkAlignment" id="alignment3">
                 <property name="visible">True</property>
index 314367462e6f403d691f2c9896972086b610075f..e480f3c70de75d05fef3be419a4a4b5819f17ac9 100644 (file)
 #include <gtk/gtk.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 #include <ui/syntax-gen.h>
 
 #include "psppire-acr.h"
@@ -470,14 +470,14 @@ toggle_sensitivity (GtkToggleButton *button, GtkWidget *target)
   gtk_widget_set_sensitive (target, state);
 }
 
-static void recode_dialog (struct data_editor *de, gboolean diff);
+static void recode_dialog (PsppireDataWindow *de, gboolean diff);
 
 
 /* Pops up the Recode Same version of the dialog box */
 void
 recode_same_dialog (GObject *o, gpointer data)
 {
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   recode_dialog (de, FALSE);
 }
@@ -486,7 +486,7 @@ recode_same_dialog (GObject *o, gpointer data)
 void
 recode_different_dialog (GObject *o, gpointer data)
 {
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   recode_dialog (de, TRUE);
 }
@@ -836,34 +836,33 @@ set_acr (struct recode_dialog *rd)
 }
 
 static void
-recode_dialog (struct data_editor *de, gboolean diff)
+recode_dialog (PsppireDataWindow *de, gboolean diff)
 {
   gint response;
 
   struct recode_dialog rd;
 
-  GladeXML *xml = XML_NEW ("recode.glade");
-
+  GtkBuilder *builder = builder_new ("recode.ui");
 
-  GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
+  GtkWidget *selector = get_widget_assert (builder, "psppire-selector1");
 
-  GtkWidget *oldandnew = get_widget_assert (xml, "button1");
+  GtkWidget *oldandnew = get_widget_assert (builder, "button1");
 
 
-  GtkWidget *output_variable_box = get_widget_assert (xml,"frame4");
+  GtkWidget *output_variable_box = get_widget_assert (builder,"frame4");
 
 
   PsppireVarStore *vs = NULL;
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  rd.change_button = get_widget_assert (xml, "change-button");
+  rd.change_button = get_widget_assert (builder, "change-button");
 
-  rd.dialog = get_widget_assert   (xml, "recode-dialog");
-  rd.dict_treeview = get_widget_assert (xml, "treeview1");
-  rd.variable_treeview =   get_widget_assert (xml, "treeview2");
-  rd.new_name_entry = get_widget_assert (xml, "dest-name-entry");
-  rd.new_label_entry = get_widget_assert (xml, "dest-label-entry");
+  rd.dialog = get_widget_assert   (builder, "recode-dialog");
+  rd.dict_treeview = get_widget_assert (builder, "treeview1");
+  rd.variable_treeview =   get_widget_assert (builder, "treeview2");
+  rd.new_name_entry = get_widget_assert (builder, "dest-name-entry");
+  rd.new_label_entry = get_widget_assert (builder, "dest-label-entry");
 
   rd.dict = vs->dict;
 
@@ -883,13 +882,10 @@ recode_dialog (struct data_editor *de, gboolean diff)
 
   rd.different = diff;
 
-  gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), de->parent.window);
-
+  gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (rd.dict_treeview),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
 
+  g_object_set (rd.dict_treeview, "dictionary", vs->dict, NULL);
 
   if ( ! rd.different )
     {
@@ -963,40 +959,40 @@ recode_dialog (struct data_editor *de, gboolean diff)
 
   /* Set up the Old & New Values subdialog */
   {
-    rd.string_button = get_widget_assert (xml, "checkbutton1");
-    rd.width_entry   = get_widget_assert (xml, "spinbutton1");
+    rd.string_button = get_widget_assert (builder, "checkbutton1");
+    rd.width_entry   = get_widget_assert (builder, "spinbutton1");
 
-    rd.convert_button           = get_widget_assert (xml, "checkbutton2");
+    rd.convert_button           = get_widget_assert (builder, "checkbutton2");
 
-    rd.ov_range_lower_entry = get_widget_assert (xml, "entry5");
-    rd.ov_range_upper_entry  = get_widget_assert (xml, "entry3");
-    rd.ov_low_up_entry       = get_widget_assert (xml, "entry6");
-    rd.ov_high_down_entry    = get_widget_assert (xml, "entry7");
+    rd.ov_range_lower_entry = get_widget_assert (builder, "entry5");
+    rd.ov_range_upper_entry  = get_widget_assert (builder, "entry3");
+    rd.ov_low_up_entry       = get_widget_assert (builder, "entry6");
+    rd.ov_high_down_entry    = get_widget_assert (builder, "entry7");
 
-    rd.new_value_entry = get_widget_assert (xml, "entry1");
-    rd.ov_value_entry  = get_widget_assert (xml, "entry2");
+    rd.new_value_entry = get_widget_assert (builder, "entry1");
+    rd.ov_value_entry  = get_widget_assert (builder, "entry2");
 
-    rd.toggle[BUTTON_NEW_VALUE]  = get_widget_assert (xml, "radiobutton1");
-    rd.toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (xml, "radiobutton2");
-    rd.toggle[BUTTON_NEW_COPY]   = get_widget_assert (xml, "radiobutton3");
-    rd.toggle[BUTTON_OLD_VALUE]  = get_widget_assert (xml, "radiobutton4");
-    rd.toggle[BUTTON_OLD_SYSMIS] = get_widget_assert (xml, "radiobutton6");
-    rd.toggle[BUTTON_OLD_MISSING]= get_widget_assert (xml, "radiobutton7");
-    rd.toggle[BUTTON_OLD_RANGE]  = get_widget_assert (xml, "radiobutton8");
-    rd.toggle[BUTTON_OLD_LOW_UP] = get_widget_assert (xml, "radiobutton10");
-    rd.toggle[BUTTON_OLD_HIGH_DOWN] = get_widget_assert (xml, "radiobutton5");
-    rd.toggle[BUTTON_OLD_ELSE]   = get_widget_assert (xml, "radiobutton11");
+    rd.toggle[BUTTON_NEW_VALUE]  = get_widget_assert (builder, "radiobutton1");
+    rd.toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (builder, "radiobutton2");
+    rd.toggle[BUTTON_NEW_COPY]   = get_widget_assert (builder, "radiobutton3");
+    rd.toggle[BUTTON_OLD_VALUE]  = get_widget_assert (builder, "radiobutton4");
+    rd.toggle[BUTTON_OLD_SYSMIS] = get_widget_assert (builder, "radiobutton6");
+    rd.toggle[BUTTON_OLD_MISSING]= get_widget_assert (builder, "radiobutton7");
+    rd.toggle[BUTTON_OLD_RANGE]  = get_widget_assert (builder, "radiobutton8");
+    rd.toggle[BUTTON_OLD_LOW_UP] = get_widget_assert (builder, "radiobutton10");
+    rd.toggle[BUTTON_OLD_HIGH_DOWN] = get_widget_assert (builder, "radiobutton5");
+    rd.toggle[BUTTON_OLD_ELSE]   = get_widget_assert (builder, "radiobutton11");
 
-    rd.new_copy_label = get_widget_assert (xml, "label3");
-    rd.strings_box    = get_widget_assert (xml, "table3");
+    rd.new_copy_label = get_widget_assert (builder, "label3");
+    rd.strings_box    = get_widget_assert (builder, "table3");
 
     rd.old_and_new_dialog =
-      PSPPIRE_DIALOG (get_widget_assert (xml, "old-new-values-dialog"));
+      PSPPIRE_DIALOG (get_widget_assert (builder, "old-new-values-dialog"));
 
     gtk_window_set_transient_for (GTK_WINDOW (rd.old_and_new_dialog),
-                                 de->parent.window);
+                                 GTK_WINDOW (de));
 
-    rd.acr = PSPPIRE_ACR (get_widget_assert (xml, "psppire-acr1"));
+    rd.acr = PSPPIRE_ACR (get_widget_assert (builder, "psppire-acr1"));
 
     g_signal_connect_swapped (rd.toggle[BUTTON_NEW_VALUE], "toggled",
                      G_CALLBACK (set_acr), &rd);
@@ -1047,11 +1043,11 @@ recode_dialog (struct data_editor *de, gboolean diff)
 
     g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
                      G_CALLBACK (toggle_sensitivity),
-                     get_widget_assert (xml, "entry3"));
+                     get_widget_assert (builder, "entry3"));
 
     g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
                      G_CALLBACK (toggle_sensitivity),
-                     get_widget_assert (xml, "entry5"));
+                     get_widget_assert (builder, "entry5"));
 
     g_signal_connect (rd.toggle[BUTTON_OLD_LOW_UP], "toggled",
                      G_CALLBACK (toggle_sensitivity), rd.ov_low_up_entry);
@@ -1085,6 +1081,7 @@ recode_dialog (struct data_editor *de, gboolean diff)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&rd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -1094,11 +1091,7 @@ recode_dialog (struct data_editor *de, gboolean diff)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&rd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
@@ -1111,7 +1104,7 @@ recode_dialog (struct data_editor *de, gboolean diff)
   gtk_list_store_clear (GTK_LIST_STORE (rd.value_map));
   g_object_unref (rd.value_map);
 
-  g_object_unref (xml);
+  g_object_unref (builder);
 }
 
 /* Initialise VAL to reflect the current status of RD */
index a3b795e6960842c92ed8d5c126669332ad4eed70..6edcc0ab719f22c9ac753d72f89de85f3c3cc924 100644 (file)
             <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
             <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
             <child>
-              <widget class="GtkTreeView" id="treeview1">
+              <widget class="PsppireDictView" id="treeview1">
                 <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>
index 4ee11fe9982ffd9a55ea4e1091aa7d9db081f795..b42dffe50b06b1583a91f38766aeafcb3bc36880 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -214,7 +214,7 @@ dialog_state_valid (gpointer data)
 
   GtkTreeIter notused;
 
-  return (gtk_tree_model_get_iter_first (dep_vars, &notused) 
+  return (gtk_tree_model_get_iter_first (dep_vars, &notused)
     && gtk_tree_model_get_iter_first (indep_vars, &notused));
 }
 
@@ -223,11 +223,10 @@ void
 regression_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
-
   struct regression_dialog rd;
 
-  GladeXML *xml = XML_NEW ("regression.glade");
+  GtkBuilder *xml = builder_new ("regression.ui");
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
   PsppireVarStore *vs;
 
   GtkWidget *dialog = get_widget_assert   (xml, "regression-dialog");
@@ -251,11 +250,9 @@ regression_dialog (GObject *o, gpointer data)
                                  stats
                                  );
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (source, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (dest_dep), vs->dict);
   set_dest_model (GTK_TREE_VIEW (dest_indep), vs->dict);
@@ -286,8 +283,8 @@ regression_dialog (GObject *o, gpointer data)
   rd.current_opts.pred = FALSE;
   rd.current_opts.resid = FALSE;
 
-  gtk_window_set_transient_for (GTK_WINDOW (rd.save_dialog), de->parent.window);
-  gtk_window_set_transient_for (GTK_WINDOW (rd.stat_dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (rd.save_dialog), GTK_WINDOW (de));
+  gtk_window_set_transient_for (GTK_WINDOW (rd.stat_dialog), GTK_WINDOW (de));
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &rd);
 
@@ -307,6 +304,7 @@ regression_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&rd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -316,11 +314,7 @@ regression_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&rd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index ca79ba350c9f34f89ac155f37b368656f1212a43..d34a61c9f059a496999d873d85d2b9ce8e057383 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void regression_dialog (GObject *o, gpointer data);
 
index 3ee4973b32418352015bac1c68a801931d44c8f5..e6bac4d8e8979e4157743ec538c18d37e2334161 100644 (file)
@@ -95,7 +95,7 @@
                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="dict-view">
+                  <widget class="PsppireDictView" id="dict-view">
                     <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>
diff --git a/src/ui/gui/reliability-dialog.c b/src/ui/gui/reliability-dialog.c
new file mode 100644 (file)
index 0000000..4d01573
--- /dev/null
@@ -0,0 +1,228 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 "dialog-common.h"
+#include <language/syntax-string-source.h>
+#include "reliability-dialog.h"
+#include "psppire-selector.h"
+#include "psppire-dictview.h"
+#include "psppire-dialog.h"
+
+#include "psppire-data-window.h"
+
+#include "helper.h"
+
+#include <gtk/gtk.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+struct reliability
+{
+  PsppireDict *dict;
+  GtkWidget *model_combo;
+  GtkWidget *variables;
+  GtkWidget *split_point_hbox;
+  GtkWidget *split_spinbutton;
+};
+
+
+static char * generate_syntax (const struct reliability *rd);
+
+
+static void
+on_vars_changed (struct reliability *rd)
+{
+  GtkTreeModel *tm =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variables));
+
+  gint n_vars = gtk_tree_model_iter_n_children (tm, NULL);
+
+  gint current_value =
+    gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (rd->split_spinbutton));
+
+  gint new_value = current_value;
+
+  gtk_spin_button_set_range (GTK_SPIN_BUTTON (rd->split_spinbutton),
+                            0, n_vars - 1);
+
+  if ( current_value > n_vars - 1)
+    new_value = n_vars - 1;
+
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (rd->split_spinbutton),
+                            new_value);
+}
+
+static void
+on_method_change (struct reliability *rd)
+{
+  gtk_widget_set_sensitive (rd->split_point_hbox,
+                           ( 1 == gtk_combo_box_get_active (GTK_COMBO_BOX (rd->model_combo))));
+
+}
+
+static void
+refresh (PsppireDialog *dialog, struct reliability *rd)
+{
+  GtkTreeModel *liststore =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variables));
+  gtk_list_store_clear (GTK_LIST_STORE (liststore));
+
+  gtk_combo_box_set_active (GTK_COMBO_BOX (rd->model_combo), 0);
+
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (rd->split_spinbutton), 0);
+
+  gtk_spin_button_set_range (GTK_SPIN_BUTTON (rd->split_spinbutton),
+                            0, 0);
+}
+
+
+static gboolean
+dialog_state_valid (gpointer data)
+{
+  struct reliability *rd = data;
+
+  GtkTreeModel *liststore =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variables));
+
+  return (2 <= gtk_tree_model_iter_n_children (liststore, NULL));
+}
+
+
+/* Pops up the Reliability dialog box */
+void
+reliability_dialog (GObject *o, gpointer data)
+{
+  struct reliability rd;
+  gint response;
+
+  GtkBuilder *xml = builder_new ("reliability.ui");
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+  PsppireVarStore *vs;
+
+  GtkWidget *dialog = get_widget_assert   (xml, "reliability-dialog");
+  GtkWidget *source = get_widget_assert   (xml, "dict-view");
+
+  GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
+
+  rd.split_point_hbox = get_widget_assert (xml, "split-point-hbox");
+
+  rd.variables = get_widget_assert   (xml, "treeview2");
+
+  rd.model_combo = get_widget_assert   (xml, "combobox1");
+  rd.split_spinbutton = get_widget_assert (xml, "spinbutton1");
+
+  g_signal_connect_swapped (rd.model_combo, "changed",
+                           G_CALLBACK (on_method_change), &rd);
+
+  g_object_get (de->data_editor, "var-store", &vs, NULL);
+
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
+
+  g_object_set (source, "dictionary", vs->dict, NULL);
+
+  rd.dict = vs->dict;
+
+  set_dest_model (GTK_TREE_VIEW (rd.variables), vs->dict);
+
+  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
+                                source,
+                                rd.variables,
+                                insert_source_row_into_tree_view,
+                                NULL,
+                                NULL);
+
+  {
+    GtkTreeModel *tm =
+      gtk_tree_view_get_model (GTK_TREE_VIEW (rd.variables));
+
+
+    g_signal_connect_swapped (tm, "row-inserted",
+                     G_CALLBACK (on_vars_changed), &rd);
+
+    g_signal_connect_swapped (tm, "row-deleted",
+                     G_CALLBACK (on_vars_changed), &rd);
+  }
+
+  g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &rd);
+
+  psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog),
+                                     dialog_state_valid, &rd);
+
+  response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
+
+
+  switch (response)
+    {
+    case GTK_RESPONSE_OK:
+      {
+       gchar *syntax = generate_syntax (&rd);
+
+       struct getl_interface *sss = create_syntax_string_source (syntax);
+       execute_syntax (sss);
+
+       g_free (syntax);
+      }
+      break;
+    case PSPPIRE_RESPONSE_PASTE:
+      {
+       gchar *syntax = generate_syntax (&rd);
+        paste_syntax_in_new_window (syntax);
+
+       g_free (syntax);
+      }
+      break;
+    default:
+      break;
+    }
+
+  g_object_unref (xml);
+}
+
+
+\f
+
+static char *
+generate_syntax (const struct reliability *rd)
+{
+  gchar *text;
+  GString *string = g_string_new ("RELIABILITY");
+
+  g_string_append (string, "\n\t/VARIABLES=");
+  append_variable_names (string, rd->dict, GTK_TREE_VIEW (rd->variables), 0);
+
+
+  g_string_append (string, "\n\t/MODEL=");
+
+  if ( 0 == gtk_combo_box_get_active (GTK_COMBO_BOX (rd->model_combo)))
+    g_string_append (string, "ALPHA");
+  else
+    g_string_append_printf (string, "SPLIT (%d)",
+                           gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (rd->split_spinbutton))
+                           );
+
+  g_string_append (string, ".\n");
+
+  text = string->str;
+
+  g_string_free (string, FALSE);
+
+  return text;
+}
diff --git a/src/ui/gui/reliability-dialog.h b/src/ui/gui/reliability-dialog.h
new file mode 100644 (file)
index 0000000..5e8bfbf
--- /dev/null
@@ -0,0 +1,25 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 __RELIABILITY_DIALOG_H
+#define __RELIABILITY_DIALOG_H
+
+
+#include <gtk/gtk.h>
+
+void reliability_dialog (GObject *o, gpointer data);
+
+#endif
diff --git a/src/ui/gui/reliability.glade b/src/ui/gui/reliability.glade
new file mode 100644 (file)
index 0000000..fd10686
--- /dev/null
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Fri Apr  3 12:11:45 2009 -->
+<glade-interface>
+  <requires lib="psppire"/>
+  <widget class="PsppireDialog" id="reliability-dialog">
+    <property name="title" translatable="yes">Reliability Analysis</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <widget class="GtkHBox" id="dialog-hbox1">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="spacing">12</property>
+            <child>
+              <widget class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <child>
+                  <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                    <child>
+                      <widget class="PsppireDictView" id="dict-view">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="has_tooltip">True</property>
+                        <property name="border_width">5</property>
+                        <property name="headers_visible">False</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="xscale">0</property>
+                    <property name="yscale">0</property>
+                    <child>
+                      <widget class="PsppireSelector" id="psppire-selector1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="no_show_all">True</property>
+                        <property name="border_width">5</property>
+                        <property name="response_id">0</property>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame1">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment1">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkScrolledWindow" id="scrolledwindow2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                            <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                            <child>
+                              <widget class="GtkTreeView" id="treeview2">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="headers_visible">False</property>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">_Items:</property>
+                        <property name="use_markup">True</property>
+                        <property name="use_underline">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkHBox" id="hbox2">
+                <property name="visible">True</property>
+                <child>
+                  <widget class="GtkLabel" id="">
+                    <property name="visible">True</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">Model:   </property>
+                    <property name="justify">GTK_JUSTIFY_RIGHT</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkComboBox" id="combobox1">
+                    <property name="visible">True</property>
+                    <property name="items" translatable="yes">Alpha
+Split</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkHBox" id="split-point-hbox">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="spacing">5</property>
+                <child>
+                  <widget class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Variables in first split:</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkSpinButton" id="spinbutton1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="adjustment">0 0 100 1 10 10</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+        </child>
+        <child>
+          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
index f241c79d3558bcda1ec38f294f76f7a81373131d..3c36f4ededbfa10c201e959af1d235b017d43683 100644 (file)
 
 #include "select-cases-dialog.h"
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include "helper.h"
 #include "psppire-dialog.h"
-#include "data-editor.h"
-#include "dialog-common.h"
+#include "psppire-data-window.h"
+#include "psppire-selector.h"
 #include "dict-display.h"
+#include "dialog-common.h"
 #include "widget-io.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 
 #include <gettext.h>
@@ -42,7 +42,7 @@
 struct select_cases_dialog
 {
   /* The XML that created the dialog */
-  GladeXML *xml;
+  GtkBuilder *xml;
 
   GtkWidget *spinbutton ;
   GtkWidget *spinbutton1 ;
@@ -110,7 +110,7 @@ sample_subdialog (GtkButton *b, gpointer data)
       gtk_table_attach_defaults (GTK_TABLE (table),
                                 scd->hbox1, 1, 2, 0, 1);
 
-      g_signal_connect (G_OBJECT (percent), "toggled",
+      g_signal_connect (percent, "toggled",
                        G_CALLBACK (set_sensitivity_from_toggle), scd->hbox1);
 
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (percent), TRUE);
@@ -134,7 +134,7 @@ sample_subdialog (GtkButton *b, gpointer data)
       gtk_table_attach_defaults (GTK_TABLE (table),
                                 scd->hbox2, 1, 2, 1, 2);
 
-      g_signal_connect (G_OBJECT (sample_n_cases), "toggled",
+      g_signal_connect (sample_n_cases, "toggled",
                        G_CALLBACK (set_sensitivity_from_toggle), scd->hbox2);
 
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_n_cases), FALSE);
@@ -241,13 +241,13 @@ select_cases_dialog (GObject *o, gpointer data)
   gint response;
   struct select_cases_dialog scd = {0,0,0,0,0,0};
   GtkWidget *dialog   ;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
   GtkWidget *entry = NULL;
   GtkWidget *selector ;
   GtkWidget *button_range;
   GtkWidget *button_sample;
 
-  scd.xml = XML_NEW ("psppire.glade");
+  scd.xml = builder_new ("psppire.ui");
 
   g_object_get (de->data_editor, "data-store", &scd.data_store, NULL);
 
@@ -320,14 +320,15 @@ select_cases_dialog (GObject *o, gpointer data)
 
 
   dialog = get_widget_assert (scd.xml, "select-cases-dialog");
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   {
     GtkWidget *source = get_widget_assert   (scd.xml, "select-cases-treeview");
 
-    attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                  scd.data_store->dict,
-                                  GTK_SELECTION_SINGLE, NULL);
+    g_object_set (source, "dictionary",
+                 scd.data_store->dict,
+                 "selection-mode",
+                 GTK_SELECTION_SINGLE, NULL);
 
     psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
                                   source,
@@ -354,6 +355,7 @@ select_cases_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&scd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -363,11 +365,7 @@ select_cases_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&scd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index 533992e0c5d191ecc8ae953de08e2beb698e77f3..b585b096fc43c966a342b8b692be714d1939894d 100644 (file)
@@ -19,7 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 
 void select_cases_dialog (GObject *o, gpointer data);
diff --git a/src/ui/gui/sheet/automake.mk b/src/ui/gui/sheet/automake.mk
new file mode 100644 (file)
index 0000000..b95de45
--- /dev/null
@@ -0,0 +1,12 @@
+## Process this file with automake to produce Makefile.in  -*- makefile -*-
+
+noinst_LTLIBRARIES += src/ui/gui/sheet/libsheet.la
+
+src_ui_gui_sheet_libsheet_la_CFLAGS = $(GTK_CFLAGS)
+
+src_ui_gui_sheet_libsheet_la_SOURCES = \
+       src/ui/gui/sheet/psppire-axis.c \
+       src/ui/gui/sheet/psppire-axis.h \
+       src/ui/gui/sheet/psppire-sheetmodel.c \
+       src/ui/gui/sheet/psppire-sheetmodel.h
+
diff --git a/src/ui/gui/sheet/psppire-axis.c b/src/ui/gui/sheet/psppire-axis.c
new file mode 100644 (file)
index 0000000..e229c94
--- /dev/null
@@ -0,0 +1,634 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   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
+   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 <string.h>
+#include <stdlib.h>
+
+#include <ui/gui/psppire-marshal.h>
+#include <libpspp/tower.h>
+#include <libpspp/pool.h>
+#include "psppire-axis.h"
+#include <math.h>
+#include <libpspp/misc.h>
+
+
+/* Signals */
+enum
+  {
+    RESIZE_UNIT,
+    n_signals
+  };
+
+static guint signals[n_signals] ;
+
+/* --- prototypes --- */
+static void psppire_axis_class_init (PsppireAxisClass  *class);
+static void psppire_axis_init  (PsppireAxis            *axis);
+static void psppire_axis_finalize   (GObject           *object);
+
+
+/* --- variables --- */
+static GObjectClass     *parent_class = NULL;
+
+
+struct axis_node
+{
+  struct tower_node pixel_node;
+  struct tower_node unit_node;
+};
+
+void
+psppire_axis_dump (const PsppireAxis *a)
+{
+  struct tower_node *n = tower_first (&a->unit_tower);
+
+  g_debug ("Axis %p", a);
+  while (n)
+    {
+      const struct axis_node *an = tower_data (n, struct axis_node, unit_node);
+      const struct tower_node *pn = &an->pixel_node;
+      g_debug ("%ld units of height %g",
+              n->size, pn->size / (gdouble) n->size);
+
+      n =  tower_next (&a->unit_tower, n);
+    }
+  g_debug ("\n");
+}
+
+/* Increment the size of every unit by INC.
+   Note that INC is signed. So if INC is negative,
+   then size will end up smaller.
+*/
+static void
+axis_increment (PsppireAxis *axis, gint inc)
+{
+  struct tower_node *n = tower_first (&axis->pixel_tower);
+
+  while (n)
+    {
+      struct axis_node *an = tower_data (n, struct axis_node, pixel_node);
+      struct tower_node *pn = &an->pixel_node;
+      const gint existing_size = tower_node_get_size (pn);
+
+      tower_resize (&axis->pixel_tower, pn, existing_size + inc *
+                   tower_node_get_size (&an->unit_node));
+
+      n = tower_next (&axis->pixel_tower, n);
+    }
+}
+
+
+/* Return the unit covered by PIXEL */
+gint
+psppire_axis_unit_at_pixel (const PsppireAxis *a, glong pixel)
+{
+  unsigned long int start;
+  struct tower_node *n;
+  struct axis_node *an;
+  gdouble fraction;
+
+  glong size = tower_height (&a->pixel_tower);
+
+  g_return_val_if_fail (pixel >= 0, -1);
+
+  if (pixel >= size)
+    {
+      gint n_items = tower_height (&a->unit_tower);
+      glong extra = pixel - size;
+
+      return n_items - 1 + DIV_RND_UP (extra,  a->default_size);
+    }
+
+
+  n = tower_lookup (&a->pixel_tower, pixel, &start);
+  an = tower_data (n, struct axis_node, pixel_node);
+
+  fraction = (pixel - start) / (gdouble) tower_node_get_size (&an->pixel_node);
+
+  return  tower_node_get_level (&an->unit_node)
+    + fraction * tower_node_get_size (&an->unit_node);
+}
+
+
+gint
+psppire_axis_unit_count (const PsppireAxis *a)
+{
+  glong filler = 0;
+  glong actual_size;
+
+  actual_size = tower_height (&a->pixel_tower);
+
+  if ( actual_size < a->min_extent )
+    filler = DIV_RND_UP (a->min_extent - actual_size, a->default_size);
+
+  return tower_height (&a->unit_tower) + filler;
+}
+
+
+/* Return the starting pixel of UNIT */
+glong
+psppire_axis_start_pixel (const PsppireAxis *a, gint unit)
+{
+  gdouble fraction;
+  struct tower_node *n ;
+  struct axis_node *an;
+
+  unsigned long int start;
+
+  gint the_count, size ;
+
+  the_count =  tower_height (&a->unit_tower);
+  size = tower_height (&a->pixel_tower);
+
+  if ( unit >= the_count)
+    {
+      return  size + (unit - the_count) * a->default_size;
+    }
+
+  if ( unit < 0)
+    return -1;
+
+  if ( unit >= tower_height (&a->unit_tower))
+    return -1;
+
+  n = tower_lookup (&a->unit_tower, unit, &start);
+
+  an = tower_data (n, struct axis_node, unit_node);
+
+  fraction = (unit - start) / (gdouble) tower_node_get_size (&an->unit_node);
+
+  return  tower_node_get_level (&an->pixel_node) +
+    nearbyint (fraction * tower_node_get_size (&an->pixel_node));
+}
+
+gint
+psppire_axis_unit_size (const PsppireAxis *axis, gint unit)
+{
+  struct tower_node *n ;
+  struct axis_node *an;
+
+  unsigned long int start;
+
+  if  (unit >= tower_height (&axis->unit_tower))
+    return axis->default_size;
+
+  if ( unit < 0)
+    return 0;
+
+  if ( unit >= tower_height (&axis->unit_tower))
+    return 0;
+
+  n = tower_lookup (&axis->unit_tower, unit, &start);
+
+  an = tower_data (n, struct axis_node, unit_node);
+
+  return nearbyint (tower_node_get_size (&an->pixel_node)
+                    / (gdouble) tower_node_get_size (&an->unit_node));
+}
+
+
+
+/* --- functions --- */
+/**
+ * psppire_axis_get_type:
+ * @returns: the type ID for accelerator groups.
+ */
+GType
+psppire_axis_get_type (void)
+{
+  static GType object_type = 0;
+
+  if (!object_type)
+    {
+      static const GTypeInfo object_info = {
+       sizeof (PsppireAxisClass),
+       (GBaseInitFunc) NULL,
+       (GBaseFinalizeFunc) NULL,
+       (GClassInitFunc) psppire_axis_class_init,
+       NULL,   /* class_finalize */
+       NULL,   /* class_data */
+       sizeof (PsppireAxis),
+       0,      /* n_preallocs */
+       (GInstanceInitFunc) psppire_axis_init,
+      };
+
+      object_type = g_type_register_static (G_TYPE_OBJECT,
+                                           "PsppireAxis",
+                                           &object_info, 0);
+
+    }
+
+  return object_type;
+}
+
+enum
+  {
+    PROP_0,
+    PROP_MIN_EXTENT,
+    PROP_DEFAULT_SIZE,
+    PROP_PADDING
+  };
+
+
+static void
+psppire_axis_get_property (GObject         *object,
+                          guint            prop_id,
+                          GValue          *value,
+                          GParamSpec      *pspec)
+{
+  PsppireAxis *axis = PSPPIRE_AXIS (object);
+
+  switch (prop_id)
+    {
+    case PROP_PADDING:
+      g_value_set_int (value, axis->padding);
+      break;
+    case PROP_MIN_EXTENT:
+      g_value_set_long (value, axis->min_extent);
+      break;
+    case PROP_DEFAULT_SIZE:
+      g_value_set_int (value, axis->default_size);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+static void
+psppire_axis_set_property (GObject         *object,
+                          guint            prop_id,
+                          const GValue    *value,
+                          GParamSpec      *pspec)
+{
+  PsppireAxis *axis = PSPPIRE_AXIS (object);
+
+  switch (prop_id)
+    {
+    case PROP_PADDING:
+      {
+       const gint old_value = axis->padding;
+       axis->padding = g_value_get_int (value);
+       axis_increment (axis, axis->padding - old_value);
+      }
+      break;
+    case PROP_MIN_EXTENT:
+      axis->min_extent = g_value_get_long (value);
+      break;
+    case PROP_DEFAULT_SIZE:
+      axis->default_size = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+static void
+psppire_axis_class_init (PsppireAxisClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  GParamSpec *padding_spec;
+  GParamSpec *min_extent_spec;
+  GParamSpec *default_size_spec;
+
+  parent_class = g_type_class_peek_parent (class);
+
+  object_class->set_property = psppire_axis_set_property;
+  object_class->get_property = psppire_axis_get_property;
+
+
+  min_extent_spec =
+    g_param_spec_long ("minimum-extent",
+                      "Minimum Extent",
+                      "The smallest extent to which the axis will provide units (typically set to the height/width of the associated widget).",
+                      0, G_MAXLONG,
+                      0,
+                      G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
+
+  g_object_class_install_property (object_class,
+                                   PROP_MIN_EXTENT,
+                                   min_extent_spec);
+
+
+  default_size_spec =
+    g_param_spec_int ("default-size",
+                     "Default Size",
+                     "The size given to units which haven't been explicity inserted",
+                     0, G_MAXINT,
+                     25,
+                     G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
+
+
+  g_object_class_install_property (object_class,
+                                   PROP_DEFAULT_SIZE,
+                                   default_size_spec);
+
+  padding_spec =
+    g_param_spec_int ("padding",
+                     "Padding",
+                     "Extra space implicitly added to each unit",
+                     0, G_MAXINT,
+                     0,
+                     G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
+
+
+  g_object_class_install_property (object_class,
+                                   PROP_PADDING,
+                                   padding_spec);
+
+
+
+  signals[RESIZE_UNIT] =
+    g_signal_new ("resize-unit",
+                 G_TYPE_FROM_CLASS (object_class),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 psppire_marshal_VOID__INT_LONG,
+                 G_TYPE_NONE,
+                 2,
+                 G_TYPE_INT,
+                 G_TYPE_LONG
+                 );
+
+
+  object_class->finalize = psppire_axis_finalize;
+}
+
+
+static void
+psppire_axis_init (PsppireAxis *axis)
+{
+  tower_init (&axis->pixel_tower);
+  tower_init (&axis->unit_tower);
+
+  axis->pool = pool_create ();
+  axis->padding = 0;
+}
+
+
+static void
+psppire_axis_finalize (GObject *object)
+{
+  PsppireAxis *a = PSPPIRE_AXIS (object);
+  pool_destroy (a->pool);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * psppire_axis_new:
+ * @returns: a new #PsppireAxis object
+ *
+ * Creates a new #PsppireAxis.
+ */
+PsppireAxis*
+psppire_axis_new (void)
+{
+  return g_object_new (G_TYPE_PSPPIRE_AXIS, NULL);
+}
+
+
+\f
+
+void
+psppire_axis_append (PsppireAxis *a, gint size)
+{
+  psppire_axis_append_n (a, 1, size);
+}
+
+
+/* Append N_UNITS of size SIZE to A.
+   The value of the "padding" property will be added to SIZE
+   unit before appending.
+*/
+void
+psppire_axis_append_n (PsppireAxis *a, gint n_units, gint size)
+{
+  struct axis_node *node;
+
+  if  (n_units == 0)
+    return;
+
+  node = pool_malloc (a->pool, sizeof *node);
+
+  tower_insert (&a->unit_tower, n_units, &node->unit_node, NULL);
+  tower_insert (&a->pixel_tower, (size + a->padding) * n_units,
+    &node->pixel_node, NULL);
+}
+
+
+/* Split the node of both towers at POSN */
+static void
+split (PsppireAxis *a, gint posn)
+{
+  unsigned long int existing_unit_size;
+  unsigned long int existing_pixel_size;
+  unsigned long int start;
+  gdouble fraction;
+  struct axis_node *new_node ;
+  struct tower_node *n;
+  struct axis_node *existing_node;
+
+  g_return_if_fail (posn <= tower_height (&a->unit_tower));
+
+  /* Nothing needs to be done */
+  if ( posn == 0 || posn  == tower_height (&a->unit_tower))
+    return;
+
+  n = tower_lookup (&a->unit_tower, posn, &start);
+
+  existing_node = tower_data (n, struct axis_node, unit_node);
+
+  /* Nothing needs to be done, if the range element is already split here */
+  if ( posn - start == 0)
+    return;
+
+  existing_unit_size = tower_node_get_size (&existing_node->unit_node);
+  existing_pixel_size = tower_node_get_size (&existing_node->pixel_node);
+
+  fraction = (posn - start) / (gdouble) existing_unit_size;
+
+  new_node = pool_malloc (a->pool, sizeof (*new_node));
+
+  tower_resize (&a->unit_tower, &existing_node->unit_node, posn - start);
+
+  tower_resize (&a->pixel_tower, &existing_node->pixel_node,
+               nearbyintf (fraction * existing_pixel_size));
+
+  tower_insert (&a->unit_tower,
+               existing_unit_size - (posn - start),
+               &new_node->unit_node,
+               tower_next (&a->unit_tower, &existing_node->unit_node));
+
+
+  tower_insert (&a->pixel_tower,
+               nearbyintf (existing_pixel_size * (1 - fraction)),
+               &new_node->pixel_node,
+               tower_next (&a->pixel_tower, &existing_node->pixel_node));
+}
+
+
+/* Insert a new unit of size SIZE before POSN.
+   The value of the "padding" property will be added to SIZE before
+   the unit is inserted.
+ */
+void
+psppire_axis_insert (PsppireAxis *a, gint posn, gint size)
+{
+  struct axis_node *before = NULL;
+  struct axis_node *new_node;
+
+  g_return_if_fail ( posn >= 0);
+  g_return_if_fail ( posn <= tower_height (&a->unit_tower));
+
+  if ( posn < tower_height (&a->unit_tower))
+    {
+      unsigned long int start = 0;
+      struct tower_node *n;
+
+      split (a, posn);
+
+      n = tower_lookup (&a->unit_tower, posn, &start);
+      g_assert (posn == start);
+
+      before = tower_data (n, struct axis_node, unit_node);
+    }
+
+  new_node = pool_malloc (a->pool, sizeof (*new_node));
+
+  tower_insert (&a->unit_tower,
+               1,
+               &new_node->unit_node,
+               before ? &before->unit_node : NULL);
+
+  tower_insert (&a->pixel_tower,
+               size + a->padding,
+               &new_node->pixel_node,
+               before ? &before->pixel_node : NULL);
+}
+
+
+/* Make the element at POSN singular.
+   Return a pointer to the node for this element */
+static struct axis_node *
+make_single (PsppireAxis *a, gint posn)
+{
+  unsigned long int start;
+  struct tower_node *n;
+
+  g_return_val_if_fail (posn < tower_height (&a->unit_tower), NULL);
+
+  n = tower_lookup (&a->unit_tower, posn, &start);
+
+  if ( 1 != tower_node_get_size (n))
+    {
+      split (a, posn + 1);
+      n = tower_lookup (&a->unit_tower, posn, &start);
+
+      if ( 1 != tower_node_get_size (n))
+       {
+         split (a, posn);
+         n = tower_lookup (&a->unit_tower, posn, &start);
+       }
+    }
+
+  g_assert (1 == tower_node_get_size (n));
+
+  return tower_data (n, struct axis_node, unit_node);
+}
+
+
+/*
+  Set the size of the unit at POSN to be SIZE plus the
+  current value of "padding"
+ */
+void
+psppire_axis_resize (PsppireAxis *axis, gint posn, glong size)
+{
+  struct axis_node *an;
+  g_return_if_fail (posn >= 0);
+  g_return_if_fail (size > 0);
+
+  /* Silently ignore this request if the position is greater than the number of
+     units in the axis */
+  if (posn >= tower_height (&axis->unit_tower))
+    return ;
+
+  an = make_single (axis, posn);
+
+  tower_resize (&axis->pixel_tower, &an->pixel_node, size + axis->padding);
+
+  g_signal_emit (axis, signals[RESIZE_UNIT], 0, posn, size + axis->padding);
+}
+
+
+
+
+
+
+void
+psppire_axis_clear (PsppireAxis *a)
+{
+  pool_destroy (a->pool);
+  a->pool = pool_create ();
+
+  tower_init (&a->pixel_tower);
+  tower_init (&a->unit_tower);
+}
+
+
+
+void
+psppire_axis_delete (PsppireAxis *a, gint first, gint n_units)
+{
+  gint units_to_delete = n_units;
+  unsigned long int start;
+  struct tower_node *unit_node ;
+  g_return_if_fail (first + n_units <= tower_height (&a->unit_tower));
+
+  split (a, first);
+  split (a, first + n_units);
+
+  unit_node = tower_lookup (&a->unit_tower, first, &start);
+  g_assert (start == first);
+
+  while (units_to_delete > 0)
+    {
+      struct tower_node *next_unit_node;
+      struct axis_node *an = tower_data (unit_node,
+                                        struct axis_node, unit_node);
+
+      g_assert (unit_node == &an->unit_node);
+      g_assert (unit_node->size <= n_units);
+
+      units_to_delete -= unit_node->size;
+
+      next_unit_node = tower_next (&a->unit_tower, unit_node);
+
+      tower_delete (&a->unit_tower, unit_node);
+      tower_delete (&a->pixel_tower, &an->pixel_node);
+
+      pool_free (a->pool, an);
+
+      unit_node = next_unit_node;
+    }
+}
diff --git a/src/ui/gui/sheet/psppire-axis.h b/src/ui/gui/sheet/psppire-axis.h
new file mode 100644 (file)
index 0000000..50a6a39
--- /dev/null
@@ -0,0 +1,92 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   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
+   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 PSPPIRE_AXIS_H__
+#define PSPPIRE_AXIS_H__
+
+
+#include <glib-object.h>
+#include <glib.h>
+#include <libpspp/tower.h>
+
+G_BEGIN_DECLS
+
+
+/* --- type macros --- */
+#define G_TYPE_PSPPIRE_AXIS              (psppire_axis_get_type ())
+#define PSPPIRE_AXIS(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_PSPPIRE_AXIS, PsppireAxis))
+#define PSPPIRE_AXIS_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_PSPPIRE_AXIS, PsppireAxisClass))
+#define PSPPIRE_IS_AXIS(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_PSPPIRE_AXIS))
+#define PSPPIRE_IS_AXIS_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_PSPPIRE_AXIS))
+#define PSPPIRE_AXIS_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_PSPPIRE_AXIS, PsppireAxisClass))
+
+
+
+/* --- typedefs & structures --- */
+typedef struct _PsppireAxis       PsppireAxis;
+typedef struct _PsppireAxisClass PsppireAxisClass;
+
+struct pool;
+
+struct _PsppireAxis
+{
+  GObject  parent;
+
+  struct tower pixel_tower;
+  struct tower unit_tower;
+
+  struct pool *pool;
+
+  glong min_extent;
+  gint default_size;
+  gint padding;
+};
+
+struct _PsppireAxisClass
+{
+  GObjectClass parent_class;
+};
+
+GType          psppire_axis_get_type (void);
+
+PsppireAxis*   psppire_axis_new (void);
+
+\f
+/* Interface between axis and model */
+
+void psppire_axis_insert (PsppireAxis *a, gint posn, gint size);
+
+void psppire_axis_append (PsppireAxis *a, gint size);
+
+void psppire_axis_append_n (PsppireAxis *a, gint n_units, gint size);
+
+void psppire_axis_resize (PsppireAxis *a, gint posn, glong size);
+
+void psppire_axis_clear (PsppireAxis *);
+
+void psppire_axis_delete (PsppireAxis *, gint first, gint n_cases);
+
+\f
+
+gint  psppire_axis_unit_count (const PsppireAxis *);
+glong psppire_axis_start_pixel (const PsppireAxis *a, gint unit);
+gint  psppire_axis_unit_size (const PsppireAxis *a, gint unit);
+gint  psppire_axis_unit_at_pixel (const PsppireAxis *a, glong pixel);
+
+G_END_DECLS
+
+#endif /* PSPPIRE_AXIS_H__ */
diff --git a/src/ui/gui/sheet/psppire-sheetmodel.c b/src/ui/gui/sheet/psppire-sheetmodel.c
new file mode 100644 (file)
index 0000000..a948d45
--- /dev/null
@@ -0,0 +1,548 @@
+/* PsppireSheetModel --- an abstract model for the PsppireSheet widget.
+   Copyright (C) 2006, 2008 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
+   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 <glib.h>
+#include "psppire-sheetmodel.h"
+#include <ui/gui/psppire-marshal.h>
+
+enum {
+  RANGE_CHANGED,
+  ROWS_INSERTED,
+  ROWS_DELETED,
+  COLUMNS_INSERTED,
+  COLUMNS_DELETED,
+  LAST_SIGNAL
+};
+
+static guint sheet_model_signals[LAST_SIGNAL] = { 0 };
+
+
+static void      psppire_sheet_model_base_init   (gpointer           g_class);
+
+
+GType
+psppire_sheet_model_get_type (void)
+{
+  static GType sheet_model_type = 0;
+
+  if (! sheet_model_type)
+    {
+      static const GTypeInfo sheet_model_info =
+      {
+        sizeof (PsppireSheetModelIface), /* class_size */
+       psppire_sheet_model_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       NULL,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      sheet_model_type =
+       g_type_register_static (G_TYPE_INTERFACE, "PsppireSheetModel",
+                               &sheet_model_info, 0);
+
+      g_type_interface_add_prerequisite (sheet_model_type, G_TYPE_OBJECT);
+    }
+
+  return sheet_model_type;
+}
+
+static void
+psppire_sheet_model_base_init (gpointer g_class)
+{
+  static gboolean initialized = FALSE;
+
+  if (! initialized)
+    {
+      sheet_model_signals[RANGE_CHANGED] =
+       g_signal_new ("range_changed",
+                     PSPPIRE_TYPE_SHEET_MODEL,
+                     G_SIGNAL_RUN_LAST,
+                     G_STRUCT_OFFSET (PsppireSheetModelIface, range_changed),
+                     NULL, NULL,
+                     psppire_marshal_VOID__INT_INT_INT_INT,
+                     G_TYPE_NONE, 4,
+                     G_TYPE_INT,
+                     G_TYPE_INT,
+                     G_TYPE_INT,
+                     G_TYPE_INT);
+
+
+
+      sheet_model_signals[ROWS_INSERTED] =
+       g_signal_new ("rows_inserted",
+                     PSPPIRE_TYPE_SHEET_MODEL,
+                     G_SIGNAL_RUN_LAST,
+                     G_STRUCT_OFFSET (PsppireSheetModelIface, rows_inserted),
+                     NULL, NULL,
+                     psppire_marshal_VOID__INT_INT,
+                     G_TYPE_NONE, 2,
+                     G_TYPE_INT,
+                     G_TYPE_INT);
+
+
+      sheet_model_signals[ROWS_DELETED] =
+       g_signal_new ("rows_deleted",
+                     PSPPIRE_TYPE_SHEET_MODEL,
+                     G_SIGNAL_RUN_LAST,
+                     G_STRUCT_OFFSET (PsppireSheetModelIface, rows_deleted),
+                     NULL, NULL,
+                     psppire_marshal_VOID__INT_INT,
+                     G_TYPE_NONE, 2,
+                     G_TYPE_INT,
+                     G_TYPE_INT);
+
+      sheet_model_signals[COLUMNS_INSERTED] =
+       g_signal_new ("columns_inserted",
+                     PSPPIRE_TYPE_SHEET_MODEL,
+                     G_SIGNAL_RUN_LAST,
+                     G_STRUCT_OFFSET (PsppireSheetModelIface, columns_inserted),
+                     NULL, NULL,
+                     psppire_marshal_VOID__INT_INT,
+                     G_TYPE_NONE, 2,
+                     G_TYPE_INT,
+                     G_TYPE_INT);
+
+
+      sheet_model_signals[COLUMNS_DELETED] =
+       g_signal_new ("columns_deleted",
+                     PSPPIRE_TYPE_SHEET_MODEL,
+                     G_SIGNAL_RUN_LAST,
+                     G_STRUCT_OFFSET (PsppireSheetModelIface, columns_deleted),
+                     NULL, NULL,
+                     psppire_marshal_VOID__INT_INT,
+                     G_TYPE_NONE, 2,
+                     G_TYPE_INT,
+                     G_TYPE_INT);
+
+
+      initialized = TRUE;
+    }
+}
+
+
+/**
+ * psppire_sheet_model_free_strings
+ * @sheet_model: A #PsppireSheetModel
+ *
+ * Returns: True if strings obtained with get_string should be freed by the
+ * sheet when no longer required.
+ **/
+gboolean
+psppire_sheet_model_free_strings (const PsppireSheetModel *sheet_model)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), FALSE);
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->free_strings;
+}
+
+
+/**
+ * psppire_sheet_model_get_string:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row of the cell to be retrieved.
+ * @column: The column of the cell to be retrieved.
+ *
+ * Retrieves the datum at location ROW, COLUMN in the form of a string.
+ * Returns: The string representation of the datum, or NULL on error.
+ **/
+gchar *
+psppire_sheet_model_get_string (const PsppireSheetModel *sheet_model,
+                         glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), 0);
+
+  g_assert (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->get_string);
+
+  return (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->get_string) (sheet_model, row, column);
+}
+
+/**
+ * psppire_sheet_model_set_string
+ * @sheet_model: A #PsppireSheetModel
+ * @text: The text describing the datum to be set.
+ * @row: The row of the cell to be cleared.
+ * @column: The column of the cell to be cleared.
+ *
+ * Sets the datum at a location from a string.
+ * Returns: TRUE if the datum was changed, FALSE otherwise.
+ **/
+gboolean
+psppire_sheet_model_set_string      (PsppireSheetModel *sheet_model,
+                                const gchar *text,
+                                glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), FALSE);
+
+  g_assert (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->set_string);
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->set_string (sheet_model,
+                                                           text, row, column);
+}
+
+
+
+/**
+ * psppire_sheet_model_datum_clear:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row of the cell to be cleared.
+ * @column: The column of the cell to be cleared.
+ *
+ * Called when the datum at a location is to be cleared.
+ * Returns: TRUE if the datum was cleared, FALSE otherwise.
+ **/
+gboolean
+psppire_sheet_model_datum_clear    (PsppireSheetModel *sheet_model,
+                               glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), FALSE);
+
+  g_assert (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum);
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum (sheet_model,
+                                                               row, column);
+}
+
+
+/**
+ * psppire_sheet_model_range_changed:
+ * @sheet_model: A #PsppireSheetModel
+ * @range: The #PsppireSheetRange range of cells which have changed.
+ *
+ * Emits the "range_changed" signal on @sheet_model.
+ **/
+void
+psppire_sheet_model_range_changed (PsppireSheetModel *sheet_model,
+                              glong row0, glong col0,
+                              glong rowi, glong coli)
+{
+  g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
+
+  g_signal_emit (sheet_model, sheet_model_signals[RANGE_CHANGED], 0,
+                row0, col0, rowi, coli);
+}
+
+
+
+
+/**
+ * psppire_sheet_model_rows_inserted:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row before which the new rows should be inserted.
+ * @n_rows: The number of rows to insert.
+ *
+ * Emits the "rows_inserted" signal on @sheet_model.
+ **/
+void
+psppire_sheet_model_rows_inserted (PsppireSheetModel *sheet_model,
+                              glong row, glong n_rows)
+{
+  g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
+
+  g_signal_emit (sheet_model, sheet_model_signals[ROWS_INSERTED], 0,
+                row, n_rows);
+}
+
+
+/**
+ * psppire_sheet_model_columns_inserted:
+ * @sheet_model: A #PsppireSheetModel
+ * @column: The column before which the new columns should be inserted.
+ * @n_columns: The number of columns to insert.
+ *
+ * Emits the "columns_inserted" signal on @sheet_model.
+ **/
+void
+psppire_sheet_model_columns_inserted (PsppireSheetModel *sheet_model,
+                              glong column, glong n_columns)
+{
+  g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
+
+  g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_INSERTED], 0,
+                column, n_columns);
+}
+
+
+
+
+/**
+ * psppire_sheet_model_rows_deleted:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The first row to be deleted.
+ * @n_rows: The number of rows to delete.
+ *
+ * Emits the "rows_deleted" signal on @sheet_model.
+ **/
+void
+psppire_sheet_model_rows_deleted (PsppireSheetModel *sheet_model,
+                              glong row, glong n_rows)
+{
+  g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
+
+  g_signal_emit (sheet_model, sheet_model_signals[ROWS_DELETED], 0,
+                row, n_rows);
+}
+
+
+
+/**
+ * psppire_sheet_model_columns_deleted:
+ * @sheet_model: A #PsppireSheetModel
+ * @column: The first column to be deleted.
+ * @n_columns: The number of columns to delete.
+ *
+ * Emits the "columns_deleted" signal on @sheet_model.
+ **/
+void
+psppire_sheet_model_columns_deleted (PsppireSheetModel *sheet_model,
+                              glong column, glong n_columns)
+{
+  g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
+
+  g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_DELETED], 0,
+                column, n_columns);
+}
+
+
+
+
+
+/**
+ * psppire_sheet_model_is_editable:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row
+ * @column: The column
+ *
+ * Returns: TRUE if the cell is editable, FALSE otherwise
+ **/
+gboolean
+psppire_sheet_model_is_editable (const PsppireSheetModel *model,
+                            glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), TRUE);
+
+  if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->is_editable )
+    return TRUE;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->is_editable (model,
+                                                         row, column);
+}
+
+
+/**
+ * psppire_sheet_model_get_foreground:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row
+ * @column: The column
+ *
+ * Returns the foreground colour of the cell at @row, @column
+ * The color is unallocated.  It will be allocated by the viewing object.
+ **/
+GdkColor *
+psppire_sheet_model_get_foreground (const PsppireSheetModel *model,
+                               glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+
+  if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_foreground )
+    return NULL;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_foreground (model,
+                                                           row, column);
+}
+
+/**
+ * psppire_sheet_model_get_background:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row
+ * @column: The column
+ *
+ * Returns the background colour of the cell at @row, @column
+ * The color is unallocated.  It will be allocated by the viewing object.
+ **/
+GdkColor *
+psppire_sheet_model_get_background (const PsppireSheetModel *model,
+                               glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+
+  if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_background )
+    return NULL;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_background (model,
+                                                           row, column);
+}
+
+/**
+ * psppire_sheet_model_get_justification:
+ * @sheet_model: A #PsppireSheetModel
+ * @row: The row
+ * @column: The column
+ *
+ * Returns the justification of the cell at @row, @column
+ * Returns: the justification, or NULL on error.
+ **/
+const GtkJustification *
+psppire_sheet_model_get_justification (const PsppireSheetModel *model,
+                                  glong row, glong column)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+
+  if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_justification)
+    return NULL;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_justification (model,
+                                                              row, column);
+}
+
+
+/**
+ * psppire_sheet_model_get_column_count:
+ * @model: A #PsppireSheetModel
+ *
+ * Returns the total number of columns represented by the model
+ **/
+glong
+psppire_sheet_model_get_column_count (const PsppireSheetModel *model)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), -1);
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_count (model);
+}
+
+/**
+ * psppire_sheet_model_get_row_count:
+ * @model: A #PsppireSheetModel
+ *
+ * Returns the total number of rows represented by the model
+ **/
+gint
+psppire_sheet_model_get_row_count(const PsppireSheetModel *model)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), -1);
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_count (model);
+}
+
+\f
+
+/* Column related functions  */
+gboolean
+psppire_sheet_model_get_column_sensitivity (const PsppireSheetModel *model, gint col)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), FALSE);
+
+  if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_sensitivity)
+    return TRUE;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_sensitivity (model, col);
+}
+
+
+gchar *
+psppire_sheet_model_get_column_subtitle (const PsppireSheetModel *model,
+                                  gint col)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+  g_return_val_if_fail (col >= 0, NULL);
+
+  if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_subtitle)
+    return NULL;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_subtitle (model, col);
+}
+
+
+PsppireSheetButton *
+psppire_sheet_model_get_column_button (const PsppireSheetModel *model,
+                                gint col)
+{
+  PsppireSheetButton *button = psppire_sheet_button_new ();
+
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+
+  if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_title)
+    button->label = PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_title (model, col);
+
+  button->overstruck = FALSE;
+
+  return button;
+}
+
+GtkJustification
+psppire_sheet_model_get_column_justification (const PsppireSheetModel *model,
+                                       gint col)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), GTK_JUSTIFY_LEFT);
+
+  if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_justification)
+    return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_justification (model, col);
+
+  return GTK_JUSTIFY_LEFT;
+}
+
+\f
+
+gboolean
+psppire_sheet_model_get_row_sensitivity (const PsppireSheetModel *model, gint row)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), FALSE);
+
+  if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_sensitivity)
+    return TRUE;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_sensitivity (model, row);
+}
+
+
+
+gchar *
+psppire_sheet_model_get_row_subtitle (const PsppireSheetModel *model,
+                               gint row)
+{
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+
+  if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_subtitle)
+    return NULL;
+
+  return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_subtitle (model, row);
+}
+
+
+PsppireSheetButton *
+psppire_sheet_model_get_row_button (const PsppireSheetModel *model,
+                                gint row)
+{
+  PsppireSheetButton *button = psppire_sheet_button_new ();
+
+  g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
+
+  if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_title)
+    button->label =
+      PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_title (model, row);
+
+  if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_overstrike)
+    button->overstruck =
+      PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_overstrike (model, row);
+
+  return button;
+}
+
diff --git a/src/ui/gui/sheet/psppire-sheetmodel.h b/src/ui/gui/sheet/psppire-sheetmodel.h
new file mode 100644 (file)
index 0000000..c851907
--- /dev/null
@@ -0,0 +1,223 @@
+/* PsppireSheetModel --- an abstract model for the PsppireSheet widget.
+ * Copyright (C) 2006, 2008 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
+   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 __PSPPIRE_SHEET_MODEL_H__
+#define __PSPPIRE_SHEET_MODEL_H__
+
+
+/* This file provides an abstract interface or the data displayed by the
+   PsppireSheet widget */
+
+#include <glib-object.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <gtk-contrib/gtkextra-sheet.h>
+
+G_BEGIN_DECLS
+
+#define PSPPIRE_TYPE_SHEET_MODEL            (psppire_sheet_model_get_type ())
+#define PSPPIRE_SHEET_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_TYPE_SHEET_MODEL, PsppireSheetModel))
+#define PSPPIRE_IS_SHEET_MODEL(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_SHEET_MODEL))
+#define PSPPIRE_SHEET_MODEL_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), PSPPIRE_TYPE_SHEET_MODEL, PsppireSheetModelIface))
+
+typedef enum
+{
+  PSPPIRE_SHEET_LEFT_BORDER     = 1 << 0,
+  PSPPIRE_SHEET_RIGHT_BORDER    = 1 << 1,
+  PSPPIRE_SHEET_TOP_BORDER      = 1 << 2,
+  PSPPIRE_SHEET_BOTTOM_BORDER   = 1 << 3
+} PsppireSheetBorderType ;
+
+
+typedef struct _PsppireSheetModel        PsppireSheetModel; /* Dummy typedef */
+typedef struct _PsppireSheetModelIface   PsppireSheetModelIface;
+typedef struct _PsppireSheetRange PsppireSheetRange;
+typedef struct _PsppireSheetCellBorder     PsppireSheetCellBorder;
+
+struct _PsppireSheetRange
+{
+  gint row0, col0; /* upper-left cell */
+  gint rowi, coli; /* lower-right cell */
+};
+
+struct _PsppireSheetCellBorder
+{
+  PsppireSheetBorderType mask;
+  guint width;
+  GdkLineStyle line_style;
+  GdkCapStyle cap_style;
+  GdkJoinStyle join_style;
+  GdkColor color;
+};
+
+
+
+struct _PsppireSheetModelIface
+{
+  GTypeInterface g_iface;
+
+  gboolean free_strings;
+
+  /* Signals */
+  void         (* range_changed)    (PsppireSheetModel *sheet_model,
+                                    glong row0, glong col0,
+                                    glong rowi, glong coli);
+
+  void         (* rows_inserted)    (PsppireSheetModel *sheet_model,
+                                    glong row, glong n_rows);
+
+  void         (* rows_deleted)     (PsppireSheetModel *sheet_model,
+                                    glong row, glong n_rows);
+
+  void         (* columns_inserted)    (PsppireSheetModel *sheet_model,
+                                       glong column, glong n_columns);
+
+  void         (* columns_deleted)     (PsppireSheetModel *sheet_model,
+                                       glong column, glong n_columns);
+
+
+
+  /* Virtual Table */
+
+  gchar *      (* get_string)      (const PsppireSheetModel *sheet_model,
+                                   glong row, glong column);
+
+  gboolean  (* set_string) (PsppireSheetModel *sheet_model,
+                           const gchar *s, glong row, glong column);
+
+  gboolean  (* clear_datum) (PsppireSheetModel *sheet_model,
+                            glong row, glong column);
+
+  gboolean (* is_editable) (const PsppireSheetModel *sheet_model, glong row, glong column);
+
+  GdkColor *  (* get_foreground) (const PsppireSheetModel *sheet_model,
+                                 glong row, glong column);
+
+  GdkColor *  (* get_background) (const PsppireSheetModel *sheet_model,
+                                 glong row, glong column);
+
+  const GtkJustification *  (* get_justification) (const PsppireSheetModel *sheet_model,
+                                                  glong row, glong column);
+
+  /* column related metadata */
+
+  gchar * (*get_column_title) (const PsppireSheetModel *, gint col);
+  gchar * (*get_column_subtitle) (const PsppireSheetModel *, gint col);
+  gboolean (*get_column_sensitivity) (const PsppireSheetModel *, gint col);
+  GtkJustification (*get_column_justification) (const PsppireSheetModel *mode, gint col);
+  const PsppireSheetButton * (* get_button) (const PsppireSheetModel *model, gint col);
+
+  glong (*get_column_count) (const PsppireSheetModel *model);
+
+
+  /* row related metadata */
+  gchar * (*get_row_title) (const PsppireSheetModel *, gint row);
+  gchar * (*get_row_subtitle) (const PsppireSheetModel *, gint row);
+  glong (*get_row_count) (const PsppireSheetModel *model);
+  gboolean (*get_row_sensitivity) (const PsppireSheetModel *, gint row);
+
+  gboolean (*get_row_overstrike) (const PsppireSheetModel *, gint row);
+};
+
+
+
+GType              psppire_sheet_model_get_type   (void) G_GNUC_CONST;
+
+
+gchar * psppire_sheet_model_get_string (const PsppireSheetModel *sheet_model,
+                                 glong row, glong column);
+
+gboolean  psppire_sheet_model_set_string (PsppireSheetModel *sheet_model,
+                                   const gchar *s,
+                                   glong row, glong column);
+
+gboolean psppire_sheet_model_datum_clear    (PsppireSheetModel *sheet_model,
+                                      glong row, glong column);
+
+
+void psppire_sheet_model_range_changed (PsppireSheetModel *sheet_model,
+                                 glong row0, glong col0,
+                                 glong rowi, glong coli);
+
+void psppire_sheet_model_rows_deleted (PsppireSheetModel *sheet_model,
+                                glong row, glong n_rows);
+
+void psppire_sheet_model_rows_inserted (PsppireSheetModel *sheet_model,
+                                 glong row, glong n_rows);
+
+void psppire_sheet_model_columns_inserted (PsppireSheetModel *sheet_model,
+                                    glong column, glong n_columns);
+
+void psppire_sheet_model_columns_deleted (PsppireSheetModel *sheet_model,
+                                   glong column, glong n_columns);
+
+
+gboolean psppire_sheet_model_is_editable (const PsppireSheetModel *model,
+                                   glong row, glong column);
+
+gboolean psppire_sheet_model_is_visible
+ (const PsppireSheetModel *model, glong row, glong column);
+
+
+GdkColor *psppire_sheet_model_get_foreground
+ (const PsppireSheetModel *model, glong row, glong column);
+
+GdkColor *psppire_sheet_model_get_background
+ (const PsppireSheetModel *model, glong row, glong column);
+
+const GtkJustification *psppire_sheet_model_get_justification
+ (const PsppireSheetModel *model, glong row, glong column);
+
+const PsppireSheetCellBorder * psppire_sheet_model_get_cell_border
+ (const PsppireSheetModel *model, glong row, glong column);
+
+gboolean psppire_sheet_model_free_strings (const PsppireSheetModel *sheet_model);
+
+glong psppire_sheet_model_get_column_count (const PsppireSheetModel *sheet_model);
+
+gint psppire_sheet_model_get_row_count (const PsppireSheetModel *sheet_model);
+
+\f
+
+gboolean psppire_sheet_model_get_column_sensitivity (const PsppireSheetModel *model,
+                                              gint col);
+
+gchar * psppire_sheet_model_get_column_subtitle (const PsppireSheetModel *model,
+                                           gint col);
+
+PsppireSheetButton * psppire_sheet_model_get_column_button (const PsppireSheetModel *, gint);
+
+GtkJustification psppire_sheet_model_get_column_justification (const PsppireSheetModel *,
+                                                        gint);
+
+\f
+
+gboolean psppire_sheet_model_get_row_sensitivity (const PsppireSheetModel *model,
+                                           gint row);
+
+
+gchar * psppire_sheet_model_get_row_subtitle (const PsppireSheetModel *model,
+                                           gint row);
+
+
+PsppireSheetButton * psppire_sheet_model_get_row_button (const PsppireSheetModel *, gint);
+
+
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_SHEET_MODEL_H__ */
index 4c83676f212e5f1c26784a006d647236d5e5fd66..b5d1bbaafa1d9803007a65ee5f9ae8ce25cb86ad 100644 (file)
 #include "sort-cases-dialog.h"
 #include "helper.h"
 #include "psppire-dialog.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-var-store.h"
 #include "dialog-common.h"
+#include "psppire-selector.h"
 #include "dict-display.h"
 
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 static void
 refresh (PsppireDialog *dialog, GtkTreeView *dest)
@@ -91,11 +92,11 @@ void
 sort_cases_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   struct sort_cases_dialog scd;
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "sort-cases-dialog");
 
@@ -108,11 +109,9 @@ sort_cases_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (source, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
 
@@ -143,6 +142,7 @@ sort_cases_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&scd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -152,11 +152,7 @@ sort_cases_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&scd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index badcdd60f81af57130e53701883bd99e245b6931..000d2c00810634bbecc07a921b8dbca26afe7477 100644 (file)
@@ -19,8 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-
 
 void sort_cases_dialog (GObject *o, gpointer data);
 
index 22cdb9140483b5393010997bf40eb05f29c80961..063f186263f77370da93d956f1e6f759fb7d762f 100644 (file)
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "dict-display.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 #include <data/dictionary.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 
 #include "dialog-common.h"
@@ -39,7 +38,7 @@
 struct split_file_dialog
 {
   /* The XML that created the dialog */
-  GladeXML *xml;
+  GtkBuilder *xml;
 
   /* The dictionary to which this dialog pertains */
   PsppireDict *dict;
@@ -109,7 +108,7 @@ static void
 on_off_toggled (GtkToggleButton *togglebutton,
                gpointer         user_data)
 {
-  GladeXML *xml = user_data;
+  GtkBuilder *xml = user_data;
   GtkWidget *dest =   get_widget_assert (xml, "split-file-grouping-vars");
   GtkWidget *selector = get_widget_assert (xml, "split-file-selector");
   GtkWidget *source = get_widget_assert (xml, "split-file-dict-treeview");
@@ -167,7 +166,7 @@ void
 split_file_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
   struct split_file_dialog sfd;
   PsppireVarStore *vs ;
 
@@ -177,7 +176,7 @@ split_file_dialog (GObject *o, gpointer data)
   GtkWidget *selector ;
   GtkWidget *on_off   ;
 
-  sfd.xml = XML_NEW ("psppire.glade");
+  sfd.xml = builder_new ("psppire.ui");
 
   dialog = get_widget_assert   (sfd.xml, "split-file-dialog");
   source = get_widget_assert   (sfd.xml, "split-file-dict-treeview");
@@ -192,9 +191,8 @@ split_file_dialog (GObject *o, gpointer data)
   sfd.selector  = PSPPIRE_SELECTOR (
                                    get_widget_assert   (sfd.xml, "split-file-selector"));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (source, "dictionary",
+                                vs->dict, NULL);
 
 
   g_signal_connect (on_off, "toggled", G_CALLBACK(on_off_toggled),  sfd.xml);
@@ -211,7 +209,7 @@ split_file_dialog (GObject *o, gpointer data)
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &sfd);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
 
@@ -221,6 +219,7 @@ split_file_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&sfd);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -230,11 +229,7 @@ split_file_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&sfd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index f7617a893155faf03b786680c6e0c14114603b04..214370513b7369087344f60c636f450704f3b7a7 100644 (file)
 #include <gtk/gtk.h>
 
 #include "syntax-editor-source.h"
-#include "syntax-editor.h"
+#include "psppire-syntax-window.h"
 
 #include "xalloc.h"
 
 struct syntax_editor_source
   {
     struct getl_interface parent;
-    const struct syntax_editor *se;
+    GtkTextBuffer *buffer;
     GtkTextIter i;
     GtkTextIter end;
+    const gchar *name;
   };
 
 
@@ -49,10 +50,8 @@ always_false (const struct getl_interface *i UNUSED)
 static const char *
 name (const struct getl_interface *i)
 {
-  const struct syntax_editor_source *ses =
-    (const struct syntax_editor_source *) i;
-
-  return window_name ((const struct editor_window *) ses->se);
+  const struct syntax_editor_source *ses = (const struct syntax_editor_source *) i;
+  return ses->name;
 }
 
 
@@ -81,7 +80,7 @@ read_line_from_buffer (struct getl_interface *i,
   next_line = ses->i;
   gtk_text_iter_forward_line (&next_line);
 
-  text = gtk_text_buffer_get_text (ses->se->buffer,
+  text = gtk_text_buffer_get_text (ses->buffer,
                                   &ses->i, &next_line,
                                   FALSE);
   g_strchomp (text);
@@ -103,16 +102,18 @@ do_close (struct getl_interface *i )
 }
 
 struct getl_interface *
-create_syntax_editor_source (const struct syntax_editor *se,
+create_syntax_editor_source (GtkTextBuffer *buffer,
                             GtkTextIter start,
-                            GtkTextIter stop
+                            GtkTextIter stop,
+                            const gchar *nm
                             )
 {
   struct syntax_editor_source *ses = xzalloc (sizeof *ses);
 
-  ses->se = se;
+  ses->buffer = buffer;
   ses->i = start;
   ses->end = stop;
+  ses->name = nm;
 
 
   ses->parent.interactive = always_false;
index ca198393fbed07e338c2f1686a4274354e16bf61..f8d08eaaadbcde3a28f6ff61cfae0e9f051abf2c 100644 (file)
@@ -23,9 +23,10 @@ struct getl_interface;
 struct syntax_editor;
 
 struct getl_interface *
-create_syntax_editor_source (const struct syntax_editor *se,
+create_syntax_editor_source (GtkTextBuffer *buffer,
                             GtkTextIter start,
-                            GtkTextIter stop
+                            GtkTextIter stop,
+                            const gchar *name
                             );
 
 
diff --git a/src/ui/gui/syntax-editor.c b/src/ui/gui/syntax-editor.c
deleted file mode 100644 (file)
index 35749fc..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006 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 <stdlib.h>
-#include <gettext.h>
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-#include <glade/glade.h>
-#include <gtk/gtk.h>
-#include <libpspp/message.h>
-#include <libpspp/getl.h>
-#include "helper.h"
-#include "data-editor.h"
-#include "about.h"
-
-#include "window-manager.h"
-
-#include <data/dictionary.h>
-#include <language/lexer/lexer.h>
-#include <language/command.h>
-#include <data/procedure.h>
-#include "syntax-editor.h"
-#include "syntax-editor-source.h"
-
-extern struct source_stream *the_source_stream ;
-extern struct dataset *the_dataset;
-
-static gboolean save_editor_to_file (struct syntax_editor *se,
-                                    const gchar *filename,
-                                    GError **err);
-
-/* Append ".sps" to FILENAME if necessary.
-   The returned result must be freed when no longer required.
- */
-static gchar *
-append_suffix (const gchar *filename)
-{
-  if ( ! g_str_has_suffix (filename, ".sps" ) &&
-       ! g_str_has_suffix (filename, ".SPS" ) )
-    {
-      return g_strdup_printf ("%s.sps", filename);
-    }
-
-  return strdup (filename);
-}
-
-/* If the buffer's modified flag is set, then save it, and close the window.
-   Otherwise just close the window.
-*/
-static void
-save_if_modified (struct syntax_editor *se)
-{
-  struct editor_window *e = (struct editor_window *) se;
-  if ( TRUE == gtk_text_buffer_get_modified (se->buffer))
-    {
-      gint response;
-      GtkWidget *dialog =
-       gtk_message_dialog_new (GTK_WINDOW (e->window),
-                               GTK_DIALOG_MODAL,
-                               GTK_MESSAGE_QUESTION,
-                               GTK_BUTTONS_NONE,
-                               _("Save contents of syntax editor to %s?"),
-                               e->name
-                               );
-
-      gtk_dialog_add_button  (GTK_DIALOG (dialog),
-                             GTK_STOCK_YES,
-                             GTK_RESPONSE_ACCEPT);
-      gtk_dialog_add_button  (GTK_DIALOG (dialog),
-                             GTK_STOCK_NO,
-                             GTK_RESPONSE_REJECT);
-      gtk_dialog_add_button  (GTK_DIALOG (dialog),
-                             GTK_STOCK_CANCEL,
-                             GTK_RESPONSE_CANCEL);
-
-
-      response = gtk_dialog_run (GTK_DIALOG (dialog));
-
-      gtk_widget_destroy (dialog);
-
-      if ( response == GTK_RESPONSE_ACCEPT )
-       {
-         GError *err = NULL;
-
-         if ( ! save_editor_to_file (se, e->name, &err) )
-           {
-             msg (ME, err->message);
-             g_error_free (err);
-           }
-       }
-
-      if ( response == GTK_RESPONSE_CANCEL )
-       return ;
-    }
-
-  gtk_widget_destroy (GTK_WIDGET (e->window));
-}
-
-/* Callback for the File->SaveAs menuitem */
-static void
-on_syntax_save_as (GtkMenuItem *menuitem, gpointer user_data)
-{
-  GtkFileFilter *filter;
-  gint response;
-  struct syntax_editor *se = user_data;
-  struct editor_window *e = user_data;
-
-  GtkWidget *dialog =
-    gtk_file_chooser_dialog_new (_("Save Syntax"),
-                                GTK_WINDOW (e->window),
-                                GTK_FILE_CHOOSER_ACTION_SAVE,
-                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
-                                NULL);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
-  gtk_file_filter_add_pattern (filter, "*.sps");
-  gtk_file_filter_add_pattern (filter, "*.SPS");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("All Files"));
-  gtk_file_filter_add_pattern (filter, "*");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
-                                                 TRUE);
-  response = gtk_dialog_run (GTK_DIALOG (dialog));
-
-  if ( response == GTK_RESPONSE_ACCEPT )
-    {
-      GError *err = NULL;
-      char *filename =
-       gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
-
-      if ( save_editor_to_file (se, filename, &err) )
-       {
-         g_free (e->name);
-         e->name = g_strdup (filename);
-       }
-      else
-       {
-         msg ( ME, err->message );
-         g_error_free (err);
-       }
-
-      free (filename);
-    }
-
-  gtk_widget_destroy ( dialog );
-}
-
-/* Callback for the File->Save menuitem */
-static void
-on_syntax_save (GtkMenuItem *menuitem, gpointer user_data)
-{
-  struct syntax_editor *se = user_data;
-  struct editor_window *e = user_data;
-
-  if ( e->name == NULL )
-    on_syntax_save_as (menuitem, user_data);
-  else
-    {
-      GError *err = NULL;
-      save_editor_to_file (se, e->name, &err);
-      if ( err )
-       {
-         msg (ME, err->message);
-         g_error_free (err);
-       }
-    }
-}
-
-
-/* Callback for the "delete" action (clicking the x on the top right
-   hand corner of the window) */
-static gboolean
-on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
-{
-  struct syntax_editor *se = user_data;
-  save_if_modified (se);
-  return TRUE;
-}
-
-
-/* Callback for the File->Quit menuitem */
-static gboolean
-on_quit (GtkMenuItem *menuitem, gpointer    user_data)
-{
-  struct syntax_editor *se = user_data;
-  save_if_modified (se);
-  return FALSE;
-}
-
-static void
-editor_execute_syntax (const struct syntax_editor *se, GtkTextIter start,
-               GtkTextIter stop)
-{
-  execute_syntax (create_syntax_editor_source (se, start, stop));
-}
-
-/* Parse and execute all the text in the buffer */
-static void
-on_run_all (GtkMenuItem *menuitem, gpointer user_data)
-{
-  GtkTextIter begin, end;
-  struct syntax_editor *se = user_data;
-
-  gtk_text_buffer_get_iter_at_offset (se->buffer, &begin, 0);
-  gtk_text_buffer_get_iter_at_offset (se->buffer, &end, -1);
-
-  editor_execute_syntax (se, begin, end);
-}
-
-/* Parse and execute the currently selected text */
-static void
-on_run_selection (GtkMenuItem *menuitem, gpointer user_data)
-{
-  GtkTextIter begin, end;
-  struct syntax_editor *se = user_data;
-
-  if ( gtk_text_buffer_get_selection_bounds (se->buffer, &begin, &end) )
-    editor_execute_syntax (se, begin, end);
-}
-
-
-/* Parse and execute the current line */
-static void
-on_run_current_line (GtkMenuItem *menuitem, gpointer user_data)
-{
-  GtkTextIter begin, end;
-  GtkTextIter here;
-  gint line;
-
-  struct syntax_editor *se = user_data;
-
-  /* Get the current line */
-  gtk_text_buffer_get_iter_at_mark (se->buffer,
-                                   &here,
-                                   gtk_text_buffer_get_insert (se->buffer)
-                                   );
-
-  line = gtk_text_iter_get_line (&here) ;
-
-  /* Now set begin and end to the start of this line, and start of
-     following line respectively */
-  gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
-  gtk_text_buffer_get_iter_at_line (se->buffer, &end, line + 1);
-
-  editor_execute_syntax (se, begin, end);
-}
-
-
-
-/* Parse and execute the from the current line, to the end of the
-   buffer */
-static void
-on_run_to_end (GtkMenuItem *menuitem, gpointer user_data)
-{
-  GtkTextIter begin, end;
-  GtkTextIter here;
-  gint line;
-
-  struct syntax_editor *se = user_data;
-
-  /* Get the current line */
-  gtk_text_buffer_get_iter_at_mark (se->buffer,
-                                   &here,
-                                   gtk_text_buffer_get_insert (se->buffer)
-                                   );
-
-  line = gtk_text_iter_get_line (&here) ;
-
-  /* Now set begin and end to the start of this line, and end of buffer
-     respectively */
-  gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
-  gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
-
-  editor_execute_syntax (se, begin, end);
-}
-
-
-
-
-/*
-  Create a new syntax editor with NAME.
-  If NAME is NULL, a name will be automatically assigned
-*/
-struct syntax_editor *
-new_syntax_editor (void)
-{
-  GladeXML *xml = XML_NEW ("syntax-editor.glade");
-
-  GtkWidget *text_view;
-  struct syntax_editor *se ;
-  struct editor_window *e;
-
-  connect_help (xml);
-
-  se = g_malloc (sizeof (*se));
-
-  e = (struct editor_window *)se;
-
-  e->window = GTK_WINDOW (get_widget_assert (xml, "syntax_editor"));
-  text_view = get_widget_assert (xml, "syntax_text_view");
-  se->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
-  se->lexer = lex_create (the_source_stream);
-
-  g_signal_connect (get_widget_assert (xml,"file_new_syntax"),
-                   "activate",
-                   G_CALLBACK (new_syntax_window),
-                   e->window);
-
-  g_signal_connect (get_widget_assert (xml,"file_open_syntax"),
-                   "activate",
-                   G_CALLBACK (open_syntax_window),
-                   e->window);
-
-  g_signal_connect (get_widget_assert (xml,"file_new_data"),
-                   "activate",
-                   G_CALLBACK (new_data_window),
-                   e->window);
-
-  g_signal_connect (get_widget_assert (xml,"help_about"),
-                   "activate",
-                   G_CALLBACK (about_new),
-                   e->window);
-
-  g_signal_connect (get_widget_assert (xml,"help_reference"),
-                   "activate",
-                   G_CALLBACK (reference_manual),
-                   NULL);
-
-
-  g_signal_connect (get_widget_assert (xml, "file_save"),
-                   "activate",
-                   G_CALLBACK (on_syntax_save),
-                   se);
-
-  g_signal_connect (get_widget_assert (xml, "file_save_as"),
-                   "activate",
-                   G_CALLBACK (on_syntax_save_as),
-                   se);
-
-
-  g_signal_connect (get_widget_assert (xml,"file_quit"),
-                   "activate",
-                   G_CALLBACK (on_quit),
-                   se);
-
-
-  g_signal_connect (get_widget_assert (xml,"run_all"),
-                   "activate",
-                   G_CALLBACK (on_run_all),
-                   se);
-
-
-  g_signal_connect (get_widget_assert (xml,"run_selection"),
-                   "activate",
-                   G_CALLBACK (on_run_selection),
-                   se);
-
-  g_signal_connect (get_widget_assert (xml,"run_current_line"),
-                   "activate",
-                   G_CALLBACK (on_run_current_line),
-                   se);
-
-
-  g_signal_connect (get_widget_assert (xml,"run_to_end"),
-                   "activate",
-                   G_CALLBACK (on_run_to_end),
-                   se);
-
-
-  g_signal_connect (get_widget_assert (xml,"windows_minimise_all"),
-                   "activate",
-                   G_CALLBACK (minimise_all_windows),
-                   NULL);
-
-
-
-  g_object_unref (xml);
-
-  g_signal_connect (e->window, "delete-event",
-                   G_CALLBACK (on_delete), se);
-
-
-
-  return se;
-}
-
-/*
-   Callback for the File->New->Syntax menuitem
-*/
-void
-new_syntax_window (GtkMenuItem     *menuitem,
-                  gpointer         user_data)
-{
-  window_create (WINDOW_SYNTAX, NULL);
-}
-
-
-/*
-  Save BUFFER to the file called FILENAME.
-  If successful, clears the buffer's modified flag
-*/
-static gboolean
-save_editor_to_file (struct syntax_editor *se,
-                    const gchar *filename,
-                    GError **err)
-{
-  GtkTextBuffer *buffer = se->buffer;
-  gboolean result ;
-  GtkTextIter start, stop;
-  gchar *text;
-
-  gchar *suffixedname;
-  gchar *glibfilename;
-  g_assert (filename);
-
-  suffixedname = append_suffix (filename);
-
-  glibfilename = g_filename_from_utf8 (suffixedname, -1, 0, 0, err);
-
-  g_free ( suffixedname);
-
-  if ( ! glibfilename )
-    return FALSE;
-
-  gtk_text_buffer_get_iter_at_line (buffer, &start, 0);
-  gtk_text_buffer_get_iter_at_offset (buffer, &stop, -1);
-
-  text = gtk_text_buffer_get_text (buffer, &start, &stop, FALSE);
-
-  result =  g_file_set_contents (glibfilename, text, -1, err);
-
-  if ( result )
-    {
-      window_set_name_from_filename ((struct editor_window *) se, filename);
-      gtk_text_buffer_set_modified (buffer, FALSE);
-    }
-
-  return result;
-}
-
-
-/*
-  Loads the buffer from the file called FILENAME
-*/
-gboolean
-load_editor_from_file (struct syntax_editor *se,
-                      const gchar *filename,
-                      GError **err)
-{
-  GtkTextBuffer *buffer = se->buffer;
-  gchar *text;
-  GtkTextIter iter;
-
-  gchar *glibfilename = g_filename_from_utf8 (filename, -1, 0, 0, err);
-
-  if ( ! glibfilename )
-    return FALSE;
-
-  /* FIXME: What if it's a very big file ? */
-  if ( ! g_file_get_contents (glibfilename, &text, NULL, err) )
-    {
-      g_free (glibfilename);
-      return FALSE;
-    }
-  g_free (glibfilename);
-
-  gtk_text_buffer_get_iter_at_line (buffer, &iter, 0);
-
-  gtk_text_buffer_insert (buffer, &iter, text, -1);
-
-
-  window_set_name_from_filename ((struct editor_window *)se, filename);
-  gtk_text_buffer_set_modified (buffer, FALSE);
-
-  return TRUE;
-}
-
-
-/* Callback for the File->Open->Syntax menuitem */
-void
-open_syntax_window (GtkMenuItem *menuitem, gpointer parent)
-{
-  GtkFileFilter *filter;
-  gint response;
-
-  GtkWidget *dialog =
-    gtk_file_chooser_dialog_new (_("Open Syntax"),
-                                GTK_WINDOW (parent),
-                                GTK_FILE_CHOOSER_ACTION_OPEN,
-                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                GTK_STOCK_OPEN,   GTK_RESPONSE_ACCEPT,
-                                NULL);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
-  gtk_file_filter_add_pattern (filter, "*.sps");
-  gtk_file_filter_add_pattern (filter, "*.SPS");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("All Files"));
-  gtk_file_filter_add_pattern (filter, "*");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  response = gtk_dialog_run (GTK_DIALOG (dialog));
-
-  if (response == GTK_RESPONSE_ACCEPT)
-    {
-      const char *file_name = gtk_file_chooser_get_filename
-       (GTK_FILE_CHOOSER (dialog));
-
-      struct syntax_editor *se = (struct syntax_editor *)
-       window_create (WINDOW_SYNTAX, file_name);
-
-      if ( load_editor_from_file (se, file_name, NULL) )
-#if RECENT_LISTS_AVAILABLE
-      {
-       GtkRecentManager *manager = gtk_recent_manager_get_default();
-       gchar *uri = g_filename_to_uri (file_name, NULL, NULL);
-
-       gtk_recent_manager_remove_item (manager, uri, NULL);
-       if ( ! gtk_recent_manager_add_item (manager, uri))
-         g_warning ("Could not add item %s to recent list\n",uri);
-
-       g_free (uri);
-      }
-#else
-      ;
-#endif
-
-    }
-
-  gtk_widget_destroy (dialog);
-}
-
index 8895c8f3aa8d78ffee6d663d789d79c92cc28ae3..8f565e12e987e14e3a278ab9aede0447d102f70c 100644 (file)
@@ -3,31 +3,6 @@
 
 <glade-interface>
 
-<widget class="GtkWindow" id="syntax_editor">
-  <property name="default_width">640</property>
-  <property name="default_height">480</property>
-  <property name="can_focus">True</property>
-  <property name="title" translatable="yes">Psppire Syntax Editor</property>
-  <property name="type">GTK_WINDOW_TOPLEVEL</property>
-  <property name="window_position">GTK_WIN_POS_NONE</property>
-  <property name="modal">False</property>
-  <property name="resizable">True</property>
-  <property name="destroy_with_parent">False</property>
-  <property name="decorated">True</property>
-  <property name="skip_taskbar_hint">False</property>
-  <property name="skip_pager_hint">False</property>
-  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
-  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
-  <property name="focus_on_map">True</property>
-  <property name="urgency_hint">False</property>
-
-  <child>
-    <widget class="GtkVBox" id="vbox14">
-      <property name="visible">True</property>
-      <property name="homogeneous">False</property>
-      <property name="spacing">0</property>
-
-      <child>
        <widget class="GtkMenuBar" id="menubar2">
          <property name="visible">True</property>
          <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
            </widget>
          </child>
        </widget>
-       <packing>
-         <property name="padding">0</property>
-         <property name="expand">False</property>
-         <property name="fill">False</property>
-       </packing>
-      </child>
-
-      <child>
+
+
        <widget class="GtkScrolledWindow" id="scrolledwindow8">
          <property name="visible">True</property>
          <property name="can_focus">True</property>
            </widget>
          </child>
        </widget>
-       <packing>
-         <property name="padding">0</property>
-         <property name="expand">True</property>
-         <property name="fill">True</property>
-       </packing>
-      </child>
-
-      <child>
+
        <widget class="GtkStatusbar" id="statusbar2">
          <property name="visible">True</property>
          <property name="has_resize_grip">True</property>
        </widget>
-       <packing>
-         <property name="padding">0</property>
-         <property name="expand">False</property>
-         <property name="fill">False</property>
-       </packing>
-      </child>
-    </widget>
-  </child>
-</widget>
 
 </glade-interface>
diff --git a/src/ui/gui/syntax-editor.h b/src/ui/gui/syntax-editor.h
deleted file mode 100644 (file)
index b3943ec..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  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
-   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 SYNTAX_EDITOR_H
-#define SYNTAX_EDITOR_H
-
-#include <gtk/gtk.h>
-
-#include "window-manager.h"
-
-struct lexer;
-
-struct syntax_editor
-{
-  struct editor_window parent;
-  GtkTextBuffer *buffer;  /* The buffer which contains the text */
-  struct lexer *lexer;    /* Lexer to parse syntax */
-};
-
-
-struct syntax_editor * new_syntax_editor (void);
-
-void new_syntax_window (GtkMenuItem *, gpointer);
-
-void open_syntax_window (GtkMenuItem *, gpointer);
-
-gboolean load_editor_from_file (struct syntax_editor *se,
-                               const gchar *filename,
-                               GError **err);
-#endif
index 9c3ea41daeb3f4511884ac6be6c6a359ad2af7f7..e5a04a076a22d833a2397a9b899c97ef695860ff 100644 (file)
 
 
 #include <config.h>
-#include <glade/glade.h>
 #include <gtk/gtk.h>
 #include "t-test-independent-samples-dialog.h"
 #include "psppire-dict.h"
 #include "psppire-var-store.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-dialog.h"
 #include "dialog-common.h"
 #include "dict-display.h"
@@ -32,7 +31,7 @@
 #include <ui/syntax-gen.h>
 
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 #include <gl/xalloc.h>
 
@@ -91,7 +90,7 @@ tt_groups_dialog_destroy (struct tt_groups_dialog *grps)
 }
 
 static struct tt_groups_dialog *
-tt_groups_dialog_create (GladeXML *xml, GtkWindow *parent)
+tt_groups_dialog_create (GtkBuilder *xml, GtkWindow *parent)
 {
   struct tt_groups_dialog *grps = xmalloc (sizeof (*grps));
 
@@ -127,7 +126,7 @@ tt_groups_dialog_create (GladeXML *xml, GtkWindow *parent)
 
 struct tt_indep_samples_dialog
 {
-  GladeXML *xml;  /* The xml that generated the widgets */
+  GtkBuilder *xml;  /* The xml that generated the widgets */
   GtkWidget *dialog;
   PsppireDict *dict;
   GtkWidget *define_groups_button;
@@ -392,11 +391,11 @@ t_test_independent_samples_dialog (GObject *o, gpointer data)
 {
   struct tt_indep_samples_dialog tt_d;
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   PsppireVarStore *vs = NULL;
 
-  GladeXML *xml = XML_NEW ("t-test.glade");
+  GtkBuilder *xml = builder_new ("t-test.ui");
 
   GtkWidget *dict_view =
     get_widget_assert (xml, "indep-samples-t-test-treeview1");
@@ -421,15 +420,15 @@ t_test_independent_samples_dialog (GObject *o, gpointer data)
 
   tt_d.define_groups_button = get_widget_assert (xml, "define-groups-button");
   tt_d.groups_entry = get_widget_assert (xml, "indep-samples-t-test-entry");
-  tt_d.opts = tt_options_dialog_create (xml, de->parent.window);
-  tt_d.grps = tt_groups_dialog_create (xml, de->parent.window);
+  tt_d.opts = tt_options_dialog_create (xml, GTK_WINDOW (de));
+  tt_d.grps = tt_groups_dialog_create (xml, GTK_WINDOW (de));
 
 
-  gtk_window_set_transient_for (GTK_WINDOW (tt_d.dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (tt_d.dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
+  g_object_set (dict_view, "dictionary", 
                                 vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+                                NULL);
 
   set_dest_model (GTK_TREE_VIEW (test_variables_treeview), vs->dict);
 
@@ -475,6 +474,7 @@ t_test_independent_samples_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&tt_d);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -484,12 +484,7 @@ t_test_independent_samples_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&tt_d);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
+        paste_syntax_in_new_window (syntax);
        g_free (syntax);
       }
       break;
index ccecb075ec8a7fafd2744b8c2bf2473f6f9d01dc..2c48c5d9c6c3dffa690fb6cfa48a3764bfc82e54 100644 (file)
 
 
 #include <config.h>
-#include <glade/glade.h>
 #include <gtk/gtk.h>
 #include "t-test-one-sample.h"
 #include "psppire-dict.h"
 #include "psppire-var-store.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-dialog.h"
 #include "dialog-common.h"
 #include "dict-display.h"
@@ -31,7 +30,6 @@
 
 #include "t-test-options.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
 
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
@@ -124,11 +122,11 @@ t_test_one_sample_dialog (GObject *o, gpointer data)
 {
   struct tt_one_sample_dialog tt_d;
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   PsppireVarStore *vs = NULL;
 
-  GladeXML *xml = XML_NEW ("t-test.glade");
+  GtkBuilder *xml = builder_new ("t-test.ui");
 
   GtkWidget *dict_view =
     get_widget_assert (xml, "one-sample-t-test-treeview2");
@@ -145,14 +143,14 @@ t_test_one_sample_dialog (GObject *o, gpointer data)
   tt_d.dict = vs->dict;
   tt_d.vars_treeview = get_widget_assert (xml, "one-sample-t-test-treeview1");
   tt_d.test_value_entry = get_widget_assert (xml, "test-value-entry");
-  tt_d.opt = tt_options_dialog_create (xml, de->parent.window);
+  tt_d.opt = tt_options_dialog_create (xml, GTK_WINDOW (de));
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
+  g_object_set (dict_view, "dictionary",
                                 vs->dict,
-                                GTK_SELECTION_MULTIPLE,
-                                var_is_numeric);
+       "predicate",
+                                var_is_numeric, NULL);
 
   set_dest_model (GTK_TREE_VIEW (tt_d.vars_treeview), vs->dict);
 
@@ -181,6 +179,7 @@ t_test_one_sample_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&tt_d);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -191,10 +190,7 @@ t_test_one_sample_dialog (GObject *o, gpointer data)
       {
        gchar *syntax = generate_syntax (&tt_d);
 
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index eaf7c43f00da785ea8cd30269343a35008a85b9d..ed8ac042daaf7e3e420eb739dcfd0e32a6ef24a1 100644 (file)
@@ -52,7 +52,7 @@ struct tt_options_dialog
 };
 
 struct tt_options_dialog *
-tt_options_dialog_create (GladeXML *xml, GtkWindow *parent)
+tt_options_dialog_create (GtkBuilder *xml, GtkWindow *parent)
 {
   struct tt_options_dialog *tto = xmalloc (sizeof (*tto));
 
index fdb2d87cf6e744db157e63c26747c1029348b5bf..e80ef44496c5e21e08e0eabd1cafc90a047694ef 100644 (file)
@@ -24,7 +24,7 @@ struct tt_options_dialog;
 
 void tt_options_dialog_run (struct tt_options_dialog *);
 
-struct tt_options_dialog * tt_options_dialog_create (GladeXML *, GtkWindow *);
+struct tt_options_dialog * tt_options_dialog_create (GtkBuilder *, GtkWindow *);
 
 void tt_options_dialog_destroy (struct tt_options_dialog *);
 
index e89e6d74733d01b1033220c1da216ab51c13f244..a239a47193b4eba5eb778355847ea2b1d5bc1490 100644 (file)
 
 #include <config.h>
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include <language/syntax-string-source.h>
 
-#include "data-editor.h"
+#include "psppire-data-window.h"
+#include "psppire-selector.h"
 
 #include "psppire-dict.h"
 #include "psppire-var-store.h"
 #include "t-test-paired-samples.h"
 #include "t-test-options.h"
 
-#include "dict-display.h"
 #include "dialog-common.h"
 #include "psppire-dialog.h"
 
-#include "syntax-editor.h"
-
 #include "helper.h"
 
 #include "psppire-var-ptr.h"
@@ -183,11 +180,11 @@ t_test_paired_samples_dialog (GObject *o, gpointer data)
 {
   struct tt_paired_samples_dialog tt_d;
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   PsppireVarStore *vs = NULL;
 
-  GladeXML *xml = XML_NEW ("t-test.glade");
+  GtkBuilder *xml = builder_new ("t-test.ui");
 
   GtkWidget *dict_view =
     get_widget_assert (xml, "paired-samples-t-test-treeview1");
@@ -203,15 +200,15 @@ t_test_paired_samples_dialog (GObject *o, gpointer data)
   tt_d.dict = vs->dict;
   tt_d.pairs_treeview =
    get_widget_assert (xml, "paired-samples-t-test-treeview2");
-  tt_d.opt = tt_options_dialog_create (xml, de->parent.window);
+  tt_d.opt = tt_options_dialog_create (xml, GTK_WINDOW (de));
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
+  g_object_set (dict_view, "dictionary",
                                 vs->dict,
-                                GTK_SELECTION_MULTIPLE,
-                                var_is_numeric);
+                                "predicate",
+                                var_is_numeric, NULL);
 
   {
     tt_d.list_store =
@@ -255,6 +252,7 @@ t_test_paired_samples_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (&tt_d);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -264,11 +262,7 @@ t_test_paired_samples_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&tt_d);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
index e355daf442b655be6422835c2022ff7bcacb2070..040a99ea432721dd17e7e43354536b5adb3994c5 100644 (file)
@@ -26,7 +26,7 @@
                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="indep-samples-t-test-treeview1">
+                  <widget class="PsppireDictView" id="indep-samples-t-test-treeview1">
                     <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="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
             <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
             <child>
-              <widget class="GtkTreeView" id="one-sample-t-test-treeview2">
+              <widget class="PsppireDictView" id="one-sample-t-test-treeview2">
                 <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="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                 <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                 <child>
-                  <widget class="GtkTreeView" id="paired-samples-t-test-treeview1">
+                  <widget class="PsppireDictView" id="paired-samples-t-test-treeview1">
                     <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>
index 11ca7b044e70e3e99196e99856f2a9d7ab6ee01f..79f77627cf5b9d5b6f2c136e64c46ffd005dfc90 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008  Free Software Foundation
+   Copyright (C) 2008, 2009  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
 
 #include <errno.h>
 
-#include <gtksheet/gtksheet.h>
+#include <gtk-contrib/psppire-sheet.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <libpspp/assertion.h>
 #include <libpspp/message.h>
 #include <ui/syntax-gen.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-sheet.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/helper.h>
 
 #include "error.h"
 #include "xalloc.h"
 #define N_(msgid) msgid
 
 
-#if !GTK_CHECK_VERSION (2, 10, 0)
-
-void
-text_data_import_assistant (GObject *o, gpointer de_)
-{
-  struct data_editor *de = de_;
-
-  GtkWidget *dialog =
-    gtk_message_dialog_new  (de->parent.window,
-                            GTK_DIALOG_MODAL,
-                            GTK_MESSAGE_WARNING,
-                            GTK_BUTTONS_CLOSE,
-                            _("The text import assistant has not been "
-                              "compiled into this build of PSPPIRE, because "
-                              "GTK+ version 2.10.0 or later was not available."));
-
-  gtk_dialog_run (GTK_DIALOG (dialog));
-
-  gtk_widget_destroy (dialog);
-}
-
-#else
-
 /* TextImportModel, a GtkTreeModel used by the text data import
    dialog. */
 enum
@@ -111,7 +88,7 @@ static void destroy_file (struct import_assistant *);
 /* The main body of the GTK+ assistant and related data. */
 struct assistant
   {
-    GladeXML *xml;
+    GtkBuilder *builder;
     GtkAssistant *assistant;
     GMainLoop *main_loop;
     GtkWidget *paste_button;
@@ -256,10 +233,8 @@ static void pop_watch_cursor (struct import_assistant *);
 
 /* Pops up the Text Data Import assistant. */
 void
-text_data_import_assistant (GObject *o, gpointer de_)
+text_data_import_assistant (GObject *o, GtkWindow *parent_window)
 {
-  struct data_editor *de = de_;
-  GtkWindow *parent_window = de->parent.window;
   struct import_assistant *ia;
 
   ia = xzalloc (sizeof *ia);
@@ -293,9 +268,7 @@ text_data_import_assistant (GObject *o, gpointer de_)
     case PSPPIRE_RESPONSE_PASTE:
       {
        char *syntax = generate_syntax (ia);
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
        free (syntax);
       }
       break;
@@ -602,7 +575,7 @@ init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
 {
   struct assistant *a = &ia->asst;
 
-  a->xml = XML_NEW ("text-data-import.glade");
+  a->builder = builder_new ("text-data-import.ui");
   a->assistant = GTK_ASSISTANT (gtk_assistant_new ());
   g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia);
   g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia);
@@ -616,6 +589,7 @@ init_assistant (struct import_assistant *ia, GtkWindow *parent_window)
   gtk_window_set_title (GTK_WINDOW (a->assistant),
                         _("Importing Delimited Text Data"));
   gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window);
+  gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon");
 
   a->prop_renderer = gtk_cell_renderer_text_new ();
   g_object_ref_sink (a->prop_renderer);
@@ -634,7 +608,7 @@ destroy_assistant (struct import_assistant *ia)
 
   g_object_unref (a->prop_renderer);
   g_object_unref (a->fixed_renderer);
-  g_object_unref (a->xml);
+  g_object_unref (a->builder);
 }
 
 /* Appends a page of the given TYPE, with PAGE as its content, to
@@ -675,6 +649,13 @@ static void
 on_prepare (GtkAssistant *assistant, GtkWidget *page,
             struct import_assistant *ia)
 {
+
+  if (gtk_assistant_get_page_type (assistant, page)
+      == GTK_ASSISTANT_PAGE_CONFIRM)
+    gtk_widget_grab_focus (assistant->apply);
+  else
+    gtk_widget_grab_focus (assistant->forward);
+
   if (page == ia->separators.page)
     prepare_separators_page (ia);
   else if (page == ia->formats.page)
@@ -746,17 +727,17 @@ static void on_intro_amount_changed (GtkToggleButton *button,
 static void
 init_intro_page (struct import_assistant *ia)
 {
-  GladeXML *xml = ia->asst.xml;
+  GtkBuilder *builder = ia->asst.builder;
   struct intro_page *p = &ia->intro;
   struct string s;
 
-  p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"),
+  p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"),
                                    GTK_ASSISTANT_PAGE_INTRO);
-  p->all_cases_button = get_widget_assert (xml, "import-all-cases");
-  p->n_cases_button = get_widget_assert (xml, "import-n-cases");
-  p->n_cases_spin = get_widget_assert (xml, "n-cases-spin");
-  p->percent_button = get_widget_assert (xml, "import-percent");
-  p->percent_spin = get_widget_assert (xml, "percent-spin");
+  p->all_cases_button = get_widget_assert (builder, "import-all-cases");
+  p->n_cases_button = get_widget_assert (builder, "import-n-cases");
+  p->n_cases_spin = get_widget_assert (builder, "n-cases-spin");
+  p->percent_button = get_widget_assert (builder, "import-percent");
+  p->percent_spin = get_widget_assert (builder, "percent-spin");
   g_signal_connect (p->all_cases_button, "toggled",
                     G_CALLBACK (on_intro_amount_changed), ia);
   g_signal_connect (p->n_cases_button, "toggled",
@@ -794,7 +775,7 @@ init_intro_page (struct import_assistant *ia)
     }
   ds_put_cstr (&s, _("You may choose below how much of the file should "
                      "actually be imported."));
-  gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")),
+  gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
                       ds_cstr (&s));
   ds_destroy (&s);
 }
@@ -839,14 +820,14 @@ static void
 init_first_line_page (struct import_assistant *ia)
 {
   struct first_line_page *p = &ia->first_line;
-  GladeXML *xml = ia->asst.xml;
+  GtkBuilder *builder = ia->asst.builder;
 
-  p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"),
+  p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"),
                                    GTK_ASSISTANT_PAGE_CONTENT);
-  gtk_widget_destroy (get_widget_assert (xml, "first-line"));
+  gtk_widget_destroy (get_widget_assert (builder, "first-line"));
   p->tree_view = create_lines_tree_view (
-    GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia);
-  p->variable_names_cb = get_widget_assert (xml, "variable-names");
+    GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia);
+  p->variable_names_cb = get_widget_assert (builder, "variable-names");
   gtk_tree_selection_set_mode (
     gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)),
     GTK_SELECTION_BROWSE);
@@ -1020,37 +1001,63 @@ static const struct separator separators[] =
   };
 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
 
+static void
+set_quote_list (GtkComboBoxEntry *cb)
+{
+  GtkListStore *list =  gtk_list_store_new (1, G_TYPE_STRING);
+  GtkTreeIter iter;
+  gint i;
+  const gchar *seperator[3] = {"'\"", "\'", "\""};
+
+  for (i = 0; i < 3; i++)
+    {
+      const gchar *s = seperator[i];
+
+      /* Add a new row to the model */
+      gtk_list_store_append (list, &iter);
+      gtk_list_store_set (list, &iter,
+                          0, s,
+                          -1);
+
+    }
+
+  gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
+
+  gtk_combo_box_entry_set_text_column (cb, 0);
+}
+
 /* Initializes IA's separators substructure. */
 static void
 init_separators_page (struct import_assistant *ia)
 {
-  GladeXML *xml = ia->asst.xml;
+  GtkBuilder *builder = ia->asst.builder;
   struct separators_page *p = &ia->separators;
   size_t i;
 
   choose_likely_separators (ia);
 
-  p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"),
+  p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"),
                                    GTK_ASSISTANT_PAGE_CONTENT);
-  p->custom_cb = get_widget_assert (xml, "custom-cb");
-  p->custom_entry = get_widget_assert (xml, "custom-entry");
-  p->quote_combo = get_widget_assert (xml, "quote-combo");
+  p->custom_cb = get_widget_assert (builder, "custom-cb");
+  p->custom_entry = get_widget_assert (builder, "custom-entry");
+  p->quote_combo = get_widget_assert (builder, "quote-combo");
   p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo)));
-  p->quote_cb = get_widget_assert (xml, "quote-cb");
-  p->escape_cb = get_widget_assert (xml, "escape");
+  p->quote_cb = get_widget_assert (builder, "quote-cb");
+  p->escape_cb = get_widget_assert (builder, "escape");
 
   set_separators (ia);
-  p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields"));
-  g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed",
+  set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo));
+  p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields"));
+  g_signal_connect (p->quote_combo, "changed",
                     G_CALLBACK (on_quote_combo_change), ia);
   g_signal_connect (p->quote_cb, "toggled",
                     G_CALLBACK (on_quote_cb_toggle), ia);
-  g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text",
+  g_signal_connect (p->custom_entry, "notify::text",
                     G_CALLBACK (on_separators_custom_entry_notify), ia);
   g_signal_connect (p->custom_cb, "toggled",
                     G_CALLBACK (on_separators_custom_cb_toggle), ia);
   for (i = 0; i < SEPARATOR_CNT; i++)
-    g_signal_connect (get_widget_assert (xml, separators[i].name),
+    g_signal_connect (get_widget_assert (builder, separators[i].name),
                       "toggled", G_CALLBACK (on_separator_toggle), ia);
   g_signal_connect (p->escape_cb, "toggled",
                     G_CALLBACK (on_separator_toggle), ia);
@@ -1319,7 +1326,7 @@ revise_fields_preview (struct import_assistant *ia)
   choose_column_names (ia);
   ia->separators.fields_tree_view = create_data_tree_view (
     true,
-    GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")),
+    GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")),
     ia);
 
   pop_watch_cursor (ia);
@@ -1360,7 +1367,7 @@ set_separators (struct import_assistant *ia)
   for (i = 0; i < SEPARATOR_CNT; i++)
     {
       const struct separator *s = &separators[i];
-      GtkWidget *button = get_widget_assert (ia->asst.xml, s->name);
+      GtkWidget *button = get_widget_assert (ia->asst.builder, s->name);
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
                                     (seps & (1u << i)) != 0);
     }
@@ -1372,6 +1379,7 @@ set_separators (struct import_assistant *ia)
   ds_destroy (&custom);
 
   any_quotes = !ds_is_empty (&s->quotes);
+
   gtk_entry_set_text (s->quote_entry,
                       any_quotes ? ds_cstr (&s->quotes) : "\"");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb),
@@ -1393,7 +1401,7 @@ get_separators (struct import_assistant *ia)
   for (i = 0; i < SEPARATOR_CNT; i++)
     {
       const struct separator *sep = &separators[i];
-      GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name);
+      GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name);
       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
         ds_put_char (&s->separators, sep->c);
     }
@@ -1527,12 +1535,12 @@ static void clear_modified_vars (struct import_assistant *);
 static void
 init_formats_page (struct import_assistant *ia)
 {
-  GladeXML *xml = ia->asst.xml;
+  GtkBuilder *builder = ia->asst.builder;
   struct formats_page *p = &ia->formats;
 
-  p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"),
+  p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"),
                                    GTK_ASSISTANT_PAGE_CONFIRM);
-  p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "data"));
+  p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data"));
   p->modified_vars = NULL;
   p->modified_var_cnt = 0;
 }
@@ -1623,17 +1631,15 @@ prepare_formats_page (struct import_assistant *ia)
      hold a reference via ia->formats.dict. */
   var_store = psppire_var_store_new (psppire_dict);
   g_object_set (var_store,
-                "trailing-rows", 1,
                 "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS,
                 (void *) NULL);
   var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ());
   g_object_set (var_sheet,
-                "row-geometry", var_store,
                 "model", var_store,
                 "may-create-vars", FALSE,
                 (void *) NULL);
 
-  vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller"));
+  vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller"));
   old_var_sheet = gtk_bin_get_child (vars_scroller);
   if (old_var_sheet != NULL)
     gtk_widget_destroy (old_var_sheet);
@@ -1643,7 +1649,7 @@ prepare_formats_page (struct import_assistant *ia)
   gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view));
   ia->formats.data_tree_view = create_data_tree_view (
     false,
-    GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")),
+    GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")),
     ia);
 
   pop_watch_cursor (ia);
@@ -2292,10 +2298,11 @@ push_watch_cursor (struct import_assistant *ia)
   if (++ia->asst.watch_cursor == 1)
     {
       GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
-      GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
+      GdkDisplay *display = gtk_widget_get_display (widget);
+      GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
       gdk_window_set_cursor (widget->window, cursor);
       gdk_cursor_unref (cursor);
-      gdk_display_flush (gtk_widget_get_display (widget));
+      gdk_display_flush (display);
     }
 }
 
@@ -2306,9 +2313,7 @@ pop_watch_cursor (struct import_assistant *ia)
 {
   if (--ia->asst.watch_cursor == 0)
     {
-      GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);;
+      GtkWidget *widget = GTK_WIDGET (ia->asst.assistant);
       gdk_window_set_cursor (widget->window, NULL);
     }
 }
-
-#endif
index 6ee9dd52c4f706b8a381a46a75df379f383958b6..3b74c8cc5793682ae93e8786aae3210c5156c1ea 100644 (file)
@@ -1,14 +1,16 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.0 on Fri Mar  7 23:09:37 2008 -->
+<!--Generated with glade3 3.4.5 on Thu Dec 18 20:39:52 2008 -->
 <glade-interface>
   <widget class="GtkWindow" id="Intro">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">12</property>
     <property name="title" translatable="yes">Importing Textual Data</property>
     <child>
       <widget class="GtkVBox" id="vbox3">
         <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="spacing">12</property>
         <child>
           <widget class="GtkLabel" id="intro-label">
             <property name="visible">True</property>
@@ -39,6 +41,7 @@ The selected file contains N lines of text.  Only the first M of these will be s
                       <widget class="GtkVBox" id="vbox1">
                         <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="spacing">6</property>
                         <child>
                           <widget class="GtkRadioButton" id="import-all-cases">
                             <property name="visible">True</property>
@@ -54,6 +57,7 @@ The selected file contains N lines of text.  Only the first M of these will be s
                           <widget class="GtkHBox" id="hbox1">
                             <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="spacing">6</property>
                             <child>
                               <widget class="GtkRadioButton" id="import-n-cases">
                                 <property name="visible">True</property>
@@ -109,6 +113,7 @@ The selected file contains N lines of text.  Only the first M of these will be s
                           <widget class="GtkHBox" id="hbox3">
                             <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="spacing">6</property>
                             <child>
                               <widget class="GtkRadioButton" id="import-percent">
                                 <property name="visible">True</property>
@@ -186,11 +191,13 @@ The selected file contains N lines of text.  Only the first M of these will be s
   </widget>
   <widget class="GtkWindow" id="FirstLine">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">12</property>
     <property name="title" translatable="yes">Select Data to Import</property>
     <child>
       <widget class="GtkVBox" id="vbox2">
         <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="spacing">6</property>
         <child>
           <widget class="GtkLabel" id="label3">
             <property name="visible">True</property>
@@ -201,20 +208,6 @@ The selected file contains N lines of text.  Only the first M of these will be s
             <property name="expand">False</property>
           </packing>
         </child>
-        <child>
-          <widget class="GtkCheckButton" id="variable-names">
-            <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="label" translatable="yes">Line above selected line contains variable names</property>
-            <property name="response_id">0</property>
-            <property name="draw_indicator">True</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
         <child>
           <widget class="GtkScrolledWindow" id="first-line-scroller">
             <property name="visible">True</property>
@@ -232,6 +225,20 @@ The selected file contains N lines of text.  Only the first M of these will be s
             </child>
           </widget>
           <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkCheckButton" id="variable-names">
+            <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="label" translatable="yes">Line above selected line contains variable names</property>
+            <property name="response_id">0</property>
+            <property name="draw_indicator">True</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
             <property name="position">2</property>
           </packing>
         </child>
@@ -240,15 +247,18 @@ The selected file contains N lines of text.  Only the first M of these will be s
   </widget>
   <widget class="GtkWindow" id="Separators">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">12</property>
     <property name="title" translatable="yes">Choose Separators</property>
     <child>
       <widget class="GtkVBox" id="vbox4">
         <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="spacing">12</property>
         <child>
           <widget class="GtkHBox" id="hbox2">
             <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="spacing">12</property>
             <child>
               <widget class="GtkFrame" id="foo">
                 <property name="visible">True</property>
@@ -266,38 +276,42 @@ The selected file contains N lines of text.  Only the first M of these will be s
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                         <property name="n_rows">4</property>
                         <property name="n_columns">3</property>
+                        <property name="column_spacing">6</property>
+                        <property name="row_spacing">6</property>
                         <child>
-                          <widget class="GtkCheckButton" id="space">
+                          <widget class="GtkEntry" id="custom-entry">
                             <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="label" translatable="yes">_Space</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
                           </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">3</property>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
+                          </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="tab">
+                          <widget class="GtkCheckButton" id="custom-cb">
                             <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="label" translatable="yes">Ta_b</property>
+                            <property name="label" translatable="yes">C_ustom</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="bang">
+                          <widget class="GtkCheckButton" id="slash">
                             <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="label" translatable="yes">Ban_g (!)</property>
+                            <property name="label" translatable="yes">Slas_h (/)</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
@@ -305,38 +319,40 @@ The selected file contains N lines of text.  Only the first M of these will be s
                           <packing>
                             <property name="left_attach">2</property>
                             <property name="right_attach">3</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="colon">
+                          <widget class="GtkCheckButton" id="semicolon">
                             <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="label" translatable="yes">_Colon (:)</property>
+                            <property name="label" translatable="yes">Semicolo_n (;)</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="comma">
+                          <widget class="GtkCheckButton" id="pipe">
                             <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="label" translatable="yes">Co_mma (,)</property>
+                            <property name="label" translatable="yes">P_ipe (|)</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
                           </packing>
                         </child>
                         <child>
@@ -357,43 +373,43 @@ The selected file contains N lines of text.  Only the first M of these will be s
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="pipe">
+                          <widget class="GtkCheckButton" id="comma">
                             <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="label" translatable="yes">P_ipe (|)</property>
+                            <property name="label" translatable="yes">Co_mma (,)</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="semicolon">
+                          <widget class="GtkCheckButton" id="colon">
                             <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="label" translatable="yes">Semicolo_n (;)</property>
+                            <property name="label" translatable="yes">_Colon (:)</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="slash">
+                          <widget class="GtkCheckButton" id="bang">
                             <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="label" translatable="yes">Slas_h (/)</property>
+                            <property name="label" translatable="yes">Ban_g (!)</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
@@ -401,37 +417,33 @@ The selected file contains N lines of text.  Only the first M of these will be s
                           <packing>
                             <property name="left_attach">2</property>
                             <property name="right_attach">3</property>
-                            <property name="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="custom-cb">
+                          <widget class="GtkCheckButton" id="tab">
                             <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="label" translatable="yes">C_ustom</property>
+                            <property name="label" translatable="yes">Ta_b</property>
                             <property name="use_underline">True</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkEntry" id="custom-entry">
+                          <widget class="GtkCheckButton" id="space">
                             <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="label" translatable="yes">_Space</property>
+                            <property name="use_underline">True</property>
+                            <property name="response_id">0</property>
+                            <property name="draw_indicator">True</property>
                           </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">3</property>
-                            <property name="top_attach">3</property>
-                            <property name="bottom_attach">4</property>
-                          </packing>
                         </child>
                       </widget>
                     </child>
@@ -467,18 +479,22 @@ The selected file contains N lines of text.  Only the first M of these will be s
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                         <property name="n_rows">2</property>
                         <property name="n_columns">2</property>
+                        <property name="column_spacing">6</property>
+                        <property name="row_spacing">6</property>
                         <child>
-                          <widget class="GtkCheckButton" id="quote-cb">
+                          <widget class="GtkCheckButton" id="escape">
                             <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="label" translatable="yes">Quote separator characters with</property>
+                            <property name="label" translatable="yes">Doubled quote mark treated as escape</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="x_options"></property>
-                            <property name="y_options">GTK_FILL</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="y_options"></property>
                           </packing>
                         </child>
                         <child>
@@ -486,10 +502,6 @@ The selected file contains N lines of text.  Only the first M of these will be s
                             <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="has_frame">False</property>
-                            <property name="items" translatable="yes">"'
-"
-'
-</property>
                             <child internal-child="entry">
                               <widget class="GtkEntry" id="quote">
                                 <property name="visible">True</property>
@@ -507,19 +519,17 @@ The selected file contains N lines of text.  Only the first M of these will be s
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkCheckButton" id="escape">
+                          <widget class="GtkCheckButton" id="quote-cb">
                             <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="label" translatable="yes">Doubled quote mark treated as escape</property>
+                            <property name="label" translatable="yes">Quote separator characters with</property>
                             <property name="response_id">0</property>
                             <property name="draw_indicator">True</property>
                           </widget>
                           <packing>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
+                            <property name="x_options"></property>
+                            <property name="y_options">GTK_FILL</property>
                           </packing>
                         </child>
                       </widget>
@@ -598,11 +608,13 @@ The selected file contains N lines of text.  Only the first M of these will be s
   </widget>
   <widget class="GtkWindow" id="Formats">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">12</property>
     <property name="title" translatable="yes">Adjust Variable Formats</property>
     <child>
       <widget class="GtkVBox" id="vbox5">
         <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="spacing">12</property>
         <child>
           <widget class="GtkLabel" id="label12">
             <property name="visible">True</property>
index 621e196c2914ea43e9b2f5995be2acf8f1c0ea91..7ff758b0d279824a67f4c4043feb8b999f648ed1 100644 (file)
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "dict-display.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 #include "dialog-common.h"
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <gettext.h>
 
 #include "psppire-var-store.h"
 
 
-static gchar * generate_syntax (PsppireDict *dict, GladeXML *xml);
+static gchar * generate_syntax (PsppireDict *dict, GtkBuilder *xml);
 
 static void
 refresh (PsppireDialog *dialog, gpointer data)
 {
-  GladeXML *xml = data;
+  GtkBuilder *xml = data;
   GtkWidget *dest = get_widget_assert (xml, "variables-treeview");
   GtkWidget *entry = get_widget_assert (xml, "new-name-entry");
   GtkTreeModel *dmodel = gtk_tree_view_get_model (GTK_TREE_VIEW (dest));
@@ -57,7 +56,7 @@ refresh (PsppireDialog *dialog, gpointer data)
 static gboolean
 dialog_state_valid (gpointer data)
 {
-  GladeXML *xml = data;
+  GtkBuilder *xml = data;
 
   GtkWidget *tv = get_widget_assert (xml, "variables-treeview");
   GtkWidget *entry = get_widget_assert (xml, "new-name-entry");
@@ -80,9 +79,9 @@ void
 transpose_dialog (GObject *o, gpointer data)
 {
   gint response ;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   PsppireVarStore *vs = NULL;
 
@@ -95,9 +94,7 @@ transpose_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  g_object_set (source, "dictionary", vs->dict, NULL);
 
   set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
 
@@ -117,7 +114,7 @@ transpose_dialog (GObject *o, gpointer data)
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  xml);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog),
                                      dialog_state_valid, xml);
@@ -129,6 +126,7 @@ transpose_dialog (GObject *o, gpointer data)
     case GTK_RESPONSE_OK:
       {
        gchar *syntax = generate_syntax (vs->dict, xml);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -138,11 +136,7 @@ transpose_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (vs->dict, xml);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
@@ -159,7 +153,7 @@ transpose_dialog (GObject *o, gpointer data)
      FLIP /VARIABLES=var_list /NEWNAMES=var_name.
   */
 static gchar *
-generate_syntax (PsppireDict *dict, GladeXML *xml)
+generate_syntax (PsppireDict *dict, GtkBuilder *xml)
 {
   const gchar *text;
   GString *string = g_string_new ("FLIP");
index 710d176653502ec876669733632802c03bedebb7..f6d0ab738b74270f508dab07ac82e6e852d132cf 100644 (file)
@@ -270,6 +270,7 @@ on_change (GtkWidget *w, gpointer data)
   gtk_widget_set_sensitive (dialog->change_button, FALSE);
 
   repopulate_dialog (dialog);
+  gtk_widget_grab_focus (dialog->value_entry);
 
   return FALSE;
 }
@@ -296,6 +297,7 @@ on_add (GtkWidget *w, gpointer data)
   gtk_widget_set_sensitive (dialog->add_button, FALSE);
 
   repopulate_dialog (dialog);
+  gtk_widget_grab_focus (dialog->value_entry);
 
   return FALSE;
 }
@@ -311,6 +313,7 @@ on_remove (GtkWidget *w, gpointer data)
   val_labs_remove (dialog->labs, vl->value);
 
   repopulate_dialog (dialog);
+  gtk_widget_grab_focus (dialog->value_entry);
 
   gtk_widget_set_sensitive (dialog->remove_button, FALSE);
 
@@ -361,23 +364,22 @@ on_select_row                  (GtkTreeView *treeview,
 /* Create a new dialog box
    (there should  normally be only one)*/
 struct val_labs_dialog *
-val_labs_dialog_create (GladeXML *xml)
+val_labs_dialog_create (GtkWindow *toplevel)
 {
   GtkTreeViewColumn *column;
 
   GtkCellRenderer *renderer ;
 
-  struct val_labs_dialog *dialog = g_malloc (sizeof (*dialog));
+  GtkBuilder *xml = builder_new ("var-sheet-dialogs.ui");
 
-  connect_help (xml);
+  struct val_labs_dialog *dialog = g_malloc (sizeof (*dialog));
 
   dialog->window = get_widget_assert (xml,"val_labs_dialog");
   dialog->value_entry = get_widget_assert (xml,"value_entry");
   dialog->label_entry = get_widget_assert (xml,"label_entry");
 
   gtk_window_set_transient_for
-    (GTK_WINDOW (dialog->window),
-     GTK_WINDOW (get_widget_assert (xml, "data_editor")));
+    (GTK_WINDOW (dialog->window), toplevel);
 
   dialog->add_button = get_widget_assert (xml, "val_labs_add");
   dialog->remove_button = get_widget_assert (xml, "val_labs_remove");
@@ -434,6 +436,8 @@ val_labs_dialog_create (GladeXML *xml)
 
   dialog->labs = 0;
 
+  g_object_unref (xml);
+
   return dialog;
 }
 
@@ -528,6 +532,8 @@ val_labs_dialog_show (struct val_labs_dialog *dialog)
   gtk_widget_set_sensitive (dialog->change_button, FALSE);
   gtk_widget_set_sensitive (dialog->add_button, FALSE);
 
+  gtk_widget_grab_focus (dialog->value_entry);
+
   repopulate_dialog (dialog);
   gtk_widget_show (dialog->window);
 }
index ffe3ed9b85e701f757ccf4ed829e366d00b7dba4..6b1d0e378b68ca04b95313c57864a3aeaaf277c6 100644 (file)
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include <data/variable.h>
 
 
 struct val_labs;
 
 
-struct val_labs_dialog * val_labs_dialog_create (GladeXML *);
+struct val_labs_dialog * val_labs_dialog_create (GtkWindow *);
 
 void val_labs_dialog_show (struct val_labs_dialog *);
 
index c69730f6f2a104abf607d64a9a335bb15bb15d7e..07c7a4736ff08e8537d62182383592fc4aec0ec1 100644 (file)
@@ -11,7 +11,7 @@
 
 #include "helper.h"
 
-const static gchar none[] = N_("None");
+static const gchar none[] = N_("None");
 
 gchar *
 name_to_string (const struct variable *var, GError **err)
diff --git a/src/ui/gui/var-sheet-dialogs.glade b/src/ui/gui/var-sheet-dialogs.glade
new file mode 100644 (file)
index 0000000..658ca0a
--- /dev/null
@@ -0,0 +1,921 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkWindow" id="var_type_dialog">
+    <property name="border_width">6</property>
+    <property name="title" translatable="yes">Variable Type</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="default_width">485</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <widget class="GtkHBox" id="hbox1">
+        <property name="visible">True</property>
+        <property name="border_width">5</property>
+        <property name="spacing">5</property>
+        <child>
+          <widget class="GtkVBox" id="vbox2">
+            <property name="visible">True</property>
+            <property name="border_width">13</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Numeric</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Comma</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton3">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Dot</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton4">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Scientific notation</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton5">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Date</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton6">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Dollar</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">5</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton7">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Custom currency</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">6</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="radiobutton8">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">String</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">7</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkVBox" id="middle_box">
+            <property name="visible">True</property>
+            <property name="spacing">10</property>
+            <child>
+              <widget class="GtkScrolledWindow" id="scrolledwindow4">
+                <property name="width_request">20</property>
+                <property name="height_request">194</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                <property name="shadow_type">GTK_SHADOW_IN</property>
+                <child>
+                  <widget class="GtkTreeView" id="date_format_list_view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkHBox" id="custom_currency_hbox">
+                <property name="spacing">15</property>
+                <child>
+                  <widget class="GtkScrolledWindow" id="scrolledwindow5">
+                    <property name="width_request">1</property>
+                    <property name="height_request">120</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+                    <property name="shadow_type">GTK_SHADOW_IN</property>
+                    <child>
+                      <widget class="GtkTreeView" id="custom_treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="headers_visible">False</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="Sample">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment2">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox10">
+                            <property name="visible">True</property>
+                            <property name="homogeneous">True</property>
+                            <child>
+                              <widget class="GtkLabel" id="psample_label">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">positive</property>
+                              </widget>
+                            </child>
+                            <child>
+                              <widget class="GtkLabel" id="nsample_label">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">negative</property>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label13">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Sample</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="pack_type">GTK_PACK_END</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="dollar_window">
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                <property name="shadow_type">GTK_SHADOW_IN</property>
+                <child>
+                  <widget class="GtkTreeView" id="dollar_treeview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="width_decimals">
+                <property name="width_request">100</property>
+                <property name="height_request">50</property>
+                <property name="visible">True</property>
+                <property name="n_rows">2</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">2</property>
+                <property name="row_spacing">1</property>
+                <child>
+                  <widget class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkLabel" id="width_label">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Width:</property>
+                        <property name="justify">GTK_JUSTIFY_RIGHT</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="pack_type">GTK_PACK_END</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="decimals_entry">
+                    <property name="width_request">25</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkEntry" id="width_entry">
+                    <property name="width_request">25</property>
+                    <property name="can_focus">True</property>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="decimals_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Decimal Places:</property>
+                    <property name="justify">GTK_JUSTIFY_RIGHT</property>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkVButtonBox" id="vbuttonbox6">
+            <property name="visible">True</property>
+            <property name="spacing">5</property>
+            <property name="layout_style">GTK_BUTTONBOX_START</property>
+            <child>
+              <widget class="GtkButton" id="var_type_ok">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="var_type_cancel">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="help_button_variable_type">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-help</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="val_labs_dialog">
+    <property name="title" translatable="yes">Value Labels</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <widget class="GtkHBox" id="hbox3">
+        <property name="visible">True</property>
+        <property name="border_width">5</property>
+        <child>
+          <widget class="GtkFrame" id="frame1">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <child>
+              <widget class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="border_width">8</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <widget class="GtkTable" id="table3">
+                    <property name="visible">True</property>
+                    <property name="n_rows">2</property>
+                    <property name="n_columns">2</property>
+                    <property name="row_spacing">5</property>
+                    <child>
+                      <widget class="GtkScrolledWindow" id="scrolledwindow3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                        <child>
+                          <widget class="GtkTreeView" id="treeview1">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="headers_visible">False</property>
+                            <property name="enable_search">False</property>
+                          </widget>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkTable" id="table4">
+                        <property name="visible">True</property>
+                        <property name="border_width">5</property>
+                        <property name="n_rows">2</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">5</property>
+                        <property name="row_spacing">4</property>
+                        <child>
+                          <widget class="GtkHBox" id="hbox4">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkEntry" id="value_entry">
+                                <property name="width_request">85</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="padding">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkEntry" id="label_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label6">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Value Label:</property>
+                          </widget>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label5">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Value:</property>
+                          </widget>
+                          <packing>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="right_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkVButtonBox" id="vbuttonbox2">
+                        <property name="visible">True</property>
+                        <property name="border_width">5</property>
+                        <child>
+                          <widget class="GtkButton" id="val_labs_add">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="label">gtk-add</property>
+                            <property name="use_stock">True</property>
+                            <property name="response_id">0</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkButton" id="val_labs_change">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="label">gtk-apply</property>
+                            <property name="use_stock">True</property>
+                            <property name="response_id">0</property>
+                          </widget>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkButton" id="val_labs_remove">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="label">gtk-remove</property>
+                            <property name="use_stock">True</property>
+                            <property name="response_id">0</property>
+                          </widget>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Value Labels</property>
+                <property name="use_markup">True</property>
+              </widget>
+              <packing>
+                <property name="type">label_item</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="padding">10</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkVButtonBox" id="vbuttonbox3">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="spacing">5</property>
+            <property name="layout_style">GTK_BUTTONBOX_START</property>
+            <child>
+              <widget class="GtkButton" id="val_labs_ok">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="val_labs_cancel">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="help_button_value_labels">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-help</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="missing_values_dialog">
+    <property name="border_width">12</property>
+    <property name="title" translatable="yes">Missing Values</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <widget class="GtkVBox" id="vbox3">
+        <property name="visible">True</property>
+        <property name="spacing">12</property>
+        <child>
+          <widget class="GtkFrame" id="frame9">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">GTK_SHADOW_NONE</property>
+            <child>
+              <widget class="GtkAlignment" id="alignment4">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="no_missing">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">_No missing values</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </widget>
+              <packing>
+                <property name="type">label_item</property>
+              </packing>
+            </child>
+          </widget>
+        </child>
+        <child>
+          <widget class="GtkFrame" id="frame4">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">GTK_SHADOW_NONE</property>
+            <child>
+              <widget class="GtkAlignment" id="alignment3">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <widget class="GtkHBox" id="hbox5">
+                    <property name="visible">True</property>
+                    <property name="border_width">5</property>
+                    <property name="spacing">6</property>
+                    <property name="homogeneous">True</property>
+                    <child>
+                      <widget class="GtkEntry" id="mv0">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="mv1">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="mv2">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="discrete_missing">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">_Discrete missing values</property>
+                <property name="use_underline">True</property>
+                <property name="focus_on_click">False</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">no_missing</property>
+              </widget>
+              <packing>
+                <property name="type">label_item</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkFrame" id="frame10">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">GTK_SHADOW_NONE</property>
+            <child>
+              <widget class="GtkAlignment" id="alignment5">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <widget class="GtkTable" id="table1">
+                    <property name="visible">True</property>
+                    <property name="n_rows">3</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">6</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <widget class="GtkLabel" id="label11">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">_Low:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">mv-low</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="mv-low">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label12">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">_High:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">mv-high</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="mv-high">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label10">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Di_screte value:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">mv-discrete</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="mv-discrete">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </widget>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkRadioButton" id="range_missing">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">_Range plus one optional discrete missing value</property>
+                <property name="use_underline">True</property>
+                <property name="focus_on_click">False</property>
+                <property name="response_id">0</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">no_missing</property>
+              </widget>
+              <packing>
+                <property name="type">label_item</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHButtonBox" id="hbuttonbox1">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="missing_val_cancel">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="help_button_missing_values">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-help</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="missing_val_ok">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
index db2e85b5f792bc5b4c5ee96daed8891ae8b0de19..c433bf3b2535e080e926093b2be5193c1760a252 100644 (file)
@@ -21,7 +21,6 @@
 #include <config.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <stdlib.h>
 #include <string.h>
@@ -322,14 +321,14 @@ set_format_type_from_treeview (GtkTreeView *treeview, gpointer data)
 
 
 
-/* Create the structure from the XML definitions */
+/* Create the structure */
 struct var_type_dialog *
-var_type_dialog_create (GladeXML *xml)
+var_type_dialog_create (GtkWindow *toplevel)
 {
   gint i;
   struct var_type_dialog *dialog = g_malloc (sizeof (struct var_type_dialog));
 
-  g_assert (xml);
+  GtkBuilder *xml = builder_new ("var-sheet-dialogs.ui");
 
   dialog->window = get_widget_assert (xml,"var_type_dialog");
   dialog->active_button = -1;
@@ -339,7 +338,7 @@ var_type_dialog_create (GladeXML *xml)
                    G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 
   gtk_window_set_transient_for (GTK_WINDOW (dialog->window),
-                              GTK_WINDOW (get_widget_assert (xml, "data_editor")));
+                               toplevel);
 
   dialog->radioButton[BUTTON_NUMERIC] =
     get_widget_assert (xml,"radiobutton1");
@@ -555,10 +554,10 @@ var_type_dialog_create (GladeXML *xml)
   g_signal_connect (get_widget_assert (xml, "var_type_cancel") , "clicked",
                    G_CALLBACK (hide_dialog),
                    dialog);
-
-
   }
 
+  g_object_unref (xml);
+
   return dialog;
 }
 
index cab34096c3b812fbb748ad5da676be3bc174746f..504b5c7abb04b1f64f9cbb508c937b49186a9730 100644 (file)
@@ -84,7 +84,7 @@ struct var_type_dialog
 };
 
 
-struct var_type_dialog * var_type_dialog_create (GladeXML *xml);
+struct var_type_dialog * var_type_dialog_create (GtkWindow *);
 
 
 void var_type_dialog_set_variable (struct var_type_dialog *dialog,
index 533021e762cb45f73e1a996444cde856e725689a..612643b4351b4447f72b3b35449a1e63793dafd6 100644 (file)
 
 #include <config.h>
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
-#include "dict-display.h"
 #include "var-display.h"
 #include <data/variable.h>
 #include <data/format.h>
 #include <data/value-labels.h>
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "psppire-dialog.h"
 #include "psppire-var-store.h"
+#include "psppire-dictview.h"
 #include "helper.h"
 
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 
 #include <gettext.h>
 #define N_(msgid) msgid
 
 
-static struct variable *
-get_selected_variable (GtkTreeView *treeview)
-{
-  struct variable *var;
-  GtkTreeModel *top_model;
-  GtkTreeIter top_iter;
-
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-
-  GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
-
-  if (! gtk_tree_selection_get_selected (selection,
-                                        &top_model, &top_iter))
-    {
-      return NULL;
-    }
-
-  get_base_model (top_model, &top_iter, &model, &iter);
-
-  g_assert (PSPPIRE_IS_DICT (model));
-
-  gtk_tree_model_get (model,
-                     &iter, DICT_TVM_COL_VAR, &var, -1);
-
-  return var;
-}
-
-
 
 static void
-populate_text (GtkTreeView *treeview, gpointer data)
+populate_text (PsppireDictView *treeview, gpointer data)
 {
   gchar *text = 0;
   GString *gstring;
 
   GtkTextBuffer *textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(data));
-  const struct variable *var = get_selected_variable (treeview);
+  const struct variable *var =
+    psppire_dict_view_get_selected_variable (treeview);
+
   if ( var == NULL)
     return;
 
@@ -158,17 +130,17 @@ treeview_item_selected (gpointer data)
 }
 
 
-static gchar * generate_syntax (GtkTreeView *treeview);
+static gchar * generate_syntax (PsppireDictView *treeview);
 
 
 void
 variable_info_dialog (GObject *o, gpointer data)
 {
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
 
   gint response ;
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("variable-info-dialog.ui");
 
   GtkWidget *dialog = get_widget_assert (xml, "variable-info-dialog");
   GtkWidget *treeview = get_widget_assert (xml, "treeview2");
@@ -178,13 +150,12 @@ variable_info_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (treeview),
-                                vs->dict,
-                                GTK_SELECTION_SINGLE,
-                                NULL );
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
+  g_object_set (treeview,
+               "dictionary", vs->dict,
+               "selection-mode", GTK_SELECTION_SINGLE,
+               NULL);
 
   g_signal_connect (treeview, "cursor-changed", G_CALLBACK (populate_text),
                    textview);
@@ -202,23 +173,21 @@ variable_info_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_GOTO:
       {
        const struct variable *var =
-         get_selected_variable (GTK_TREE_VIEW (treeview));
+         psppire_dict_view_get_selected_variable (PSPPIRE_DICT_VIEW (treeview));
 
        if ( NULL == var)
          goto done;
 
-       g_object_set (de->data_editor, "current-variable",  var_get_dict_index (var), NULL);
+       g_object_set (de->data_editor,
+                     "current-variable", var_get_dict_index (var),
+                     NULL);
       }
 
       break;
     case PSPPIRE_RESPONSE_PASTE:
       {
-       gchar *syntax = generate_syntax (GTK_TREE_VIEW (treeview));
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
+       gchar *syntax = generate_syntax (PSPPIRE_DICT_VIEW (treeview));
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
@@ -232,9 +201,10 @@ variable_info_dialog (GObject *o, gpointer data)
 }
 
 static gchar *
-generate_syntax (GtkTreeView *treeview)
+generate_syntax (PsppireDictView *treeview)
 {
-  const struct variable *var = get_selected_variable (treeview);
+  const struct variable *var =
+    psppire_dict_view_get_selected_variable (treeview);
 
   if ( NULL == var)
     return g_strdup ("");
diff --git a/src/ui/gui/variable-info-dialog.glade b/src/ui/gui/variable-info-dialog.glade
new file mode 100644 (file)
index 0000000..1acb84e
--- /dev/null
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <requires lib="psppire"/>
+  <widget class="PsppireDialog" id="variable-info-dialog">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="title">Variables</property>
+    <property name="modal">True</property>
+    <property name="slidable">True</property>
+    <child internal-child="hbox">
+      <widget class="GtkHPaned" id="dialog-hbox2">
+        <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>
+        <child>
+          <widget class="GtkScrolledWindow" id="scrolledwindow13">
+            <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_NEVER</property>
+            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="shadow_type">GTK_SHADOW_IN</property>
+            <child>
+              <widget class="PsppireDictView" id="treeview2">
+                <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="headers_visible">False</property>
+                <property name="reorderable">True</property>
+                <property name="fixed_height_mode">True</property>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="resize">False</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkVBox" id="vbox23">
+            <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="spacing">5</property>
+            <child>
+              <widget class="GtkLabel" id="label24">
+                <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="xalign">0</property>
+                <property name="label" translatable="yes">Variable Information:</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">5</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="scrolledwindow14">
+                <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_NEVER</property>
+                <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                <property name="shadow_type">GTK_SHADOW_IN</property>
+                <child>
+                  <widget class="GtkTextView" id="textview1">
+                    <property name="height_request">200</property>
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="editable">False</property>
+                    <property name="wrap_mode">GTK_WRAP_WORD_CHAR</property>
+                    <property name="left_margin">3</property>
+                    <property name="cursor_visible">False</property>
+                    <property name="accepts_tab">False</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="padding">5</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="PsppireHButtonBox" id="psppire-hbuttonbox4">
+                <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="border_width">5</property>
+                <property name="homogeneous">True</property>
+                <property name="buttons">PSPPIRE_BUTTON_GOTO_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK | PSPPIRE_BUTTON_PASTE_MASK</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">GTK_PACK_END</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
index 25a0aded6a5fd5ee0bb90a6cec2a451fb9314e0b..e775506f3ec30d0eeb2747865dc4425708fda1e2 100644 (file)
@@ -17,9 +17,7 @@
 #ifndef __VARIABLE_DIALOG_H
 #define __VARIABLE_DIALOG_H
 
-
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 void variable_info_dialog (GObject *o, gpointer data);
 
index 223568bf0e1a7e6af9afd21028301e206221ba87..6fccdfb8e11904c420b38d52ca6a84d4306c510d 100644 (file)
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
 #include "helper.h"
-#include "data-editor.h"
+#include "psppire-data-window.h"
 #include "dict-display.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
@@ -104,10 +103,10 @@ void
 weight_cases_dialog (GObject *o, gpointer data)
 {
   gint response;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
   struct weight_cases_dialog wcd;
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   GtkWidget *dialog = get_widget_assert (xml, "weight-cases-dialog");
   GtkWidget *source = get_widget_assert (xml, "weight-cases-treeview");
@@ -122,7 +121,7 @@ weight_cases_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs,  NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
   g_signal_connect (radiobutton1, "toggled", G_CALLBACK (on_toggle), entry);
   g_signal_connect (selector, "selected", G_CALLBACK (on_select),
@@ -131,11 +130,10 @@ weight_cases_dialog (GObject *o, gpointer data)
   g_signal_connect (selector, "de-selected", G_CALLBACK (on_deselect),
                    radiobutton1);
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_SINGLE,
-                                var_is_numeric
-                                );
+  g_object_set (source, "dictionary", vs->dict,
+                                "selection-mode", GTK_SELECTION_SINGLE,
+                                "predicate", var_is_numeric,
+                                NULL);
 
   psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
                                 source,
@@ -172,12 +170,7 @@ weight_cases_dialog (GObject *o, gpointer data)
     case PSPPIRE_RESPONSE_PASTE:
       {
        gchar *syntax = generate_syntax (&wcd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
+        paste_syntax_in_new_window (syntax);
        g_free (syntax);
       }
       break;
diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c
new file mode 100644 (file)
index 0000000..979224f
--- /dev/null
@@ -0,0 +1,28 @@
+#include <config.h>
+
+#include "widgets.h"
+
+
+#include "psppire-dialog.h"
+#include "psppire-selector.h"
+#include "psppire-vbuttonbox.h"
+#include "psppire-hbuttonbox.h"
+#include "psppire-keypad.h"
+#include "psppire-acr.h"
+#include "psppire-dictview.h"
+
+
+/* Any custom widgets which are to be used in GtkBuilder ui files
+   need to be preregistered, otherwise GtkBuilder refuses to 
+   acknowledge their existence. */
+void
+preregister_widgets (void)
+{
+  psppire_dialog_get_type ();
+  psppire_selector_get_type ();
+  psppire_vbutton_box_get_type ();
+  psppire_hbutton_box_get_type ();
+  psppire_keypad_get_type ();
+  psppire_acr_get_type ();
+  psppire_dict_view_get_type ();
+}
diff --git a/src/ui/gui/widgets.h b/src/ui/gui/widgets.h
new file mode 100644 (file)
index 0000000..74ced04
--- /dev/null
@@ -0,0 +1,22 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 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
+   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 WIDGETS_H
+#define WIDGETS_H
+
+void preregister_widgets (void);
+
+#endif
diff --git a/src/ui/gui/window-manager.c b/src/ui/gui/window-manager.c
deleted file mode 100644 (file)
index 91f44a5..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006, 2007  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
-   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 "relocatable.h"
-
-#include <glib.h>
-#include "syntax-editor.h"
-#include "data-editor.h"
-#include "output-viewer.h"
-
-#include <gettext.h>
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-
-#include "window-manager.h"
-
-
-
-/* A list of struct editor_windows */
-static GSList *window_list = NULL;
-
-
-static void
-deregister_window (GtkWindow *w, gpointer data)
-{
-  struct editor_window *e = data;
-
-  window_list = g_slist_remove (window_list, e);
-
-  if ( g_slist_length (window_list) == 0 )
-    gtk_main_quit ();
-};
-
-
-static void
-register_window (struct editor_window *e)
-{
-  window_list = g_slist_prepend (window_list, e);
-}
-
-
-static gint
-next_window_id (void)
-{
-  return g_slist_length (window_list);
-}
-
-void
-minimise_all_windows (void)
-{
-  const GSList *i = NULL;
-
-  for (i = window_list; i != NULL ; i = i->next)
-    {
-      struct editor_window *e = i->data;
-      gtk_window_iconify (e->window);
-    }
-}
-
-static void set_window_name (struct editor_window *e, const gchar *name );
-
-
-struct editor_window *
-window_create (enum window_type type, const gchar *name)
-{
-  struct editor_window *e;
-  switch (type)
-    {
-    case WINDOW_SYNTAX:
-      e = (struct editor_window *) new_syntax_editor ();
-      break;
-    case WINDOW_DATA:
-      e = (struct editor_window *) new_data_editor ();
-      break;
-    case WINDOW_OUTPUT:
-      e = (struct editor_window *) new_output_viewer ();
-      break;
-    default:
-      g_assert_not_reached ();
-    };
-
-  e->type = type;
-  e->name = NULL;
-
-  set_window_name (e, name);
-
-
-  gtk_window_set_icon_from_file (GTK_WINDOW (e->window),
-                                relocate (PKGDATADIR "/psppicon.png"), 0);
-
-  g_signal_connect (e->window, "destroy",
-                   G_CALLBACK (deregister_window), e);
-
-  register_window (e);
-
-  gtk_widget_show (GTK_WIDGET (e->window));
-
-  return e;
-}
-
-void
-default_window_name (struct editor_window *w)
-{
-  set_window_name (w, NULL);
-}
-
-static void
-set_window_name (struct editor_window *e,
-                const gchar *name )
-{
-  gchar *title ;
-  g_free (e->name);
-
-  e->name = NULL;
-
-  if ( name )
-    {
-      e->name =  g_strdup (name);
-      return;
-    }
-
-  switch (e->type )
-    {
-    case WINDOW_SYNTAX:
-      e->name = g_strdup_printf (_("Syntax%d"), next_window_id () );
-      title = g_strdup_printf (_("%s --- PSPP Syntax Editor"), e->name);
-      break;
-    case WINDOW_DATA:
-      e->name = g_strdup_printf (_("Untitled%d"), next_window_id () );
-      title = g_strdup_printf (_("%s --- PSPP Data Editor"), e->name);
-      break;
-    case WINDOW_OUTPUT:
-      e->name = g_strdup_printf (_("Output%d"), next_window_id () );
-      title = g_strdup_printf (_("%s --- PSPP Output"), e->name);
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-
-  gtk_window_set_title (GTK_WINDOW (e->window), title);
-
-  g_free (title);
-}
-
-
-/* Set the name of this window based on FILENAME.
-   FILENAME is in "filename encoding" */
-void
-window_set_name_from_filename (struct editor_window *e,
-                              const gchar *fn)
-{
-  gchar *title;
-  gchar *filename = g_filename_to_utf8 (fn, -1, NULL, NULL, NULL);
-  gchar *basename = g_path_get_basename (filename);
-
-  set_window_name (e, filename);
-
-  switch (e->type)
-    {
-    case WINDOW_SYNTAX:
-      title = g_strdup_printf (_("%s --- PSPP Syntax Editor"), basename);
-      break;
-    case WINDOW_DATA:
-      title = g_strdup_printf (_("%s --- PSPP Data Editor"), basename);
-      break;
-    default:
-      g_assert_not_reached ();
-    }
-  g_free (basename);
-
-  gtk_window_set_title (GTK_WINDOW (e->window), title);
-
-  g_free (title);
-  g_free (filename);
-}
-
-const gchar *
-window_name (const struct editor_window *e)
-{
-  return e->name;
-}
diff --git a/src/ui/gui/window-manager.h b/src/ui/gui/window-manager.h
deleted file mode 100644 (file)
index 2bb2fd7..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  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
-   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 WINDOW_MANAGER_H
-#define WINDOW_MANAGER_H
-
-#include <gtk/gtk.h>
-
-enum window_type
-  {
-    WINDOW_DATA,
-    WINDOW_SYNTAX,
-    WINDOW_OUTPUT
-  };
-
-
-struct editor_window
- {
-  GtkWindow *window;      /* The top level window of the editor */
-  gchar *name;            /* The name of this editor (UTF-8) */
-  enum window_type type;
- } ;
-
-struct editor_window * window_create (enum window_type type,
-                                     const gchar *name);
-
-const gchar * window_name (const struct editor_window *);
-
-/* Set the name of this window based on FILENAME.
-   FILENAME is in "filename encoding" */
-void window_set_name_from_filename (struct editor_window *e,
-                                   const gchar *filename);
-
-void default_window_name (struct editor_window *w);
-
-void minimise_all_windows (void);
-
-
-#endif
diff --git a/src/ui/source-init-opts.c b/src/ui/source-init-opts.c
new file mode 100644 (file)
index 0000000..43ae5c6
--- /dev/null
@@ -0,0 +1,136 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 <argp.h>
+#include "source-init-opts.h"
+#include <stdbool.h>
+#include <xalloc.h>
+#include <string.h>
+#include <output/output.h>
+#include <data/file-name.h>
+#include <libpspp/getl.h>
+#include <language/syntax-file.h>
+#include <stdlib.h>
+#include <libpspp/llx.h>
+#include <data/por-file-reader.h>
+#include <data/sys-file-reader.h>
+#include <libpspp/message.h>
+#include <ui/syntax-gen.h>
+#include <language/syntax-string-source.h>
+#include <data/file-name.h>
+#include <data/settings.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+static const struct argp_option post_init_options [] = {
+  {"algorithm", 'a', "{compatible|enhanced}", 0, N_("set to `compatible' if you want output calculated from broken algorithms"), 0},
+  {"include", 'I', "DIR", 0, N_("Append DIR to include path"), 0},
+  {"no-include", 'I', 0, 0, N_("Clear include path"), 0},
+  {"no-statrc", 'r', 0, 0, N_("Disable execution of .pspp/rc at startup"), 0},
+  {"config-dir", 'B', "DIR", 0, N_("Set configuration directory to DIR"), 0},
+  {"safer", 's', 0, 0,  N_("Don't allow some unsafe operations"), 0},
+  {"syntax", 'x', "{compatible|enhanced}", 0, N_("Set to `compatible' if you want only to accept SPSS compatible syntax"), 0},
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_post_init_opts (int key, char *arg, struct argp_state *state)
+{
+  struct source_init
+  {
+    bool process_statrc;
+  };
+
+  struct source_init *sip = state->hook;
+
+  struct source_stream *ss = state->input;
+
+  if ( state->input == NULL)
+    return 0;
+
+  switch (key)
+    {
+    case ARGP_KEY_INIT:
+      state->hook = sip = xzalloc (sizeof (struct source_init));
+      sip->process_statrc = true;
+      break;
+    case ARGP_KEY_FINI:
+      free (sip);
+      break;
+    case  'a':
+      if ( 0 == strcmp (arg, "compatible") )
+       settings_set_algorithm (COMPATIBLE);
+      else if ( 0 == strcmp (arg, "enhanced"))
+       settings_set_algorithm (ENHANCED);
+      else
+       {
+         argp_failure (state, 1, 0, _("Algorithm must be either \"compatible\" or \"enhanced\"."));
+       }
+      break;
+    case 'B':
+      config_path = arg;
+      break;
+    case 'I':
+      if (arg == NULL || !strcmp (arg, "-"))
+       getl_clear_include_path (ss);
+      else
+       getl_add_include_dir (ss, arg);
+      break;
+    case 'r':
+      sip->process_statrc = false;
+      break;
+    case ARGP_KEY_SUCCESS:
+      if (sip->process_statrc)
+       {
+         char *pspprc_fn = fn_search_path ("rc", config_path);
+         if (pspprc_fn != NULL)
+           {
+             getl_append_source (ss,
+                                 create_syntax_file_source (pspprc_fn),
+                                 GETL_BATCH,
+                                 ERRMODE_CONTINUE
+                                 );
+
+             free (pspprc_fn);
+           }
+       }
+      break;
+    case 's':
+      settings_set_safer_mode ();
+      break;
+    case 'x':
+      if ( 0 == strcmp (arg, "compatible") )
+       settings_set_syntax (COMPATIBLE);
+      else if ( 0 == strcmp (arg, "enhanced"))
+       settings_set_syntax (ENHANCED);
+      else
+       {
+         argp_failure (state, 1, 0, _("Syntax must be either \"compatible\" or \"enhanced\"."));
+       }
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+const struct argp post_init_argp =
+  {post_init_options, parse_post_init_opts, 0, 0, 0, 0, 0};
+
diff --git a/src/ui/source-init-opts.h b/src/ui/source-init-opts.h
new file mode 100644 (file)
index 0000000..8749353
--- /dev/null
@@ -0,0 +1,24 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 SOURCE_INIT_OPTS
+#define SOURCE_INIT_OPTS
+
+extern const struct argp post_init_argp;
+
+#endif
+
index 501f1f37ffb2b2a61a919cd140ee9d23361cfca9..244e528b0748cd349c1481d3856aff5990c98e8a 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdarg.h>
 #include <stddef.h>
 #include <libpspp/compiler.h>
+#include <libpspp/str.h>
 
 struct fmt_spec;
 struct substring;
index 80c6599ab9704c061c6e181bfc13b2b9ee92230f..5ab098577a57f015040543faf7387960ab46a8cd 100644 (file)
@@ -1,45 +1,35 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-noinst_LIBRARIES += src/ui/terminal/libui.a
+noinst_LTLIBRARIES += src/ui/terminal/libui.la
 
-src_ui_terminal_libui_a_SOURCES = \
-       src/ui/terminal/command-line.c \
-       src/ui/terminal/command-line.h \
+src_ui_terminal_libui_la_SOURCES = \
        src/ui/terminal/read-line.c \
        src/ui/terminal/read-line.h \
        src/ui/terminal/main.c \
        src/ui/terminal/msg-ui.c \
        src/ui/terminal/msg-ui.h \
        src/ui/terminal/terminal.c \
-       src/ui/terminal/terminal.h      
+       src/ui/terminal/terminal.h \
+       src/ui/terminal/terminal-opts.c \
+       src/ui/terminal/terminal-opts.h 
 
-src_ui_terminal_libui_a_CFLAGS = -DINSTALLDIR=\"$(bindir)\" $(NCURSES_CFLAGS)
 
-bin_PROGRAMS += src/ui/terminal/pspp
+src_ui_terminal_libui_la_CFLAGS = -DINSTALLDIR=\"$(bindir)\" $(NCURSES_CFLAGS)
 
+bin_PROGRAMS += src/ui/terminal/pspp
 
 src_ui_terminal_pspp_SOURCES =
 
 src_ui_terminal_pspp_LDADD = \
-       src/ui/terminal/libui.a \
-       src/language/liblanguage.a \
-       src/output/charts/libcharts.a \
-       src/output/liboutput.a \
-       src/math/libpspp_math.a  \
-       src/ui/libuicommon.a \
-       lib/linreg/liblinreg.a  \
-       src/data/libdata.a \
-       src/libpspp/libpspp.a \
-       $(LIBXML2_LIBS) \
-       $(PG_LIBS) \
+       src/ui/terminal/libui.la \
+       src/ui/libuicommon.la \
+       src/libpspp.la \
+       src/libpspp-core.la \
        $(NCURSES_LIBS) \
        $(LIBICONV) \
-       gl/libgl.la \
        @LIBINTL@ @LIBREADLINE@
 
 
-
-
 src_ui_terminal_pspp_LDFLAGS = $(PSPP_LDFLAGS) $(PG_LDFLAGS)
 
 if RELOCATABLE_VIA_LD
diff --git a/src/ui/terminal/command-line.c b/src/ui/terminal/command-line.c
deleted file mode 100644 (file)
index 95b22ce..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 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 "command-line.h"
-#include "msg-ui.h"
-#include <libpspp/message.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <errno.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <libpspp/assertion.h>
-#include <libpspp/copyleft.h>
-#include <libpspp/message.h>
-#include <language/syntax-file.h>
-#include "progname.h"
-#include <data/settings.h>
-#include <output/output.h>
-#include <data/file-name.h>
-#include <libpspp/getl.h>
-#include <libpspp/str.h>
-#include <libpspp/version.h>
-#include <libpspp/verbose-msg.h>
-#include "read-line.h"
-
-#include "xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-static void usage (void);
-
-/* Parses the command line specified by ARGC and ARGV as received by
-   main().  Returns true if normal execution should proceed,
-   false if the command-line indicates that PSPP should exit. */
-bool
-parse_command_line (int argc, char **argv, struct source_stream *ss)
-{
-  static struct option long_options[] =
-  {
-    {"algorithm", required_argument, NULL, 'a'},
-    {"command", required_argument, NULL, 'c'},
-    {"config-directory", required_argument, NULL, 'B'},
-    {"device", required_argument, NULL, 'o'},
-    {"dry-run", no_argument, NULL, 'n'},
-    {"edit", no_argument, NULL, 'n'},
-    {"error-file", required_argument, NULL, 'e'},
-    {"help", no_argument, NULL, 'h'},
-    {"include-directory", required_argument, NULL, 'I'},
-    {"interactive", no_argument, NULL, 'i'},
-    {"just-print", no_argument, NULL, 'n'},
-    {"list", no_argument, NULL, 'l'},
-    {"no-include", no_argument, NULL, 'I'},
-    {"no-statrc", no_argument, NULL, 'r'},
-    {"out-file", required_argument, NULL, 'f'},
-    {"pipe", no_argument, NULL, 'p'},
-    {"recon", no_argument, NULL, 'n'},
-    {"safer", no_argument, NULL, 's'},
-    {"syntax", required_argument, NULL, 'x'},
-    {"testing-mode", no_argument, NULL, 'T'},
-    {"verbose", no_argument, NULL, 'v'},
-    {"version", no_argument, NULL, 'V'},
-    {0, 0, 0, 0},
-  };
-
-  int c, i;
-
-  bool cleared_device_defaults = false;
-  bool process_statrc = true;
-  bool interactive_mode = false;
-  int syntax_files = 0;
-
-  for (;;)
-    {
-      c = getopt_long (argc, argv, "a:x:B:c:e:f:hiI:lno:prsvV", long_options, NULL);
-      if (c == -1)
-       break;
-
-      switch (c)
-       {
-         /* Compatibility options */
-        case 'a':
-         if ( 0 == strcmp(optarg,"compatible") )
-             settings_set_algorithm(COMPATIBLE);
-         else if ( 0 == strcmp(optarg,"enhanced"))
-             settings_set_algorithm(ENHANCED);
-         else
-           {
-             usage ();
-              return false;
-           }
-         break;
-
-       case 'x':
-         if ( 0 == strcmp(optarg,"compatible") )
-           settings_set_syntax (COMPATIBLE);
-         else if ( 0 == strcmp(optarg,"enhanced"))
-           settings_set_syntax (ENHANCED);
-         else
-           {
-             usage ();
-              return false;
-           }
-         break;
-       case 'e':
-         msg_ui_set_error_file (optarg);
-         break;
-       case 'B':
-         config_path = optarg;
-         break;
-       case 'f':
-         printf (_("%s is not yet implemented."), "-f");
-          putchar('\n');
-         break;
-       case 'h':
-         usage ();
-          return false;
-       case 'i':
-         interactive_mode = true;
-         break;
-       case 'I':
-         if (optarg == NULL || !strcmp (optarg, "-"))
-           getl_clear_include_path (ss);
-         else
-           getl_add_include_dir (ss, optarg);
-         break;
-       case 'l':
-         outp_list_classes ();
-          return false;
-       case 'n':
-         printf (_("%s is not yet implemented."),"-n");
-          putchar('\n');
-         break;
-       case 'o':
-         if (!cleared_device_defaults)
-           {
-             outp_configure_clear ();
-             cleared_device_defaults = true;
-           }
-         outp_configure_add (optarg);
-         break;
-       case 'p':
-         printf (_("%s is not yet implemented."),"-p");
-          putchar('\n');
-         break;
-       case 'r':
-         process_statrc = false;
-         break;
-       case 's':
-         settings_set_safer_mode ();
-         break;
-       case 'v':
-         verbose_increment_level ();
-         break;
-       case 'V':
-         puts (version);
-         puts (legal);
-         return false;
-        case 'T':
-          settings_set_testing_mode (true);
-          break;
-       case '?':
-         usage ();
-          return false;
-       case 0:
-         break;
-       default:
-         NOT_REACHED ();
-       }
-    }
-
-  if (process_statrc)
-    {
-      char *pspprc_fn = fn_search_path ("rc", config_path);
-      if (pspprc_fn != NULL)
-        {
-         getl_append_source (ss,
-                             create_syntax_file_source (pspprc_fn),
-                             GETL_BATCH,
-                             ERRMODE_CONTINUE
-                             );
-
-          free (pspprc_fn);
-        }
-    }
-
-  for (i = optind; i < argc; i++)
-    if (strchr (argv[i], '='))
-      outp_configure_macro (argv[i]);
-    else
-      {
-       getl_append_source (ss,
-                           create_syntax_file_source (argv[i]),
-                           GETL_BATCH,
-                           ERRMODE_CONTINUE
-                           );
-        syntax_files++;
-      }
-
-  if (!syntax_files || interactive_mode)
-    {
-      getl_append_source (ss, create_readln_source (),
-                         GETL_INTERACTIVE,
-                         ERRMODE_CONTINUE
-                         );
-      if (!cleared_device_defaults)
-        outp_configure_add ("interactive");
-    }
-
-  return true;
-}
-
-/* Message that describes PSPP command-line syntax. */
-static const char pre_syntax_message[] =
-N_("PSPP, a program for statistical analysis of sample data.\n"
-"\nUsage: %s [OPTION]... FILE...\n"
-"\nIf a long option shows an argument as mandatory, then it is mandatory\n"
-"for the equivalent short option also.  Similarly for optional arguments.\n"
-"\nConfiguration:\n"
-"  -a, --algorithm={compatible|enhanced}\n"
-"                            set to `compatible' if you want output\n"
-"                            calculated from broken algorithms\n"
-"  -B, --config-dir=DIR      set configuration directory to DIR\n"
-"  -o, --device=DEVICE       select output driver DEVICE and disable defaults\n"
-"\nInput and output:\n"
-"  -e, --error-file=FILE     send error messages to FILE (appended)\n"
-"  -f, --out-file=FILE       send output to FILE (overwritten)\n"
-"  -p, --pipe                read syntax from stdin, send output to stdout\n"
-"  -I-, --no-include         clear include path\n"
-"  -I, --include=DIR         append DIR to include path\n"
-"\nLanguage modifiers:\n"
-"  -i, --interactive         interpret syntax in interactive mode\n"
-"  -n, --edit                just check syntax; don't actually run the code\n"
-"  -r, --no-statrc           disable execution of .pspp/rc at startup\n"
-"  -s, --safer               don't allow some unsafe operations\n"
-"  -x, --syntax={compatible|enhanced}\n"
-"                            set to `compatible' if you want only to accept\n"
-"                            spss compatible syntax\n"
-"\nInformative output:\n"
-"  -h, --help                print this help, then exit\n"
-"  -l, --list                print a list of known driver classes, then exit\n"
-"  -V, --version             show PSPP version, then exit\n"
-"  -v, --verbose             increments verbosity level\n"
-"\nNon-option arguments:\n"
-" FILE                       syntax file to execute\n"
-" KEY=VALUE                  overrides macros in output initialization file\n"
-"\n");
-
-/* Message that describes PSPP command-line syntax, continued. */
-static const char post_syntax_message[] = N_("\nReport bugs to <%s>.\n");
-
-/* Writes a syntax description to stdout. */
-static void
-usage (void)
-{
-  printf (gettext (pre_syntax_message), program_name);
-  outp_list_classes ();
-  printf (gettext (post_syntax_message), PACKAGE_BUGREPORT);
-}
diff --git a/src/ui/terminal/command-line.h b/src/ui/terminal/command-line.h
deleted file mode 100644 (file)
index 601c73c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 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/>. */
-
-#if !INCLUDED_CMDLINE_H
-#define INCLUDED_CMDLINE_H 1
-
-#include <stdbool.h>
-
-struct source_stream ;
-
-bool parse_command_line (int argc, char **argv, struct source_stream *);
-
-#endif /* cmdline.h */
index b8cabb3acb0fff818faf3e1de71766309b21f7ce..e37cace39ec05af2527c6e23493689b46e75d75b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <math/random.h>
 #include <output/output.h>
 #include <ui/debugger.h>
-#include <ui/terminal/command-line.h>
 #include <ui/terminal/msg-ui.h>
 #include <ui/terminal/read-line.h>
 #include <ui/terminal/terminal.h>
+#include <ui/terminal/terminal-opts.h>
+#include <ui/command-line.h>
+#include <ui/source-init-opts.h>
 
 #include "fatal-signal.h"
 #include "progname.h"
@@ -73,12 +75,15 @@ static struct dataset * the_dataset = NULL;
 static struct lexer *the_lexer;
 static struct source_stream *the_source_stream ;
 
+const char *argp_program_version = version;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
 /* Program entry point. */
 int
 main (int argc, char **argv)
 {
   int *view_width_p, *view_length_p;
-
+  struct command_line_processor *clp;
   set_program_name (argv[0]);
 
   signal (SIGABRT, bug_handler);
@@ -105,37 +110,58 @@ main (int argc, char **argv)
 
   the_dataset = create_dataset ();
 
-  if (parse_command_line (argc, argv, the_source_stream))
+
+
+  clp = command_line_processor_create (_("PSPP --- A program for statistical analysis"),
+                                      _("FILE1, FILE2 ... FILEn"), NULL);
+
+  command_line_processor_add_options (clp, &io_argp,
+                                     _("Options affecting input and output locations:"), the_source_stream);
+
+  command_line_processor_add_options (clp, &test_argp,
+                                     _("Diagnositic options:"), the_source_stream);
+
+  command_line_processor_add_options (clp, &post_init_argp,
+                                     _("Options affecting syntax and behavior:"), the_source_stream);
+
+  command_line_processor_parse (clp, argc, argv);
+
+  msg_ui_init (the_source_stream);
+
+  if (!settings_get_testing_mode ())
+    {
+      outp_read_devices ();
+    }
+  else
+    {
+      outp_configure_driver_line
+       (
+        ss_cstr ("raw-ascii:ascii:listing:width=9999 length=9999 "
+                 "output-file=\"pspp.list\" emphasis=none "
+                 "headers=off paginate=off squeeze=on "
+                 "top-margin=0 bottom-margin=0"));
+    }
+
+  the_lexer = lex_create (the_source_stream);
+
+  for (;;)
     {
-      msg_ui_init (the_source_stream);
-      if (!settings_get_testing_mode ())
-        outp_read_devices ();
+      int result = cmd_parse (the_lexer, the_dataset);
+
+      if (result == CMD_EOF || result == CMD_FINISH)
+       break;
+      if (result == CMD_CASCADING_FAILURE &&
+         !getl_is_interactive (the_source_stream))
+       {
+         msg (SE, _("Stopping syntax file processing here to avoid "
+                    "a cascade of dependent command failures."));
+         getl_abort_noninteractive (the_source_stream);
+       }
       else
-        outp_configure_driver_line (
-          ss_cstr ("raw-ascii:ascii:listing:width=9999 length=9999 "
-                   "output-file=\"pspp.list\" emphasis=none "
-                   "headers=off paginate=off squeeze=on "
-                   "top-margin=0 bottom-margin=0"));
-      the_lexer = lex_create (the_source_stream);
-
-      for (;;)
-        {
-          int result = cmd_parse (the_lexer, the_dataset);
-
-          if (result == CMD_EOF || result == CMD_FINISH)
-            break;
-          if (result == CMD_CASCADING_FAILURE &&
-             !getl_is_interactive (the_source_stream))
-            {
-              msg (SE, _("Stopping syntax file processing here to avoid "
-                         "a cascade of dependent command failures."));
-              getl_abort_noninteractive (the_source_stream);
-            }
-          else
-            check_msg_count (the_source_stream);
-        }
+       check_msg_count (the_source_stream);
     }
 
+
   clean_up ();
   return any_errors ();
 }
index b03efb95e5712456cc5929dd42ab7f61cb75d3c4..682d753d8fbd36200f68e33677c239e5ef5b6c17 100644 (file)
@@ -267,7 +267,7 @@ write_stream (int line_indent, struct substring line, void *stream_)
 
 /* Writes LINE to the journal. */
 static void
-write_journal (int line_indent, struct substring line, void *unused UNUSED)
+write_journal (int line_indent UNUSED, struct substring line, void *unused UNUSED)
 {
   char *s = xstrndup (ss_data (line), ss_length (line));
   journal_write (true, s);
diff --git a/src/ui/terminal/terminal-opts.c b/src/ui/terminal/terminal-opts.c
new file mode 100644 (file)
index 0000000..d2ddc6e
--- /dev/null
@@ -0,0 +1,198 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 <argp.h>
+#include <stdbool.h>
+#include <xalloc.h>
+#include <stdlib.h>
+#include <data/settings.h>
+#include <output/output.h>
+#include "msg-ui.h"
+#include <ui/command-line.h>
+#include <libpspp/verbose-msg.h>
+#include <libpspp/llx.h>
+#include <data/file-name.h>
+#include "terminal-opts.h"
+#include <libpspp/getl.h>
+#include <language/syntax-file.h>
+#include "read-line.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+static const struct argp_option test_options [] =
+  {
+    {"verbose", 'v', 0, 0, N_("Increase diagnostic verbosity level"), 0},
+    {"testing-mode", 'T', 0, OPTION_HIDDEN, 0, 0},
+
+    { 0, 0, 0, 0, 0, 0 }
+  };
+
+static error_t
+parse_test_opts (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case 'T':
+      settings_set_testing_mode (true);
+      break;
+    case 'v':
+      verbose_increment_level ();
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const struct argp_option io_options [] =
+  {
+    {"error-file", 'e', "FILE", 0,
+     N_("Send error messages to FILE (appended)"), 0},
+
+    {"device", 'o', "DEVICE", 0,
+     N_("Select output driver DEVICE and disable defaults"), 0},
+
+    {"list", 'l', 0, 0,
+     N_("Print a list of known driver classes, then exit"), 0},
+
+    {"interactive", 'i', 0, 0, N_("Start an interactive session"), 0},
+
+    { 0, 0, 0, 0, 0, 0 }
+  };
+
+
+static error_t
+parse_io_opts (int key, char *arg, struct argp_state *state)
+{
+  struct source_init
+  {
+    struct llx_list file_list;
+    bool cleared_device_defaults;
+    bool interactive;
+  };
+
+  struct fn_element {
+    struct ll ll;
+    const char *fn;
+  };
+
+  struct source_init *sip = state->hook;
+
+  struct source_stream *ss = state->input;
+
+  struct command_line_processor *clp = get_subject (state);
+
+  switch (key)
+    {
+    case ARGP_KEY_INIT:
+      state->hook = sip = xzalloc (sizeof (struct source_init));
+      llx_init (&sip->file_list);
+      break;
+    case ARGP_KEY_ARG:
+      if (strchr (arg, '='))
+       outp_configure_macro (arg);
+      else
+       {
+         llx_push_tail (&sip->file_list, arg, &llx_malloc_mgr);
+       }
+      break;
+    case ARGP_KEY_SUCCESS:
+      {
+      struct llx *llx = llx_null (&sip->file_list);
+      while ((llx = llx_next (llx)) != llx_null (&sip->file_list))
+       {
+         const char *fn = llx_data (llx);
+         /* Assume it's a syntax file */
+         getl_append_source (ss,
+                             create_syntax_file_source (fn),
+                             GETL_BATCH,
+                             ERRMODE_CONTINUE
+                             );
+
+       }
+
+      if (sip->interactive || llx_is_empty (&sip->file_list))
+       {
+         getl_append_source (ss, create_readln_source (),
+                             GETL_INTERACTIVE,
+                             ERRMODE_CONTINUE
+                             );
+
+         if (!sip->cleared_device_defaults)
+           outp_configure_add ("interactive");
+       }
+      }
+      break;
+    case ARGP_KEY_FINI:
+      free (sip);
+      break;
+    case 'e':
+      msg_ui_set_error_file (arg);
+      break;
+    case 'i':
+      sip->interactive = true;
+      break;
+    case 'l':
+      outp_list_classes ();
+      break;
+    case 'o':
+      if (! sip->cleared_device_defaults)
+       {
+         outp_configure_clear ();
+         sip->cleared_device_defaults = true;
+       }
+      outp_configure_add (arg);
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+const struct argp io_argp =  {io_options, parse_io_opts, 0, 0, 0, 0, 0};
+const struct argp test_argp =  {test_options, parse_test_opts, 0, 0, 0, 0, 0};
+
+#if 0
+static const struct argp_child children [] =
+  {
+    {&io_argp, 0, N_("Options affecting input and output locations:"), 0},
+    {&test_argp, 0, N_("Diagnostic options:"), 0},
+    {0, 0, 0, 0}
+  };
+
+
+static error_t
+propagate_aux (int key, char *arg, struct argp_state *state)
+{
+  if ( key == ARGP_KEY_INIT)
+    {
+      int i;
+      for (i = 0 ; i < sizeof (children) / sizeof (children[0]) - 1 ; ++i)
+       state->child_inputs[i] = state->input;
+    }
+
+  return ARGP_ERR_UNKNOWN;
+}
+
+const struct argp terminal_argp =  {NULL, propagate_aux, 0, 0, children, 0, 0};
+
+#endif
diff --git a/src/ui/terminal/terminal-opts.h b/src/ui/terminal/terminal-opts.h
new file mode 100644 (file)
index 0000000..e5d032d
--- /dev/null
@@ -0,0 +1,27 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  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
+   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 TERMINAL_OPTS
+#define TERMINAL_OPTS
+
+extern const struct argp io_argp ;
+extern const struct argp test_argp ;
+
+extern const struct argp terminal_argp;
+
+#endif
+
index b0039df456be5dffdbd5cae89c1d9a660650f700..6db16ebc47418e188feca860916ac67afeb72c53 100644 (file)
@@ -6,8 +6,12 @@ TESTS_ENVIRONMENT += PERL='@PERL@' PG_CONFIG='@PG_CONFIG@'
 # Allow locale_charset to find charset.alias before running "make install".
 TESTS_ENVIRONMENT += CHARSETALIASDIR='$(abs_top_builddir)/gl'
 
+TESTS_ENVIRONMENT += LC_ALL=C
+
 dist_TESTS = \
+       tests/command/add-files.sh \
        tests/command/aggregate.sh \
+       tests/command/attributes.sh \
        tests/command/autorecod.sh \
        tests/command/beg-data.sh \
        tests/command/bignum.sh \
@@ -31,6 +35,7 @@ dist_TESTS = \
        tests/command/input-program.sh \
        tests/command/insert.sh \
        tests/command/lag.sh \
+       tests/command/line-ends.sh \
        tests/command/list.sh \
        tests/command/loop.sh \
        tests/command/longvars.sh \
@@ -40,6 +45,7 @@ dist_TESTS = \
        tests/command/n_of_cases.sh \
        tests/command/npar-binomial.sh \
        tests/command/npar-chisquare.sh \
+       tests/command/npar-wilcoxon.sh \
        tests/command/oneway.sh \
        tests/command/oneway-missing.sh \
        tests/command/oneway-with-splits.sh \
@@ -50,6 +56,7 @@ dist_TESTS = \
        tests/command/rename.sh \
        tests/command/regression.sh \
        tests/command/regression-qr.sh \
+       tests/command/reliability.sh \
        tests/command/sample.sh \
        tests/command/sort.sh \
        tests/command/sysfiles.sh \
@@ -68,6 +75,7 @@ dist_TESTS = \
        tests/command/t-test-pairs.sh \
        tests/command/trimmed-mean.sh \
        tests/command/tabs.sh \
+       tests/command/update.sh \
        tests/command/use.sh \
        tests/command/variable-display.sh \
        tests/command/vector.sh \
@@ -107,6 +115,8 @@ dist_TESTS = \
        tests/bugs/double-frequency.sh \
        tests/bugs/empty-do-repeat.sh \
        tests/bugs/get.sh \
+       tests/bugs/examine-crash.sh \
+       tests/bugs/examine-crash2.sh \
        tests/bugs/examine-1sample.sh \
        tests/bugs/examine-missing.sh \
        tests/bugs/examine-missing2.sh \
@@ -168,6 +178,8 @@ nodist_TESTS = \
        tests/libpspp/abt-test \
        tests/libpspp/bt-test \
        tests/libpspp/heap-test \
+       tests/libpspp/hmap-test \
+       tests/libpspp/hmapx-test \
        tests/libpspp/ll-test \
        tests/libpspp/llx-test \
        tests/libpspp/range-map-test \
@@ -187,6 +199,7 @@ tests_libpspp_ll_test_SOURCES = \
        src/libpspp/ll.h \
        tests/libpspp/ll-test.c
 tests_libpspp_ll_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_ll_test_CFLAGS = $(AM_CFLAGS)
 
 tests_libpspp_llx_test_SOURCES = \
        src/libpspp/ll.c \
@@ -195,6 +208,7 @@ tests_libpspp_llx_test_SOURCES = \
        src/libpspp/llx.h \
        tests/libpspp/llx-test.c
 tests_libpspp_llx_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_llx_test_CFLAGS = $(AM_CFLAGS)
 
 tests_libpspp_heap_test_SOURCES = \
        src/libpspp/heap.c \
@@ -202,9 +216,25 @@ tests_libpspp_heap_test_SOURCES = \
        src/libpspp/pool.c \
        src/libpspp/pool.h \
        tests/libpspp/heap-test.c
-tests_libpspp_heap_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_heap_test_LDADD = gl/libgl.la @LIBINTL@ 
 tests_libpspp_heap_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
+tests_libpspp_hmap_test_SOURCES = \
+       src/libpspp/hmap.c \
+       src/libpspp/hmap.h \
+       tests/libpspp/hmap-test.c
+tests_libpspp_hmap_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_hmap_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
+tests_libpspp_hmapx_test_SOURCES = \
+       src/libpspp/hmap.c \
+       src/libpspp/hmap.h \
+       src/libpspp/hmapx.c \
+       src/libpspp/hmapx.h \
+       tests/libpspp/hmapx-test.c
+tests_libpspp_hmapx_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_hmapx_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
 tests_libpspp_abt_test_SOURCES = \
        src/libpspp/abt.c \
        src/libpspp/abt.h \
@@ -236,12 +266,12 @@ tests_libpspp_range_set_test_SOURCES = \
        src/libpspp/range-set.c \
        src/libpspp/range-set.h \
        tests/libpspp/range-set-test.c
-tests_libpspp_range_set_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_range_set_test_LDADD = gl/libgl.la @LIBINTL@ 
 tests_libpspp_range_set_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
 tests_libpspp_str_test_SOURCES = \
        tests/libpspp/str-test.c
-tests_libpspp_str_test_LDADD = src/libpspp/libpspp.a gl/libgl.la @LIBINTL@
+tests_libpspp_str_test_LDADD = src/libpspp/libpspp.la gl/libgl.la @LIBINTL@ 
 
 tests_libpspp_tower_test_SOURCES = \
        src/libpspp/abt.c \
@@ -251,7 +281,7 @@ tests_libpspp_tower_test_SOURCES = \
        src/libpspp/tower.c \
        src/libpspp/tower.h \
        tests/libpspp/tower-test.c
-tests_libpspp_tower_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_tower_test_LDADD = gl/libgl.la @LIBINTL@ 
 tests_libpspp_tower_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
 tests_libpspp_sparse_array_test_SOURCES = \
@@ -260,7 +290,7 @@ tests_libpspp_sparse_array_test_SOURCES = \
        src/libpspp/pool.c \
        src/libpspp/pool.h \
        tests/libpspp/sparse-array-test.c
-tests_libpspp_sparse_array_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_sparse_array_test_LDADD = gl/libgl.la @LIBINTL@ 
 tests_libpspp_sparse_array_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
 tests_formats_inexactify_SOURCES = tests/formats/inexactify.c
@@ -270,7 +300,7 @@ tests_dissect_sysfile_SOURCES = \
        src/libpspp/integer-format.c \
        src/libpspp/float-format.c \
        tests/dissect-sysfile.c
-tests_dissect_sysfile_LDADD = gl/libgl.la @LIBINTL@
+tests_dissect_sysfile_LDADD = gl/libgl.la @LIBINTL@ 
 tests_dissect_sysfile_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
 
 EXTRA_DIST += \
@@ -313,11 +343,12 @@ EXTRA_DIST += \
 
 CLEANFILES += *.save pspp.* foo*
 
-DIST_HOOKS += check-for-export-var-val
 check-for-export-var-val:
        @if grep -q 'export .*=' $(dist_TESTS) ; then \
         echo 'One or more tests contain non-portable "export VAR=val" syntax' ; \
         false ; \
        fi
 
+DIST_HOOKS += check-for-export-var-val
+
 EXTRA_DIST += tests/OChangeLog
diff --git a/tests/bugs/examine-crash.sh b/tests/bugs/examine-crash.sh
new file mode 100755 (executable)
index 0000000..6cd172f
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+# This program tests for a bug which crashed EXAMINE
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir  are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR"
+       return ; 
+     fi
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+cat <<EOF > $TESTFILE
+data list list /a * x * y *.
+begin data.
+3 1 3
+5 1 4
+7 2 3
+end data.
+
+examine a by x by y
+       /statistics=DESCRIPTIVES
+       . 
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;
diff --git a/tests/bugs/examine-crash2.sh b/tests/bugs/examine-crash2.sh
new file mode 100755 (executable)
index 0000000..01fe8f9
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+# This program tests for a bug which crashed pspp
+# when two consecutive EXAMINE commands are run.
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir  are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR"
+       return ; 
+     fi
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+cat <<EOF > $TESTFILE
+data list list /y * z *.
+begin data.
+6 4
+5 3
+7 6
+end data.
+
+EXAMINE /VARIABLES= z BY y.
+
+EXAMINE /VARIABLES= z. 
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;
index 965b600ee230f2eff7e9adf5e8bcbc69d2cfba95..2cd6c14a6a00d930722149a77d5fc5da0d86fe7e 100755 (executable)
@@ -59,7 +59,7 @@ mkdir -p $TEMPDIR
 
 cd $TEMPDIR
 
-activity="create program"
+activity="create program 1"
 cat > $TESTFILE << EOF
 DATA LIST LIST /x * y *.
 BEGIN DATA.
@@ -77,7 +77,7 @@ EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-activity="run program"
+activity="run program 1"
 $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
@@ -92,27 +92,54 @@ diff -b  $TEMPDIR/pspp.list - <<EOF
 |y       |F8.0  |
 +--------+------+
 2.1 EXAMINE.  Case Processing Summary
-#=#=============================#
-# #            Cases            #
-# #---------+---------+---------#
-# #  Valid  | Missing |  Total  #
-# #-+-------+-+-------+-+-------#
-# #N|Percent|N|Percent|N|Percent#
-#=#=#=======#=#=======#=#=======#
-#x#6|    86%|1|    14%|7|   100%#
-#=#=#=======#=#=======#=#=======#
+#=#===============================#
+# #             Cases             #
+# #----------+----------+---------#
+# #   Valid  |  Missing |  Total  #
+# #-+--------+-+--------+-+-------#
+# #N| Percent|N| Percent|N|Percent#
+#=#=#========#=#========#=#=======#
+#x#6|85.7143%|1|14.2857%|7|   100%#
+#=#=#========#=#========#=#=======#
 2.2 EXAMINE.  Case Processing Summary
-#==========#=============================#
-#          #            Cases            #
-#          #---------+---------+---------#
-#          #  Valid  | Missing |  Total  #
-#          #-+-------+-+-------+-+-------#
-#      y   #N|Percent|N|Percent|N|Percent#
-#==========#=#=======#=#=======#=#=======#
-#x     1.00#4|   100%|0|     0%|4|   100%#
-#      2.00#2|    67%|1|    33%|3|   100%#
-#==========#=#=======#=#=======#=#=======#
+#==========#===============================#
+#          #             Cases             #
+#          #----------+----------+---------#
+#          #   Valid  |  Missing |  Total  #
+#          #-+--------+-+--------+-+-------#
+#      y   #N| Percent|N| Percent|N|Percent#
+#==========#=#========#=#========#=#=======#
+#x     1.00#4|    100%|0|      0%|4|   100%#
+#      2.00#2|66.6667%|1|33.3333%|3|   100%#
+#==========#=#========#=#========#=#=======#
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
+
+#Make sure this doesn't interfere with percentiles operation.
+
+activity="create program 2"
+cat > $TESTFILE << EOF
+DATA LIST LIST /X *.
+BEGIN DATA.
+99
+99
+5.00
+END DATA.
+
+MISSING VALUE X (99).
+
+EXAMINE /x
+        /PERCENTILES=HAVERAGE.
+
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 2"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
 pass;
diff --git a/tests/command/add-files.sh b/tests/command/add-files.sh
new file mode 100755 (executable)
index 0000000..48232f2
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+# This program tests the ADD FILES procedure
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/add-files.pspp
+
+
+# ensure that top_builddir  are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR" 
+       return ; 
+     fi
+    cd /
+    rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="data create"
+cat > a.data <<EOF
+1aB
+8aM
+3aE
+5aG
+0aA
+5aH
+6aI
+7aJ
+2aD
+7aK
+1aC
+7aL
+4aF
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+cat > b.data <<EOF
+1bN
+3bO
+4bP
+6bQ
+7bR
+9bS
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+cat > concatenate.out <<EOF
+A B C D INA INB
+- - - - --- ---
+1 a B     1   0
+8 a M     1   0
+3 a E     1   0
+5 a G     1   0
+0 a A     1   0
+5 a H     1   0
+6 a I     1   0
+7 a J     1   0
+2 a D     1   0
+7 a K     1   0
+1 a C     1   0
+7 a L     1   0
+4 a F     1   0
+1 b   N   0   1
+3 b   O   0   1
+4 b   P   0   1
+6 b   Q   0   1
+7 b   R   0   1
+9 b   S   0   1
+EOF
+
+cat > interleave.out <<EOF
+A B C D INA INB FIRST LAST
+- - - - --- --- ----- ----
+0 a A     1   0     1   1
+1 a B     1   0            1    0
+1 a C     1   0            0    0
+1 b   N   0   1            0    1
+2 a D     1   0            1    1
+3 a E     1   0            1    0
+3 b   O   0   1            0    1
+4 a F     1   0            1    0
+4 b   P   0   1            0    1
+5 a G     1   0            1    0
+5 a H     1   0            0    1
+6 a I     1   0            1    0
+6 b   Q   0   1            0    1
+7 a J     1   0            1    0
+7 a K     1   0            0    0
+7 a L     1   0            0    0
+7 b   R   0   1            0    1
+8 a M     1   0            1    1
+9 b   S   0   1            1    1
+EOF
+
+# Test ADD FILES.
+dla="data list notable file='a.data' /A B C 1-3 (a)."
+sa="save outfile='a.sys'."
+dlb="data list notable file='b.data' /A B C 1-3 (a)."
+sb="save outfile='b.sys'."
+for type in interleave concatenate; do
+    if test $type = interleave; then
+       by="/by a /first=FIRST /last=LAST"
+       sort="/sort"
+    else
+       by=
+       sort=
+    fi
+    for sources in ss sa as; do
+       name="$type-$sources"
+       activity="create $name.pspp"
+       {
+           if [ $sources = ss ]; then
+               cat <<EOF
+$dla
+$sa
+$dlb
+$sb
+add files file='a.sys' /in=INA $sort
+         /file='b.sys' /in=INB /rename c=D
+         $by.
+EOF
+           elif [ $sources = sa ]; then
+               cat <<EOF
+$dla
+$sa
+$dlb
+add files file='a.sys' /in=INA $sort
+         /file=* /in=INB /rename c=D
+         $by.
+EOF
+           elif [ $sources = as ]; then
+               cat <<EOF
+$dlb
+$sb
+$dla
+add files file=* /in=INA $sort
+            /file='b.sys' /in=INB /rename c=D
+           $by.
+EOF
+           else
+               activity="internal error"
+               no_result
+           fi
+           echo 'list.'
+       } > $name.pspp
+       if [ $? -ne 0 ] ; then no_result ; fi
+
+       activity="run $name.pspp"
+       $SUPERVISOR $PSPP --testing-mode $name.pspp 
+       if [ $? -ne 0 ] ; then no_result ; fi
+
+       activity="check $name output"
+       perl -pi -e 's/^\s*$//g' pspp.list
+       perl -pi -e 's/^\s*$//g' $type.out
+       diff -u -b -w pspp.list $type.out
+       if [ $? -ne 0 ] ; then fail ; fi
+    done
+done
+
+pass;
index 0992e45bced84e734faab4dd25c57b00c68a2f39..63abc6f3b003ac093ee59434736522d9d64d27c8 100755 (executable)
@@ -146,6 +146,8 @@ cat > agg-skel.pspp <<EOF
        /NPOUT23I = pout.(n, 2, 3)
        /SPOUT23 = pout(s, '2', '3')
        /SPOUT23I = pout.(s, '2', '3')
+       /NMEDIAN = median(n)
+       /NMEDIANI = median.(n)
        /NSD = sd(n)
        /NSDI = sd.(n)
        /NSUM = sum(n)
@@ -158,12 +160,13 @@ warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-o
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFIN23 SFIN23I NFLT2 NFLT2I SFLT2 SFLT2I NFIRST NFIRSTI SFIRST SFIRSTI NFOUT23 NFOUT23I SFOUT23 SFOUT23I NLAST NLASTI SLAST SLASTI NMAX NMAXI SMAX SMAXI    NMEAN   NMEANI NMIN NMINI SMIN SMINI       NN      NNI       SN      SNI   NNMISS  NNMISSI   SNMISS  SNMISSI     NNU    NNUI     SNU    SNUI NNUMISS NNUMISSI SNUMISS SNUMISSI NPGT2 NPGT2I SPGT2 SPGT2I NPIN23 NPIN23I SPIN23 SPIN23I NPLT2 NPLT2I SPLT2 SPLT2I NPOUT23 NPOUT23I SPOUT23 SPOUT23I      NSD     NSDI     NSUM    NSUMI
-- -------- -------- ------- ------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------ ------- ------ ------- ------- -------- ------- -------- ----- ------ ----- ------ ---- ----- ---- ----- -------- -------- ---- ----- ---- ----- -------- -------- -------- -------- -------- -------- -------- -------- ------- ------- ------- ------- ------- -------- ------- -------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------- -------- ------- -------- -------- -------- -------- --------
-1     7.00     7.00       6       6  .333   .429  .333   .429   .333    .286   .333    .286  .500   .429  .500   .429      0       0      0       0    .667     .714    .667     .714     5      5     5      5    5     5    5     5     2.00     2.29    0     0    0     0     6.00     7.00     6.00     7.00     1.00      .00     1.00      .00       5       6       5       6       1        0       1        0  33.3   42.9  33.3   42.9   33.3    28.6   33.3    28.6  50.0   42.9  50.0   42.9    66.7     71.4    66.7     71.4     1.79     1.80    12.00    16.00 
-2     5.00     5.00       4       4 1.000  1.000 1.000  1.000   .000    .000   .000    .000  .000   .000  .000   .000      6       6      6       4   1.000    1.000   1.000    1.000     8      8     8      8    8     8    8     8     7.00     7.00    6     6    6     4     3.00     3.00     3.00     5.00     2.00     2.00     2.00      .00       3       3       3       4       1        1       1        0 100.0  100.0 100.0  100.0     .0      .0     .0      .0    .0     .0    .0     .0   100.0    100.0   100.0    100.0     1.00     1.00    21.00    21.00 
-3     2.00     2.00       1       1  .000   .000  .000   .000   .000    .000   .000    .000 1.000  1.000 1.000  1.000      1       1      1       1   1.000    1.000   1.000    1.000     1      1     1      1    1     1    1     1     1.00     1.00    1     1    1     1     2.00     2.00     2.00     2.00      .00      .00      .00      .00       1       1       1       1       0        0       0        0    .0     .0    .0     .0     .0      .0     .0      .0 100.0  100.0 100.0  100.0   100.0    100.0   100.0    100.0      .00      .00     2.00     2.00 
-4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .   
+G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFIN23 SFIN23I NFLT2 NFLT2I SFLT2 SFLT2I NFIRST NFIRSTI SFIRST SFIRSTI NFOUT23 NFOUT23I SFOUT23 SFOUT23I NLAST NLASTI SLAST SLASTI NMAX NMAXI SMAX SMAXI    NMEAN   NMEANI NMIN NMINI SMIN SMINI       NN      NNI       SN      SNI   NNMISS  NNMISSI   SNMISS  SNMISSI     NNU    NNUI     SNU    SNUI NNUMISS NNUMISSI SNUMISS SNUMISSI NPGT2 NPGT2I SPGT2 SPGT2I NPIN23 NPIN23I SPIN23 SPIN23I NPLT2 NPLT2I SPLT2 SPLT2I NPOUT23 NPOUT23I SPOUT23 SPOUT23I  NMEDIAN NMEDIANI      NSD     NSDI     NSUM    NSUMI
+- -------- -------- ------- ------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------ ------- ------ ------- ------- -------- ------- -------- ----- ------ ----- ------ ---- ----- ---- ----- -------- -------- ---- ----- ---- ----- -------- -------- -------- -------- -------- -------- -------- -------- ------- ------- ------- ------- ------- -------- ------- -------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------- -------- ------- -------- -------- -------- -------- -------- -------- --------
+1     7.00     7.00       6       6  .333   .429  .333   .429   .333    .286   .333    .286  .500   .429  .500   .429      0       0      0       0    .667     .714    .667     .714     5      5     5      5    5     5    5     5     2.00     2.29    0     0    0     0     6.00     7.00     6.00     7.00     1.00      .00     1.00      .00       5       6       5       6       1        0       1        0  33.3   42.9  33.3   42.9   33.3    28.6   33.3    28.6  50.0   42.9  50.0   42.9    66.7     71.4    66.7     71.4     1.50     2.00     1.79     1.80    12.00    16.00 
+2     5.00     5.00       4       4 1.000  1.000 1.000  1.000   .000    .000   .000    .000  .000   .000  .000   .000      6       6      6       4   1.000    1.000   1.000    1.000     8      8     8      8    8     8    8     8     7.00     7.00    6     6    6     4     3.00     3.00     3.00     5.00     2.00     2.00     2.00      .00       3       3       3       4       1        1       1        0 100.0  100.0 100.0  100.0     .0      .0     .0      .0    .0     .0    .0     .0   100.0    100.0   100.0    100.0     7.00     7.00     1.00     1.00    21.00    21.00 
+3     2.00     2.00       1       1  .000   .000  .000   .000   .000    .000   .000    .000 1.000  1.000 1.000  1.000      1       1      1       1   1.000    1.000   1.000    1.000     1      1     1      1    1     1    1     1     1.00     1.00    1     1    1     1     2.00     2.00     2.00     2.00      .00      .00      .00      .00       1       1       1       1       0        0       0        0    .0     .0    .0     .0     .0      .0     .0      .0 100.0  100.0 100.0  100.0   100.0    100.0   100.0    100.0     1.00     1.00      .00      .00     2.00     2.00 
+4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      NaN      NaN      .        .        .        .   
+
 EOF
 
 activity="expected output (columnwise missing) create"
@@ -172,12 +175,12 @@ warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-o
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFIN23 SFIN23I NFLT2 NFLT2I SFLT2 SFLT2I NFIRST NFIRSTI SFIRST SFIRSTI NFOUT23 NFOUT23I SFOUT23 SFOUT23I NLAST NLASTI SLAST SLASTI NMAX NMAXI SMAX SMAXI    NMEAN   NMEANI NMIN NMINI SMIN SMINI       NN      NNI       SN      SNI   NNMISS  NNMISSI   SNMISS  SNMISSI     NNU    NNUI     SNU    SNUI NNUMISS NNUMISSI SNUMISS SNUMISSI NPGT2 NPGT2I SPGT2 SPGT2I NPIN23 NPIN23I SPIN23 SPIN23I NPLT2 NPLT2I SPLT2 SPLT2I NPOUT23 NPOUT23I SPOUT23 SPOUT23I      NSD     NSDI     NSUM    NSUMI
-- -------- -------- ------- ------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------ ------- ------ ------- ------- -------- ------- -------- ----- ------ ----- ------ ---- ----- ---- ----- -------- -------- ---- ----- ---- ----- -------- -------- -------- -------- -------- -------- -------- -------- ------- ------- ------- ------- ------- -------- ------- -------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------- -------- ------- -------- -------- -------- -------- --------
-1     7.00     7.00       6       6  .      .429  .      .429   .       .286   .       .286  .      .429  .      .429      .       0              0    .        .714    .        .714     .      5            5    .     5          5      .       2.29    .     0          0     6.00     7.00     6.00     7.00     1.00      .00     1.00      .00       5       6       5       6       1        0       1        0    .    42.9    .    42.9     .     28.6     .     28.6    .    42.9    .    42.9      .      71.4      .      71.4      .       1.80      .      16.00 
-2     5.00     5.00       4       4  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            8    .     .          8      .        .      .     .          4     3.00     3.00     3.00     5.00     2.00     2.00     2.00      .00       3       3       3       4       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .   
-3     2.00     2.00       1       1  .000   .000  .000   .000   .000    .000   .000    .000 1.000  1.000 1.000  1.000      1       1      1       1   1.000    1.000   1.000    1.000     1      1     1      1    1     1    1     1     1.00     1.00    1     1    1     1     2.00     2.00     2.00     2.00      .00      .00      .00      .00       1       1       1       1       0        0       0        0    .0     .0    .0     .0     .0      .0     .0      .0 100.0  100.0 100.0  100.0   100.0    100.0   100.0    100.0      .00      .00     2.00     2.00 
-4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .   
+G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFIN23 SFIN23I NFLT2 NFLT2I SFLT2 SFLT2I NFIRST NFIRSTI SFIRST SFIRSTI NFOUT23 NFOUT23I SFOUT23 SFOUT23I NLAST NLASTI SLAST SLASTI NMAX NMAXI SMAX SMAXI    NMEAN   NMEANI NMIN NMINI SMIN SMINI       NN      NNI       SN      SNI   NNMISS  NNMISSI   SNMISS  SNMISSI     NNU    NNUI     SNU    SNUI NNUMISS NNUMISSI SNUMISS SNUMISSI NPGT2 NPGT2I SPGT2 SPGT2I NPIN23 NPIN23I SPIN23 SPIN23I NPLT2 NPLT2I SPLT2 SPLT2I NPOUT23 NPOUT23I SPOUT23 SPOUT23I  NMEDIAN NMEDIANI      NSD     NSDI     NSUM    NSUMI
+- -------- -------- ------- ------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------ ------- ------ ------- ------- -------- ------- -------- ----- ------ ----- ------ ---- ----- ---- ----- -------- -------- ---- ----- ---- ----- -------- -------- -------- -------- -------- -------- -------- -------- ------- ------- ------- ------- ------- -------- ------- -------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------- -------- ------- -------- -------- -------- -------- -------- -------- --------
+1     7.00     7.00       6       6  .      .429  .      .429   .       .286   .       .286  .      .429  .      .429      .       0              0    .        .714    .        .714     .      5            5    .     5          5      .       2.29    .     0          0     6.00     7.00     6.00     7.00     1.00      .00     1.00      .00       5       6       5       6       1        0       1        0    .    42.9    .    42.9     .     28.6     .     28.6    .    42.9    .    42.9      .      71.4      .      71.4      .       2.00      .       1.80      .      16.00 
+2     5.00     5.00       4       4  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            8    .     .          8      .        .      .     .          4     3.00     3.00     3.00     5.00     2.00     2.00     2.00      .00       3       3       3       4       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .        .        .   
+3     2.00     2.00       1       1  .000   .000  .000   .000   .000    .000   .000    .000 1.000  1.000 1.000  1.000      1       1      1       1   1.000    1.000   1.000    1.000     1      1     1      1    1     1    1     1     1.00     1.00    1     1    1     1     2.00     2.00     2.00     2.00      .00      .00      .00      .00       1       1       1       1       0        0       0        0    .0     .0    .0     .0     .0      .0     .0      .0 100.0  100.0 100.0  100.0   100.0    100.0   100.0    100.0     1.00     1.00      .00      .00     2.00     2.00 
+4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .        .        .   
 EOF
 
 for outfile in scratch active external; do
diff --git a/tests/command/attributes.sh b/tests/command/attributes.sh
new file mode 100755 (executable)
index 0000000..d4d36e6
--- /dev/null
@@ -0,0 +1,148 @@
+#!/bin/sh
+
+# This program tests VARIABLE ATTRIBUTE and DATAFILE ATTRIBUTE
+# commands, including the ability to write attributes to system files
+# and read them back in again.
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_builddir  are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR" 
+       return ; 
+     fi
+     cd /
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create program"
+cat > $TESTFILE <<EOF
+DATA LIST FREE/a b c.
+BEGIN DATA.
+1 2 3
+END DATA.
+
+DATAFILE ATTRIBUTE
+       ATTRIBUTE=key('value')
+                  array('array element 1')
+                  Array[2]('array element 2').
+VARIABLE ATTRIBUTE
+        VARIABLES=a b
+        ATTRIBUTE=ValidationRule[2]("a + b > 2")
+                  ValidationRule[1]('a * b > 3')
+       /VARIABLES=c
+        ATTRIBUTE=QuestionWording('X or Y?').
+DISPLAY ATTRIBUTES.
+
+SAVE OUTFILE='attributes.sav'.
+NEW FILE.
+GET FILE='attributes.sav'.
+
+DATAFILE ATTRIBUTE
+         DELETE=Array[1] Array[2].
+VARIABLE ATTRIBUTE
+         VARIABLES=a
+         DELETE=ValidationRule
+        /VARIABLE=b
+         DELETE=validationrule[2].
+
+DISPLAY ATTRIBUTES.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="compare output"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  -w $TEMPDIR/pspp.list - << EOF
+1.1 DISPLAY.  
++--------+-----------------+-----------------------------------+
+|Variable|Description      |                                   |
+#========#=================#===================================#
+|a       |Custom attributes|                                   |
+|        |ValidationRule[1]|a * b > 3                          |
+|        |ValidationRule[2]|a + b > 2                          |
++--------+-----------------+-----------------------------------+
+|b       |Custom attributes|                                   |
+|        |ValidationRule[1]|a * b > 3                          |
+|        |ValidationRule[2]|a + b > 2                          |
++--------+-----------------+-----------------------------------+
+|c       |Custom attributes|                                   |
+|        |QuestionWording  |X or Y?                            |
++--------+-----------------+-----------------------------------+
+1.2 DISPLAY.  Custom data file attributes.
++---------+---------------+
+|Attribute|Value          |
+#=========#===============#
+|array[1] |array element 1|
+|array[2] |array element 2|
+|key      |value          |
++---------+---------------+
+2.1 DISPLAY.  
++--------+---------------+-----------------------------------+
+|Variable|Description    |                                   |
+#========#===============#===================================#
+|b       |Custom attribut|s:                                 |
+|        |ValidationRule |a * b > 3                          |
++--------+---------------+-----------------------------------+
+|c       |Custom attribut|s:                                 |
+|        |QuestionWording|X or Y?                            |
++--------+---------------+-----------------------------------+
+2.2 DISPLAY.  Custom data file attributes.
++---------+---------------+
+|Attribute|Value          |
+#=========#===============#
+|array    |array element 2|
+|key      |value          |
++---------+---------------+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass;
index 6e789c7b25a0f6421796a252fa6b85a90fb85b7b..67dfed0a5757fa0d859d743e6a407ef26a3a10eb 100755 (executable)
@@ -112,23 +112,23 @@ diff -b  $TEMPDIR/pspp.list - << EOF
 #V1#23.00|   100%|.00|     0%|23.00|   100%#
 #==#=====#=======#===#=======#=====#=======#
 1.2 EXAMINE.  Extreme Values
-#============#===========#========#
-#            #Case Number|  Value #
-#============#===========#========#
-#V1 Highest 1#         21|   20.00#
-#           2#         20|   19.00#
-#           3#         19|   18.00#
-#           4#         19|   18.00#
-#           5#         18|   17.00#
-#           6#         17|   16.00#
-#  ----------#-----------+--------#
-#    Lowest 1#          1|    1.00#
-#           2#          2|    2.00#
-#           3#          4|    3.00#
-#           4#          3|    3.00#
-#           5#          3|    3.00#
-#           6#          5|    4.00#
-#============#===========#========#
+#============#===========#=====#
+#            #Case Number|Value#
+#============#===========#=====#
+#V1 Highest 1#         21|20.00#
+#           2#         20|19.00#
+#           3#         19|18.00#
+#           4#         19|18.00#
+#           5#         18|17.00#
+#           6#         17|16.00#
+#  ----------#-----------+-----#
+#    Lowest 1#          1| 1.00#
+#           2#          2| 2.00#
+#           3#          3| 3.00#
+#           4#          3| 3.00#
+#           5#          4| 3.00#
+#           6#          5| 4.00#
+#============#===========#=====#
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 6468e8143e4fb37f36d6b0be09f27003be4c1ce4..11534385ce509ff3f8f5153aa6cb88c09b0cfd0e 100755 (executable)
@@ -143,17 +143,17 @@ Case#  QUALITY        W    BRAND
 #Breaking Strain#24.00|   100%|.00|     0%|24.00|   100%#
 #===============#=====#=======#===#=======#=====#=======#
 2.2 EXAMINE.  Extreme Values
-#=========================#===========#========#
-#                         #Case Number|  Value #
-#=========================#===========#========#
-#Breaking Strain Highest 1#         12|    7.00#
-#                        2#         16|    6.00#
-#                        3#         14|    5.00#
-#               ----------#-----------+--------#
-#                 Lowest 1#          4|    1.00#
-#                        2#          3|    1.00#
-#                        3#          3|    1.00#
-#=========================#===========#========#
+#=========================#===========#=====#
+#                         #Case Number|Value#
+#=========================#===========#=====#
+#Breaking Strain Highest 1#         12| 7.00#
+#                        2#         16| 6.00#
+#                               3#          7| 5.00#
+#               ----------#-----------+-----#
+#                 Lowest 1#          3| 1.00#
+#                        2#          3| 1.00#
+#                        3#          4| 1.00#
+#=========================#===========#=====#
 2.3 EXAMINE.  Descriptives
 #============================================================#=========#==========#
 #                                                            #Statistic|Std. Error#
@@ -185,33 +185,33 @@ Case#  QUALITY        W    BRAND
 #                Charlies    #8.00|   100%|.00|     0%|8.00|   100%#
 #============================#====#=======#===#=======#====#=======#
 2.5 EXAMINE.  Extreme Values
-#======================================#===========#========#
-#                Manufacturer          #Case Number|  Value #
-#======================================#===========#========#
-#Breaking Strain Aspeger      Highest 1#          6|    4.00#
-#                                     2#          5|    4.00#
-#                                     3#          1|    3.00#
-#                            ----------#-----------+--------#
-#                              Lowest 1#          4|    1.00#
-#                                     2#          3|    1.00#
-#                                     3#          3|    1.00#
-#               -----------------------#-----------+--------#
-#                Bloggs       Highest 1#          7|    5.00#
-#                                     2#          9|    4.00#
-#                                     3#          9|    4.00#
-#                            ----------#-----------+--------#
-#                              Lowest 1#         10|    2.00#
-#                                     2#          8|    2.00#
-#                                     3#         11|    3.00#
-#               -----------------------#-----------+--------#
-#                Charlies     Highest 1#         12|    7.00#
-#                                     2#         16|    6.00#
-#                                     3#         14|    5.00#
-#                            ----------#-----------+--------#
-#                              Lowest 1#         15|    3.00#
-#                                     2#         13|    4.00#
-#                                     3#         13|    4.00#
-#======================================#===========#========#
+#======================================#===========#=====#
+#                Manufacturer          #Case Number|Value#
+#======================================#===========#=====#
+#Breaking Strain Aspeger      Highest 1#          5| 4.00#
+#                                     2#          6| 4.00#
+#                                     3#          1| 3.00#
+#                            ----------#-----------+-----#
+#                              Lowest 1#          3| 1.00#
+#                                     2#          3| 1.00#
+#                                     3#          4| 1.00#
+#               -----------------------#-----------+-----#
+#                Bloggs       Highest 1#          7| 5.00#
+#                                     2#          9| 4.00#
+#                                     3#          9| 4.00#
+#                            ----------#-----------+-----#
+#                              Lowest 1#          8| 2.00#
+#                                     2#         10| 2.00#
+#                                     3#         11| 3.00#
+#               -----------------------#-----------+-----#
+#                       Charlies     Highest 1#         12| 7.00#
+#                                     2#         16| 6.00#
+#                                     3#         14| 5.00#
+#                            ----------#-----------+-----#
+#                              Lowest 1#         15| 3.00#
+#                                     2#         13| 4.00#
+#                                     3#         13| 4.00#
+#======================================#===========#=====#
 2.6 EXAMINE.  Descriptives
 #=========================================================================#=========#==========#
 #                Manufacturer                                             #Statistic|Std. Error#
index d08c7a71edabdc0a24cf7906606cb44df3520f9b..f80dc5fc4a0df3f1c4250ef32e0bf3bb45e7a524 100755 (executable)
@@ -2,7 +2,8 @@
 
 # This program tests the INSERT command
 
-TEMPDIR=/tmp/pspp-tst-$$
+BASETEMPDIR=/tmp/pspp-tst-$$
+TEMPDIR=$BASETEMPDIR/link
 TESTFILE=$TEMPDIR/`basename $0`.sps
 
 # ensure that top_srcdir and top_builddir  are absolute
@@ -23,11 +24,11 @@ export LANG
 cleanup()
 {
      if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
-       echo "NOT cleaning $TEMPDIR"
+       echo "NOT cleaning $BASETEMPDIR"
        return ; 
      fi
      cd /
-     rm -rf $TEMPDIR
+     rm -rf $BASETEMPDIR
 }
 
 
@@ -54,7 +55,9 @@ pass()
     exit 0;
 }
 
-mkdir -p $TEMPDIR
+mkdir -p $BASETEMPDIR/target
+
+ln -s $BASETEMPDIR/target $TEMPDIR
 
 cd $TEMPDIR
 
@@ -249,7 +252,6 @@ $TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
-
 # Test for regression against bug #24569 in which PSPP crashed
 # upon attempt to insert a nonexistent file.
 activity="create wrapper 9"
diff --git a/tests/command/line-ends.sh b/tests/command/line-ends.sh
new file mode 100755 (executable)
index 0000000..ca03b5c
--- /dev/null
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# This program tests that DATA LIST can be used to read input files
+# with varying line ends (LF only, CR LF, CR only).
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_builddir  are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then
+       echo "NOT cleaning $TEMPDIR"
+       return ;
+     fi
+     cd /
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+# Create command file.
+activity="create program"
+cat > $TESTFILE << EOF
+data list list notable file='input.txt'/a b c.
+list.
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="create input.txt"
+printf '1 2 3\n4 5 6\r\n7 8 9\r10 11 12\n13 14 15 \r\n16 17 18\r' > input.txt
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+# Make sure that input.txt actually received the data that we expect.
+# It might not have, if we're running on a system that translates \n
+# into some other sequence.
+activity="check input.txt"
+cksum input.txt > input.cksum
+diff input.cksum - <<EOF
+4116052799 48 input.txt
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode $TESTFILE
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="compare output"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  $TEMPDIR/pspp.list - << EOF
+       a        b        c
+-------- -------- --------
+    1.00     2.00     3.00
+    4.00     5.00     6.00
+    7.00     8.00     9.00
+   10.00    11.00    12.00
+   13.00    14.00    15.00
+   16.00    17.00    18.00
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass;
index 965539c8815fecc490e020b7e53f35b6afaba659..9726b065c7da65d9bf09a13e188da46d062d8ad8 100755 (executable)
@@ -59,19 +59,19 @@ cd $TEMPDIR
 
 activity="data create"
 cat > a.data <<EOF
-0aA
 1aB
-1aC
-2aD
+8aM
 3aE
-4aF
 5aG
+0aA
 5aH
 6aI
 7aJ
+2aD
 7aK
+1aC
 7aL
-8aM
+4aF
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 cat > b.data <<EOF
@@ -143,24 +143,27 @@ $dla
 $sa
 $dlb
 $sb
-match files $type1='a.sys' /in=INA /$type2='b.sys' /in=INB /rename c=D /by a
-            /first=FIRST /last=LAST.
+match files $type1='a.sys' /in=INA /sort
+           /$type2='b.sys' /in=INB /rename c=D
+           /by a /first=FIRST /last=LAST.
 EOF
            elif [ $sources = sa ]; then
                cat <<EOF
 $dla
 $sa
 $dlb
-match files $type1='a.sys' /in=INA /$type2=* /in=INB /rename c=D /by a
-            /first=FIRST /last=LAST.
+match files $type1='a.sys' /in=INA /sort
+           /$type2=* /in=INB /rename c=D
+           /by a /first=FIRST /last=LAST.
 EOF
            elif [ $sources = as ]; then
                cat <<EOF
 $dlb
 $sb
 $dla
-match files $type1=* /in=INA /$type2='b.sys' /in=INB /rename c=D /by a
-            /first=FIRST /last=LAST.
+match files $type1=* /in=INA /sort
+           /$type2='b.sys' /in=INB /rename c=D
+           /by a /first=FIRST /last=LAST.
 EOF
            else
                activity="internal error"
@@ -204,19 +207,19 @@ perl -pi -e 's/^\s*$//g' pspp.list
 diff -b -w - pspp.list <<EOF
 A B C D E F
 - - - - - -
-0 a A 1 b N
-1 a B 3 b O
-1 a C 4 b P
-2 a D 6 b Q
-3 a E 7 b R
-4 a F 9 b S
-5 a G
-5 a H
+1 a B 1 b N
+8 a M 3 b O
+3 a E 4 b P
+5 a G 6 b Q
+0 a A 7 b R
+5 a H 9 b S
 6 a I
 7 a J
+2 a D
 7 a K
+1 a C
 7 a L
-8 a M
+4 a F
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 439398e2172e02d24ff4e5eacafff5e22d1404ef..0db048dd34703054bd88b7da91a17f81ae579812 100755 (executable)
@@ -212,6 +212,36 @@ WEIGHT BY w.
 NPAR TESTS
        /BINOMIAL(0.5) = x
        .
+
+ECHO 'P == 0.5; N1/N2 == 1 Cutpoint' .
+
+DATA LIST LIST NOTABLE /x * w *.
+BEGIN DATA.
+9    3
+10   7
+11   16
+END DATA.
+
+WEIGHT BY w.
+
+NPAR TESTS
+       /BINOMIAL(0.5) = x (10)
+       .
+
+ECHO 'P == 0.5; N1/N2 == 1 Named values' .
+
+DATA LIST LIST NOTABLE /x * w *.
+BEGIN DATA.
+10   10
+15   45
+20   13
+END DATA.
+
+WEIGHT BY w.
+
+NPAR TESTS
+       /BINOMIAL(0.5) = x (10, 20)
+       .
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
@@ -230,7 +260,7 @@ P < 0.5; N1/N2 < 1
 +-+------#--------+-----+--------------+----------+---------------------+
 |x|Group1#    1.00| 6.00|          .286|      .300|                 .551|
 | |Group2#    2.00|15.00|          .714|          |                     |
-| |Total #        |21.00|          1.00|          |                     |
+| |Total #        |21.00|         1.000|          |                     |
 +-+------#--------+-----+--------------+----------+---------------------+
 P < 0.5; N1/N2 > 1
 2.1 NPAR TESTS.  Binomial Test
@@ -239,7 +269,7 @@ P < 0.5; N1/N2 > 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1| 7|          .538|      .400|                 .229|
 | |Group2#       2| 6|          .462|          |                     |
-| |Total #        |13|             1|          |                     |
+| |Total #        |13|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P < 0.5; N1/N2 = 1
 3.1 NPAR TESTS.  Binomial Test
@@ -248,7 +278,7 @@ P < 0.5; N1/N2 = 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1| 8|          .500|      .400|                 .284|
 | |Group2#       2| 8|          .500|          |                     |
-| |Total #        |16|             1|          |                     |
+| |Total #        |16|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P > 0.5; N1/N2 < 1
 4.1 NPAR TESTS.  Binomial Test
@@ -257,7 +287,7 @@ P > 0.5; N1/N2 < 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1|11|          .478|      .600|                 .164|
 | |Group2#       2|12|          .522|          |                     |
-| |Total #        |23|             1|          |                     |
+| |Total #        |23|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P > 0.5; N1/N2 > 1
 5.1 NPAR TESTS.  Binomial Test
@@ -266,7 +296,7 @@ P > 0.5; N1/N2 > 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1|11|          .550|      .600|                 .404|
 | |Group2#       2| 9|          .450|          |                     |
-| |Total #        |20|             1|          |                     |
+| |Total #        |20|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P > 0.5; N1/N2 == 1
 6.1 NPAR TESTS.  Binomial Test
@@ -275,7 +305,7 @@ P > 0.5; N1/N2 == 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1|11|          .500|      .600|                 .228|
 | |Group2#       2|11|          .500|          |                     |
-| |Total #        |22|             1|          |                     |
+| |Total #        |22|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P == 0.5; N1/N2 < 1
 7.1 NPAR TESTS.  Binomial Test
@@ -284,7 +314,7 @@ P == 0.5; N1/N2 < 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1| 8|          .348|      .500|                 .210|
 | |Group2#       2|15|          .652|          |                     |
-| |Total #        |23|             1|          |                     |
+| |Total #        |23|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P == 0.5; N1/N2 > 1
 8.1 NPAR TESTS.  Binomial Test
@@ -293,7 +323,7 @@ P == 0.5; N1/N2 > 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1|12|          .667|      .500|                 .238|
 | |Group2#       2| 6|          .333|          |                     |
-| |Total #        |18|             1|          |                     |
+| |Total #        |18|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
 P == 0.5; N1/N2 == 1
 9.1 NPAR TESTS.  Binomial Test
@@ -302,8 +332,26 @@ P == 0.5; N1/N2 == 1
 +-+------#--------+--+--------------+----------+---------------------+
 |x|Group1#       1|10|          .500|      .500|                1.000|
 | |Group2#       2|10|          .500|          |                     |
-| |Total #        |20|             1|          |                     |
+| |Total #        |20|         1.000|          |                     |
 +-+------#--------+--+--------------+----------+---------------------+
+P == 0.5; N1/N2 == 1 Cutpoint
+10.1 NPAR TESTS.  Binomial Test
++-+------#--------+------+--------------+----------+---------------------+
+| |      #Category|   N  |Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
++-+------#--------+------+--------------+----------+---------------------+
+|x|Group1#   <= 10|10.000|          .385|      .500|                 .327|
+| |Group2#        |16.000|          .615|          |                     |
+| |Total #        |26.000|         1.000|          |                     |
++-+------#--------+------+--------------+----------+---------------------+
+P == 0.5; N1/N2 == 1 Named values
+11.1 NPAR TESTS.  Binomial Test
++-+------#--------+------+--------------+----------+---------------------+
+| |      #Category|   N  |Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
++-+------#--------+------+--------------+----------+---------------------+
+|x|Group1#  10.000|10.000|          .435|      .500|                 .678|
+| |Group2#  20.000|13.000|          .565|          |                     |
+| |Total #        |23.000|         1.000|          |                     |
++-+------#--------+------+--------------+----------+---------------------+
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
diff --git a/tests/command/npar-wilcoxon.sh b/tests/command/npar-wilcoxon.sh
new file mode 100755 (executable)
index 0000000..ae0d39f
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/sh
+
+# This program tests the wilcoxon subcommand of npar tests
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir  are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR"
+       return ; 
+     fi
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create program 1"
+cat > $TESTFILE <<  EOF
+data list notable list /foo * bar * w (f8.0).
+begin data.
+1.00     1.00   1
+1.00     2.00   1
+2.00     1.00   1
+1.00     4.00   1
+2.00     5.00   1
+1.00    19.00   1
+2.00     7.00   1
+4.00     5.00   1
+1.00    12.00   1
+2.00    13.00   1
+2.00     2.00   1
+12.00      .00  2
+12.00     1.00  1
+13.00     1.00  1
+end data
+
+variable labels foo "first" bar "second".
+
+weight by w.
+
+npar test
+ /wilcoxon=foo with bar (paired)
+ /missing analysis
+ /method=exact.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 1"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="generate results"
+cat > $TEMPDIR/results.txt <<EOF
+1.1 NPAR TESTS.  Ranks
+#============================#==#=========#============#
+#                            # N|Mean Rank|Sum of Ranks#
+#============================#==#=========#============#
+#second - firstNegative Ranks# 5|     8.60|       43.00#
+#              Positive Ranks# 8|     6.00|       48.00#
+#              Ties          # 2|         |            #
+#              Total         #15|         |            #
+#============================#==#=========#============#
+
+1.2 NPAR TESTS.  Test Statistics
+#=====================#==============#
+#                     #second - first#
+#=====================#==============#
+#Z                    #          -.18#
+#Asymp. Sig (2-tailed)#           .86#
+#Exact Sig (2-tailed) #           .89#
+#Exact Sig (1-tailed) #           .45#
+#=====================#==============#
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="compare output 1"
+diff pspp.list $TEMPDIR/results.txt
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+
+# No weights this time. But some missing values
+activity="create program 2"
+cat > $TESTFILE <<  EOF
+data list notable list /foo * bar * dummy *.
+begin data.
+1.00     1.00    1
+1.00     2.00    1
+2.00     1.00    1
+1.00     4.00    .
+2.00     5.00    .
+1.00    19.00    .
+2.00     7.00    1
+4.00     5.00    1
+1.00    12.00    1
+2.00    13.00    1
+2.00     2.00    1
+12.00      .00   1
+12.00      .00   1
+34.2       .     1
+12.00     1.00   1  
+13.00     1.00   1
+end data
+
+variable labels foo "first" bar "second".
+
+npar test
+ /wilcoxon=foo with bar (paired)
+ /missing analysis
+ /method=exact.
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program 2"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="compare output 2"
+diff pspp.list $TEMPDIR/results.txt
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+
+pass;
diff --git a/tests/command/reliability.sh b/tests/command/reliability.sh
new file mode 100755 (executable)
index 0000000..ed4b4b8
--- /dev/null
@@ -0,0 +1,345 @@
+#!/bin/sh
+
+# This program tests the reliability command.
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/`basename $0`.sps
+
+# ensure that top_srcdir and top_builddir  are absolute
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+top_srcdir=`cd $top_srcdir; pwd`
+top_builddir=`cd $top_builddir; pwd`
+
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+LANG=C
+export LANG
+
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR"
+       return ; 
+     fi
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+
+activity="create program"
+cat > $TESTFILE <<EOF
+
+data list notable list  /var1 *
+       var2  *
+       var6  *
+       var7  *
+       var8  *
+       var9  *
+       var11 *
+       var12 *
+       var15 *
+       var16 *
+       var17 *
+       var19 *
+       .
+
+begin data.
+6 7 7 5 7 7 7 7 7 7 6 6
+6 7 7 6 7 6 7 5 6 5 7 7
+6 6 7 6 5 3 6 4 5 6 4 5
+4 6 5 6 6 5 4 3 5 6 5 6
+5 6 5 5 6 5 4 4 6 6 5 5
+6 6 7 6 6 5 6 5 6 6 5 6
+5 6 6 5 6 5 5 4 6 5 5 5
+5 7 7 7 7 7 6 5 7 7 7 7
+6 6 6 5 5 7 6 5 6 6 5 6
+. . . . . . . . . . . .
+6 6 5 5 5 6 6 4 6 5 5 5
+7 7 7 6 7 6 7 6 6 6 7 6
+4 7 6 6 6 5 5 4 4 5 5 6
+5 6 3 5 4 1 4 6 2 3 3 2
+3 6 6 5 6 2 4 2 2 4 4 5
+6 6 7 5 6 5 7 6 5 6 6 5
+6 5 6 6 5 6 6 6 6 4 5 5
+5 7 7 . 6 6 6 5 6 6 6 6
+5 7 5 5 4 6 7 6 5 4 6 5
+7 7 7 6 7 7 7 6 7 7 7 6
+3 6 5 6 5 7 7 3 4 7 5 7
+6 7 7 6 5 6 5 5 6 6 6 6
+5 5 6 5 5 5 5 4 5 5 5 6
+6 6 7 4 5 6 6 6 6 5 5 6
+6 5 6 6 4 4 5 4 5 6 4 5
+5 6 7 6 6 7 7 5 6 6 6 5
+5 6 5 7 4 6 6 5 7 7 5 6
+. . . . . . . . . . . .
+7 6 6 5 6 6 7 6 6 5 5 6
+6 6 7 7 7 7 7 6 7 6 6 7
+7 5 5 . 5 . 7 3 5 4 5 3
+7 6 7 5 4 5 7 5 7 5 5 6
+6 5 6 6 6 5 5 5 5 6 5 6
+7 7 7 7 7 7 7 7 5 6 7 7
+. . . . . . . . . . . .
+5 5 6 7 5 6 6 4 6 6 6 5
+6 6 5 7 5 6 7 5 6 5 4 6
+7 6 7 6 7 5 6 7 7 6 6 6
+5 6 5 6 5 6 7 2 5 7 3 7
+6 6 5 6 5 6 6 6 6 6 5 6
+7 6 7 6 6 6 6 6 6 7 6 7
+7 7 6 5 6 6 7 7 7 4 6 5
+3 7 7 6 6 7 7 7 6 6 6 4
+3 5 3 4 3 3 3 3 3 3 3 5
+5 7 7 7 5 7 6 2 6 7 6 7
+7 7 7 7 7 7 7 6 7 7 7 6
+6 5 7 4 4 4 5 6 5 5 4 5
+4 7 7 4 4 3 6 3 5 3 4 5
+7 7 7 7 7 7 7 7 7 7 7 5
+3 6 5 5 4 5 4 4 5 5 3 5
+6 7 6 6 6 7 7 6 6 6 7 6
+2 5 4 6 3 2 2 2 2 7 2 2
+4 6 6 5 5 5 6 5 5 6 6 5
+5 7 4 5 6 6 6 5 6 6 5 6
+5 7 7 5 6 5 6 5 5 4 5 4
+4 5 6 5 6 4 5 5 5 4 5 5
+7 6 6 5 5 6 7 5 6 5 7 6
+5 6 6 5 4 5 5 3 4 5 5 5
+5 7 6 4 4 5 6 5 6 4 4 6
+6 6 6 6 5 7 7 6 5 5 6 6
+6 6 7 6 7 6 6 5 6 7 6 5
+7 6 7 6 7 6 7 7 5 5 6 6
+5 6 6 5 5 5 6 5 6 7 7 5
+5 6 6 5 6 5 6 6 6 6 6 6
+5 5 5 5 6 4 5 3 4 7 6 5
+5 7 7 6 6 6 6 5 6 7 6 7
+6 6 7 7 7 5 6 5 5 5 5 4
+2 7 5 4 6 5 5 2 5 6 4 6
+6 7 7 5 6 6 7 6 6 7 5 7
+5 6 7 6 6 3 5 7 6 6 5 6
+6 6 6 3 5 5 5 6 6 6 4 5
+4 7 7 4 7 4 5 5 5 7 4 4
+. . . . . . . . . . . .
+6 6 7 6 7 6 7 7 6 7 7 6
+. . . . . . . . . . . .
+5 6 5 7 6 5 6 6 5 6 4 6
+5 5 5 5 4 5 5 5 7 5 5 5
+6 6 6 4 5 4 6 6 6 4 5 4
+6 5 7 4 6 4 6 5 6 6 6 3
+5 7 6 5 5 5 5 5 6 7 6 6
+5 5 7 7 5 5 6 6 5 5 5 7
+5 6 7 6 7 5 6 4 6 7 6 7
+4 5 5 5 6 5 6 5 6 6 5 6
+6 5 5 5 6 3 4 5 5 4 5 3
+6 6 6 5 5 5 4 3 4 5 5 5
+6 7 7 6 2 3 6 6 6 5 7 7
+6 7 5 5 6 6 6 5 6 6 6 6
+6 7 7 6 7 7 7 5 5 6 6 6
+6 6 6 6 7 6 6 7 6 6 6 6
+5 6 6 6 3 5 6 6 5 5 4 6
+4 6 5 6 6 5 6 5 6 6 5 5
+6 4 6 5 4 6 7 4 5 6 5 5
+6 7 6 4 6 5 7 6 7 7 6 5
+6 7 7 6 7 6 7 7 7 6 6 6
+6 6 6 4 5 6 7 7 5 6 4 4
+3 3 5 3 3 1 5 6 3 2 3 3
+7 7 5 6 6 7 7 6 7 7 7 7
+5 6 6 6 7 5 4 5 4 7 6 7
+3 6 5 4 3 3 3 5 5 6 3 4
+5 7 6 4 6 5 5 6 6 7 5 6
+5 7 6 6 6 6 6 5 6 7 7 6
+7 7 5 6 7 7 7 7 6 5 7 7
+6 7 6 6 5 6 7 7 6 5 6 6
+6 7 7 7 7 6 6 7 6 7 7 7
+4 6 4 7 3 6 5 5 4 3 5 6
+5 5 7 5 4 6 7 5 4 6 6 5
+5 5 6 4 6 5 7 6 5 5 5 6
+. . . . . . . . . . . .
+. . . . . . . . . . . .
+5 7 7 5 6 6 7 7 6 6 6 7
+6 7 7 1 2 1 7 7 5 5 5 2
+. . . . . . . . . . . .
+3 7 4 6 4 7 4 6 4 7 4 7
+5 7 3 5 5 6 7 5 4 7 7 4
+4 7 7 5 4 6 7 7 6 5 4 4
+6 6 2 2 6 4 6 5 5 1 5 2
+5 5 6 4 5 4 6 5 5 6 5 5
+. . . . . . . . . . . .
+5 7 6 6 6 6 6 6 5 6 6 6
+6 6 6 5 6 6 6 6 7 5 6 7
+3 6 3 3 5 3 3 5 3 5 7 4
+4 4 6 3 3 3 4 3 4 2 3 6
+5 7 7 6 5 4 7 5 7 7 3 7
+4 5 4 4 4 4 3 3 3 4 3 3
+6 7 7 5 6 6 7 5 4 5 5 5
+3 5 3 3 1 3 4 3 4 7 6 7
+4 5 4 4 4 3 4 5 6 6 4 5
+5 6 3 4 5 3 5 3 4 5 6 4
+5 5 5 6 6 6 6 4 5 6 6 5
+6 7 7 2 2 6 7 7 7 7 5 7
+5 7 7 4 6 5 7 5 5 5 6 6
+6 6 7 7 5 5 5 7 6 7 7 7
+6 5 7 3 6 5 6 5 5 6 5 4
+5 7 6 5 6 6 6 5 6 5 5 6
+4 5 5 5 6 3 5 3 3 6 5 5
+. . . . . . . . . . . .
+5 6 6 4 4 4 5 3 5 5 2 6
+5 6 7 5 5 6 6 5 5 6 6 6
+6 7 7 6 4 7 7 6 7 5 6 7
+6 6 5 4 5 2 7 6 6 5 6 6
+2 2 2 2 2 2 3 2 3 1 1 2
+end data.
+
+RELIABILITY
+  /VARIABLES=var2 var8 var15 var17 var6
+  /SCALE('Everything') var6 var8 var15 var17
+  /MODEL=ALPHA.
+
+RELIABILITY
+  /VARIABLES=var6 var8 var15 var17
+  /SCALE('Nothing') ALL
+  /MODEL=SPLIT(2)
+ .
+
+RELIABILITY
+  /VARIABLES=var2 var6 var8 var15 var17 var19
+  /SCALE('Totals') var6 var8 var15 var17 
+  /SUMMARY = total
+ .
+
+
+RELIABILITY
+  /VARIABLES=var6 var8 var15 var17 
+  .
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="compare output"
+diff pspp.list - << EOF
+Scale: Everything
+
+1.1 RELIABILITY.  Case Processing Summary
+#==============#===#======#
+#              # N |   %  #
+#==============#===#======#
+#Cases Valid   #131| 92.91#
+#      Excluded# 10|  7.09#
+#      Total   #141|100.00#
+#==============#===#======#
+
+1.2 RELIABILITY.  Reliability Statistics
+#================#==========#
+#Cronbach's Alpha#N of items#
+#================#==========#
+#             .75#         4#
+#================#==========#
+
+Scale: Nothing
+
+2.1 RELIABILITY.  Case Processing Summary
+#==============#===#======#
+#              # N |   %  #
+#==============#===#======#
+#Cases Valid   #131| 92.91#
+#      Excluded# 10|  7.09#
+#      Total   #141|100.00#
+#==============#===#======#
+
+2.2 RELIABILITY.  Reliability Statistics
+#==========================================================#===#
+#Cronbach's Alpha               Part 1           Value     #.55#
+#                                                N of Items#  2#
+#                               Part 2           Value     #.63#
+#                                                N of Items#  2#
+#                               Total N of Items           #  4#
+#Correlation Between Forms                                 #.61#
+#Spearman-Brown Coefficient     Equal Length               #.75#
+#                               Unequal Length             #.75#
+#Guttman Split-Half Coefficient                            #.75#
+#==========================================================#===#
+
+Scale: Totals
+
+3.1 RELIABILITY.  Case Processing Summary
+#==============#===#======#
+#              # N |   %  #
+#==============#===#======#
+#Cases Valid   #131| 92.91#
+#      Excluded# 10|  7.09#
+#      Total   #141|100.00#
+#==============#===#======#
+
+3.2 RELIABILITY.  Reliability Statistics
+#================#==========#
+#Cronbach's Alpha#N of items#
+#================#==========#
+#             .75#         4#
+#================#==========#
+
+3.3 RELIABILITY.  Item-Total Statistics
+#=====#==========================#==============================#================================#================================#
+#     #Scale Mean if Item Deleted|Scale Variance if Item Deleted|Corrected Item-Total Correlation|Cronbach's Alpha if Item Deleted#
+#=====#==========================#==============================#================================#================================#
+#var6 #                     15.97|                          8.43|                             .51|                             .71#
+#var8 #                     16.56|                          7.86|                             .53|                             .70#
+#var15#                     16.47|                          8.45|                             .56|                             .68#
+#var17#                     16.60|                          8.00|                             .57|                             .67#
+#=====#==========================#==============================#================================#================================#
+
+Scale: ANY
+
+4.1 RELIABILITY.  Case Processing Summary
+#==============#===#======#
+#              # N |   %  #
+#==============#===#======#
+#Cases Valid   #131| 92.91#
+#      Excluded# 10|  7.09#
+#      Total   #141|100.00#
+#==============#===#======#
+
+4.2 RELIABILITY.  Reliability Statistics
+#================#==========#
+#Cronbach's Alpha#N of items#
+#================#==========#
+#             .75#         4#
+#================#==========#
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;
diff --git a/tests/command/update.sh b/tests/command/update.sh
new file mode 100755 (executable)
index 0000000..8bcb567
--- /dev/null
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+# This program tests the UPDATE procedure
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/update.pspp
+
+
+# ensure that top_builddir  are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR" 
+       return ; 
+     fi
+    cd /
+    rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="data create"
+cat > a.data <<EOF
+1aB
+8aM
+3aE
+5aG
+0aA
+5aH
+6aI
+7aJ
+2aD
+7aK
+1aC
+7aL
+4aF
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+cat > b.data <<EOF
+1bN
+3bO
+4bP
+6bQ
+7bR
+9bS
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+cat > update.out <<EOF
+A B C D INA INB
+- - - - --- ---
+0 a A     1   0
+1 b B N   1   1
+1 a C     1   0
+2 a D     1   0
+3 b E O   1   1
+4 b F P   1   1
+5 a G     1   0
+5 a H     1   0
+6 b I Q   1   1
+7 b J R   1   1
+7 a K     1   0
+7 a L     1   0
+8 a M     1   0
+9 b   S   0   1
+EOF
+perl -pi -e 's/^\s*$//g' update.out
+
+# Test UPDATE.
+dla="data list notable file='a.data' /A B C 1-3 (a)."
+sa="save outfile='a.sys'."
+dlb="data list notable file='b.data' /A B C 1-3 (a)."
+sb="save outfile='b.sys'."
+for sources in ss sa as; do
+    name="$sources"
+    activity="create $name.pspp"
+    {
+       if [ $sources = ss ]; then
+           cat <<EOF
+set errors=terminal.
+$dla
+$sa
+$dlb
+$sb
+update file='a.sys' /in=INA /sort
+         /file='b.sys' /in=INB /rename c=D
+         /by a.
+EOF
+       elif [ $sources = sa ]; then
+           cat <<EOF
+set errors=terminal.
+$dla
+$sa
+$dlb
+
+update file='a.sys' /in=INA /sort
+      /file=* /in=INB /rename c=D
+      /by a.
+EOF
+       elif [ $sources = as ]; then
+           cat <<EOF
+set errors=terminal.
+$dlb
+$sb
+$dla
+
+update file=* /in=INA /sort
+      /file='b.sys' /in=INB /rename c=D
+      /by a.
+EOF
+       else
+           activity="internal error"
+           no_result
+       fi
+       echo 'list.'
+    } > $name.pspp
+    if [ $? -ne 0 ] ; then no_result ; fi
+
+    activity="run $name.pspp"
+    rm -f errors
+    $SUPERVISOR $PSPP --testing-mode --error-file=errors $name.pspp
+    if [ $? -ne 0 ] ; then no_result ; fi
+
+    activity="check $name output"
+    perl -pi -e 's/^\s*$//g' pspp.list
+    diff -c -b -w pspp.list update.out
+    if [ $? -ne 0 ] ; then fail ; fi
+    diff -c -b -w - errors <<EOF
+$name.pspp:8: warning: UPDATE: Encountered 3 sets of duplicate cases in the master file.
+EOF
+    if [ $? -ne 0 ] ; then fail ; fi
+done
+
+pass;
index 532684237745e5eef82aafa0eefa8f977f160e4a..25d01158ee77b1c80062f8c6689f7a5f0fb608fa 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2008 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
@@ -62,12 +62,18 @@ static void read_long_var_name_map (struct sfm_reader *r,
                                     size_t size, size_t count);
 static void read_long_string_map (struct sfm_reader *r,
                                   size_t size, size_t count);
+static void read_datafile_attributes (struct sfm_reader *r,
+                                      size_t size, size_t count);
+static void read_variable_attributes (struct sfm_reader *r,
+                                      size_t size, size_t count);
 
-static struct variable_to_value_map *open_variable_to_value_map (
+static struct text_record *open_text_record (
   struct sfm_reader *, size_t size);
-static void close_variable_to_value_map (struct variable_to_value_map *);
-static bool read_variable_to_value_map (struct variable_to_value_map *,
-                                        char **key, char **value);
+static void close_text_record (struct text_record *);
+static bool read_variable_to_value_pair (struct text_record *,
+                                         char **key, char **value);
+static char *text_tokenize (struct text_record *, int delimiter);
+static bool text_match (struct text_record *text, int c);
 
 static void usage (int exit_code);
 static void sys_warn (struct sfm_reader *, const char *, ...)
@@ -87,50 +93,61 @@ int
 main (int argc, char *argv[])
 {
   struct sfm_reader r;
-  int rec_type;
+  int i;
 
   set_program_name (argv[0]);
-  if (argc != 2)
+  if (argc < 2)
     usage (EXIT_FAILURE);
 
-  r.file_name = argv[1];
-  r.file = fopen (r.file_name, "rb");
-  if (r.file == NULL)
-    error (EXIT_FAILURE, errno, "error opening \"%s\"", r.file_name);
-  r.n_variable_records = 0;
-  r.n_variables = 0;
-
-  read_header (&r);
-  while ((rec_type = read_int (&r)) != 999)
+  for (i = 1; i < argc; i++) 
     {
-      switch (rec_type)
+      int rec_type;
+
+      r.file_name = argv[i];
+      r.file = fopen (r.file_name, "rb");
+      if (r.file == NULL)
+        error (EXIT_FAILURE, errno, "error opening \"%s\"", r.file_name);
+      r.n_variable_records = 0;
+      r.n_variables = 0;
+
+      if (argc > 2)
+        printf ("Reading \"%s\":\n", r.file_name);
+      
+      read_header (&r);
+      while ((rec_type = read_int (&r)) != 999)
         {
-        case 2:
-          read_variable_record (&r);
-          break;
+          switch (rec_type)
+            {
+            case 2:
+              read_variable_record (&r);
+              break;
 
-        case 3:
-          read_value_label_record (&r);
-          break;
+            case 3:
+              read_value_label_record (&r);
+              break;
 
-        case 4:
-          sys_error (&r, _("Misplaced type 4 record."));
+            case 4:
+              sys_error (&r, _("Misplaced type 4 record."));
 
-        case 6:
-          read_document_record (&r);
-          break;
+            case 6:
+              read_document_record (&r);
+              break;
 
-        case 7:
-          read_extension_record (&r);
-          break;
+            case 7:
+              read_extension_record (&r);
+              break;
 
-        default:
-          sys_error (&r, _("Unrecognized record type %d."), rec_type);
+            default:
+              sys_error (&r, _("Unrecognized record type %d."), rec_type);
+            }
         }
-    }
-  printf ("%08lx: end-of-dictionary record (first byte of data at %08lx)\n",
-          ftell (r.file), ftell (r.file) + 4);
+      printf ("%08lx: end-of-dictionary record "
+              "(first byte of data at %08lx)\n",
+              ftell (r.file), ftell (r.file) + 4);
 
+      fclose (r.file);
+    }
+  
   return 0;
 }
 
@@ -486,9 +503,12 @@ read_extension_record (struct sfm_reader *r)
       break;
 
     case 17:
-      /* Text field that defines variable attributes.  New in
-         SPSS 14. */
-      break;
+      read_datafile_attributes (r, size, count);
+      return;
+
+    case 18:
+      read_variable_attributes (r, size, count);
+      return;
 
     default:
       sys_warn (r, _("Unrecognized record type 7, subtype %d."), subtype);
@@ -613,15 +633,15 @@ read_display_parameters (struct sfm_reader *r, size_t size, size_t count)
 static void
 read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count)
 {
-  struct variable_to_value_map *map;
+  struct text_record *text;
   char *var;
   char *long_name;
 
   printf ("%08lx: long variable names (short => long)\n", ftell (r->file));
-  map = open_variable_to_value_map (r, size * count);
-  while (read_variable_to_value_map (map, &var, &long_name))
+  text = open_text_record (r, size * count);
+  while (read_variable_to_value_pair (text, &var, &long_name))
     printf ("\t%s => %s\n", var, long_name);
-  close_variable_to_value_map (map);
+  close_text_record (text);
 }
 
 /* Reads record type 7, subtype 14, which gives the real length
@@ -629,89 +649,170 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count)
 static void
 read_long_string_map (struct sfm_reader *r, size_t size, size_t count)
 {
-  struct variable_to_value_map *map;
+  struct text_record *text;
   char *var;
   char *length_s;
 
   printf ("%08lx: very long strings (variable => length)\n", ftell (r->file));
-  map = open_variable_to_value_map (r, size * count);
-  while (read_variable_to_value_map (map, &var, &length_s))
+  text = open_text_record (r, size * count);
+  while (read_variable_to_value_pair (text, &var, &length_s))
     printf ("\t%s => %d\n", var, atoi (length_s));
-  close_variable_to_value_map (map);
+  close_text_record (text);
+}
+
+static bool
+read_attributes (struct sfm_reader *r, struct text_record *text,
+                 const char *variable)
+{
+  const char *key;
+  int index;
+
+  for (;;) 
+    {
+      key = text_tokenize (text, '(');
+      if (key == NULL)
+        return true;
+  
+      for (index = 1; ; index++)
+        {
+          /* Parse the value. */
+          const char *value = text_tokenize (text, '\n');
+          if (value == NULL) 
+            {
+              sys_warn (r, _("%s: Error parsing attribute value %s[%d]"),
+                        variable, key, index);
+              return false;
+            }
+          if (strlen (value) < 2
+              || value[0] != '\'' || value[strlen (value) - 1] != '\'')
+            sys_warn (r, _("%s: Attribute value %s[%d] is not quoted: %s"),
+                      variable, key, index, value);
+          else
+            printf ("\t%s: %s[%d] = \"%.*s\"\n",
+                    variable, key, index, (int) strlen (value) - 2, value + 1);
+
+          /* Was this the last value for this attribute? */
+          if (text_match (text, ')'))
+            break;
+        }
+
+      if (text_match (text, '/'))
+        return true; 
+    }
+}
+
+static void
+read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count) 
+{
+  struct text_record *text;
+  
+  printf ("%08lx: datafile attributes\n", ftell (r->file));
+  text = open_text_record (r, size * count);
+  read_attributes (r, text, "datafile");
+  close_text_record (text);
+}
+
+static void
+read_variable_attributes (struct sfm_reader *r, size_t size, size_t count) 
+{
+  struct text_record *text;
+  
+  printf ("%08lx: variable attributes\n", ftell (r->file));
+  text = open_text_record (r, size * count);
+  for (;;) 
+    {
+      const char *variable = text_tokenize (text, ':');
+      if (variable == NULL || !read_attributes (r, text, variable))
+        break; 
+    }
+  close_text_record (text);
 }
 \f
-/* Helpers for reading records that contain "variable=value"
-   pairs. */
+/* Helpers for reading records that consist of structured text
+   strings. */
 
 /* State. */
-struct variable_to_value_map
+struct text_record
   {
     char *buffer;               /* Record contents. */
     size_t size;                /* Size of buffer. */
     size_t pos;                 /* Current position in buffer. */
   };
 
-/* Reads SIZE bytes into a "variable=value" map for R,
-   and returns the map. */
-static struct variable_to_value_map *
-open_variable_to_value_map (struct sfm_reader *r, size_t size)
+/* Reads SIZE bytes into a text record for R,
+   and returns the new text record. */
+static struct text_record *
+open_text_record (struct sfm_reader *r, size_t size)
 {
-  struct variable_to_value_map *map = xmalloc (sizeof *map);
+  struct text_record *text = xmalloc (sizeof *text);
   char *buffer = xmalloc (size + 1);
   read_bytes (r, buffer, size);
-  map->buffer = buffer;
-  map->size = size;
-  map->pos = 0;
-  return map;
+  text->buffer = buffer;
+  text->size = size;
+  text->pos = 0;
+  return text;
 }
 
-/* Closes MAP and frees its storage.
-   Not really needed, because the pool will free the map anyway,
+/* Closes TEXT and frees its storage.
+   Not really needed, because the pool will free the text record anyway,
    but can be used to free it earlier. */
 static void
-close_variable_to_value_map (struct variable_to_value_map *map)
+close_text_record (struct text_record *text)
 {
-  free (map);
-  free (map->buffer);
+  free (text->buffer);
+  free (text);
 }
 
 static char *
-tokenize (struct variable_to_value_map *map, int delimiter)
+text_tokenize (struct text_record *text, int delimiter)
 {
-  size_t start = map->pos;
-  while (map->pos < map->size
-         && map->buffer[map->pos] != delimiter
-         && map->buffer[map->pos] != '\0')
-    map->pos++;
-  if (map->pos == map->size)
+  size_t start = text->pos;
+  while (text->pos < text->size
+         && text->buffer[text->pos] != delimiter
+         && text->buffer[text->pos] != '\0')
+    text->pos++;
+  if (text->pos == text->size)
     return NULL;
-  map->buffer[map->pos++] = '\0';
-  return &map->buffer[start];
+  text->buffer[text->pos++] = '\0';
+  return &text->buffer[start];
+}
+
+static bool
+text_match (struct text_record *text, int c) 
+{
+  if (text->pos < text->size && text->buffer[text->pos] == c) 
+    {
+      text->pos++;
+      return true;
+    }
+  else
+    return false;
 }
 
-/* Reads the next variable=value pair from MAP.
+/* Reads a variable=value pair from TEXT.
    Looks up the variable in DICT and stores it into *VAR.
    Stores a null-terminated value into *VALUE. */
 static bool
-read_variable_to_value_map (struct variable_to_value_map *map,
-                            char **key, char **value)
+read_variable_to_value_pair (struct text_record *text,
+                             char **key, char **value)
 {
-  *key = tokenize (map, '=');
-  *value = tokenize (map, '\t');
+  *key = text_tokenize (text, '=');
+  *value = text_tokenize (text, '\t');
   if (!*key || !*value)
     return false;
 
-  while (map->pos < map->size
-         && (map->buffer[map->pos] == '\t'
-             || map->buffer[map->pos] == '\0'))
-    map->pos++;
+  while (text->pos < text->size
+         && (text->buffer[text->pos] == '\t'
+             || text->buffer[text->pos] == '\0'))
+    text->pos++;
   return true;
 }
 \f
 static void
 usage (int exit_code)
 {
-  printf ("usage: %s SYSFILE, where SYSFILE is the name of a system file\n",
+  printf ("usage: %s SYSFILE...\n"
+          "where each SYSFILE is the name of a system file\n",
           program_name);
   exit (exit_code);
 }
index 1c29a7a523f2a4ee42e4b3f5dc2f030d19f83c63..cb0f5ba3a04161a27ca712c4b43df36c7e83e2a8 100755 (executable)
@@ -60,6 +60,7 @@ cat > $TEMPDIR/valuelabel.stat <<EOF
 DATA LIST notable /n 1 s 2(a).
 VALUE LABELS /n 0 'Very dissatisfied'
                 1 'Dissatisfied'
+               1.5 'Slightly Peeved'
                 2 'Neutral'
                 3 'Satisfied'
                 4 'Very satisfied'.
@@ -82,6 +83,7 @@ BEGIN DATA.
 5f
 6g
 END DATA.
+
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
diff --git a/tests/libpspp/hmap-test.c b/tests/libpspp/hmap-test.c
new file mode 100644 (file)
index 0000000..e59ea46
--- /dev/null
@@ -0,0 +1,999 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007, 2008 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/>. */
+
+/* This is a test program for the hmap_* routines defined in
+   hmap.c.  This test program aims to be as comprehensive as
+   possible.  "gcov -a -b" should report 100% coverage of lines,
+   blocks and branches in hmap.c (when compiled with -DNDEBUG).
+   "valgrind --leak-check=yes --show-reachable=yes" should give a
+   clean report. */
+
+/* Warning:
+
+   GCC 4.3 will miscompile this test program, specifically
+   test_moved(), given small changes.  This is a bug in GCC
+   triggered by the test program, not by the library under test,
+   so you may safely ignore it.  To avoid miscompilation, compile
+   this file with GCC 4.2 or earlier or GCC 4.4 or later.
+
+   Here is a minimal test program that demonstrates the same or a
+   similar bug in GCC 4.3:
+
+   #include <stdio.h>
+   #include <stdlib.h>
+
+   struct node
+     {
+       struct node *next;
+       unsigned int data1;
+       int data2;
+     };
+   struct list
+     {
+       struct node *head;
+       int dummy;
+     };
+
+   static void *
+   xmalloc (int n)
+   {
+     return malloc (n);
+   }
+
+   static void
+   check_list (struct list *list)
+   {
+     int i __attribute__((unused));
+     struct node *e;
+     for (e = list->head; e != NULL; e = e->next)
+       if (e->data1 != e->data2)
+         abort ();
+   }
+
+   int
+   main (void)
+   {
+   #define MAX_ELEMS 2
+     struct node *elements = xmalloc (MAX_ELEMS * sizeof *elements);
+     int *values = xmalloc (MAX_ELEMS * sizeof *values);
+     struct list list;
+     int i;
+
+     list.head = NULL;
+     for (i = 0; i < MAX_ELEMS; i++)
+       {
+         values[i] = elements[i].data2 = i;
+         elements[i].data1 = elements[i].data2;
+         elements[i].next = list.head;
+         list.head = &elements[i];
+       }
+     check_list (&list);
+     return 0;
+   }
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/hmap.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/compiler.h>
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* Exit with a failure code.
+   (Place a breakpoint on this function while debugging.) */
+static void
+check_die (void)
+{
+  exit (EXIT_FAILURE);
+}
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line)
+{
+  if (!ok)
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      check_die ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+
+/* Prints a message about memory exhaustion and exits with a
+   failure code. */
+static void
+xalloc_die (void)
+{
+  printf ("virtual memory exhausted\n");
+  exit (EXIT_FAILURE);
+}
+
+static void *xmalloc (size_t n) MALLOC_LIKE;
+static void *xnmalloc (size_t n, size_t m) MALLOC_LIKE;
+static void *xmemdup (const void *p, size_t n) MALLOC_LIKE;
+
+/* Allocates and returns N bytes of memory. */
+static void *
+xmalloc (size_t n)
+{
+  if (n != 0)
+    {
+      void *p = malloc (n);
+      if (p == NULL)
+        xalloc_die ();
+
+      return p;
+    }
+  else
+    return NULL;
+}
+
+static void *
+xmemdup (const void *p, size_t n)
+{
+  void *q = xmalloc (n);
+  memcpy (q, p, n);
+  return q;
+}
+
+/* Allocates and returns N * M bytes of memory. */
+static void *
+xnmalloc (size_t n, size_t m)
+{
+  if ((size_t) -1 / m <= n)
+    xalloc_die ();
+  return xmalloc (n * m);
+}
+\f
+/* Node type and support routines. */
+
+/* Test data element. */
+struct element
+  {
+    struct hmap_node node;    /* Embedded hash table element. */
+    int data;                 /* Primary value. */
+  };
+
+/* Returns the `struct element' that NODE is embedded within. */
+static struct element *
+hmap_node_to_element (const struct hmap_node *node)
+{
+  return HMAP_DATA (node, struct element, node);
+}
+
+/* Compares A and B and returns a strcmp-type return value. */
+static int
+compare_ints (const void *a_, const void *b_)
+{
+  const int *a = a_;
+  const int *b = b_;
+
+  return *a < *b ? -1 : *a > *b;
+}
+
+/* Swaps *A and *B. */
+static void
+swap (int *a, int *b)
+{
+  int t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of the CNT integers starting at VALUES. */
+static void
+reverse (int *values, size_t cnt)
+{
+  size_t i = 0;
+  size_t j = cnt;
+
+  while (j > i)
+    swap (&values[i++], &values[--j]);
+}
+
+/* Arranges the CNT elements in VALUES into the lexicographically
+   next greater permutation.  Returns true if successful.
+   If VALUES is already the lexicographically greatest
+   permutation of its elements (i.e. ordered from greatest to
+   smallest), arranges them into the lexicographically least
+   permutation (i.e. ordered from smallest to largest) and
+   returns false. */
+static bool
+next_permutation (int *values, size_t cnt)
+{
+  if (cnt > 0)
+    {
+      size_t i = cnt - 1;
+      while (i != 0)
+        {
+          i--;
+          if (values[i] < values[i + 1])
+            {
+              size_t j;
+              for (j = cnt - 1; values[i] >= values[j]; j--)
+                continue;
+              swap (values + i, values + j);
+              reverse (values + (i + 1), cnt - (i + 1));
+              return true;
+            }
+        }
+
+      reverse (values, cnt);
+    }
+
+  return false;
+}
+
+/* Returns N!. */
+static unsigned int
+factorial (unsigned int n)
+{
+  unsigned int value = 1;
+  while (n > 1)
+    value *= n--;
+  return value;
+}
+
+/* Randomly shuffles the CNT elements in ARRAY, each of which is
+   SIZE bytes in size. */
+static void
+random_shuffle (void *array_, size_t cnt, size_t size)
+{
+  char *array = array_;
+  char *tmp = xmalloc (size);
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = rand () % (cnt - i) + i;
+      if (i != j)
+        {
+          memcpy (tmp, array + j * size, size);
+          memcpy (array + j * size, array + i * size, size);
+          memcpy (array + i * size, tmp, size);
+        }
+    }
+
+  free (tmp);
+}
+
+typedef size_t hash_function (int data);
+
+static size_t
+identity_hash (int data) 
+{
+  return data;
+}
+
+static size_t
+constant_hash (int data UNUSED) 
+{
+  return 0x12345678u;
+}
+
+static inline uint32_t
+md4_round (uint32_t a, uint32_t b, uint32_t c, uint32_t d,
+           uint32_t data, uint32_t n)
+{
+  uint32_t x = a + (d ^ (b & (c ^ d))) + data;
+  return (x << n) | (x >> (32 - n));
+}
+
+static size_t
+random_hash (int data)
+{
+  uint32_t a = data;
+  uint32_t b = data;
+  uint32_t c = data;
+  uint32_t d = data;
+  a = md4_round (a, b, c, d, 0, 3);
+  d = md4_round (d, a, b, c, 1, 7);
+  c = md4_round (c, d, a, b, 2, 11);
+  b = md4_round (b, c, d, a, 3, 19);
+  return a ^ b ^ c ^ d;
+}
+
+static struct hmap_node *
+find_element (struct hmap *hmap, int data, hash_function *hash)
+{
+  struct element *e;
+  HMAP_FOR_EACH_WITH_HASH (e, struct element, node, hash (data), hmap)
+    if (e->data == data)
+      break;
+  return &e->node;
+}
+
+/* Checks that HMAP contains the CNT ints in DATA, that its
+   structure is correct, and that certain operations on HMAP
+   produce the expected results. */
+static void
+check_hmap (struct hmap *hmap, const int data[], size_t cnt,
+            hash_function *hash)
+{
+  size_t i, j;
+  int *order;
+
+  check (hmap_count (hmap) == cnt);
+  check (cnt <= hmap_capacity (hmap));
+
+  order = xmemdup (data, cnt * sizeof *data);
+  qsort (order, cnt, sizeof *order, compare_ints);
+
+  for (i = 0; i < cnt; i = j)
+    {
+      struct element *e;
+      int count;
+
+      for (j = i + 1; j < cnt; j++)
+        if (order[i] != order[j])
+          break;
+
+      count = 0;
+      HMAP_FOR_EACH_WITH_HASH (e, struct element, node, hash (order[i]), hmap)
+        if (e->data == order[i]) 
+          count++;
+
+      check (count == j - i);
+    }
+
+  check (find_element (hmap, -1, hash) == NULL);
+
+  if (cnt == 0)
+    check (hmap_first (hmap) == NULL);
+  else
+    {
+      struct hmap_node *p;
+      int left;
+
+      left = cnt;
+      for (p = hmap_first (hmap), i = 0; i < cnt; p = hmap_next (hmap, p), i++)
+        {
+          struct element *e = hmap_node_to_element (p);
+          size_t j;
+
+          check (hmap_node_hash (&e->node) == hash (e->data));
+          for (j = 0; j < left; j++)
+            if (order[j] == e->data) 
+              {
+                order[j] = order[--left];
+                goto next;
+              }
+          check_die ();
+
+        next: ;
+        }
+      check (p == NULL);
+    }
+
+  free (order);
+}
+
+/* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
+   HMAP in the order specified by INSERTIONS, then deletes them in
+   the order specified by DELETIONS, checking the HMAP's contents
+   for correctness after each operation.  Uses HASH as the hash
+   function. */
+static void
+test_insert_delete (const int insertions[],
+                    const int deletions[],
+                    size_t cnt,
+                    hash_function *hash)
+{
+  struct element *elements;
+  struct hmap hmap;
+  size_t i;
+
+  elements = xnmalloc (cnt, sizeof *elements);
+  for (i = 0; i < cnt; i++)
+    elements[i].data = i;
+
+  hmap_init (&hmap);
+  hmap_reserve (&hmap, 1);
+  check_hmap (&hmap, NULL, 0, hash);
+  for (i = 0; i < cnt; i++)
+    {
+      size_t capacity;
+      hmap_insert (&hmap, &elements[insertions[i]].node, hash (insertions[i]));
+      check_hmap (&hmap, insertions, i + 1, hash);
+
+      /* A series of insertions should not produce a shrinkable hmap. */
+      capacity = hmap_capacity (&hmap);
+      hmap_shrink (&hmap);
+      check (capacity == hmap_capacity (&hmap));
+    }
+  for (i = 0; i < cnt; i++)
+    {
+      hmap_delete (&hmap, &elements[deletions[i]].node);
+      check_hmap (&hmap, deletions + i + 1, cnt - i - 1, hash);
+    }
+  hmap_destroy (&hmap);
+
+  free (elements);
+}
+\f
+/* Inserts values into an HMAP in each possible order, then
+   removes them in each possible order, up to a specified maximum
+   size, using hash function HASH. */
+static void
+test_insert_any_remove_any (hash_function *hash)
+{
+  const int max_elems = 5;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int ins_perm_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+
+      for (ins_perm_cnt = 0;
+           ins_perm_cnt == 0 || next_permutation (insertions, cnt);
+           ins_perm_cnt++)
+        {
+          unsigned int del_perm_cnt;
+          int i;
+
+          for (i = 0; i < cnt; i++)
+            deletions[i] = i;
+
+          for (del_perm_cnt = 0;
+               del_perm_cnt == 0 || next_permutation (deletions, cnt);
+               del_perm_cnt++)
+            test_insert_delete (insertions, deletions, cnt, hash);
+
+          check (del_perm_cnt == factorial (cnt));
+        }
+      check (ins_perm_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+static void
+test_insert_any_remove_any_random_hash (void) 
+{
+  test_insert_any_remove_any (random_hash);
+}
+
+static void
+test_insert_any_remove_any_identity_hash (void) 
+{
+  test_insert_any_remove_any (identity_hash);
+}
+
+static void
+test_insert_any_remove_any_constant_hash (void) 
+{
+  test_insert_any_remove_any (constant_hash);
+}
+
+/* Inserts values into an HMAP in each possible order, then
+   removes them in the same order, up to a specified maximum
+   size, using hash function HASH. */
+static void
+test_insert_any_remove_same (hash_function *hash)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      for (i = 0; i < cnt; i++)
+        values[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        test_insert_delete (values, values, cnt, hash);
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+    }
+}
+
+static void
+test_insert_any_remove_same_random_hash (void) 
+{
+  test_insert_any_remove_same (random_hash);
+}
+
+static void
+test_insert_any_remove_same_identity_hash (void) 
+{
+  test_insert_any_remove_same (identity_hash);
+}
+
+static void
+test_insert_any_remove_same_constant_hash (void) 
+{
+  test_insert_any_remove_same (constant_hash);
+}
+
+/* Inserts values into an HMAP in each possible order, then
+   removes them in reverse order, up to a specified maximum
+   size, using hash function HASH. */
+static void
+test_insert_any_remove_reverse (hash_function *hash)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int permutation_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (insertions, cnt);
+           permutation_cnt++)
+        {
+          memcpy (deletions, insertions, sizeof *insertions * cnt);
+          reverse (deletions, cnt);
+
+          test_insert_delete (insertions, deletions, cnt, hash);
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+static void
+test_insert_any_remove_reverse_random_hash (void)
+{
+  test_insert_any_remove_reverse (random_hash);
+}
+
+static void
+test_insert_any_remove_reverse_identity_hash (void)
+{
+  test_insert_any_remove_reverse (identity_hash);
+}
+
+static void
+test_insert_any_remove_reverse_constant_hash (void)
+{
+  test_insert_any_remove_reverse (constant_hash);
+}
+
+/* Inserts and removes up to MAX_ELEMS values in an hmap, in
+   random order, using hash function HASH. */
+static void
+test_random_sequence (int max_elems, hash_function *hash)
+{
+  const int max_trials = 8;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt += 2)
+    {
+      int *insertions, *deletions;
+      int trial;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+      for (i = 0; i < cnt; i++)
+        deletions[i] = i;
+
+      for (trial = 0; trial < max_trials; trial++)
+        {
+          random_shuffle (insertions, cnt, sizeof *insertions);
+          random_shuffle (deletions, cnt, sizeof *deletions);
+
+          test_insert_delete (insertions, deletions, cnt, hash);
+        }
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+static void
+test_random_sequence_random_hash (void) 
+{
+  test_random_sequence (64, random_hash);
+}
+
+static void
+test_random_sequence_identity_hash (void) 
+{
+  test_random_sequence (64, identity_hash);
+}
+
+static void
+test_random_sequence_constant_hash (void) 
+{
+  test_random_sequence (32, constant_hash);
+}
+
+/* Inserts MAX_ELEMS elements into an HMAP in ascending order,
+   then delete in ascending order and shrink the hmap at each
+   step, using hash function HASH. */
+static void
+test_insert_ordered (int max_elems, hash_function *hash)
+{
+  struct element *elements;
+  int *values;
+  struct hmap hmap;
+  int i;
+
+  hmap_init (&hmap);
+  elements = xnmalloc (max_elems, sizeof *elements);
+  values = xnmalloc (max_elems, sizeof *values);
+  for (i = 0; i < max_elems; i++)
+    {
+      values[i] = elements[i].data = i;
+      hmap_insert (&hmap, &elements[i].node, hash (elements[i].data));
+      check_hmap (&hmap, values, i + 1, hash);
+
+      if (hash == identity_hash) 
+        {
+          /* Check that every every hash bucket has (almost) the
+             same number of nodes in it.  */
+          int min = INT_MAX;
+          int max = INT_MIN;
+          int j;
+
+          for (j = 0; j <= hmap.mask; j++) 
+            {
+              int count = 0;
+              struct hmap_node *node;
+
+              for (node = hmap.buckets[j]; node != NULL; node = node->next)
+                count++;
+              if (count < min)
+                min = count;
+              if (count > max)
+                max = count;
+            }
+          check (max - min <= 1);
+        }
+    }
+  for (i = 0; i < max_elems; i++)
+    {
+      hmap_delete (&hmap, &elements[i].node);
+      hmap_shrink (&hmap);
+      check_hmap (&hmap, values + i + 1, max_elems - i - 1, hash);
+    }
+  hmap_destroy (&hmap);
+  free (elements);
+  free (values);
+}
+
+static void
+test_insert_ordered_random_hash (void)
+{
+  test_insert_ordered (1024, random_hash);
+}
+
+static void
+test_insert_ordered_identity_hash (void)
+{
+  test_insert_ordered (1024, identity_hash);
+}
+
+static void
+test_insert_ordered_constant_hash (void)
+{
+  test_insert_ordered (128, constant_hash);
+}
+
+/* Inserts up to MAX_ELEMS elements into an HMAP, then moves the
+   nodes around in memory, using hash function HASH. */
+static void
+test_moved (int max_elems, hash_function *hash)
+{
+  struct element *e[2];
+  int cur;
+  int *values;
+  struct hmap hmap;
+  int i, j;
+
+  hmap_init (&hmap);
+  e[0] = xnmalloc (max_elems, sizeof *e[0]);
+  e[1] = xnmalloc (max_elems, sizeof *e[1]);
+  values = xnmalloc (max_elems, sizeof *values);
+  cur = 0;
+  for (i = 0; i < max_elems; i++)
+    {
+      values[i] = e[cur][i].data = i;
+      hmap_insert (&hmap, &e[cur][i].node, hash (e[cur][i].data));
+      check_hmap (&hmap, values, i + 1, hash);
+
+      for (j = 0; j <= i; j++)
+        {
+          e[!cur][j] = e[cur][j];
+          hmap_moved (&hmap, &e[!cur][j].node, &e[cur][j].node);
+          check_hmap (&hmap, values, i + 1, hash);
+        }
+      cur = !cur;
+    }
+  hmap_destroy (&hmap);
+  free (e[0]);
+  free (e[1]);
+  free (values);
+}
+
+static void
+test_moved_random_hash (void) 
+{
+  test_moved (128, random_hash);
+}
+
+static void
+test_moved_identity_hash (void) 
+{
+  test_moved (128, identity_hash);
+}
+
+static void
+test_moved_constant_hash (void) 
+{
+  test_moved (32, constant_hash);
+}
+
+/* Inserts values into an HMAP, then changes their values, using
+   hash function HASH. */
+static void
+test_changed (hash_function *hash)
+{
+  const int max_elems = 6;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values, *changed_values;
+      struct element *elements;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      changed_values = xnmalloc (cnt, sizeof *changed_values);
+      elements = xnmalloc (cnt, sizeof *elements);
+      for (i = 0; i < cnt; i++)
+        values[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        {
+          for (i = 0; i < cnt; i++)
+            {
+              int j, k;
+              for (j = 0; j <= cnt; j++)
+                {
+                  struct hmap hmap;
+
+                  hmap_init (&hmap);
+
+                  /* Add to HMAP in order. */
+                  for (k = 0; k < cnt; k++)
+                    {
+                      int n = values[k];
+                      elements[n].data = n;
+                      hmap_insert (&hmap, &elements[n].node,
+                                   hash (elements[n].data));
+                    }
+                  check_hmap (&hmap, values, cnt, hash);
+
+                  /* Change value i to j. */
+                  elements[i].data = j;
+                  hmap_changed (&hmap, &elements[i].node,
+                                hash (elements[i].data));
+                  for (k = 0; k < cnt; k++)
+                    changed_values[k] = k;
+                  changed_values[i] = j;
+                  check_hmap (&hmap, changed_values, cnt, hash);
+
+                  hmap_destroy (&hmap);
+                }
+            }
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+      free (changed_values);
+      free (elements);
+    }
+}
+
+static void
+test_changed_random_hash (void)
+{
+  test_changed (random_hash);
+}
+
+static void
+test_changed_identity_hash (void)
+{
+  test_changed (identity_hash);
+}
+
+static void
+test_changed_constant_hash (void)
+{
+  test_changed (constant_hash);
+}
+
+static void
+test_swap (int max_elems, hash_function *hash) 
+{
+  struct element *elements;
+  int *values;
+  struct hmap a, b;
+  struct hmap *working, *empty;
+  int i;
+
+  hmap_init (&a);
+  hmap_init (&b);
+  working = &a;
+  empty = &b;
+  elements = xnmalloc (max_elems, sizeof *elements);
+  values = xnmalloc (max_elems, sizeof *values);
+  for (i = 0; i < max_elems; i++)
+    {
+      struct hmap *tmp;
+      values[i] = elements[i].data = i;
+      hmap_insert (working, &elements[i].node, hash (elements[i].data));
+      check_hmap (working, values, i + 1, hash);
+      check_hmap (empty, NULL, 0, hash);
+      hmap_swap (&a, &b);
+      tmp = working;
+      working = empty;
+      empty = tmp;
+    }
+  hmap_destroy (&a);
+  hmap_destroy (&b);
+  free (elements);
+  free (values);
+}
+
+static void
+test_swap_random_hash (void) 
+{
+  test_swap (128, random_hash);
+}
+
+static void
+test_destroy_null (void) 
+{
+  hmap_destroy (NULL);
+}
+
+/* Test shrinking an empty hash table. */
+static void
+test_shrink_empty (void)
+{
+  struct hmap hmap;
+
+  hmap_init (&hmap);
+  hmap_reserve (&hmap, 123);
+  hmap_shrink (&hmap);
+  hmap_destroy (&hmap);
+}
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name)
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void)
+{
+  run_test (test_insert_any_remove_any_random_hash,
+            "insert any order, delete any order (random hash)");
+  run_test (test_insert_any_remove_any_identity_hash,
+            "insert any order, delete any order (identity hash)");
+  run_test (test_insert_any_remove_any_constant_hash,
+            "insert any order, delete any order (constant hash)");
+
+  run_test (test_insert_any_remove_same_random_hash,
+            "insert any order, delete same order (random hash)");
+  run_test (test_insert_any_remove_same_identity_hash,
+            "insert any order, delete same order (identity hash)");
+  run_test (test_insert_any_remove_same_constant_hash,
+            "insert any order, delete same order (constant hash)");
+
+  run_test (test_insert_any_remove_reverse_random_hash,
+            "insert any order, delete reverse order (random hash)");
+  run_test (test_insert_any_remove_reverse_identity_hash,
+            "insert any order, delete reverse order (identity hash)");
+  run_test (test_insert_any_remove_reverse_constant_hash,
+            "insert any order, delete reverse order (constant hash)");
+
+  run_test (test_random_sequence_random_hash,
+            "insert and delete in random sequence (random hash)");
+  run_test (test_random_sequence_identity_hash,
+            "insert and delete in random sequence (identity hash)");
+  run_test (test_random_sequence_constant_hash,
+            "insert and delete in random sequence (constant hash)");
+
+  run_test (test_insert_ordered_random_hash,
+            "insert in ascending order (random hash)");
+  run_test (test_insert_ordered_identity_hash,
+            "insert in ascending order (identity hash)");
+  run_test (test_insert_ordered_constant_hash,
+            "insert in ascending order (constant hash)");
+
+  run_test (test_moved_random_hash,
+            "move elements around in memory (random hash)");
+  run_test (test_moved_identity_hash,
+            "move elements around in memory (identity hash)");
+  run_test (test_moved_constant_hash,
+            "move elements around in memory (constant hash)");
+
+  run_test (test_changed_random_hash,
+            "change key data in nodes (random hash)");
+  run_test (test_changed_identity_hash,
+            "change key data in nodes (identity hash)");
+  run_test (test_changed_constant_hash,
+            "change key data in nodes (constant hash)");
+
+  run_test (test_swap_random_hash, "test swapping tables");
+
+  run_test (test_destroy_null, "test destroying null table");
+  run_test (test_shrink_empty, "test shrinking an empty table");
+
+  putchar ('\n');
+
+  return 0;
+}
diff --git a/tests/libpspp/hmapx-test.c b/tests/libpspp/hmapx-test.c
new file mode 100644 (file)
index 0000000..fc08ca6
--- /dev/null
@@ -0,0 +1,1034 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007, 2008 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/>. */
+
+/* This is a test program for the hmapx_* routines defined in
+   hmapx.c.  This test program aims to be as comprehensive as
+   possible.  "gcov -a -b" should report 100% coverage of lines,
+   blocks and branches in hmapx.c (when compiled with -DNDEBUG).
+   "valgrind --leak-check=yes --show-reachable=yes" should give a
+   clean report. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/hmapx.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/compiler.h>
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line)
+{
+  if (!ok)
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      abort ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+
+/* Prints a message about memory exhaustion and exits with a
+   failure code. */
+static void
+xalloc_die (void)
+{
+  printf ("virtual memory exhausted\n");
+  exit (EXIT_FAILURE);
+}
+
+/* Allocates and returns N bytes of memory. */
+static void *
+xmalloc (size_t n)
+{
+  if (n != 0)
+    {
+      void *p = malloc (n);
+      if (p == NULL)
+        xalloc_die ();
+
+      return p;
+    }
+  else
+    return NULL;
+}
+
+static void *
+xmemdup (const void *p, size_t n)
+{
+  void *q = xmalloc (n);
+  memcpy (q, p, n);
+  return q;
+}
+
+/* Allocates and returns N * M bytes of memory. */
+static void *
+xnmalloc (size_t n, size_t m)
+{
+  if ((size_t) -1 / m <= n)
+    xalloc_die ();
+  return xmalloc (n * m);
+}
+\f
+/* Node type and support routines. */
+
+/* Test data element. */
+struct element
+  {
+    int data;                 /* Primary value. */
+  };
+
+/* Compares A and B and returns a strcmp-type return value. */
+static int
+compare_ints (const void *a_, const void *b_)
+{
+  const int *a = a_;
+  const int *b = b_;
+
+  return *a < *b ? -1 : *a > *b;
+}
+
+/* Swaps *A and *B. */
+static void
+swap (int *a, int *b)
+{
+  int t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of the CNT integers starting at VALUES. */
+static void
+reverse (int *values, size_t cnt)
+{
+  size_t i = 0;
+  size_t j = cnt;
+
+  while (j > i)
+    swap (&values[i++], &values[--j]);
+}
+
+/* Arranges the CNT elements in VALUES into the lexicographically
+   next greater permutation.  Returns true if successful.
+   If VALUES is already the lexicographically greatest
+   permutation of its elements (i.e. ordered from greatest to
+   smallest), arranges them into the lexicographically least
+   permutation (i.e. ordered from smallest to largest) and
+   returns false. */
+static bool
+next_permutation (int *values, size_t cnt)
+{
+  if (cnt > 0)
+    {
+      size_t i = cnt - 1;
+      while (i != 0)
+        {
+          i--;
+          if (values[i] < values[i + 1])
+            {
+              size_t j;
+              for (j = cnt - 1; values[i] >= values[j]; j--)
+                continue;
+              swap (values + i, values + j);
+              reverse (values + (i + 1), cnt - (i + 1));
+              return true;
+            }
+        }
+
+      reverse (values, cnt);
+    }
+
+  return false;
+}
+
+/* Returns N!. */
+static unsigned int
+factorial (unsigned int n)
+{
+  unsigned int value = 1;
+  while (n > 1)
+    value *= n--;
+  return value;
+}
+
+/* Randomly shuffles the CNT elements in ARRAY, each of which is
+   SIZE bytes in size. */
+static void
+random_shuffle (void *array_, size_t cnt, size_t size)
+{
+  char *array = array_;
+  char *tmp = xmalloc (size);
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = rand () % (cnt - i) + i;
+      if (i != j)
+        {
+          memcpy (tmp, array + j * size, size);
+          memcpy (array + j * size, array + i * size, size);
+          memcpy (array + i * size, tmp, size);
+        }
+    }
+
+  free (tmp);
+}
+
+typedef size_t hash_function (int data);
+
+static size_t
+identity_hash (int data) 
+{
+  return data;
+}
+
+static size_t
+constant_hash (int data UNUSED) 
+{
+  return 0x12345678u;
+}
+
+static inline uint32_t
+md4_round (uint32_t a, uint32_t b, uint32_t c, uint32_t d,
+           uint32_t data, uint32_t n)
+{
+  uint32_t x = a + (d ^ (b & (c ^ d))) + data;
+  return (x << n) | (x >> (32 - n));
+}
+
+static size_t
+random_hash (int data)
+{
+  uint32_t a = data;
+  uint32_t b = data;
+  uint32_t c = data;
+  uint32_t d = data;
+  a = md4_round (a, b, c, d, 0, 3);
+  d = md4_round (d, a, b, c, 1, 7);
+  c = md4_round (c, d, a, b, 2, 11);
+  b = md4_round (b, c, d, a, 3, 19);
+  return a ^ b ^ c ^ d;
+}
+
+static struct hmapx_node *
+find_element (struct hmapx *hmapx, int data, hash_function *hash)
+{
+  struct hmapx_node *node;
+  struct element *e;
+  HMAPX_FOR_EACH_WITH_HASH (e, node, hash (data), hmapx)
+    if (e->data == data)
+      break;
+  return node;
+}
+
+/* Checks that HMAPX contains the CNT ints in DATA, that its
+   structure is correct, and that certain operations on HMAPX
+   produce the expected results. */
+static void
+check_hmapx (struct hmapx *hmapx, const int data[], size_t cnt,
+            hash_function *hash)
+{
+  size_t i, j;
+  int *order;
+
+  check (hmapx_count (hmapx) == cnt);
+  check (cnt <= hmapx_capacity (hmapx));
+
+  order = xmemdup (data, cnt * sizeof *data);
+  qsort (order, cnt, sizeof *order, compare_ints);
+
+  for (i = 0; i < cnt; i = j)
+    {
+      struct hmapx_node *node;
+      struct element *e;
+      int count;
+
+      for (j = i + 1; j < cnt; j++)
+        if (order[i] != order[j])
+          break;
+
+      count = 0;
+      HMAPX_FOR_EACH_WITH_HASH (e, node, hash (order[i]), hmapx)
+        if (e->data == order[i]) 
+          count++; 
+
+      check (count == j - i);
+    }
+
+  check (find_element (hmapx, -1, hash) == NULL);
+
+  if (cnt == 0)
+    check (hmapx_first (hmapx) == NULL);
+  else
+    {
+      struct hmapx_node *p;
+      int left;
+
+      left = cnt;
+      for (p = hmapx_first (hmapx), i = 0; i < cnt;
+           p = hmapx_next (hmapx, p), i++)
+        {
+          struct element *e = hmapx_node_data (p);
+          size_t j;
+
+          check (hmapx_node_hash (p) == hash (e->data));
+          for (j = 0; j < left; j++)
+            if (order[j] == e->data) 
+              {
+                order[j] = order[--left];
+                goto next;
+              }
+          abort ();
+
+        next: ;
+        }
+      check (p == NULL);
+    }
+
+  free (order);
+}
+
+/* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
+   HMAPX in the order specified by INSERTIONS, then deletes them in
+   the order specified by DELETIONS, checking the HMAPX's contents
+   for correctness after each operation.  Uses HASH as the hash
+   function. */
+static void
+test_insert_delete (const int insertions[],
+                    const int deletions[],
+                    size_t cnt,
+                    hash_function *hash,
+                    size_t reserve)
+{
+  struct element *elements;
+  struct hmapx_node **nodes;
+  struct hmapx hmapx;
+  size_t i;
+
+  elements = xnmalloc (cnt, sizeof *elements);
+  nodes = xnmalloc (cnt, sizeof *nodes);
+  for (i = 0; i < cnt; i++)
+    elements[i].data = i;
+
+  hmapx_init (&hmapx);
+  hmapx_reserve (&hmapx, reserve);
+  check_hmapx (&hmapx, NULL, 0, hash);
+  for (i = 0; i < cnt; i++)
+    {
+      struct hmapx_node *(*insert) (struct hmapx *, void *, size_t hash);
+      size_t capacity;
+
+      /* Insert the node.  Use hmapx_insert_fast if we have not
+         yet exceeded the reserve. */
+      insert = i < reserve ? hmapx_insert_fast : hmapx_insert;
+      nodes[insertions[i]] = insert (&hmapx, &elements[insertions[i]],
+                                     hash (insertions[i]));
+      check_hmapx (&hmapx, insertions, i + 1, hash);
+
+      /* A series of insertions should not produce a shrinkable hmapx. */
+      if (i >= reserve) 
+        {
+          capacity = hmapx_capacity (&hmapx);
+          hmapx_shrink (&hmapx);
+          check (capacity == hmapx_capacity (&hmapx)); 
+        }
+    }
+  for (i = 0; i < cnt; i++)
+    {
+      hmapx_delete (&hmapx, nodes[deletions[i]]);
+      check_hmapx (&hmapx, deletions + i + 1, cnt - i - 1, hash);
+    }
+  hmapx_destroy (&hmapx);
+
+  free (elements);
+  free (nodes);
+}
+\f
+/* Inserts values into an HMAPX in each possible order, then
+   removes them in each possible order, up to a specified maximum
+   size, using hash function HASH. */
+static void
+test_insert_any_remove_any (hash_function *hash)
+{
+  const int max_elems = 5;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int ins_perm_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+
+      for (ins_perm_cnt = 0;
+           ins_perm_cnt == 0 || next_permutation (insertions, cnt);
+           ins_perm_cnt++)
+        {
+          unsigned int del_perm_cnt;
+          int i;
+
+          for (i = 0; i < cnt; i++)
+            deletions[i] = i;
+
+          for (del_perm_cnt = 0;
+               del_perm_cnt == 0 || next_permutation (deletions, cnt);
+               del_perm_cnt++)
+            test_insert_delete (insertions, deletions, cnt, hash, 1);
+
+          check (del_perm_cnt == factorial (cnt));
+        }
+      check (ins_perm_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+static void
+test_insert_any_remove_any_random_hash (void) 
+{
+  test_insert_any_remove_any (random_hash);
+}
+
+static void
+test_insert_any_remove_any_identity_hash (void) 
+{
+  test_insert_any_remove_any (identity_hash);
+}
+
+static void
+test_insert_any_remove_any_constant_hash (void) 
+{
+  test_insert_any_remove_any (constant_hash);
+}
+
+/* Inserts values into an HMAPX in each possible order, then
+   removes them in the same order, up to a specified maximum
+   size, using hash function HASH. */
+static void
+test_insert_any_remove_same (hash_function *hash)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      for (i = 0; i < cnt; i++)
+        values[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        test_insert_delete (values, values, cnt, hash, cnt / 2);
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+    }
+}
+
+static void
+test_insert_any_remove_same_random_hash (void) 
+{
+  test_insert_any_remove_same (random_hash);
+}
+
+static void
+test_insert_any_remove_same_identity_hash (void) 
+{
+  test_insert_any_remove_same (identity_hash);
+}
+
+static void
+test_insert_any_remove_same_constant_hash (void) 
+{
+  test_insert_any_remove_same (constant_hash);
+}
+
+/* Inserts values into an HMAPX in each possible order, then
+   removes them in reverse order, up to a specified maximum
+   size, using hash function HASH. */
+static void
+test_insert_any_remove_reverse (hash_function *hash)
+{
+  const int max_elems = 7;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *insertions, *deletions;
+      unsigned int permutation_cnt;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (insertions, cnt);
+           permutation_cnt++)
+        {
+          memcpy (deletions, insertions, sizeof *insertions * cnt);
+          reverse (deletions, cnt);
+
+          test_insert_delete (insertions, deletions, cnt, hash, cnt);
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+static void
+test_insert_any_remove_reverse_random_hash (void)
+{
+  test_insert_any_remove_reverse (random_hash);
+}
+
+static void
+test_insert_any_remove_reverse_identity_hash (void)
+{
+  test_insert_any_remove_reverse (identity_hash);
+}
+
+static void
+test_insert_any_remove_reverse_constant_hash (void)
+{
+  test_insert_any_remove_reverse (constant_hash);
+}
+
+/* Inserts and removes up to MAX_ELEMS values in an hmapx, in
+   random order, using hash function HASH. */
+static void
+test_random_sequence (int max_elems, hash_function *hash)
+{
+  const int max_trials = 8;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt += 2)
+    {
+      int *insertions, *deletions;
+      int trial;
+      int i;
+
+      insertions = xnmalloc (cnt, sizeof *insertions);
+      deletions = xnmalloc (cnt, sizeof *deletions);
+      for (i = 0; i < cnt; i++)
+        insertions[i] = i;
+      for (i = 0; i < cnt; i++)
+        deletions[i] = i;
+
+      for (trial = 0; trial < max_trials; trial++)
+        {
+          random_shuffle (insertions, cnt, sizeof *insertions);
+          random_shuffle (deletions, cnt, sizeof *deletions);
+
+          test_insert_delete (insertions, deletions, cnt, hash, 0);
+        }
+
+      free (insertions);
+      free (deletions);
+    }
+}
+
+static void
+test_random_sequence_random_hash (void) 
+{
+  test_random_sequence (64, random_hash);
+}
+
+static void
+test_random_sequence_identity_hash (void) 
+{
+  test_random_sequence (64, identity_hash);
+}
+
+static void
+test_random_sequence_constant_hash (void) 
+{
+  test_random_sequence (32, constant_hash);
+}
+
+/* Inserts MAX_ELEMS elements into an HMAPX in ascending order,
+   then delete in ascending order and shrink the hmapx at each
+   step, using hash function HASH. */
+static void
+test_insert_ordered (int max_elems, hash_function *hash)
+{
+  struct element *elements;
+  struct hmapx_node **nodes;
+  int *values;
+  struct hmapx hmapx;
+  int i;
+
+  hmapx_init (&hmapx);
+  elements = xnmalloc (max_elems, sizeof *elements);
+  nodes = xnmalloc (max_elems, sizeof *nodes);
+  values = xnmalloc (max_elems, sizeof *values);
+  for (i = 0; i < max_elems; i++)
+    {
+      values[i] = elements[i].data = i;
+      nodes[i] = hmapx_insert (&hmapx, &elements[i], hash (elements[i].data));
+      check_hmapx (&hmapx, values, i + 1, hash);
+
+      if (hash == identity_hash) 
+        {
+          /* Check that every every hash bucket has (almost) the
+             same number of nodes in it.  */
+          int min = INT_MAX;
+          int max = INT_MIN;
+          int j;
+
+          for (j = 0; j <= hmapx.hmap.mask; j++) 
+            {
+              int count = 0;
+              struct hmap_node *node;
+
+              for (node = hmapx.hmap.buckets[j]; node != NULL;
+                   node = node->next)
+                count++;
+              if (count < min)
+                min = count;
+              if (count > max)
+                max = count;
+            }
+          check (max - min <= 1);
+        }
+    }
+  for (i = 0; i < max_elems; i++)
+    {
+      hmapx_delete (&hmapx, nodes[i]);
+      hmapx_shrink (&hmapx);
+      check_hmapx (&hmapx, values + i + 1, max_elems - i - 1, hash);
+    }
+  hmapx_destroy (&hmapx);
+  free (elements);
+  free (nodes);
+  free (values);
+}
+
+static void
+test_insert_ordered_random_hash (void)
+{
+  test_insert_ordered (1024, random_hash);
+}
+
+static void
+test_insert_ordered_identity_hash (void)
+{
+  test_insert_ordered (1024, identity_hash);
+}
+
+static void
+test_insert_ordered_constant_hash (void)
+{
+  test_insert_ordered (128, constant_hash);
+}
+
+/* Inserts up to MAX_ELEMS elements into an HMAPX, then moves the
+   nodes around in memory, using hash function HASH. */
+static void
+test_moved (int max_elems, hash_function *hash)
+{
+  struct element *e[2];
+  int cur;
+  int *values;
+  struct hmapx_node **nodes;
+  struct hmapx hmapx;
+  int i, j;
+
+  hmapx_init (&hmapx);
+  e[0] = xnmalloc (max_elems, sizeof *e[0]);
+  e[1] = xnmalloc (max_elems, sizeof *e[1]);
+  values = xnmalloc (max_elems, sizeof *values);
+  nodes = xnmalloc (max_elems, sizeof *nodes);
+  cur = 0;
+  for (i = 0; i < max_elems; i++)
+    {
+      values[i] = e[cur][i].data = i;
+      nodes[i] = hmapx_insert (&hmapx, &e[cur][i], hash (e[cur][i].data));
+      check_hmapx (&hmapx, values, i + 1, hash);
+
+      for (j = 0; j <= i; j++)
+        {
+          e[!cur][j] = e[cur][j];
+          hmapx_move (nodes[j], &e[cur][j]);
+          check_hmapx (&hmapx, values, i + 1, hash);
+        }
+      cur = !cur;
+    }
+  hmapx_destroy (&hmapx);
+  free (e[0]);
+  free (e[1]);
+  free (values);
+  free (nodes);
+}
+
+static void
+test_moved_random_hash (void) 
+{
+  test_moved (128, random_hash);
+}
+
+static void
+test_moved_identity_hash (void) 
+{
+  test_moved (128, identity_hash);
+}
+
+static void
+test_moved_constant_hash (void) 
+{
+  test_moved (32, constant_hash);
+}
+
+/* Inserts values into an HMAPX, then changes their values, using
+   hash function HASH. */
+static void
+test_changed (hash_function *hash)
+{
+  const int max_elems = 6;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values, *changed_values;
+      struct hmapx_node **nodes;
+      struct element *elements;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      changed_values = xnmalloc (cnt, sizeof *changed_values);
+      elements = xnmalloc (cnt, sizeof *elements);
+      nodes = xnmalloc (cnt, sizeof *nodes);
+      for (i = 0; i < cnt; i++)
+        values[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        {
+          for (i = 0; i < cnt; i++)
+            {
+              int j, k;
+              for (j = 0; j <= cnt; j++)
+                {
+                  struct hmapx hmapx;
+
+                  hmapx_init (&hmapx);
+
+                  /* Add to HMAPX in order. */
+                  for (k = 0; k < cnt; k++)
+                    {
+                      int n = values[k];
+                      elements[n].data = n;
+                      nodes[n] = hmapx_insert (&hmapx, &elements[n],
+                                               hash (elements[n].data));
+                    }
+                  check_hmapx (&hmapx, values, cnt, hash);
+
+                  /* Change value i to j. */
+                  elements[i].data = j;
+                  hmapx_changed (&hmapx, nodes[i], 
+                                 hash (elements[i].data));
+                  for (k = 0; k < cnt; k++)
+                    changed_values[k] = k;
+                  changed_values[i] = j;
+                  check_hmapx (&hmapx, changed_values, cnt, hash);
+
+                  hmapx_destroy (&hmapx);
+                }
+            }
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+      free (changed_values);
+      free (elements);
+      free (nodes);
+    }
+}
+
+static void
+test_changed_random_hash (void)
+{
+  test_changed (random_hash);
+}
+
+static void
+test_changed_identity_hash (void)
+{
+  test_changed (identity_hash);
+}
+
+static void
+test_changed_constant_hash (void)
+{
+  test_changed (constant_hash);
+}
+
+/* Inserts values into an HMAPX, then changes and moves their
+   values, using hash function HASH. */
+static void
+test_change (hash_function *hash)
+{
+  const int max_elems = 6;
+  int cnt;
+
+  for (cnt = 0; cnt <= max_elems; cnt++)
+    {
+      int *values, *changed_values;
+      struct hmapx_node **nodes;
+      struct element *elements;
+      struct element replacement;
+      unsigned int permutation_cnt;
+      int i;
+
+      values = xnmalloc (cnt, sizeof *values);
+      changed_values = xnmalloc (cnt, sizeof *changed_values);
+      elements = xnmalloc (cnt, sizeof *elements);
+      nodes = xnmalloc (cnt, sizeof *nodes);
+      for (i = 0; i < cnt; i++)
+        values[i] = i;
+
+      for (permutation_cnt = 0;
+           permutation_cnt == 0 || next_permutation (values, cnt);
+           permutation_cnt++)
+        {
+          for (i = 0; i < cnt; i++)
+            {
+              int j, k;
+              for (j = 0; j <= cnt; j++)
+                {
+                  struct hmapx hmapx;
+
+                  hmapx_init (&hmapx);
+
+                  /* Add to HMAPX in order. */
+                  for (k = 0; k < cnt; k++)
+                    {
+                      int n = values[k];
+                      elements[n].data = n;
+                      nodes[n] = hmapx_insert (&hmapx, &elements[n],
+                                               hash (elements[n].data));
+                    }
+                  check_hmapx (&hmapx, values, cnt, hash);
+
+                  /* Change value i to j. */
+                  replacement.data = j;
+                  hmapx_change (&hmapx, nodes[i], &replacement, hash (j));
+                  for (k = 0; k < cnt; k++)
+                    changed_values[k] = k;
+                  changed_values[i] = j;
+                  check_hmapx (&hmapx, changed_values, cnt, hash);
+
+                  hmapx_destroy (&hmapx);
+                }
+            }
+        }
+      check (permutation_cnt == factorial (cnt));
+
+      free (values);
+      free (changed_values);
+      free (elements);
+      free (nodes);
+    }
+}
+
+static void
+test_change_random_hash (void)
+{
+  test_change (random_hash);
+}
+
+static void
+test_change_identity_hash (void)
+{
+  test_change (identity_hash);
+}
+
+static void
+test_change_constant_hash (void)
+{
+  test_change (constant_hash);
+}
+
+static void
+test_swap (int max_elems, hash_function *hash) 
+{
+  struct element *elements;
+  int *values;
+  struct hmapx a, b;
+  struct hmapx *working, *empty;
+  int i;
+
+  hmapx_init (&a);
+  hmapx_init (&b);
+  working = &a;
+  empty = &b;
+  elements = xnmalloc (max_elems, sizeof *elements);
+  values = xnmalloc (max_elems, sizeof *values);
+  for (i = 0; i < max_elems; i++)
+    {
+      struct hmapx *tmp;
+      values[i] = elements[i].data = i;
+      hmapx_insert (working, &elements[i], hash (elements[i].data));
+      check_hmapx (working, values, i + 1, hash);
+      check_hmapx (empty, NULL, 0, hash);
+      hmapx_swap (&a, &b);
+      tmp = working;
+      working = empty;
+      empty = tmp;
+    }
+  hmapx_destroy (&a);
+  hmapx_destroy (&b);
+  free (elements);
+  free (values);
+}
+
+static void
+test_swap_random_hash (void) 
+{
+  test_swap (128, random_hash);
+}
+
+static void
+test_destroy_null (void) 
+{
+  hmapx_destroy (NULL);
+}
+
+/* Test shrinking an empty hash table. */
+static void
+test_shrink_empty (void)
+{
+  struct hmapx hmapx;
+
+  hmapx_init (&hmapx);
+  hmapx_reserve (&hmapx, 123);
+  hmapx_shrink (&hmapx);
+  hmapx_destroy (&hmapx);
+}
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name)
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void)
+{
+  run_test (test_insert_any_remove_any_random_hash,
+            "insert any order, delete any order (random hash)");
+  run_test (test_insert_any_remove_any_identity_hash,
+            "insert any order, delete any order (identity hash)");
+  run_test (test_insert_any_remove_any_constant_hash,
+            "insert any order, delete any order (constant hash)");
+
+  run_test (test_insert_any_remove_same_random_hash,
+            "insert any order, delete same order (random hash)");
+  run_test (test_insert_any_remove_same_identity_hash,
+            "insert any order, delete same order (identity hash)");
+  run_test (test_insert_any_remove_same_constant_hash,
+            "insert any order, delete same order (constant hash)");
+
+  run_test (test_insert_any_remove_reverse_random_hash,
+            "insert any order, delete reverse order (random hash)");
+  run_test (test_insert_any_remove_reverse_identity_hash,
+            "insert any order, delete reverse order (identity hash)");
+  run_test (test_insert_any_remove_reverse_constant_hash,
+            "insert any order, delete reverse order (constant hash)");
+
+  run_test (test_random_sequence_random_hash,
+            "insert and delete in random sequence (random hash)");
+  run_test (test_random_sequence_identity_hash,
+            "insert and delete in random sequence (identity hash)");
+  run_test (test_random_sequence_constant_hash,
+            "insert and delete in random sequence (constant hash)");
+
+  run_test (test_insert_ordered_random_hash,
+            "insert in ascending order (random hash)");
+  run_test (test_insert_ordered_identity_hash,
+            "insert in ascending order (identity hash)");
+  run_test (test_insert_ordered_constant_hash,
+            "insert in ascending order (constant hash)");
+
+  run_test (test_moved_random_hash,
+            "move elements around in memory (random hash)");
+  run_test (test_moved_identity_hash,
+            "move elements around in memory (identity hash)");
+  run_test (test_moved_constant_hash,
+            "move elements around in memory (constant hash)");
+
+  run_test (test_changed_random_hash,
+            "change key data in nodes (random hash)");
+  run_test (test_changed_identity_hash,
+            "change key data in nodes (identity hash)");
+  run_test (test_changed_constant_hash,
+            "change key data in nodes (constant hash)");
+
+  run_test (test_change_random_hash,
+            "change and move key data in nodes (random hash)");
+  run_test (test_change_identity_hash,
+            "change and move key data in nodes (identity hash)");
+  run_test (test_change_constant_hash,
+            "change and move key data in nodes (constant hash)");
+
+  run_test (test_swap_random_hash, "test swapping tables");
+
+  run_test (test_destroy_null, "test destroying null table");
+  run_test (test_shrink_empty, "test shrinking an empty table");
+
+  putchar ('\n');
+
+  return 0;
+}
index 504e19f409aa05bb8a4df7b6969d6197d91f739b..c603c3a876a61ee2ce5e21815d93e6a8e829e1c6 100644 (file)
@@ -245,7 +245,7 @@ next_composition (int n, int *k, int parts[])
 /* A block expected to be found in a tower. */
 struct expected_block
   {
-    int height;         /* Expected height of bottom of block. */
+    int size;           /* Expected thickness of block. */
     int x;              /* Expected value for `x' member. */
   };
 
@@ -259,6 +259,7 @@ check_tower (struct tower *t,
   struct tower_node *node;
   size_t i;
 
+  check (tower_count (t) == block_cnt);
   check (tower_is_empty (t) == (block_cnt == 0));
 
   total_height = 0;
@@ -266,7 +267,7 @@ check_tower (struct tower *t,
     {
       unsigned long int level;
       for (level = total_height;
-           level < total_height + blocks[i].height;
+           level < total_height + blocks[i].size;
            level++)
         {
           struct tower_node *found;
@@ -275,8 +276,11 @@ check_tower (struct tower *t,
           check (found != NULL);
           check (tower_node_to_block (found)->x == blocks[i].x);
           check (block_start == total_height);
+          check (tower_node_get_level (found) == total_height);
+          check (tower_node_get_index (found) == i);
+          check (tower_get (t, i) == found);
         }
-      total_height += blocks[i].height;
+      total_height += blocks[i].size;
     }
   check (tower_height (t) == total_height);
 
@@ -284,7 +288,7 @@ check_tower (struct tower *t,
        node != NULL;
        node = tower_next (t, node), i++)
     {
-      check (tower_node_get_height (node) == blocks[i].height);
+      check (tower_node_get_size (node) == blocks[i].size);
       check (tower_node_to_block (node)->x == blocks[i].x);
     }
   check (i == block_cnt);
@@ -293,7 +297,7 @@ check_tower (struct tower *t,
        node != NULL;
        node = tower_prev (t, node), i--)
     {
-      check (tower_node_get_height (node) == blocks[i].height);
+      check (tower_node_get_size (node) == blocks[i].size);
       check (tower_node_to_block (node)->x == blocks[i].x);
     }
   check (i == SIZE_MAX);
@@ -312,19 +316,19 @@ test_insert (void)
     {
       unsigned int composition_cnt;
       struct expected_block *expected;
-      int *heights;
+      int *sizes;
       int block_cnt;
       int *order;
       struct block *blocks;
 
       expected = xnmalloc (cnt, sizeof *expected);
-      heights = xnmalloc (cnt, sizeof *heights);
+      sizes = xnmalloc (cnt, sizeof *sizes);
       order = xnmalloc (cnt, sizeof *order);
       blocks = xnmalloc (cnt, sizeof *blocks);
 
       block_cnt = 0;
       composition_cnt = 0;
-      while (next_composition (cnt, &block_cnt, heights))
+      while (next_composition (cnt, &block_cnt, sizes))
         {
           int i, j;
           unsigned int permutation_cnt;
@@ -338,7 +342,7 @@ test_insert (void)
               struct tower t;
 
               /* Inserts the block_cnt blocks with the given
-                 heights[] into T in the order given by order[]. */
+                 sizes[] into T in the order given by order[]. */
               tower_init (&t);
               for (i = 0; i < block_cnt; i++)
                 {
@@ -354,14 +358,14 @@ test_insert (void)
                         && (under == NULL || under->x > order[j]))
                       under = &blocks[order[j]];
 
-                  tower_insert (&t, heights[idx], &blocks[idx].node,
+                  tower_insert (&t, sizes[idx], &blocks[idx].node,
                                 under != NULL ? &under->node : NULL);
                 }
 
               /* Check that the result is what we expect. */
               for (i = 0; i < block_cnt; i++)
                 {
-                  expected[i].height = heights[i];
+                  expected[i].size = sizes[i];
                   expected[i].x = i;
                 }
               check_tower (&t, expected, block_cnt);
@@ -375,14 +379,14 @@ test_insert (void)
       check (composition_cnt == 1 << (cnt - 1));
 
       free (expected);
-      free (heights);
+      free (sizes);
       free (order);
       free (blocks);
     }
 }
 
 /* Tests deleting blocks from towers that initially contain all
-   possible sets of block heights into a tower in all possible
+   possible sets of block sizes into a tower in all possible
    orders, up to a specified maximum tower height. */
 static void
 test_delete (void)
@@ -394,19 +398,19 @@ test_delete (void)
     {
       unsigned int composition_cnt;
       struct expected_block *expected;
-      int *heights;
+      int *sizes;
       int block_cnt;
       int *order;
       struct block *blocks;
 
       expected = xnmalloc (cnt, sizeof *expected);
-      heights = xnmalloc (cnt, sizeof *heights);
+      sizes = xnmalloc (cnt, sizeof *sizes);
       order = xnmalloc (cnt, sizeof *order);
       blocks = xnmalloc (cnt, sizeof *blocks);
 
       block_cnt = 0;
       composition_cnt = 0;
-      while (next_composition (cnt, &block_cnt, heights))
+      while (next_composition (cnt, &block_cnt, sizes))
         {
           int i;
           unsigned int permutation_cnt;
@@ -424,9 +428,9 @@ test_delete (void)
               for (i = 0; i < block_cnt; i++)
                 {
                   blocks[i].x = i;
-                  tower_insert (&t, heights[i], &blocks[i].node, NULL);
+                  tower_insert (&t, sizes[i], &blocks[i].node, NULL);
                   expected[i].x = i;
-                  expected[i].height = heights[i];
+                  expected[i].size = sizes[i];
                 }
               check_tower (&t, expected, block_cnt);
 
@@ -459,14 +463,14 @@ test_delete (void)
       check (composition_cnt == 1 << (cnt - 1));
 
       free (expected);
-      free (heights);
+      free (sizes);
       free (order);
       free (blocks);
     }
 }
 
-/* Tests towers containing all possible block heights, resizing
-   the blocks to all possible heights that conserve the total
+/* Tests towers containing all possible block sizes, resizing
+   the blocks to all possible sizes that conserve the total
    tower height, up to a maximum total tower height. */
 static void
 test_resize (void)
@@ -478,27 +482,27 @@ test_resize (void)
     {
       unsigned int composition_cnt;
       struct expected_block *expected;
-      int *heights, *new_heights;
+      int *sizes, *new_sizes;
       int block_cnt;
       int *order;
       struct block *blocks;
 
       expected = xnmalloc (cnt, sizeof *expected);
-      heights = xnmalloc (cnt, sizeof *heights);
-      new_heights = xnmalloc (cnt, sizeof *new_heights);
+      sizes = xnmalloc (cnt, sizeof *sizes);
+      new_sizes = xnmalloc (cnt, sizeof *new_sizes);
       order = xnmalloc (cnt, sizeof *order);
       blocks = xnmalloc (cnt, sizeof *blocks);
 
       block_cnt = 0;
       composition_cnt = 0;
-      while (next_composition (cnt, &block_cnt, heights))
+      while (next_composition (cnt, &block_cnt, sizes))
         {
           int i;
           unsigned int resizes = 0;
 
-          for (resizes = 0, first_k_composition (cnt, block_cnt, new_heights);
+          for (resizes = 0, first_k_composition (cnt, block_cnt, new_sizes);
                (resizes == 0
-                || next_k_composition (cnt, block_cnt, new_heights));
+                || next_k_composition (cnt, block_cnt, new_sizes));
                resizes++)
             {
               struct tower t;
@@ -508,18 +512,18 @@ test_resize (void)
               for (i = 0; i < block_cnt; i++)
                 {
                   blocks[i].x = i;
-                  tower_insert (&t, heights[i], &blocks[i].node, NULL);
+                  tower_insert (&t, sizes[i], &blocks[i].node, NULL);
                   expected[i].x = i;
-                  expected[i].height = heights[i];
+                  expected[i].size = sizes[i];
                 }
               check_tower (&t, expected, block_cnt);
 
               /* Resize all the blocks. */
               for (i = 0; i < block_cnt; i++)
                 {
-                  if (expected[i].height != new_heights[i] || rand () % 2)
-                    tower_resize (&t, &blocks[i].node, new_heights[i]);
-                  expected[i].height = new_heights[i];
+                  if (expected[i].size != new_sizes[i] || rand () % 2)
+                    tower_resize (&t, &blocks[i].node, new_sizes[i]);
+                  expected[i].size = new_sizes[i];
                 }
               check_tower (&t, expected, block_cnt);
             }
@@ -530,8 +534,8 @@ test_resize (void)
       check (composition_cnt == 1 << (cnt - 1));
 
       free (expected);
-      free (new_heights);
-      free (heights);
+      free (new_sizes);
+      free (sizes);
       free (order);
       free (blocks);
     }
@@ -549,20 +553,20 @@ test_splice_out (void)
     {
       unsigned int composition_cnt;
       struct expected_block *expected;
-      int *heights, *new_heights;
+      int *sizes, *new_sizes;
       int block_cnt;
       int *order;
       struct block *blocks;
 
       expected = xnmalloc (cnt, sizeof *expected);
-      heights = xnmalloc (cnt, sizeof *heights);
-      new_heights = xnmalloc (cnt, sizeof *new_heights);
+      sizes = xnmalloc (cnt, sizeof *sizes);
+      new_sizes = xnmalloc (cnt, sizeof *new_sizes);
       order = xnmalloc (cnt, sizeof *order);
       blocks = xnmalloc (cnt, sizeof *blocks);
 
       block_cnt = 0;
       composition_cnt = 0;
-      while (next_composition (cnt, &block_cnt, heights))
+      while (next_composition (cnt, &block_cnt, sizes))
         {
           int i, j;
 
@@ -579,9 +583,9 @@ test_splice_out (void)
                 for (k = 0; k < block_cnt; k++)
                   {
                     blocks[k].x = k;
-                    tower_insert (&src, heights[k], &blocks[k].node, NULL);
+                    tower_insert (&src, sizes[k], &blocks[k].node, NULL);
                     expected[k].x = k;
-                    expected[k].height = heights[k];
+                    expected[k].size = sizes[k];
                   }
                 check_tower (&src, expected, block_cnt);
 
@@ -598,8 +602,8 @@ test_splice_out (void)
       check (composition_cnt == 1 << (cnt - 1));
 
       free (expected);
-      free (new_heights);
-      free (heights);
+      free (new_sizes);
+      free (sizes);
       free (order);
       free (blocks);
     }
@@ -617,20 +621,20 @@ test_splice_in (void)
     {
       unsigned int composition_cnt;
       struct expected_block *expected;
-      int *heights, *new_heights;
+      int *sizes, *new_sizes;
       int block_cnt;
       int *order;
       struct block *blocks;
 
       expected = xnmalloc (cnt, sizeof *expected);
-      heights = xnmalloc (cnt, sizeof *heights);
-      new_heights = xnmalloc (cnt, sizeof *new_heights);
+      sizes = xnmalloc (cnt, sizeof *sizes);
+      new_sizes = xnmalloc (cnt, sizeof *new_sizes);
       order = xnmalloc (cnt, sizeof *order);
       blocks = xnmalloc (cnt, sizeof *blocks);
 
       block_cnt = 0;
       composition_cnt = 0;
-      while (next_composition (cnt, &block_cnt, heights))
+      while (next_composition (cnt, &block_cnt, sizes))
         {
           int i, j;
 
@@ -648,9 +652,9 @@ test_splice_in (void)
                   {
                     blocks[k].x = k;
                     tower_insert (k >= i && k < j ? &src : &dst,
-                                  heights[k], &blocks[k].node, NULL);
+                                  sizes[k], &blocks[k].node, NULL);
                     expected[k].x = k;
-                    expected[k].height = heights[k];
+                    expected[k].size = sizes[k];
                   }
 
                 /* Splice SRC into DST. */
@@ -663,8 +667,8 @@ test_splice_in (void)
       check (composition_cnt == 1 << (cnt - 1));
 
       free (expected);
-      free (new_heights);
-      free (heights);
+      free (new_sizes);
+      free (sizes);
       free (order);
       free (blocks);
     }
index b9cabe0a82d34488b0d5f8b40b614dd92bd71f41..ae29cc4d1d48cc0bf962facc9072cc2ceb93628b 100755 (executable)
@@ -124,15 +124,17 @@ RECODE x (LOWEST THRU 5=1)(ELSE=COPY) INTO cx5.
 RECODE x (4 THRU HIGHEST=2)(ELSE=COPY) INTO cx6.
 RECODE x (LO THRU HI=3)(ELSE=COPY) INTO cx7.
 RECODE x (SYSMIS=4)(ELSE=COPY) INTO cx8.
-LIST x cx0 TO cx8.
+RECODE x (5=COPY)(ELSE=22) INTO cx9.
+LIST x cx0 TO cx9.
 
 * String to string, with INTO, without COPY.
-STRING s0 TO s2 (A4)/t0 TO t3 (A10).
+STRING s0 TO s3 (A4)/t0 TO t3 (A10).
 RECODE s t ('a'='b')('ab'='bc') INTO s0 t0.
 RECODE s t ('abcd'='xyzw') INTO s1 t1.
 RECODE s t ('abc'='def')(ELSE='xyz') INTO s2 t2.
 RECODE t ('a'='b')('abcdefghi'='xyz')('abcdefghij'='jklmnopqr') INTO t3.
-LIST s t s0 TO s2 t0 TO t3.
+RECODE s (MISSING='gone') INTO s3.
+LIST s t s0 TO s3 t0 TO t3.
 
 * String to string, with INTO, with COPY.
 STRING cs0 TO cs2 (A4)/ct0 TO ct3 (A10).
@@ -168,97 +170,96 @@ perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -bu $TEMPDIR/pspp.list - <<EOF
 x  x0  x1  x2  x3  x4  x5  x6  x7  x8
 - --- --- --- --- --- --- --- --- ---
-0   0   0   0   0   0   1   0   3   0
-1   9   9   8  10  10   1   1   3   1
-2   2   2   9  10  10   1   2   3   2
-3   3   8   9  10  10   1   3   3   3
-4   4   4   9  10  10   1   2   3   4
-5   5   7   9  10  10   1   2   3   5
-6   6   6   9  10  10   6   2   3   6
-7   7   7   7  10  10   7   2   3   7
-8   8   8   9  10  10   8   2   3   8
-9   9   9   1  10  11   9   2   3   9
-.   .   .   .  11  11   .   .   .   4
+0   0   0   0   0   0   1   0   3   0 
+1   9   9   8  10  10   1   1   3   1 
+2   2   2   9  10  10   1   2   3   2 
+3   3   8   9  10  10   1   3   3   3 
+4   4   4   9  10  10   1   2   3   4 
+5   5   7   9  10  10   1   2   3   5 
+6   6   6   9  10  10   6   2   3   6 
+7   7   7   7  10  10   7   2   3   7 
+8   8   8   9  10  10   8   2   3   8 
+9   9   9   1  10  11   9   2   3   9 
+.   .   .   .  11  11   .   .   .   4 
 x ix0 ix1 ix2 ix3 ix4 ix5 ix6 ix7 ix8
 - --- --- --- --- --- --- --- --- ---
-0   .   .   .   .   .   1   .   3   .
-1   9   9   8  10  10   1   .   3   .
-2   .   .   9  10  10   1   .   3   .
-3   .   8   9  10  10   1   .   3   .
-4   .   .   9  10  10   1   2   3   .
-5   .   7   9  10  10   1   2   3   .
-6   .   .   9  10  10   .   2   3   .
-7   .   .   .  10  10   .   2   3   .
-8   .   .   9  10  10   .   2   3   .
-9   .   .   1  10  11   .   2   3   .
-.   .   .   .  11  11   .   .   .   4
-x cx0 cx1 cx2 cx3 cx4 cx5 cx6 cx7 cx8
-- --- --- --- --- --- --- --- --- ---
-0   0   0   0   0   0   1   0   3   0
-1   9   9   8  10  10   1   1   3   1
-2   2   2   9  10  10   1   2   3   2
-3   3   8   9  10  10   1   3   3   3
-4   4   4   9  10  10   1   2   3   4
-5   5   7   9  10  10   1   2   3   5
-6   6   6   9  10  10   6   2   3   6
-7   7   7   7  10  10   7   2   3   7
-8   8   8   9  10  10   8   2   3   8
-9   9   9   1  10  11   9   2   3   9
-.   .   .   .  11  11   .   .   .   4
-   s          t   s0   s1   s2         t0         t1         t2         t3
----- ---------- ---- ---- ---- ---------- ---------- ---------- ----------
-                          xyz                        xyz
-a    a          b         xyz  b                     xyz        b
-ab   ab         bc        xyz  bc                    xyz
-abc  abc                  def                        def
-abcd abcd            xyzw xyz             xyzw       xyz
-123  123                  xyz                        xyz
- 123  123                 xyz                        xyz
-+1   +1                   xyz                        xyz
-1x   1x                   xyz                        xyz
-abcd abcdefghi       xyzw xyz                        xyz        xyz
-xxx  abcdefghij           xyz                        xyz        jklmnopqr
+0   .   .   .   .   .   1   .   3   . 
+1   9   9   8  10  10   1   .   3   . 
+2   .   .   9  10  10   1   .   3   . 
+3   .   8   9  10  10   1   .   3   . 
+4   .   .   9  10  10   1   2   3   . 
+5   .   7   9  10  10   1   2   3   . 
+6   .   .   9  10  10   .   2   3   . 
+7   .   .   .  10  10   .   2   3   . 
+8   .   .   9  10  10   .   2   3   . 
+9   .   .   1  10  11   .   2   3   . 
+.   .   .   .  11  11   .   .   .   4 
+x cx0 cx1 cx2 cx3 cx4 cx5 cx6 cx7 cx8      cx9
+- --- --- --- --- --- --- --- --- --- --------
+0   0   0   0   0   0   1   0   3   0    22.00 
+1   9   9   8  10  10   1   1   3   1    22.00 
+2   2   2   9  10  10   1   2   3   2    22.00 
+3   3   8   9  10  10   1   3   3   3    22.00 
+4   4   4   9  10  10   1   2   3   4    22.00 
+5   5   7   9  10  10   1   2   3   5     5.00 
+6   6   6   9  10  10   6   2   3   6    22.00 
+7   7   7   7  10  10   7   2   3   7    22.00 
+8   8   8   9  10  10   8   2   3   8    22.00 
+9   9   9   1  10  11   9   2   3   9    22.00 
+.   .   .   .  11  11   .   .   .   4    22.00 
+   s          t   s0   s1   s2   s3         t0         t1         t2         t3
+---- ---------- ---- ---- ---- ---- ---------- ---------- ---------- ----------
+                          xyz                             xyz                   
+a    a          b         xyz       b                     xyz        b          
+ab   ab         bc        xyz       bc                    xyz                   
+abc  abc                  def                             def                   
+abcd abcd            xyzw xyz                  xyzw       xyz                   
+123  123                  xyz                             xyz                   
+ 123  123                 xyz                             xyz                   
++1   +1                   xyz                             xyz                   
+1x   1x                   xyz                             xyz                   
+abcd abcdefghi       xyzw xyz                             xyz        xyz        
+xxx  abcdefghij           xyz  gone                       xyz        jklmnopqr  
    s          t  cs0  cs1  cs2        ct0        ct1        ct2        ct3
 ---- ---------- ---- ---- ---- ---------- ---------- ---------- ----------
-                          xyz                        xyz
-a    a          b    a    xyz  b          a          xyz        b
-ab   ab         bc   ab   xyz  bc         ab         xyz        ab
-abc  abc        abc  abc  def  abc        abc        def        abc
-abcd abcd       abcd xyzw xyz  abcd       xyzw       xyz        abcd
-123  123        123  123  xyz  123        123        xyz        123
- 123  123        123  123 xyz   123        123       xyz         123
-+1   +1         +1   +1   xyz  +1         +1         xyz        +1
-1x   1x         1x   1x   xyz  1x         1x         xyz        1x
-abcd abcdefghi  abcd xyzw xyz  abcdefghi  abcdefghi  xyz        xyz
-xxx  abcdefghij xxx  xxx  xyz  abcdefghij abcdefghij xyz        jklmnopqr
+                          xyz                        xyz                   
+a    a          b    a    xyz  b          a          xyz        b          
+ab   ab         bc   ab   xyz  bc         ab         xyz        ab         
+abc  abc        abc  abc  def  abc        abc        def        abc        
+abcd abcd       abcd xyzw xyz  abcd       xyzw       xyz        abcd       
+123  123        123  123  xyz  123        123        xyz        123        
+ 123  123        123  123 xyz   123        123       xyz         123       
++1   +1         +1   +1   xyz  +1         +1         xyz        +1         
+1x   1x         1x   1x   xyz  1x         1x         xyz        1x         
+abcd abcdefghi  abcd xyzw xyz  abcdefghi  abcdefghi  xyz        xyz        
+xxx  abcdefghij xxx  xxx  xyz  abcdefghij abcdefghij xyz        jklmnopqr  
    s          t ns0 ns1 ns2 nt0 nt1 nt2
 ---- ---------- --- --- --- --- --- ---
-                  .   0   3   .   0   3
-a    a            .   .   3   .   .   3
-ab   ab           .   .   3   .   .   3
-abc  abc          .   .   3   .   .   3
-abcd abcd         1   1   2   1   1   2
-123  123        123 123   3 123 123   3
- 123  123       123 123   3 123 123   3
-+1   +1           1   1   3   1   1   3
-1x   1x           .   .   1   .   .   1
-abcd abcdefghi    1   1   2   .   .   3
-xxx  abcdefghij   .   .   3   .   .   3
+                  .   0   3   .   0   3 
+a    a            .   .   3   .   .   3 
+ab   ab           .   .   3   .   .   3 
+abc  abc          .   .   3   .   .   3 
+abcd abcd         1   1   2   1   1   2 
+123  123        123 123   3 123 123   3 
+ 123  123       123 123   3 123 123   3 
++1   +1           1   1   3   1   1   3 
+1x   1x           .   .   1   .   .   1 
+abcd abcdefghi    1   1   2   .   .   3 
+xxx  abcdefghij   .   .   3   .   .   3 
 x        sx0        sx1        sx2
 - ---------- ---------- ----------
-0            xxx        foobar
-1 abcdefghij xxx        foobar
-2 abcdefghij            xyz
-3 abcdefghij xxx        xyz
-4 abcdefghij            xyz
-5 abcdefghij xxx        xyz
-6 abcdefghij            xyz
-7 abcdefghij xxx        foobar
-8 abcdefghij            foobar
-9 abcdefghij xxx        foobar
-.            xxx        xyz
+0            xxx        foobar     
+1 abcdefghij xxx        foobar     
+2 abcdefghij            xyz        
+3 abcdefghij xxx        xyz        
+4 abcdefghij            xyz        
+5 abcdefghij xxx        xyz        
+6 abcdefghij            xyz        
+7 abcdefghij xxx        foobar     
+8 abcdefghij            foobar     
+9 abcdefghij xxx        foobar     
+.            xxx        xyz        
 EOF
-
 if [ $? -ne 0 ] ; then fail ; fi
 
 pass