Merge commit 'origin/stable'
authorJohn Darrington <john@darrington.wattle.id.au>
Thu, 23 Jul 2009 06:15:20 +0000 (08:15 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Thu, 23 Jul 2009 06:15:20 +0000 (08:15 +0200)
Conflicts:

src/language/stats/t-test.q

549 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/concepts.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/tutorial.texi [new file with mode: 0644]
doc/utilities.texi
doc/variables.texi
examples/automake.mk
examples/grid.sps [new file with mode: 0644]
examples/hotel.sav [new file with mode: 0644]
examples/physiology.sav [new file with mode: 0644]
examples/repairs.sav [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/LINGUAS
po/en_GB.po
po/nl.po [new file with mode: 0644]
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/caseproto.c [new file with mode: 0644]
src/data/caseproto.h [new file with mode: 0644]
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/category.h
src/data/data-in.c
src/data/data-in.h
src/data/data-out.c
src/data/data-out.h
src/data/datasheet.c
src/data/datasheet.h
src/data/dictionary.c
src/data/dictionary.h
src/data/file-handle-def.c
src/data/file-handle-def.h
src/data/file-name.c
src/data/format-guesser.h
src/data/gnumeric-reader.c
src/data/identifier.c
src/data/lazy-casereader.c
src/data/lazy-casereader.h
src/data/missing-values.c
src/data/missing-values.h
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/short-names.c
src/data/sparse-cases.c [deleted file]
src/data/sparse-cases.h [deleted file]
src/data/subcase.c [new file with mode: 0644]
src/data/subcase.h [new file with mode: 0644]
src/data/sys-file-private.c
src/data/sys-file-private.h
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-labels.c
src/data/value-labels.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.c
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/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.q
src/language/data-io/get-data.c
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/missing-values.c
src/language/dictionary/split-file.c
src/language/dictionary/sys-file-info.c
src/language/dictionary/value-labels.c
src/language/dictionary/variable-display.c
src/language/expressions/evaluate.c
src/language/expressions/helpers.c
src/language/expressions/operations.def
src/language/lexer/automake.mk
src/language/lexer/lexer.c
src/language/lexer/lexer.h
src/language/lexer/q2c.c
src/language/lexer/range-parser.c [deleted file]
src/language/lexer/range-parser.h [deleted file]
src/language/lexer/value-parser.c [new file with mode: 0644]
src/language/lexer/value-parser.h [new file with mode: 0644]
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/correlations.q [deleted file]
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/freq.h
src/language/stats/frequencies.q
src/language/stats/glm.q
src/language/stats/npar-summary.c
src/language/stats/npar-summary.h
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/roc.c [new file with mode: 0644]
src/language/stats/sign.c [new file with mode: 0644]
src/language/stats/sign.h [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/syntax-file.c
src/language/tests/.gitignore [new file with mode: 0644]
src/language/tests/automake.mk
src/language/tests/check-model.h [deleted file]
src/language/tests/check-model.q [deleted file]
src/language/tests/datasheet-test.c [deleted file]
src/language/utilities/echo.c
src/language/utilities/include.c
src/language/utilities/set.q
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/argv-parser.c [new file with mode: 0644]
src/libpspp/argv-parser.h [new file with mode: 0644]
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/legacy-encoding.c
src/libpspp/legacy-encoding.h
src/libpspp/ll.h
src/libpspp/message.c
src/libpspp/misc.h
src/libpspp/model-checker.c
src/libpspp/model-checker.h
src/libpspp/pool.c
src/libpspp/pool.h
src/libpspp/range-set.c
src/libpspp/range-set.h
src/libpspp/sparse-array.c
src/libpspp/sparse-array.h
src/libpspp/sparse-xarray.c [new file with mode: 0644]
src/libpspp/sparse-xarray.h [new file with mode: 0644]
src/libpspp/str.c
src/libpspp/str.h
src/libpspp/tmpfile.c [new file with mode: 0644]
src/libpspp/tmpfile.h [new file with mode: 0644]
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/Makefile [deleted file]
src/output/automake.mk
src/output/chart.c
src/output/chart.h
src/output/charts/automake.mk
src/output/charts/box-whisker.c
src/output/charts/box-whisker.h
src/output/charts/cartesian.c
src/output/charts/cartesian.h
src/output/charts/dummy-chart.c
src/output/charts/plot-chart.c
src/output/charts/plot-chart.h
src/output/charts/plot-hist.c
src/output/charts/plot-hist.h
src/output/dummy-chart.c
src/output/table.c
src/output/table.h
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/dialog-common.c
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/executor.c [new file with mode: 0644]
src/ui/gui/executor.h [new file with mode: 0644]
src/ui/gui/find-dialog.c
src/ui/gui/find.glade [new file with mode: 0644]
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-display.h
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/widget-io.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.c
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/crosstabs-crash.sh
tests/bugs/crosstabs-crash2.sh
tests/bugs/crosstabs2.sh [new file with mode: 0755]
tests/bugs/examine-crash.sh [new file with mode: 0755]
tests/bugs/examine-crash2.sh [new file with mode: 0755]
tests/bugs/examine-crash3.sh [new file with mode: 0755]
tests/bugs/examine-missing2.sh
tests/bugs/shbang.sh [new file with mode: 0755]
tests/bugs/t-test-alpha.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/datasheet.sh [deleted file]
tests/command/examine-extremes.sh
tests/command/examine.sh
tests/command/get-data-gnm.sh
tests/command/insert.sh
tests/command/line-ends.sh [new file with mode: 0755]
tests/command/longvars.sh
tests/command/match-files.sh
tests/command/missing-values.sh
tests/command/npar-binomial.sh
tests/command/npar-sign.sh [new file with mode: 0755]
tests/command/npar-wilcoxon.sh [new file with mode: 0755]
tests/command/reliability.sh [new file with mode: 0755]
tests/command/roc.sh [new file with mode: 0755]
tests/command/roc2.sh [new file with mode: 0755]
tests/command/sysfile-info.sh
tests/command/update.sh [new file with mode: 0755]
tests/data/datasheet-test.c [new file with mode: 0644]
tests/data/datasheet-test.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/range-set-test.c
tests/libpspp/sparse-array-test.c
tests/libpspp/sparse-xarray-test.c [new file with mode: 0644]
tests/libpspp/sparse-xarray-test.sh [new file with mode: 0755]
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..db7a59105fbe6600eed2136d196ac4e691e710ff 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,14 +6,15 @@ most of the core libraries which ensure that PSPP runs with optimal
 speed are his work.
 
 * John Darrington wrote the graphical user interface, and the T-TEST,
-ONEWAY, EXAMINE, RANK and  NPAR TESTS commands, implemented support
-for long variable names, psql and gnumeric and made numerous revisions
-to other modules.   
+ONEWAY, EXAMINE, RANK and NPAR TESTS commands, implemented support for
+long variable names, PostgreSQL and Gnumeric and made numerous
+revisions to other modules.
 
 * Jason Stover contributed statistical and numerical functionality,
 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..df10031e1d6221403902643e9ffa63cd2a535673 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,31 @@
 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-05-24 22:25:04 blp>
+Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
 See the end for copying conditions.
 
 Please send PSPP bug reports to bug-gnu-pspp@gnu.org.
 
+Changes from 0.7.1 to 0.7.2:
+
+ * Updated Perl module interface.
+
+ * Value labels for long string variables are now supported.
+
+ * Missing values for long string variables are now supported.
+
+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 29f93365d37297ec988fd89d9fa1a2d32b793667..2697064c98a3eacdc8ec4cfe1ef35d99111fb4ee 100644 (file)
--- a/Smake
+++ b/Smake
@@ -5,11 +5,13 @@ GNULIB = ../gnulib
 GNULIB_TOOL = $(GNULIB)/gnulib-tool
 
 GNULIB_MODULES = \
+       argp \
        assert \
        byteswap \
        c-ctype \
        c-strtod \
        close \
+       count-one-bits \
        crypto/md4 \
        dirname \
        environ \
@@ -74,6 +76,8 @@ GNULIB_MODULES = \
        trunc \
        unilbrk/ulc-width-linebreaks \
        unistd \
+       unistr/u8-strlen \
+       unistr/u8-strncat \
        unlocked-io \
        vasprintf-posix \
        vfprintf-posix \
@@ -84,6 +88,7 @@ GNULIB_MODULES = \
        xalloc \
        xalloc-die \
        xmalloca \
+       xmemdup0 \
        xsize \
        xstrndup \
        xvasprintf
@@ -113,6 +118,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..c44fdd565187e1b9f800e51b0adb609f0b99bf90 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")
 
@@ -110,7 +106,7 @@ if test x"$with_libpq" != x"no" ; then
 fi
 AM_CONDITIONAL(PSQL_SUPPORT, test -n "$PG_CONFIG")
 
-dnl Checks needed for gnumeric reader
+dnl Checks needed for Gnumeric reader
 gnm_support=yes;
 PKG_CHECK_MODULES(LIBXML2, libxml-2.0,,
                           [PSPP_OPTIONAL_PREREQ([libxml2]); gnm_support=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..9402d7af0aea98ab731007f7b905f251973c13f3 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 \
@@ -22,6 +23,8 @@ doc_pspp_TEXINFOS = doc/version.texi \
        doc/not-implemented.texi \
        doc/statistics.texi \
        doc/transformation.texi \
+       doc/tutorial.texi \
+       doc/tut.texi \
        doc/regression.texi \
        doc/utilities.texi \
        doc/variables.texi \
@@ -32,6 +35,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 +48,19 @@ 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/tut.texi:
+       @$(MKDIR_P) doc
+       echo "@set example-dir $(examplesdir)" > $@
+
+
+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..1bad334e95b60a1dc0c7872ada8f2bff4b28b5a8 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
@@ -131,7 +178,7 @@ situations.
 @display
 DATA LIST [FIXED]
         @{TABLE,NOTABLE@}
-        [FILE='file-name']
+        [FILE='file-name' [ENCODING='encoding']]
         [RECORDS=record_count]
         [END=end_var]
         [SKIP=record_count]
@@ -151,6 +198,8 @@ external file.  It may be used to specify a file name as a string or a
 file handle (@pxref{File Handles}).  If the FILE subcommand is not used,
 then input is assumed to be specified within the command file using
 @cmd{BEGIN DATA}@dots{}@cmd{END DATA} (@pxref{BEGIN DATA}).
+The ENCODING subcommand may only be used if the FILE subcommand is also used.
+It specifies the character encoding of the file.
 
 The optional RECORDS subcommand, which takes a single integer as an
 argument, is used to specify the number of lines per record.  If RECORDS
@@ -269,7 +318,7 @@ Defines the following variables:
 
 @itemize @bullet
 @item
-@code{NAME}, a 10-character-wide long string variable, in columns 1
+@code{NAME}, a 10-character-wide string variable, in columns 1
 through 10.
 
 @item
@@ -310,15 +359,15 @@ Defines the following variables:
 @code{ID}, a numeric variable, in columns 1-5 of the first record.
 
 @item
-@code{NAME}, a 30-character long string variable, in columns 7-36 of the
+@code{NAME}, a 30-character string variable, in columns 7-36 of the
 first record.
 
 @item
-@code{SURNAME}, a 30-character long string variable, in columns 38-67 of
+@code{SURNAME}, a 30-character string variable, in columns 38-67 of
 the first record.
 
 @item
-@code{MINITIAL}, a 1-character short string variable, in column 69 of
+@code{MINITIAL}, a 1-character string variable, in column 69 of
 the first record.
 
 @item
@@ -344,7 +393,7 @@ This example shows keywords abbreviated to their first 3 letters.
 DATA LIST FREE
         [(@{TAB,'c'@}, @dots{})]
         [@{NOTABLE,TABLE@}]
-        [FILE='file-name']
+        [FILE='file-name' [ENCODING='encoding']]
         [SKIP=record_cnt]
         /var_spec@dots{}
 
@@ -396,7 +445,7 @@ on field width apply, but they are honored on output.
 DATA LIST LIST
         [(@{TAB,'c'@}, @dots{})]
         [@{NOTABLE,TABLE@}]
-        [FILE='file-name']
+        [FILE='file-name' [ENCODING='encoding']]
         [SKIP=record_count]
         /var_spec@dots{}
 
@@ -1095,4 +1144,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
index 5876ce36ba5bd942b6e58fb0c5c839ca57bc4369..06652d62653b1ec21ccdbc387f8993ae8f762b1f 100644 (file)
@@ -117,76 +117,88 @@ case when it processes it later.
 @subsection Runtime Typed Values
 
 When a value's type is only known at runtime, it is often represented
-as a @union{value}, defined in @file{data/value.h}.  @union{value} has
-two members: a @code{double} named @samp{f} to store a numeric value
-and an array of @code{char} named @samp{s} to a store a string value.
-A @union{value} does not identify the type or width of the data it
-contains.  Code that works with @union{values}s must therefore have
-external knowledge of its content, often through the type and width of
-a @struct{variable} (@pxref{Variables}).
-
-@cindex MAX_SHORT_STRING
-@cindex short string
-@cindex long string
-@cindex string value
-The array of @code{char} in @union{value} has only a small, fixed
-capacity of @code{MAX_SHORT_STRING} bytes.  A value that
-fits within this capacity is called a @dfn{short string}.  Any wider
-string value, which must be represented by more than one
-@union{value}, is called a @dfn{long string}.
-
-@deftypefn Macro int MAX_SHORT_STRING
-Maximum width of a short string value, never less than 8 bytes.  It is
-wider than 8 bytes on systems where @code{double} is either larger
-than 8 bytes or has stricter alignment than 8 bytes.
-@end deftypefn
+as a @union{value}, defined in @file{data/value.h}.  A @union{value}
+does not identify the type or width of the data it contains.  Code
+that works with @union{values}s must therefore have external knowledge
+of its content, often through the type and width of a
+@struct{variable} (@pxref{Variables}).
+
+@union{value} has one member that clients are permitted to access
+directly, a @code{double} named @samp{f} that stores the content of a
+numeric @union{value}.  It has other members that store the content of
+string @union{value}, but client code should use accessor functions
+instead of referring to these directly.
+
+PSPP provides some functions for working with @union{value}s.  The
+most useful are described below.  To use these functions, recall that
+a numeric value has a width of 0.
 
-@deftypefn Macro int MIN_LONG_STRING
-Minimum width of a long string value, that is, @code{MAX_SHORT_STRING
-+ 1}.
-@end deftypefn
+@deftypefun void value_init (union value *@var{value}, int @var{width})
+Initializes @var{value} as a value of the given @var{width}.  After
+initialization, the data in @var{value} are indeterminate; the caller
+is responsible for storing initial data in it.
+@end deftypefun
 
-Long string variables are slightly harder to work with than short
-string values, because they cannot be conveniently and efficiently
-allocated as block scope variables or structure members.  The PSPP
-language exposes this inconvenience to the user: there are many
-circumstances in PSPP syntax where short strings are allowed but not
-long strings.  Short string variables, for example, may have
-user-missing values, but long string variables may not (@pxref{Missing
-Observations,,,pspp, PSPP Users Guide}).
+@deftypefun void value_destroy (union value *@var{value}, int @var{width})
+Frees auxiliary storage associated with @var{value}, which must have
+the given @var{width}.
+@end deftypefun
 
-PSPP provides a few functions for working with @union{value}s.  The
-most useful are described below.  To use these functions, recall that
-a numeric value has a width of 0.
+@deftypefun bool value_needs_init (int @var{width})
+For some widths, @func{value_init} and @func{value_destroy} do not
+actually do anything, because no additional storage is needed beyond
+the size of @union{value}.  This function returns true if @var{width}
+is such a width, which case there is no actual need to call those
+functions.  This can be a useful optimization if a large number of
+@union{value}s of such a width are to be initialized or destroyed.
+
+This function returns false if @func{value_init} and
+@func{value_destroy} are actually required for the given @var{width}.
+@end deftypefun
+
+@deftypefun double value_num (const union value *@var{value})
+Returns the numeric value in @var{value}, which must have been
+initialized as a numeric value.  Equivalent to @code{@var{value}->f}.
+@end deftypefun
+
+@deftypefun {const char *} value_str (const union value *@var{value}, int @var{width})
+@deftypefunx {char *} value_str_rw (union value *@var{value}, int @var{width})
+Returns the string value in @var{value}, which must have been
+initialized with positive width @var{width}.  The string returned is
+not null-terminated.  Only @var{width} bytes of returned data may be
+accessed.
+
+The two different functions exist only for @code{const}-correctness.
+Otherwise they are identical.
 
-@deftypefun size_t value_cnt_from_width (int @var{width})
-Returns the number of consecutive @union{value}s that must be
-allocated to store a value of the given @var{width}.  For a numeric or
-short string value, the return value is 1; for long string
-variables, it is greater than 1.
+It is important that @var{width} be the correct value that was passed
+to @func{value_init}.  Passing a smaller or larger value (e.g.@:
+because that number of bytes will be accessed) will not always work
+and should be avoided.
 @end deftypefun
 
 @deftypefun void value_copy (union value *@var{dst}, @
                              const union value *@var{src}, @
                              int @var{width})
-Copies a value of the given @var{width} from the @union{value} array
-starting at @var{src} to the one starting at @var{dst}.  The two
-arrays must not overlap.
+Copies the contents of @union{value} @var{src} to @var{dst}.  Both
+@var{dst} and @var{src} must have been initialized with the specified
+@var{width}.
 @end deftypefun
 
 @deftypefun void value_set_missing (union value *@var{value}, int @var{width})
 Sets @var{value} to @code{SYSMIS} if it is numeric or to all spaces if
-it is alphanumeric, according to @var{width}.  @var{value} must point
-to the start of a @union{value} array of the given @var{width}.
+it is alphanumeric, according to @var{width}.  @var{value} must have
+been initialized with the specified @var{width}.
 @end deftypefun
 
 @anchor{value_is_resizable}
 @deftypefun bool value_is_resizable (const union value *@var{value}, int @var{old_width}, int @var{new_width})
-Determines whether @var{value} may be resized from @var{old_width} to
-@var{new_width}.  Resizing is possible if the following criteria are
-met.  First, @var{old_width} and @var{new_width} must be both numeric
-or both string widths.  Second, if @var{new_width} is a short string
-width and less than @var{old_width}, resizing is allowed only if bytes
+Determines whether @var{value}, which must have been initialized with
+the specified @var{old_width}, may be resized to @var{new_width}.
+Resizing is possible if the following criteria are met.  First,
+@var{old_width} and @var{new_width} must be both numeric or both
+string widths.  Second, if @var{new_width} is a short string width and
+less than @var{old_width}, resizing is allowed only if bytes
 @var{new_width} through @var{old_width} in @var{value} contain only
 spaces.
 
@@ -196,9 +208,36 @@ These rules are part of those used by @func{mv_is_resizable} and
 
 @deftypefun void value_resize (union value *@var{value}, int @var{old_width}, int @var{new_width})
 Resizes @var{value} from @var{old_width} to @var{new_width}, which
-must be allowed by the rules stated above.  This has an effect only if
-@var{new_width} is greater than @var{old_width}, in which case the
-bytes newly added to @var{value} are cleared to spaces.
+must be allowed by the rules stated above.  @var{value} must have been
+initialized with the specified @var{old_width} before calling this
+function.  After resizing, @var{value} has width @var{new_width}.
+
+If @var{new_width} is greater than @var{old_width}, @var{value} will
+be padded on the right with spaces to the new width.  If
+@var{new_width} is less than @var{old_width}, the rightmost bytes of
+@var{value} are truncated.
+@end deftypefun
+
+@deftypefun bool value_equal (const union value *@var{a}, const union value *@var{b}, int @var{width})
+Compares of @var{a} and @var{b}, which must both have width
+@var{width}.  Returns true if their contents are the same, false if
+they differ.
+@end deftypefun
+
+@deftypefun int value_compare_3way (const union value *@var{a}, const union value *@var{b}, int @var{width})
+Compares of @var{a} and @var{b}, which must both have width
+@var{width}.  Returns -1 if @var{a} is less than @var{b}, 0 if they
+are equal, or 1 if @var{a} is greater than @var{b}.
+
+Numeric values are compared numerically, with @code{SYSMIS} comparing
+less than any real number.  String values are compared
+lexicographically byte-by-byte.
+@end deftypefun
+
+@deftypefun size_t value_hash (const union value *@var{value}, int @var{width}, unsigned int @var{basis})
+Computes and returns a hash of @var{value}, which must have the
+specified @var{width}.  The value in @var{basis} is folded into the
+hash.
 @end deftypefun
 
 @node Input and Output Formats
@@ -615,18 +654,17 @@ Returns the name of the given format @var{type}.
 These functions provide the ability to convert data fields into
 @union{value}s and vice versa.
 
-@deftypefun bool data_in (struct substring @var{input}, enum legacy_encoding @var{legacy_encoding}, enum fmt_type @var{type}, int @var{implied_decimals}, int @var{first_column}, union value *@var{output}, int @var{width})
+@deftypefun bool data_in (struct substring @var{input}, const char *@var{encoding}, enum fmt_type @var{type}, int @var{implied_decimals}, int @var{first_column}, const struct dictionary *@var{dict}, union value *@var{output}, int @var{width})
 Parses @var{input} as a field containing data in the given format
-@var{type}.  The resulting value is stored in @var{output}, which has
-the given @var{width}.  For consistency, @var{width} must be 0 if
+@var{type}.  The resulting value is stored in @var{output}, which the
+caller must have initialized with the given @var{width}.  For
+consistency, @var{width} must be 0 if
 @var{type} is a numeric format type and greater than 0 if @var{type}
 is a string format type.
-
-Ordinarily @var{legacy_encoding} should be @code{LEGACY_NATIVE},
-indicating that @var{input} is encoded in the character set
-conventionally used on the host machine.  It may be set to
-@code{LEGACY_EBCDIC} to cause @var{input} to be re-encoded from EBCDIC
-during data parsing.
+@var{encoding} should be set to indicate the character
+encoding of @var{input}.
+@var{dict} must be a pointer to the dictionary with which @var{output}
+is associated.
 
 If @var{input} is the empty string (with length 0), @var{output} is
 set to the value set on SET BLANKS (@pxref{SET BLANKS,,,pspp, PSPP
@@ -661,21 +699,15 @@ not propagated to the caller as errors.
 This function is declared in @file{data/data-in.h}.
 @end deftypefun
 
-@deftypefun void data_out (const union value *@var{input}, const struct fmt_spec *@var{format}, char *@var{output})
-@deftypefunx void data_out_legacy (const union value *@var{input}, enum legacy_encoding @var{legacy_encoding}, const struct fmt_spec *@var{format}, char *@var{output})
-Converts the data pointed to by @var{input} into a data field in
-@var{output} according to output format specifier @var{format}, which
-must be a valid output format.  Exactly @code{@var{format}->w} bytes
-are written to @var{output}.  The width of @var{input} is also
+@deftypefun char * data_out (const union value *@var{input}, const struct fmt_spec *@var{format})
+@deftypefunx char * data_out_legacy (const union value *@var{input}, const char *@var{encoding}, const struct fmt_spec *@var{format})
+Converts the data pointed to by @var{input} into a string value, which
+will be encoded in UTF-8,  according to output format specifier @var{format}.
+Format 
+must be a valid output format.   The width of @var{input} is
 inferred from @var{format} using an algorithm equivalent to
 @func{fmt_var_width}.
 
-If @func{data_out} is called, or @func{data_out_legacy} is called with
-@var{legacy_encoding} set to @code{LEGACY_NATIVE}, @var{output} will
-be encoded in the character set conventionally used on the host
-machine.  If @var{legacy_encoding} is set to @code{LEGACY_EBCDIC},
-@var{output} will be re-encoded from EBCDIC during data output.
-
 When @var{input} contains data that cannot be represented in the given
 @var{format}, @func{data_out} may output a message using @func{msg},
 @c (@pxref{msg}),
@@ -703,28 +735,7 @@ variable, is most conveniently executed through functions on
 A @struct{missing_values} is essentially a set of @union{value}s that
 have a common value width (@pxref{Values}).  For a set of
 missing values associated with a variable (the common case), the set's
-width is the same as the variable's width.  The contents of a set of
-missing values is subject to some restrictions.  Regardless of width,
-a set of missing values is allowed to be empty.  Otherwise, its
-possible contents depend on its width:
-
-@table @asis
-@item 0 (numeric values)
-Up to three discrete numeric values, or a range of numeric values
-(which includes both ends of the range), or a range plus one discrete
-numeric value.
-
-@item 1@dots{}@t{MAX_SHORT_STRING} - 1 (short string values)
-Up to three discrete string values (with the same width as the set).
-
-@item @t{MAX_SHORT_STRING}@dots{}@t{MAX_STRING} (long string values)
-Always empty.
-@end table
-
-These somewhat arbitrary restrictions are the same as those imposed by
-SPSS.  In PSPP we could easily eliminate these restrictions, but doing
-so would also require us to extend the system file format in an
-incompatible way, which we consider a bad tradeoff.
+width is the same as the variable's width.
 
 Function prototypes and other declarations related to missing values
 are declared in @file{data/missing-values.h}.
@@ -733,18 +744,37 @@ are declared in @file{data/missing-values.h}.
 Opaque type that represents a set of missing values.
 @end deftp
 
+The contents of a set of missing values is subject to some
+restrictions.  Regardless of width, a set of missing values is allowed
+to be empty.  A set of numeric missing values may contain up to three
+discrete numeric values, or a range of numeric values (which includes
+both ends of the range), or a range plus one discrete numeric value.
+A set of string missing values may contain up to three discrete string
+values (with the same width as the set), but ranges are not supported.
+
+In addition, values in string missing values wider than
+@code{MV_MAX_STRING} bytes may contain non-space characters only in
+their first @code{MV_MAX_STRING} bytes; all the bytes after the first
+@code{MV_MAX_STRING} must be spaces.  @xref{mv_is_acceptable}, for a
+function that tests a value against these constraints.
+
+@deftypefn Macro int MV_MAX_STRING
+Number of bytes in a string missing value that are not required to be
+spaces.  The current value is 8, a value which is fixed by the system
+file format.  In PSPP we could easily eliminate this restriction, but
+doing so would also require us to extend the system file format in an
+incompatible way, which we consider a bad tradeoff.
+@end deftypefn
+
 The most often useful functions for missing values are those for
 testing whether a given value is missing, described in the following
 section.  Several other functions for creating, inspecting, and
 modifying @struct{missing_values} objects are described afterward, but
-these functions are much more rarely useful.  No function for
-destroying a @struct{missing_values} is provided, because
-@struct{missing_values} does not contain any pointers or other
-references to resources that need deallocation.
+these functions are much more rarely useful.
 
 @menu
 * Testing for Missing Values::
-* Initializing User-Missing Value Sets::
+* Creating and Destroying User-Missing Values::
 * Changing User-Missing Value Set Width::
 * Inspecting User-Missing Value Sets::
 * Modifying User-Missing Value Sets::
@@ -796,8 +826,10 @@ missing.
 @end deftp
 @end deftypefun
 
-@node Initializing User-Missing Value Sets
-@subsection Initializing User-Missing Value Sets
+@node Creating and Destroying User-Missing Values
+@subsection Creation and Destruction
+
+These functions create and destroy @struct{missing_values} objects.
 
 @deftypefun void mv_init (struct missing_values *@var{mv}, int @var{width})
 Initializes @var{mv} as a set of user-missing values.  The set is
@@ -805,6 +837,10 @@ initially empty.  Any values added to it must have the specified
 @var{width}.
 @end deftypefun
 
+@deftypefun void mv_destroy (struct missing_values *@var{mv})
+Destroys @var{mv}, which must not be referred to again.
+@end deftypefun
+
 @deftypefun void mv_copy (struct missing_values *@var{mv}, const struct missing_values *@var{old})
 Initializes @var{mv} as a copy of the existing set of user-missing
 values @var{old}.
@@ -834,11 +870,9 @@ the required width, may be used instead.
 Tests whether @var{mv}'s width may be changed to @var{new_width} using
 @func{mv_resize}.  Returns true if it is allowed, false otherwise.
 
-If @var{new_width} is a long string width, @var{mv} may be resized
-only if it is empty.  Otherwise, if @var{mv} contains any missing
-values, then it may be resized only if each missing value may be
-resized, as determined by @func{value_is_resizable}
-(@pxref{value_is_resizable}).
+If @var{mv} contains any missing values, then it may be resized only
+if each missing value may be resized, as determined by
+@func{value_is_resizable} (@pxref{value_is_resizable}).
 @end deftypefun
 
 @anchor{mv_resize}
@@ -857,8 +891,8 @@ width.
 These functions inspect the properties and contents of
 @struct{missing_values} objects.
 
-The first set of functions inspects the discrete values that numeric
-and short string sets of user-missing values may contain:
+The first set of functions inspects the discrete values that sets of
+user-missing values may contain:
 
 @deftypefun bool mv_is_empty (const struct missing_values *@var{mv})
 Returns true if @var{mv} contains no user-missing values, false if it
@@ -883,11 +917,12 @@ values, that is, if @func{mv_n_values} would return nonzero for
 @var{mv}.
 @end deftypefun
 
-@deftypefun void mv_get_value (const struct missing_values *@var{mv}, union value *@var{value}, int @var{index})
-Copies the discrete user-missing value in @var{mv} with the given
-@var{index} into @var{value}.  The index must be less than the number
-of discrete user-missing values in @var{mv}, as reported by
-@func{mv_n_values}.
+@deftypefun {const union value *} mv_get_value (const struct missing_values *@var{mv}, int @var{index})
+Returns the discrete user-missing value in @var{mv} with the given
+@var{index}.  The caller must not modify or free the returned value or
+refer to it after modifying or freeing @var{mv}.  The index must be
+less than the number of discrete user-missing values in @var{mv}, as
+reported by @func{mv_n_values}.
 @end deftypefun
 
 The second set of functions inspects the single range of values that
@@ -909,7 +944,7 @@ include a range.
 These functions modify the contents of @struct{missing_values}
 objects.
 
-The first set of functions applies to all sets of user-missing values:
+The next set of functions applies to all sets of user-missing values:
 
 @deftypefun bool mv_add_value (struct missing_values *@var{mv}, const union value *@var{value})
 @deftypefunx bool mv_add_str (struct missing_values *@var{mv}, const char @var{value}[])
@@ -917,8 +952,8 @@ The first set of functions applies to all sets of user-missing values:
 Attempts to add the given discrete @var{value} to set of user-missing
 values @var{mv}.  @var{value} must have the same width as @var{mv}.
 Returns true if @var{value} was successfully added, false if the set
-could not accept any more discrete values.  (Always returns false if
-@var{mv} is a set of long string user-missing values.)
+could not accept any more discrete values or if @var{value} is not an
+acceptable user-missing value (see @func{mv_is_acceptable} below).
 
 These functions are equivalent, except for the form in which
 @var{value} is provided, so you may use whichever function is most
@@ -930,10 +965,22 @@ Removes a discrete value from @var{mv} (which must contain at least
 one discrete value) and stores it in @var{value}.
 @end deftypefun
 
-@deftypefun void mv_replace_value (struct missing_values *@var{mv}, const union value *@var{value}, int @var{index})
-Replaces the discrete value with the given @var{index} in @var{mv}
-(which must contain at least @var{index} + 1 discrete values) with
-@var{value}.
+@deftypefun bool mv_replace_value (struct missing_values *@var{mv}, const union value *@var{value}, int @var{index})
+Attempts to replace the discrete value with the given @var{index} in
+@var{mv} (which must contain at least @var{index} + 1 discrete values)
+by @var{value}.  Returns true if successful, false if @var{value} is
+not an acceptable user-missing value (see @func{mv_is_acceptable}
+below).
+@end deftypefun
+
+@deftypefun bool mv_is_acceptable (const union value *@var{value}, int @var{width})
+@anchor{mv_is_acceptable}
+Returns true if @var{value}, which must have the specified
+@var{width}, may be added to a missing value set of the same
+@var{width}, false if it cannot.  As described above, all numeric
+values and string values of width @code{MV_MAX_STRING} or less may be
+added, but string value of greater width may be added only if bytes
+beyond the first @code{MV_MAX_STRING} are all spaces.
 @end deftypefun
 
 The second set of functions applies only to numeric sets of
@@ -965,12 +1012,7 @@ All of the values in a set of value labels have the same width, which
 for a set of value labels owned by a variable (the common case) is the
 same as its variable.
 
-Numeric and short string sets of value labels may contain any number
-of entries.  Long string sets of value labels may not contain any
-value labels at all, due to a corresponding restriction in SPSS.  In
-PSPP we could easily eliminate this restriction, but doing so would
-also require us to extend the system file format in an incompatible
-way, which we consider a bad tradeoff.
+Sets of value labels may contain any number of entries.
 
 It is rarely necessary to interact directly with a @struct{val_labs}
 object.  Instead, the most common operation, looking up the label for
@@ -1051,31 +1093,24 @@ value in it may be resized to that width, as determined by
 Changes the width of @var{val_labs}'s values to @var{new_width}, which
 must be a valid new width as determined by
 @func{val_labs_can_set_width}.
-
-If @var{new_width} is a long string width, this function deletes all
-value labels from @var{val_labs}.
 @end deftypefun
 
 @node Value Labels Adding and Removing Labels
 @subsection Adding and Removing Labels
 
 These functions add and remove value labels from a @struct{val_labs}
-object.  These functions apply only to numeric and short string sets
-of value labels.  They have no effect on long string sets of value
-labels, since these sets are always empty.
+object.
 
 @deftypefun bool val_labs_add (struct val_labs *@var{val_labs}, union value @var{value}, const char *@var{label})
 Adds @var{label} to in @var{var_labs} as a label for @var{value},
 which must have the same width as the set of value labels.  Returns
-true if successful, false if @var{value} already has a label or if
-@var{val_labs} has long string width.
+true if successful, false if @var{value} already has a label.
 @end deftypefun
 
 @deftypefun void val_labs_replace (struct val_labs *@var{val_labs}, union value @var{value}, const char *@var{label})
 Adds @var{label} to in @var{var_labs} as a label for @var{value},
 which must have the same width as the set of value labels.  If
 @var{value} already has a label in @var{var_labs}, it is replaced.
-Has no effect if @var{var_labs} has long string width.
 @end deftypefun
 
 @deftypefun bool val_labs_remove (struct val_labs *@var{val_labs}, union value @var{value})
@@ -1088,75 +1123,65 @@ was removed, false otherwise.
 @subsection Iterating through Value Labels
 
 These functions allow iteration through the set of value labels
-represented by a @struct{val_labs} object.  They are usually used in
-the context of a @code{for} loop:
+represented by a @struct{val_labs} object.  They may be used in the
+context of a @code{for} loop:
 
 @example
 struct val_labs val_labs;
-struct val_labs_iterator *i;
-struct val_lab *vl;
+const struct val_lab *vl;
 
 @dots{}
 
-for (vl = val_labs_first (val_labs, &i); vl != NULL;
-     vl = val_labs_next (val_labs, &i))
+for (vl = val_labs_first (val_labs); vl != NULL;
+     vl = val_labs_next (val_labs, vl))
   @{
     @dots{}@r{do something with @code{vl}}@dots{}
   @}
 @end example
 
-The value labels in a @struct{val_labs} must not be modified as it is
-undergoing iteration.
-
-@deftp {Structure} {struct val_lab}
-Represents a value label for iteration purposes, with two
-client-visible members:
-
-@table @code
-@item union value value
-Value being labeled, of the same width as the @struct{val_labs} being
-iterated.
-
-@item const char *label
-The label, as a null-terminated string.
-@end table
-@end deftp
-
-@deftp {Structure} {struct val_labs_iterator}
-Opaque object that represents the current state of iteration through a
-set of value value labels.  Automatically destroyed by successful
-completion of iteration.  Must be destroyed manually in other
-circumstances, by calling @func{val_labs_done}.
-@end deftp
+Value labels should not be added or deleted from a @struct{val_labs}
+as it is undergoing iteration.
 
-@deftypefun {struct val_lab *} val_labs_first (const struct val_labs *@var{val_labs}, struct val_labs_iterator **@var{iterator})
-If @var{val_labs} contains at least one value label, starts an
-iteration through @var{val_labs}, initializes @code{*@var{iterator}}
-to point to a newly allocated iterator, and returns the first value
-label in @var{val_labs}.  If @var{val_labs} is empty, sets
-@code{*@var{iterator}} to null and returns a null pointer.
+@deftypefun {const struct val_lab *} val_labs_first (const struct val_labs *@var{val_labs})
+Returns the first value label in @var{var_labs}, if it contains at
+least one value label, or a null pointer if it does not contain any
+value labels.
+@end deftypefun
 
-This function creates iterators that traverse sets of value labels in
-no particular order.
+@deftypefun {const struct val_lab *} val_labs_next (const struct val_labs *@var{val_labs}, const struct val_labs_iterator **@var{vl})
+Returns the value label in @var{var_labs} following @var{vl}, if
+@var{vl} is not the last value label in @var{val_labs}, or a null
+pointer if there are no value labels following @var{vl}.
 @end deftypefun
 
-@deftypefun {struct val_lab *} val_labs_first_sorted (const struct val_labs *@var{val_labs}, struct val_labs_iterator **@var{iterator})
-Same as @func{val_labs_first}, except that the created iterator
-traverses the set of value labels in ascending order of value.
+@deftypefun {const struct val_lab **} val_labs_sorted (const struct val_labs *@var{val_labs})
+Allocates and returns an array of pointers to value labels, which are
+sorted in increasing order by value.  The array has
+@code{val_labs_count (@var{val_labs})} elements.  The caller is
+responsible for freeing the array with @func{free} (but must not free
+any of the @struct{val_lab} elements that the array points to).
 @end deftypefun
 
-@deftypefun {struct val_lab *} val_labs_next (const struct val_labs *@var{val_labs}, struct val_labs_iterator **@var{iterator})
-Advances an iterator created with @func{val_labs_first} or
-@func{val_labs_first_sorted} to the next value label, which is
-returned.  If the set of value labels is exhausted, returns a null
-pointer after freeing @code{*@var{iterator}} and setting it to a null
-pointer.
+The iteration functions above work with pointers to @struct{val_lab}
+which is an opaque data structure that users of @struct{val_labs} must
+not modify or free directly.  The following functions work with
+objects of this type:
+
+@deftypefun {const union value *} val_lab_get_value (const struct val_lab *@var{vl})
+Returns the value of value label @var{vl}.  The caller must not modify
+or free the returned value.  (To achieve a similar result, remove the
+value label with @func{val_labs_remove}, then add the new value with
+@func{val_labs_add}.)
+
+The width of the returned value cannot be determined directly from
+@var{vl}.  It may be obtained by calling @func{val_labs_get_width} on
+the @struct{val_labs} that @var{vl} is in.
 @end deftypefun
 
-@deftypefun void val_labs_done (struct val_labs_iterator **@var{iterator})
-Frees @code{*@var{iterator}} and sets it to a null pointer.  Does
-not need to be called explicitly if @func{val_labs_next} returns a
-null pointer, indicating that all value labels have been visited.
+@deftypefun {const char *} val_lab_get_label (const struct val_lab *@var{vl})
+Returns the label in @var{vl} as a null-terminated string.  The caller
+must not modify or free the returned string.  (Use
+@func{val_labs_replace} to change a value label.)
 @end deftypefun
 
 @node Variables
@@ -1280,22 +1305,6 @@ Returns true if @var{var} is an alphanumeric (string) variable, false
 otherwise.
 @end deftypefun
 
-@deftypefun bool var_is_short_string (const struct variable *@var{var})
-Returns true if @var{var} is a string variable of width
-@code{MAX_SHORT_STRING} or less, false otherwise.
-@end deftypefun
-
-@deftypefun bool var_is_long_string (const struct variable *@var{var})
-Returns true if @var{var} is a string variable of width greater than
-@code{MAX_SHORT_STRING}, false otherwise.
-@end deftypefun
-
-@deftypefun size_t var_get_value_cnt (const struct variable *@var{var})
-Returns the number of @union{value}s needed to hold an instance of
-variable @var{var}.  @code{var_get_value_cnt (var)} is equivalent to
-@code{value_cnt_from_width (var_get_width (var))}.
-@end deftypefun
-
 @node Variable Missing Values
 @subsection Variable Missing Values
 
@@ -1313,8 +1322,8 @@ Tests whether @var{value} is a missing value of the given @var{class}
 for variable @var{var} and returns true if so, false otherwise.
 @func{var_is_num_missing} may only be applied to numeric variables;
 @func{var_is_str_missing} may only be applied to string variables.
-For string variables, @var{value} must contain exactly as many
-characters as @var{var}'s width.
+@var{value} must have been initialized with the same width as
+@var{var}.
 
 @code{var_is_@var{type}_missing (@var{var}, @var{value}, @var{class})}
 is equivalent to @code{mv_is_@var{type}_missing
@@ -1339,7 +1348,7 @@ resizable to @var{var}'s width (@pxref{mv_resize}).  The caller
 retains ownership of @var{miss}.
 @end deftypefun
 
-b@deftypefun void var_clear_missing_values (struct variable *@var{var})
+@deftypefun void var_clear_missing_values (struct variable *@var{var})
 Clears @var{var}'s missing values.  Equivalent to
 @code{var_set_missing_values (@var{var}, NULL)}.
 @end deftypefun
@@ -1360,11 +1369,13 @@ value:
 
 @deftypefun {const char *} var_lookup_value_label (const struct variable *@var{var}, const union value *@var{value})
 Looks for a label for @var{value} in @var{var}'s set of value labels.
-Returns the label if one exists, otherwise a null pointer.
+@var{value} must have the same width as @var{var}.  Returns the label
+if one exists, otherwise a null pointer.
 @end deftypefun
 
 @deftypefun void var_append_value_name (const struct variable *@var{var}, const union value *@var{value}, struct string *@var{str})
 Looks for a label for @var{value} in @var{var}'s set of value labels.
+@var{value} must have the same width as @var{var}.
 If a label exists, it will be appended to the string pointed to by @var{str}.
 Otherwise, it formats @var{value}
 using @var{var}'s print format (@pxref{Input and Output Formats}) 
@@ -1406,20 +1417,19 @@ the variable (making a second copy):
 
 @deftypefun bool var_add_value_label (struct variable *@var{var}, const union value *@var{value}, const char *@var{label})
 Attempts to add a copy of @var{label} as a label for @var{value} for
-the given @var{var}.  If @var{value} already has a label, then the old
-label is retained.  Returns true if a label is added, false if there
-was an existing label for @var{value} or if @var{var} is a long string
-variable.  Either way, the caller retains ownership of @var{value} and
-@var{label}.
+the given @var{var}.  @var{value} must have the same width as
+@var{var}.  If @var{value} already has a label, then the old label is
+retained.  Returns true if a label is added, false if there was an
+existing label for @var{value}.  Either way, the caller retains
+ownership of @var{value} and @var{label}.
 @end deftypefun
 
 @deftypefun void var_replace_value_label (struct variable *@var{var}, const union value *@var{value}, const char *@var{label})
 Attempts to add a copy of @var{label} as a label for @var{value} for
-the given @var{var}.  If @var{value} already has a label, then
+the given @var{var}.  @var{value} must have the same width as
+@var{var}.  If @var{value} already has a label, then
 @var{label} replaces the old label.  Either way, the caller retains
 ownership of @var{value} and @var{label}.
-
-If @var{var} is a long string variable, this function has no effect.
 @end deftypefun
 
 @node Variable Print and Write Formats
diff --git a/doc/dev/i18n.texi b/doc/dev/i18n.texi
new file mode 100644 (file)
index 0000000..3ab86c3
--- /dev/null
@@ -0,0 +1,138 @@
+@node Internationalisation
+@chapter Internationalisation
+
+Internationalisation in pspp is complicated.
+The most annoying aspect is that of character-encoding.
+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 locale of the user interface.
+@item The locale of the output.
+@item The locale of the data. Only the character encoding is relevant.
+@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.}
+The dictionary pertaining to the data contains a field denoting the encoding.
+Any string data stored in a @union{value} will be encoded in the
+dictionary's character set.
+
+
+@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}).  
+However, many
+files produced by early versions of spss set this to ``2'' (ASCII) regardless
+of the encoding of the data.
+Later versions contain an additional
+record (@pxref{Character Encoding Record}) describing the encoding.
+When a system file is read, the dictionary's encoding is set using information 
+gleened from the system file.
+If the encoding cannot be determined or would be unreliable, then it 
+remains unset.
+
+
+@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 (except for filenames)
+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.
+
+@subsection Filenames
+The GLib API has some special functions for dealing with filenames.
+Strings returned from functions like gtk_file_chooser_dialog_get_name are not, 
+in general, encoded in UTF8, but in ``filename'' encoding.
+If that filename is passed to another GLib function which expects a filename, 
+no conversion is necessary.
+If it's passed to a function for the purposes of displaying it (eg. in a 
+window's title-bar) it must be converted to UTF8 --- there is a special 
+function for this: g_filename_display_name or g_filename_basename.
+If however, a filename needs to be passed outside of GTK+/GLib (for example to fopen) it must be converted to the local system encoding.
+
+
+@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 (const char *@var{to}, const char *@var{from}, const char *@var{text}, int @var{len});
+
+Converts the string @var{text}, which is encoded in @var{from} to a new string encoded in @var{to} encoding.
+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
+
+In order to minimise the number of conversions required, and to simplify 
+design, PSPP attempts to store all internal strings in UTF8 encoding.
+Thus, when reading system and portable files (or any other data source),
+the following items are immediately converted to UTF8 encoding:
+@itemize
+@item Variable names
+@item Variable labels
+@item Value labels
+@end itemize
+Conversely, when writing system files, these are converted back to the
+encoding of that system file.
+
+String data stored in union values are left in their original encoding.
+These will be converted by the data_in/data_out functions.
+
+
+
+@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..a404d0d6ce3961eed3ca1ec134a2ff9f6140adac 100644 (file)
@@ -96,6 +96,9 @@ Each type of record is described separately below.
 * Variable Display Parameter Record::
 * Long Variable Names Record::
 * Very Long String Record::
+* Character Encoding Record::
+* Long String Value Labels Record::
+* Data File and Variable Attributes Records::
 * Miscellaneous Informational Records::
 * Dictionary Termination Record::
 * Data Record::
@@ -286,15 +289,20 @@ length @code{label_len}, rounded up to the nearest multiple of 32 bits.
 The first @code{label_len} characters are the variable's variable label.
 
 @item flt64 missing_values[];
-This field is present only if @code{n_missing_values} is not 0.  It has
-the same number of elements as the absolute value of
-@code{n_missing_values}.  For discrete missing values, each element
-represents one missing value.  When a range is present, the first
-element denotes the minimum value in the range, and the second element
-denotes the maximum value in the range.  When a range plus a value are
-present, the third element denotes the additional discrete missing
-value.  HIGHEST and LOWEST are indicated as described in the chapter
-introduction.
+This field is present only if @code{n_missing_values} is nonzero.  It
+has the same number of 8-byte elements as the absolute value of
+@code{n_missing_values}.  Each element is interpreted as a number for
+numeric variables (with HIGHEST and LOWEST indicated as described in
+the chapter introduction).  For string variables of width less than 8
+bytes, elements are right-padded with spaces; for string variables
+wider than 8 bytes, only the first 8 bytes of each missing value are
+specified, with the remainder implicitly all spaces.
+
+For discrete missing values, each element represents one missing
+value.  When a range is present, the first element denotes the minimum
+value in the range, and the second element denotes the maximum value
+in the range.  When a range plus a value are present, the third
+element denotes the additional discrete missing value.
 @end table
 
 The @code{print} and @code{write} members of sysfile_variable are output
@@ -396,6 +404,11 @@ Format types are defined as follows:
 @node Value Labels Records
 @section Value Labels Records
 
+The value label records documented in this section are used for
+numeric and short string variables only.  Long string variables may
+have value labels, but their value labels are recorded using a
+different record type (@pxref{Long String Value Labels Record}).
+
 The value label record has the following format:
 
 @example
@@ -456,7 +469,7 @@ A list of dictionary indexes of variables to which to apply the value
 labels (@pxref{Dictionary Index}).  There are @code{var_count}
 elements.
 
-String variables wider than 8 bytes may not have value labels.
+String variables wider than 8 bytes may not be specified in this list.
 @end table
 
 @node Document Record
@@ -545,9 +558,14 @@ Compression code.  Always set to 1.
 Machine endianness.  1 indicates big-endian, 2 indicates little-endian.
 
 @item int32 character_code;
+@anchor{character-code}
 Character code.  1 indicates EBCDIC, 2 indicates 7-bit ASCII, 3
 indicates 8-bit ASCII, 4 indicates DEC Kanji.
 Windows code page numbers are also valid.
+
+Experience has shown that in many files, this field is ignored or incorrect.
+For a more reliable indication of the file's character encoding
+see @ref{Character Encoding Record}.
 @end table
 
 @node Machine Floating-Point Info Record
@@ -791,6 +809,197 @@ After the last tuple, there may be a single byte 00, or @{00, 09@}.
 The total length is @code{count} bytes.
 @end table
 
+@node Character Encoding Record
+@section Character Encoding Record
+
+This record, if present, indicates the character encoding for string data,
+long variable names, variable labels, value labels and other strings in the
+file.
+
+@example
+/* @r{Header.} */
+int32               rec_type;
+int32               subtype;
+int32               size;
+int32               count;
+
+/* @r{Exactly @code{count} bytes of data.} */
+char                encoding[];
+@end example
+
+@table @code
+@item int32 rec_type;
+Record type.  Always set to 7.
+
+@item int32 subtype;
+Record subtype.  Always set to 20.
+
+@item int32 size;
+The size of each element in the @code{encoding} member. Always set to 1.
+
+@item int32 count;
+The total number of bytes in @code{encoding}.
+
+@item char encoding[];
+The name of the character encoding.  Normally this will be an official IANA characterset name or alias.
+See @url{http://www.iana.org/assignments/character-sets}.
+@end table
+
+This record is not present in files generated by older software.
+See also @ref{character-code}.
+
+@node Long String Value Labels Record
+@section Long String Value Labels Record
+
+This record, if present, specifies value labels for long string
+variables.
+
+@example
+/* @r{Header.} */
+int32               rec_type;
+int32               subtype;
+int32               size;
+int32               count;
+
+/* @r{Repeated up to exactly @code{count} bytes.} */
+int32               var_name_len;
+char                var_name[];
+int32               var_width;
+int32               n_labels;
+long_string_label   labels[];
+@end example
+
+@table @code
+@item int32 rec_type;
+Record type.  Always set to 7.
+
+@item int32 subtype;
+Record subtype.  Always set to 21.
+
+@item int32 size;
+Always set to 1.
+
+@item int32 count;
+The number of bytes following the header until the next header.
+
+@item int32 var_name_len;
+@itemx char var_name[];
+The number of bytes in the name of the variable that has long string
+value labels, plus the variable name itself, which consists of exactly
+@code{var_name_len} bytes.  The variable name is not padded to any
+particular boundary, nor is it null-terminated.
+
+@item int32 var_width;
+The width of the variable, in bytes, which will be between 9 and
+32767.
+
+@item int32 n_labels;
+@itemx long_string_label labels[];
+The long string labels themselves.  The @code{labels} array contains
+exactly @code{n_labels} elements, each of which has the following
+substructure:
+
+@example
+int32               value_len;
+char                value[];
+int32               label_len;
+char                label[];
+@end example
+
+@table @code
+@item int32 value_len;
+@itemx char value[];
+The string value being labeled.  @code{value_len} is the number of
+bytes in @code{value}; it is equal to @code{var_width}.  The
+@code{value} array is not padded or null-terminated.
+
+@item int32 label_len;
+@itemx char label[];
+The label for the string value.  @code{label_len}, which must be
+between 0 and 120, is the number of bytes in @code{label}.  The
+@code{label} array is not padded or null-terminated.
+@end table
+@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..345e59669d36ad6f0cc08a8f95251e3b0760fdbc 100644 (file)
@@ -15,14 +15,14 @@ strings or numbers as operands.  With few exceptions, operands may be
 full-fledged expressions in themselves.
 
 @menu
-* Boolean Values::              Boolean values.
-* Missing Values in Expressions::  Using missing values in expressions.
-* Grouping Operators::          parentheses
-* Arithmetic Operators::        add sub mul div pow
-* Logical Operators::           AND NOT OR
-* Relational Operators::        EQ GE GT LE LT NE
-* Functions::                   More-sophisticated operators.
-* Order of Operations::         Operator precedence.
+* Boolean Values::                 Boolean values
+* Missing Values in Expressions::  Using missing values in expressions
+* Grouping Operators::             parentheses
+* Arithmetic Operators::           add sub mul div pow
+* Logical Operators::              AND NOT OR
+* Relational Operators::           EQ GE GT LE LT NE
+* Functions::                      More-sophisticated operators
+* Order of Operations::            Operator precedence
 @end menu
 
 @node Boolean Values
@@ -259,7 +259,7 @@ The sections below describe each function in detail.
 * Statistical Functions::       CFVAR MAX MEAN MIN SD SUM VARIANCE
 * String Functions::            CONCAT INDEX LENGTH LOWER LPAD LTRIM NUMBER 
                                 RINDEX RPAD RTRIM STRING SUBSTR UPCASE
-* Time & Date::                 CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx
+* Time and Date::               CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx
                                 DATEDIFF DATESUM
 * Miscellaneous Functions::     LAG YRMODA VALUELABEL
 * Statistical Distribution Functions::  PDF CDF SIG IDF RV NPDF NCDF
@@ -691,7 +691,7 @@ has value @code{"cd"}; @code{SUBSTR("nonsense", 4, 10)} has the value
 Returns @var{string}, changing lowercase letters to uppercase letters.
 @end deftypefn
 
-@node Time & Date
+@node Time and Date
 @subsection Time & Date Functions
 @cindex functions, time & date
 @cindex times
@@ -702,17 +702,17 @@ For compatibility, PSPP considers dates before 15 Oct 1582 invalid.
 Most time and date functions will not accept earlier dates.
 
 @menu
-* Time & Date Concepts::        How times & dates are defined and represented
+* Time and Date Concepts::      How times & dates are defined and represented
 * Time Construction::           TIME.@{DAYS HMS@}
 * Time Extraction::             CTIME.@{DAYS HOURS MINUTES SECONDS@}
 * Date Construction::           DATE.@{DMY MDY MOYR QYR WKYR YRDAY@}
 * Date Extraction::             XDATE.@{DATE HOUR JDAY MDAY MINUTE MONTH
                                        QUARTER SECOND TDAY TIME WEEK
                                        WKDAY YEAR@}
-* Time & Date Arithmetic::      DATEDIFF DATESUM
+* Time and Date Arithmetic::    DATEDIFF DATESUM
 @end menu
 
-@node Time & Date Concepts
+@node Time and Date Concepts
 @subsubsection How times & dates are defined and represented
 
 @cindex time, concepts
@@ -1001,7 +1001,7 @@ Returns the year (as an integer 1582 or greater) corresponding to
 @var{date}.
 @end deftypefn
 
-@node Time & Date Arithmetic
+@node Time and Date Arithmetic
 @subsubsection Time and Date Arithmetic
 
 @cindex time, mathematical properties of
@@ -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..3052162fddbfac4af381b3e3e1966c7f3e0704f8 100644 (file)
@@ -1,18 +1,46 @@
 @node Invocation
-@chapter Invoking PSPP
+
+@chapter Starting PSPP
 @cindex invocation
 @cindex PSPP, invoking
 
+There are two separate user interfaces for PSPP.
+There is the command line interface, which responds to commands
+typed by the user.
+The command line interface is generally available on more platforms
+than the graphic user interface and since it doesn't require a
+graphics device it can be used from a remote terminal.
+Platforms which have a windowing system may also be able to support
+the graphic user interface.
+The graphic user interface can perform all functionality of the
+command line interface.
+In addition it gives an instantaneous view of the data, variables and
+statistical output.
+
+Whichever interface you choose, a basic understanding of the concepts
+used by PSPP is necessary before effective use of the system can be achieved.
+
+
+@menu
+* The command line user interface::  
+* The graphic user interface::  
+@end menu
+
+
+@node The command line user interface
+@section The command line user interface
+
 @cindex command line, options
 @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
@@ -26,7 +54,7 @@ pspp [ -B @var{dir} | --config-dir=@var{dir} ] [ -o @var{device} | --device=@var
 @end menu
 
 @node Non-option Arguments
-@section Non-option Arguments
+@subsection Non-option Arguments
 
 Syntax files and output device substitutions can be specified on
 PSPP's command line:
@@ -64,7 +92,7 @@ typing its name.  You can include any options on the command line as
 usual.  PSPP entirely ignores any lines beginning with @samp{#!}.
 
 @node Configuration Options
-@section Configuration Options
+@subsection Configuration Options
 
 Configuration options are used to change PSPP's configuration for the
 current run.  The configuration options are:
@@ -95,25 +123,12 @@ option disables all devices besides those mentioned on the command line.
 @end table
 
 @node Input and output options
-@section Input and output options
+@subsection Input and output options
 
 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 +142,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
@@ -140,7 +149,7 @@ check} and similar scripts.
 @end table
 
 @node Language control options
-@section Language control options
+@subsection Language control options
 
 Language control options control how PSPP syntax files are parsed and
 interpreted.  The available language control options are:
@@ -158,16 +167,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
 
@@ -181,7 +180,7 @@ HOST commands, as well as use of pipes as input and output files.
 @end table
 
 @node Informational options
-@section Informational options
+@subsection Informational options
 
 Informational options cause information about PSPP to be written to
 the terminal.  Here are the available options:
@@ -253,4 +252,19 @@ Individual directories included in file searches.
 Each verbosity level also includes messages from lower verbosity levels.
 
 @end table
-@setfilename ignored
+
+
+@node The graphic user interface
+@section The graphic user interface
+
+@cindex Graphic user interface
+@cindex PSPPIRE
+
+The graphic user interface can be started by typing @command{psppire} at a 
+command prompt.
+Alternatively many systems have a system of interactive menus or buttons 
+from which @command{psppire} can be started by a series of mouse clicks.
+
+Once the principles of the PSPP system are understood, 
+the graphic user interface is designed to be largely intuitive, and
+for this reason is covered only very briefly by this manual.
index 1345433613d3f24e7c7b26cdac457f7132761834..e23a558028805dbc9babcb360811d34c87258434 100644 (file)
@@ -3,11 +3,9 @@
 @cindex language, PSPP
 @cindex PSPP, language
 
-@quotation
-@strong{Please note:} PSPP is not even close to completion.
+@note{PSPP is not even close to completion.
 Only a few statistical procedures are implemented.  PSPP
-is a work in progress.
-@end quotation
+is a work in progress.}
 
 This chapter discusses elements common to many PSPP commands.
 Later chapters will describe individual commands in detail.
@@ -381,9 +379,7 @@ spaces.
 Variables, whether numeric or string, can have designated
 @dfn{user-missing values}.  Every user-missing value is an actual value
 for that variable.  However, most of the time user-missing values are
-treated in the same way as the system-missing value.  String variables
-that are wider than a certain width, usually 8 characters (depending on
-computer architecture), cannot have user-missing values.
+treated in the same way as the system-missing value.
 
 For more information on missing values, see the following sections:
 @ref{Variables}, @ref{MISSING VALUES}, @ref{Expressions}.  See also the
@@ -449,13 +445,9 @@ Numeric or string.
 @item Width
 (string variables only) String variables with a width of 8 characters or
 fewer are called @dfn{short string variables}.  Short string variables
-can be used in many procedures where @dfn{long string variables} (those
+may be used in a few contexts where @dfn{long string variables} (those
 with widths greater than 8) are not allowed.
 
-Certain systems may consider strings longer than 8
-characters to be short strings.  Eight characters represents a minimum
-figure for the maximum length of a short string.
-
 @item Position
 Variables in the dictionary are arranged in a specific order.
 @cmd{DISPLAY} can be used to show this order: see @ref{DISPLAY}.
@@ -497,6 +489,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 +1164,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 +1486,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..9c50904e1b4c43a330202e8ee08869a44d145e20 100644 (file)
@@ -6,6 +6,13 @@
 @c @setchapternewpage odd
 @c %**end of header
 
+
+@macro note{param1}
+@quotation
+@strong{Please note:} \param1\
+@end quotation
+@end macro
+
 @include version.texi
 
 @macro cmd{CMDNAME}
@@ -25,7 +32,7 @@
 This manual is for GNU PSPP version @value{VERSION},
 software for statistical analysis.
 
-Copyright @copyright{} 1997, 1998, 2004, 2005 Free Software Foundation, Inc.
+Copyright @copyright{} 1997, 1998, 2004, 2005, 2009 Free Software Foundation, Inc.
 
 @quotation
 Permission is granted to copy, distribute and/or modify this document
@@ -51,6 +58,14 @@ modify this GNU manual.''
 @insertcopying
 @end titlepage
 
+@chapheading Acknowledgements
+The authors wish to thank
+Network Theory Ltd 
+@url{http://www.network-theory.co.uk}
+for their financial support 
+in the production of this manual.
+
+
 @contents
 
 
@@ -66,11 +81,13 @@ modify this GNU manual.''
 * License::                     Your rights and obligations.
 
 * Invocation::                  Starting and running PSPP.
+* Using PSPP::                  How to use PSPP --- A brief tutorial.
 * Language::                    Basics of the PSPP command language.
 * 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.
@@ -94,10 +111,12 @@ modify this GNU manual.''
 @include license.texi
 
 @include invoking.texi
+@include tutorial.texi
 @include language.texi
 @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..76925d7e954d865d76217b97e79c86fa3a0ca272 100644 (file)
@@ -14,6 +14,8 @@ far.
 * ONEWAY::                      One way analysis of variance.
 * RANK::                        Compute rank scores.
 * REGRESSION::                  Linear regression.
+* RELIABILITY::                 Reliability analysis.
+* ROC::                         Receiver Operating Characteristic.
 @end menu
 
 @node DESCRIPTIVES
@@ -204,12 +206,13 @@ boundaries of the data set divided into the specified number of ranges.
 For instance, @code{/NTILES=4} would cause quartiles to be reported.
 
 The HISTOGRAM subcommand causes the output to include a histogram for
-each specified variable.  The X axis by default ranges from the
+each specified numeric variable.  The X axis by default ranges from the
 minimum to the maximum value observed in the data, but the MINIMUM and
 MAXIMUM keywords can set an explicit range.  The Y axis by default is
 labeled in frequencies; use the PERCENT keyword to causes it to be
 labeled in percent of the total observed count.  Specify NORMAL to
 superimpose a normal curve on the histogram.
+Histograms are not created for string variables.
 
 The PIECHART adds a pie chart for each variable to the data.  Each
 slice represents one value, with the size of the slice proportional to
@@ -232,7 +235,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 +274,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%.
@@ -340,9 +349,7 @@ is present, the VARIABLES subcommand must precede the TABLES
 subcommand.
 
 In general mode, numeric and string variables may be specified on
-TABLES.  Although long string variables are allowed, only their
-initial short-string parts are used.  In integer mode, only numeric
-variables are allowed.
+TABLES.  In integer mode, only numeric variables are allowed.
 
 The MISSING subcommand determines the handling of user-missing values.
 When set to TABLE, the default, missing values are dropped on a table by
@@ -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,22 @@ 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
+* SIGN::                    The Sign Test
 @end menu
 
 
@@ -524,7 +545,7 @@ produces for each variable that is the subject of any test.
      [ /BINOMIAL[(p)]=var_list[(value1[, value2)] ] ]
 @end display 
 
-The binomial test compares the observed distribution of a dichotomous 
+The /BINOMIAL subcommand compares the observed distribution of a dichotomous 
 variable with that of a binomial distribution.
 The variable @var{p} specifies the test proportion of the binomial 
 distribution.  
@@ -564,7 +585,7 @@ even for very large sample sizes.
 
 
 @node    CHISQUARE
-@subsection Chisquare test
+@subsection Chisquare Test
 @vindex CHISQUARE
 @cindex chisquare test
 
@@ -574,7 +595,7 @@ even for very large sample sizes.
 @end display 
 
 
-The chisquare test produces a chi-square statistic for the differences 
+The /CHISQUARE subcommand produces a chi-square statistic for the differences 
 between the expected and observed frequencies of the categories of a variable. 
 Optionally, a range of values may appear after the variable list.  
 If a range is given, then non integer values are truncated, and values
@@ -591,6 +612,59 @@ sum of the frequencies need not be 1.
 If no /EXPECTED subcommand is given, then then equal frequencies 
 are expected.
 
+@node WILCOXON
+@subsection Wilcoxon Matched Pairs Signed Ranks Test
+@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 medians of the 
+variables listed.
+The test does not make any assumptions about the variances of the samples.
+It does however assume that the distribution is symetrical.
+
+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.
+
+
+@node SIGN
+@subsection Sign Test
+@vindex SIGN
+@cindex sign test
+
+@display
+     [ /SIGN varlist [ WITH varlist [ (PAIRED) ]]]
+@end display
+
+The /SIGN subcommand tests for differences between medians of the 
+variables listed.
+The test does not make any assumptions about the
+distribution of the data.
+
+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.
 
 @node T-TEST
 @comment  node-name,  next,  previous,  up
@@ -766,7 +840,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 +904,120 @@ 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.
+
+
+
+@node ROC
+@section ROC
+
+@vindex ROC
+@cindex Receiver Operating Characterstic
+@cindex Area under curve
+
+@display
+ROC     @var{var_list} BY @var{state_var} (@var{state_value})
+        /PLOT = @{ CURVE [(REFERENCE)], NONE @}
+        /PRINT = [ SE ] [ COORDINATES ]
+        /CRITERIA = [ CUTOFF(@{INCLUDE,EXCLUDE@}) ]
+          [ TESTPOS (@{LARGE,SMALL@}) ]
+          [ CI (@var{confidence}) ]
+          [ DISTRIBUTION (@{FREE, NEGEXPO @}) ]
+        /MISSING=@{EXCLUDE,INCLUDE@}
+@end display
+
+
+The @cmd{ROC} command is used to plot the receiver operating characteristic curve 
+of a dataset, and to estimate the area under the curve.
+This is useful for analysing the efficacy of a variable as a predictor of a state of nature.
+
+The mandatory @var{var_list} is the list of predictor variables.
+The variable @var{state_var} is the variable whose values represent the actual states, 
+and @var{state_value} is the value of this variable which represents the positive state.
+
+The optional subcommand PLOT is used to determine if and how the ROC curve is drawn.
+The keyword CURVE means that the ROC curve should be drawn, and the optional keyword REFERENCE,
+which should be enclosed in parentheses, says that the diagonal reference line should be drawn.
+If the keyword NONE is given, then no ROC curve is drawn.
+By default, the curve is drawn with no reference line.
+
+The optional subcommand PRINT determines which additional tables should be printed.
+Two additional tables are available. 
+The SE keyword says that standard error of the area under the curve should be printed as well as
+the area itself.
+In addition, a p-value under the null hypothesis that the area under the curve equals 0.5 will be
+printed.
+The COORDINATES keyword says that a table of coordinates of the ROC curve should be printed.
+
+The CRITERIA subcommand has four optional parameters:
+@itemize @bullet
+@item The TESTPOS parameter may be LARGE or SMALL.
+LARGE is the default, and says that larger values in the predictor variables are to be 
+considered positive.  SMALL indicates that smaller values should be considered positive.
+
+@item The CI parameter specifies the confidence interval that should be printed.
+It has no effect if the SE keyword in the PRINT subcommand has not been given.
+
+@item The DISTRIBUTION parameter determines the method to be used when estimating the area
+under the curve.  
+There are two possibilities, @i{viz}: FREE and NEGEXPO.
+The FREE method uses a non-parametric estimate, and the NEGEXPO method a bi-negative 
+exponential distribution estimate.
+The NEGEXPO method should only be used when the number of positive actual states is
+equal to the number of negative actual states.
+The default is FREE.
+
+@item The CUTOFF parameter is for compatibility and is ignored.
+@end itemize
+
+The MISSING subcommand determines whether user missing values are to 
+be included or excluded in the analysis.  The default behaviour is to
+exclude them.
+Cases are excluded on a listwise basis; if any of the variables in @var{var_list} 
+or if the variable @var{state_var} is missing, then the entire case will be 
+excluded.
+
+
index 27bbb2da8d039204be8c27f9b996f867b9ba6511..fe08b9cd8fbb6eefeee8507848a52b78091685fa 100644 (file)
@@ -83,9 +83,9 @@ 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.
+and string variables.
 
 The available aggregation functions are as follows:
 
@@ -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.
@@ -236,7 +239,7 @@ COMPUTE vector(index) = expression.
 
 @cmd{COMPUTE} assigns the value of an expression to a target
 variable.  For each case, the expression is evaluated and its value
-assigned to the target variable.  Numeric and short and long string
+assigned to the target variable.  Numeric and string
 variables may be assigned.  When a string expression's width differs
 from the target variable's width, the string result of the expression
 is truncated or padded with spaces on the right as necessary.  The
@@ -287,7 +290,7 @@ one or more @dfn{test} variables for each case.
 
 The target variable values are always nonnegative integers.  They are
 never missing.  The target variable is assigned an F8.2 output format.
-@xref{Input and Output Formats}.  Any variables, including long and short
+@xref{Input and Output Formats}.  Any variables, including
 string variables, may be test variables.
 
 User-missing values of test variables are treated just like any other
@@ -432,7 +435,7 @@ Specify a boolean-valued expression (@pxref{Expressions}) to be tested
 following the IF keyword.  This expression is evaluated for each case.
 If the value is true, then the value of the expression is computed and
 assigned to the specified variable.  If the value is false or missing,
-nothing is done.  Numeric and short and long string variables may be
+nothing is done.  Numeric and string variables may be
 assigned.  When a string expression's width differs from the target
 variable's width, the string result of the expression is truncated or
 padded with spaces on the right as necessary.  The expression and
@@ -478,7 +481,7 @@ dest_value may take the following forms:
 
 @cmd{RECODE} translates data from one range of values to
 another, via flexible user-specified mappings.  Data may be remapped
-in-place or copied to new variables.  Numeric, short string, and long
+in-place or copied to new variables.  Numeric and
 string data can be recoded.
 
 Specify the list of source variables, followed by one or more mapping
@@ -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
diff --git a/doc/tutorial.texi b/doc/tutorial.texi
new file mode 100644 (file)
index 0000000..540da62
--- /dev/null
@@ -0,0 +1,858 @@
+@alias prompt = sansserif
+
+@include doc/tut.texi
+
+@node Using PSPP
+@chapter Using PSPP
+
+PSPP is a tool for the statistical analysis of sampled data.
+You can use it to discover patterns in the data,
+to explain differences in one subset of data in terms of another subset
+and to find out
+whether certain beliefs about the data are justified.
+This chapter does not attempt to introduce the theory behind the 
+statistical analysis,
+but it shows how such analysis can be performed using PSPP.
+
+For the purposes of this tutorial, it is assumed that you are using PSPP in its 
+interactive mode from the command line.
+However, the example commands can also be typed into a file and executed in 
+a post-hoc mode by typing @samp{pspp @var{filename}} at a shell prompt,
+where @var{filename} is the name of the file containing the commands.
+Alternatively, from the graphical interface, you can select
+@clicksequence{File @click{} New @click{} Syntax} to open a new syntax window
+and use the @clicksequence{Run} menu when a syntax fragment is ready to be 
+executed.
+Whichever method you choose, the syntax is identical.
+
+When using the interactive method, PSPP tells you that it's waiting for your
+data with a string like @prompt{PSPP>} or @prompt{data>}.
+In the examples of this chapter, whenever you see text like this, it
+indicates the prompt displayed by PSPP, @emph{not} something that you 
+should type.
+
+Throughout this chapter reference is made to a number of sample data files.
+So that you can try the examples for yourself,
+you should have received these files along with your copy of PSPP.@c
+@footnote{These files contain purely fictitious data.  They should not be used
+for research purposes.}
+@note{Normally these files are installed in the directory
+@file{@value{example-dir}}.
+If however your system administrator or operating system vendor has
+chosen to install them in a different location, you will have to adjust
+the examples accordingly.}
+
+
+@menu
+* Preparation of Data Files::   
+* Data Screening and Transformation::  
+* Hypothesis Testing::          
+@end menu
+
+@node Preparation of Data Files
+@section Preparation of Data Files
+
+
+Before analysis can commence,  the data must be loaded into PSPP and
+arranged such that both PSPP and humans can understand what
+the data represents.
+There are two aspects of data:
+
+@itemize @bullet
+@item The variables --- these are the parameters of a quantity
+ which has been measured or estimated in some way.
+ For example height, weight and geographic location are all variables.
+@item The observations (also called `cases') of the variables ---
+ each observation represents an instance when the variables were measured
+ or observed. 
+@end itemize
+
+@noindent
+For example, a data set which has the variables @var{height}, @var{weight}, and
+@var{name}, might have the observations:
+@example
+1881 89.2 Ahmed
+1192 107.01 Frank
+1230 67 Julie
+@end example
+@noindent
+The following sections explain how to define a dataset.
+
+@menu
+* Defining Variables::          
+* Listing the data::            
+* Reading data from a text file::  
+* Reading data from a pre-prepared PSPP file::  
+* Saving data to a PSPP file.::  
+* Reading data from other sources::  
+@end menu
+
+@node Defining Variables
+@subsection Defining Variables
+@cindex variables
+
+Variables come in two basic types, @i{viz}: @dfn{numeric} and @dfn{string}.
+Variables such as age, height and satisfaction are numeric,
+whereas name is a string variable.
+String variables are best reserved for commentary data to assist the 
+human observer.
+However they can also be used for nominal or categorical data.
+
+
+@ref{data-list} defines two variables @var{forename} and @var{height},
+and reads data into them by manual input. 
+
+@float Example, data-list
+@cartouche
+@example
+@prompt{PSPP>} data list list /forename (A12) height.
+@prompt{PSPP>} begin data.
+@prompt{data>} Ahmed 188
+@prompt{data>} Bertram 167
+@prompt{data>} Catherine 134.231
+@prompt{data>} David 109.1
+@prompt{data>} end data
+@prompt{PSPP>}
+@end example
+@end cartouche
+@caption{Manual entry of data using the @cmd{DATA LIST} command.
+Two variables
+@var{forename} and @var{height} are defined and subsequently filled
+with  manually entered data.}
+@end float
+
+There are several things to note about this example.
+
+@itemize @bullet
+@item
+The words @samp{data list list} are an example of the @cmd{DATA LIST}
+command. @xref{DATA LIST}.
+It tells PSPP to prepare for reading data.
+The word @samp{list} intentionally appears twice.
+The first occurrence is part of the @cmd{DATA LIST} call,
+whilst the second
+tells PSPP that the data is to be read as free format data with
+one record per line.
+
+@item
+The @samp{/} character is important. It marks the start of the list of
+variables which you wish to define.
+
+@item
+The text @samp{forename} is the name of the first variable,
+and @samp{(A12)} says that the variable @var{forename} is a string
+variable and that its maximum length is 12 bytes.
+The second variable's name is specified by the text @samp{height}.
+Since no format is given, this variable has the default format.
+For more information on data formats, @pxref{Input and Output Formats}.
+
+
+@item
+Normally, PSPP displays the  prompt @prompt{PSPP>} whenever it's
+expecting a command.
+However, when it's expecting data, the prompt changes to @prompt{data>}
+so that you know to enter data and not a command.
+
+@item
+At the end of every command there is a terminating @samp{.} which tells
+PSPP that the end of a command has been encountered.
+You should not enter @samp{.} when data is expected (@i{ie.} when 
+the @prompt{data>} prompt is current) since it is appropriate only for
+terminating commands.
+@end itemize
+
+@node Listing the data
+@subsection Listing the data
+@vindex LIST
+
+Once the data has been entered,
+you could type
+@example
+@prompt{PSPP>} list /format=numbered.
+@end example
+@noindent
+to list the data.
+The optional text @samp{/format=numbered} requests the case numbers to be 
+shown along with the data.
+It should show the following output:
+@example
+@group
+Case#     forename   height
+----- ------------ --------
+    1 Ahmed          188.00 
+    2 Bertram        167.00 
+    3 Catherine      134.23 
+    4 David          109.10 
+@end group
+@end example
+@noindent
+Note that the numeric variable @var{height} is displayed to 2 decimal 
+places, because the format for that variable is @samp{F8.2}.
+For a complete description of the @cmd{LIST} command, @pxref{LIST}.
+
+@node Reading data from a text file
+@subsection Reading data from a text file
+@cindex reading data
+
+The previous example showed how to define a set of variables and to 
+manually enter the data for those variables.
+Manual entering of data is tedious work, and often
+a file containing the data will be have been previously 
+prepared.
+Let us assume that you have a file called @file{mydata.dat} containing the
+ascii encoded data:
+@example
+Ahmed          188.00 
+Bertram        167.00 
+Catherine      134.23 
+David          109.10 
+@              .
+@              .
+@              .
+Zachariah      113.02
+@end example
+@noindent
+You can can tell the @cmd{DATA LIST} command to read the data directly from
+this file instead of by manual entry, with a command like:
+@example
+@prompt{PSPP>} data list file='mydata.dat' list /forename (A12) height.
+@end example
+@noindent
+Notice however, that it is still necessary to specify the names of the
+variables and their formats, since this information is not contained
+in the file.
+It is also possible to specify the file's character encoding and other 
+parameters.
+For full details refer to @pxref{DATA LIST}.
+
+@node Reading data from a pre-prepared PSPP file
+@subsection Reading data from a pre-prepared PSPP file
+@cindex system files
+@vindex GET
+
+When working with other PSPP users, or users of other software which
+uses the PSPP data format, you may be given the data in
+a pre-prepared PSPP file.
+Such files contain not only the data, but the variable definitions,
+along with their formats, labels and other meta-data.
+Conventionally, these files (sometimes called ``system'' files) 
+have the suffix @file{.sav}, but that is
+not mandatory.
+The following syntax loads a file called @file{my-file.sav}.
+@example
+@prompt{PSPP>} get file='my-file.sav'.
+@end example
+@noindent
+You will encounter several instances of this in future examples.
+
+
+@node Saving data to a PSPP file.
+@subsection Saving data to a PSPP file.
+@cindex saving
+@vindex SAVE
+
+If you want to save your data, along with the variable definitions so
+that you or other PSPP users can use it later, you can do this with
+the @cmd{SAVE} command.
+
+The following syntax will save the existing data and variables to a
+file called @file{my-new-file.sav}.
+@example
+@prompt{PSPP>} save outfile='my-new-file.sav'.
+@end example
+@noindent
+If @file{my-new-file.sav} already exists, then it will be overwritten.
+Otherwise it will be created.
+
+
+@node Reading data from other sources
+@subsection Reading data from other sources
+@cindex comma separated values
+@cindex spreadsheets
+@cindex databases
+
+Sometimes it's useful to be able to read data from comma 
+separated text, from spreadsheets, databases or other sources.
+In these instances you should
+use the @cmd{GET DATA} command (@pxref{GET DATA}).
+
+  
+@node Data Screening and Transformation
+@section Data Screening and Transformation
+
+@cindex screening
+@cindex transformation
+
+Once data has been entered, it is often desirable, or even necessary,
+to transform it in some way before performing analysis upon it.
+At the very least, it's good practice to check for errors.
+
+@menu
+* Identifying incorrect data::  
+* Dealing with suspicious data::  
+* Inverting negatively coded variables::  
+* Testing data consistency::    
+* Testing for normality ::      
+@end menu
+
+@node Identifying incorrect data
+@subsection Identifying incorrect data
+@cindex erroneous data
+@cindex errors, in data
+
+Data from real sources is rarely error free.
+PSPP has a number of procedures which can be used to help 
+identify data which might be incorrect.
+
+The @cmd{DESCRIPTIVES} command (@pxref{DESCRIPTIVES}) is used to generate
+simple linear statistics for a dataset.  It is also useful for
+identifying potential problems in the data.
+The example file @file{physiology.sav} contains a number of physiological
+measurements of a sample of healthy adults selected at random.
+However, the data entry clerk made a number of mistakes when entering
+the data.
+@ref{descriptives} illustrates the use of @cmd{DESCRIPTIVES} to screen this
+data and identify the erroneous values.
+
+@float Example, descriptives
+@cartouche
+@example
+@prompt{PSPP>} get file='@value{example-dir}/physiology.sav'.
+@prompt{PSPP>} descriptives sex, weight, height.
+@end example
+
+Output:
+@example
+DESCRIPTIVES.  Valid cases = 40; cases with missing value(s) = 0.
++--------#--+-------+-------+-------+-------+
+|Variable# N|  Mean |Std Dev|Minimum|Maximum|
+#========#==#=======#=======#=======#=======#
+|sex     #40|    .45|    .50|    .00|   1.00|
+|height  #40|1677.12| 262.87| 179.00|1903.00|
+|weight  #40|  72.12|  26.70| -55.60|  92.07|
++--------#--+-------+-------+-------+-------+
+@end example
+@end cartouche
+@caption{Using the @cmd{DESCRIPTIVES} command to display simple 
+summary information about the data.
+In this case, the results show unexpectedly low values in the Minimum
+column, suggesting incorrect data entry.}
+@end float
+
+In the output of @ref{descriptives},
+the most interesting column is the minimum value.
+The @var{weight} variable has a minimum value of less than zero,
+which is clearly erroneous.
+Similarly, the @var{height} variable's minimum value seems to be very low.
+In fact, it is more than 5 standard deviations from the mean, and is a 
+seemingly bizarre height for an adult person.
+We can examine the data in more detail with the @cmd{EXAMINE}
+command (@pxref{EXAMINE}):
+
+In @ref{examine} you can see that the lowest value of @var{height} is
+179 (which we suspect to be erroneous), but the second lowest is 1598
+which
+we know from the @cmd{DESCRIPTIVES} command 
+is within 1 standard deviation from the mean.
+Similarly the @var{weight} variable has a lowest value which is
+negative but a plausible value for the second lowest value.
+This suggests that the two extreme values are outliers and probably 
+represent data entry errors. 
+
+@float Example, examine
+@cartouche
+[@dots{} continue from @ref{descriptives}]
+@example
+@prompt{PSPP>} examine height, weight /statistics=extreme(3).    
+@end example
+
+Output:
+@example
+#===============================#===========#=======#
+#                               #Case Number| Value #
+#===============================#===========#=======#
+#Height in millimetres Highest 1#         14|1903.00#
+#                              2#         15|1884.00#
+#                              3#         12|1801.65#
+#                     ----------#-----------+-------#
+#                       Lowest 1#         30| 179.00#
+#                              2#         31|1598.00#
+#                              3#         28|1601.00#
+#                     ----------#-----------+-------#
+#Weight in kilograms   Highest 1#         13|  92.07#
+#                              2#          5|  92.07#
+#                              3#         17|  91.74#
+#                     ----------#-----------+-------#
+#                       Lowest 1#         38| -55.60#
+#                              2#         39|  54.48#
+#                              3#         33|  55.45#
+#===============================#===========#=======#
+@end example
+@end cartouche
+@caption{Using the @cmd{EXAMINE} command to see the extremities of the data
+for different variables.  Cases 30 and 38 seem to contain values
+very much lower than the rest of the data.
+They are possibly erroneous.}
+@end float
+
+@node Dealing with suspicious data
+@subsection Dealing with suspicious data
+
+@cindex SYSMIS
+@cindex recoding data
+If possible, suspect data should be checked and re-measured.
+However, this may not always be feasible, in which case the researcher may
+decide to disregard these values.
+PSPP has a feature whereby data can assume the special value `SYSMIS', and
+will be disregarded in future analysis. @xref{Missing Observations}.
+You can set the two suspect values to the `SYSMIS' value using the @cmd{RECODE}
+command.
+@example
+PSPP> recode height (179 = SYSMIS).
+PSPP> recode weight (LOWEST THRU 0 = SYSMIS).
+@end example
+@noindent
+The first command says that for any observation which has a
+@var{height} value of 179, that value should be changed to the SYSMIS
+value.
+The second command says that any @var{weight} values of zero or less
+should be changed to SYSMIS.
+From now on, they will be ignored in analysis.
+For detailed information about the @cmd{RECODE} command @pxref{RECODE}.
+
+If you now re-run the @cmd{DESCRIPTIVES} or @cmd{EXAMINE} commands in
+@ref{descriptives} and @ref{examine} you
+will see a data summary with more plausible parameters.
+You will also notice that the data summaries indicate the two missing values.
+
+@node Inverting negatively coded variables
+@subsection Inverting negatively coded variables
+
+@cindex Likert scale
+@cindex Inverting data
+Data entry errors are not the only reason for wanting to recode data.
+The sample file @file{hotel.sav} comprises data gathered from a 
+customer satisfaction survey of clients at a particular hotel.
+In @ref{reliability}, this file is loaded for analysis.
+The line @code{display dictionary.} tells PSPP to display the
+variables and associated data.
+The output from this command has been omitted from the example for the sake of clarity, but
+you will notice that each of the variables
+@var{v1}, @var{v2} @dots{} @var{v5}  are measured on a 5 point Likert scale,
+with 1 meaning ``Strongly disagree'' and 5 meaning ``Strongly agree''.
+Whilst variables @var{v1}, @var{v2} and @var{v4} record responses
+to a positively posed question, variables @var{v3} and @var{v5} are 
+responses to negatively worded questions.
+In order to perform meaningful analysis, we need to recode the variables so
+that they all measure in the same direction.
+We could use the @cmd{RECODE} command, with syntax such as:
+@example
+recode v3 (1 = 5) (2 = 4) (4 = 2) (5 = 1).
+@end example
+@noindent
+However an easier and more elegant way uses the @cmd{COMPUTE}
+command (@pxref{COMPUTE}).
+Since the variables are Likert variables in the range (1 @dots{} 5),
+subtracting their value  from 6 has the effect of inverting them:
+@example
+compute @var{var} = 6 - @var{var}.
+@end example
+@noindent
+@ref{reliability} uses this technique to recode the variables 
+@var{v3} and @var{v5}.
+After applying  @cmd{COMPUTE} for both variables,
+all subsequent commands will use the inverted values.
+
+
+@node Testing data consistency
+@subsection Testing data consistency
+
+@cindex reliability
+@cindex consistency
+
+A sensible check to perform on survey data is the calculation of
+reliability.
+This gives the statistician some confidence that the questionnaires have been 
+completed thoughtfully.
+If you examine the labels of variables @var{v1},  @var{v3} and @var{v5},
+you will notice that they ask very similar questions.
+One would therefore expect the values of these variables (after recoding) 
+to closely follow one another, and we can test that with the @cmd{RELIABILITY} 
+command (@pxref{RELIABILITY}).
+@ref{reliability} shows a PSPP session where the user (after recoding
+negatively scaled variables) requests reliability statistics for
+@var{v1}, @var{v3} and @var{v5}.
+
+@float Example, reliability
+@cartouche
+@example
+@prompt{PSPP>} get file='@value{example-dir}/hotel.sav'.
+@prompt{PSPP>} display dictionary.
+@prompt{PSPP>} * recode negatively worded questions.
+@prompt{PSPP>} compute v3 = 6 - v3.
+@prompt{PSPP>} compute v5 = 6 - v5.
+@prompt{PSPP>} reliability v1, v3, v5.
+@end example
+
+Output (dictionary information omitted for clarity):
+@example
+1.1 RELIABILITY.  Case Processing Summary
+#==============#==#======#
+#              # N|   %  #
+#==============#==#======#
+#Cases Valid   #17|100.00#
+#      Excluded# 0|   .00#
+#      Total   #17|100.00#
+#==============#==#======#
+
+1.2 RELIABILITY.  Reliability Statistics
+#================#==========#
+#Cronbach's Alpha#N of items#
+#================#==========#
+#             .86#         3#
+#================#==========#
+@end example
+@end cartouche
+@caption{Recoding negatively scaled variables, and testing for
+reliability with the @cmd{RELIABILITY} command. The Cronbach Alpha
+coefficient suggests a high degree of reliability among variables
+@var{v1}, @var{v2} and @var{v5}.}
+@end float
+
+As a rule of thumb, many statisticians consider a value of Cronbach's Alpha of 
+0.7 or higher to indicate reliable data.
+Here, the value is 0.86 so the data and the recoding that we performed 
+are vindicated.
+
+
+@node Testing for normality 
+@subsection Testing for normality
+@cindex normality, testing 
+
+Many statistical tests rely upon certain properties of the data.
+One common property, upon which many linear tests depend, is that of
+normality --- the data must have been drawn from a normal distribution.
+It is necessary then to ensure normality before deciding upon the
+test procedure to use.  One way to do this uses the @cmd{EXAMINE} command.
+
+In @ref{normality}, a researcher was examining the failure rates
+of equipment produced by an engineering company.
+The file @file{repairs.sav} contains the mean time between
+failures (@var{mtbf}) of some items of equipment subject to the study.
+Before performing linear analysis on the data, 
+the researcher wanted to ascertain that the data is normally distributed.
+
+A normal distribution has a skewness and kurtosis of zero.
+Looking at the skewness of @var{mtbf} in @ref{normality} it is clear
+that the mtbf figures have a lot of positive skew and are therefore
+not drawn from a normally distributed variable.
+Positive skew can often be compensated for by applying a logarithmic 
+transformation.
+This is done with the @cmd{COMPUTE} command in the line
+@example
+compute mtbf_ln = ln (mtbf).
+@end example
+@noindent
+Rather than redefining the existing variable, this use of @cmd{COMPUTE}
+defines a new variable @var{mtbf_ln} which is
+the natural logarithm of @var{mtbf}.
+The final command in this example calls @cmd{EXAMINE} on this new variable,
+and it can be seen from the results that both the skewness and
+kurtosis for @var{mtbf_ln} are very close to zero.
+This provides some confidence that the @var{mtbf_ln} variable is 
+normally distributed and thus safe for linear analysis.
+In the event that no suitable transformation can be found,
+then it would be worth considering 
+an appropriate non-parametric test instead of a linear one.
+@xref{NPAR TESTS}, for information about non-parametric tests.
+
+@float Example, normality
+@cartouche
+@example
+@prompt{PSPP>} get file='@value{example-dir}/repairs.sav'.
+@prompt{PSPP>} examine mtbf 
+                /statistics=descriptives.
+@prompt{PSPP>} compute mtbf_ln = ln (mtbf).
+@prompt{PSPP>} examine mtbf_ln
+                /statistics=descriptives.
+@end example
+
+Output:
+@example
+1.2 EXAMINE.  Descriptives
+#====================================================#=========#==========#
+#                                                    #Statistic|Std. Error#
+#====================================================#=========#==========#
+#mtbf    Mean                                        #   8.32  |   1.62   #
+#        95% Confidence Interval for Mean Lower Bound#   4.85  |          #
+#                                         Upper Bound#  11.79  |          #
+#        5% Trimmed Mean                             #   7.69  |          #
+#        Median                                      #   8.12  |          #
+#        Variance                                    #  39.21  |          #
+#        Std. Deviation                              #   6.26  |          #
+#        Minimum                                     #   1.63  |          #
+#        Maximum                                     #  26.47  |          #
+#        Range                                       #  24.84  |          #
+#        Interquartile Range                         #   5.83  |          #
+#        Skewness                                    #   1.85  |    .58   #
+#        Kurtosis                                    #   4.49  |   1.12   #
+#====================================================#=========#==========#
+
+2.2 EXAMINE.  Descriptives
+#====================================================#=========#==========#
+#                                                    #Statistic|Std. Error#
+#====================================================#=========#==========#
+#mtbf_ln Mean                                        #   1.88  |    .19   #
+#        95% Confidence Interval for Mean Lower Bound#   1.47  |          #
+#                                         Upper Bound#   2.29  |          #
+#        5% Trimmed Mean                             #   1.88  |          #
+#        Median                                      #   2.09  |          #
+#        Variance                                    #   .54   |          #
+#        Std. Deviation                              #   .74   |          #
+#        Minimum                                     #   .49   |          #
+#        Maximum                                     #   3.28  |          #
+#        Range                                       #   2.79  |          #
+#        Interquartile Range                         #   .92   |          #
+#        Skewness                                    #   -.16  |    .58   #
+#        Kurtosis                                    #   -.09  |   1.12   #
+#====================================================#=========#==========#
+@end example
+@end cartouche
+@caption{Testing for normality using the @cmd{EXAMINE} command and applying
+a logarithmic transformation.
+The @var{mtbf} variable has a large positive skew and is therefore
+unsuitable for linear statistical analysis.
+However the transformed variable (@var{mtbf_ln}) is close to normal and
+would appear to be more suitable.}
+@end float
+
+
+@node Hypothesis Testing
+@section Hypothesis Testing
+
+@cindex Hypothesis testing
+@cindex p-value
+@cindex null hypothesis
+
+One of the most fundamental purposes of statistical analysis
+is hypothesis testing.
+Researchers commonly need to test hypotheses about a set of data.
+For example, she might want to test whether one set of data comes from
+the same distribution as another,
+or
+whether the mean of a dataset significantly differs from a particular
+value.
+This section presents just some of the possible tests that PSPP offers.
+
+The researcher starts by making a @dfn{null hypothesis}.
+Often this is a hypothesis which he suspects to be false.
+For example, if he suspects that @var{A} is greater than @var{B} he will 
+state the null hypothesis as @math{ @var{A} = @var{B}}.@c
+@footnote{This example assumes that it is already proven that @var{B} is
+not greater than @var{A}.}
+
+The @dfn{p-value} is a recurring concept in hypothesis testing.
+It is the highest acceptable probability that the evidence implying a
+null hypothesis is false, could have been obtained when the null
+hypothesis is in fact true.
+Note that this is not the same as ``the probability of making an
+error'' nor is it the same as ``the probability of rejecting a
+hypothesis when it is true''.
+
+
+
+@menu
+* Testing for differences of means::  
+* Linear Regression::           
+@end menu
+
+@node Testing for differences of means
+@subsection Testing for differences of means
+
+@cindex T-test
+@vindex T-TEST
+
+A common statistical test involves hypotheses about means.
+The @cmd{T-TEST} command is used to find out whether or not two separate 
+subsets have the same mean.
+
+@ref{t-test} uses the file @file{physiology.sav} previously
+encountered.
+A researcher suspected that the heights and core body 
+temperature of persons might be different depending upon their sex.
+To investigate this, he posed two null hypotheses: 
+@itemize @bullet
+@item The mean heights of males and females in the population are equal.
+@item The mean body temperature of males and
+      females in the population are equal.
+@end itemize
+@noindent
+For the purposes of the investigation the researcher
+decided to use a  p-value of 0.05.
+
+In addition to the T-test, the @cmd{T-TEST} command also performs the 
+Levene test for equal variances.
+If the variances are equal, then a more powerful form of the T-test can be used.
+However if it is unsafe to assume equal variances,
+then an alternative calculation is necessary.
+PSPP performs both calculations.
+
+For the @var{height} variable, the output shows the significance of the 
+Levene test to be 0.33 which means there is a 
+33% probability that the  
+Levene test produces this outcome when the variances are unequal.
+Such a probability is too high
+to assume that the variances are equal so the row
+for unequal variances should be used.
+Examining this row, the two tailed significance for the @var{height} t-test 
+is less than 0.05, so it is safe to reject the null hypothesis and conclude
+that the mean heights of males and females are unequal.
+
+For the @var{temperature} variable, the significance of the Levene test 
+is 0.58 so again, it is unsafe to use the row for equal variances.
+The unequal variances row indicates that the two tailed significance for
+@var{temperature} is 0.19.  Since this is greater than 0.05 we must reject
+the null hypothesis and conclude that there is insufficient evidence to 
+suggest that the body temperature of male and female persons are different.
+
+@float Example, t-test
+@cartouche
+@example
+@prompt{PSPP>} get file='@value{example-dir}/physiology.sav'.
+@prompt{PSPP>} recode height (179 = SYSMIS).
+@prompt{PSPP>} t-test group=sex(0,1) /variables = height temperature.
+@end example
+Output:
+@example
+1.1 T-TEST.  Group Statistics
+#==================#==#=======#==============#========#
+#              sex | N|  Mean |Std. Deviation|SE. Mean#
+#==================#==#=======#==============#========#
+#height      Male  |22|1796.49|         49.71|   10.60#
+#            Female|17|1610.77|         25.43|    6.17#
+#temperature Male  |22|  36.68|          1.95|     .42#
+#            Female|18|  37.43|          1.61|     .38#
+#==================#==#=======#==============#========#
+1.2 T-TEST.  Independent Samples Test
+#===========================#=========#===============================   =#
+#                           # Levene's| t-test for Equality of Means      #
+#                           #----+----+------+-----+------+---------+-   -#
+#                           #    |    |      |     |      |         |     #
+#                           #    |    |      |     |Sig. 2|         |     #
+#                           #  F |Sig.|   t  |  df |tailed|Mean Diff|     #
+#===========================#====#====#======#=====#======#=========#=   =#
+#height      Equal variances# .97| .33| 14.02|37.00|   .00|   185.72| ... #
+#          Unequal variances#    |    | 15.15|32.71|   .00|   185.72| ... #
+#temperature Equal variances# .31| .58| -1.31|38.00|   .20|     -.75| ... #
+#          Unequal variances#    |    | -1.33|37.99|   .19|     -.75| ... #
+#===========================#====#====#======#=====#======#=========#=   =#
+@end example                                                          
+@end cartouche
+@caption{The @cmd{T-TEST} command tests for differences of means. 
+Here, the @var{height} variable's two tailed significance is less than
+0.05, so the null hypothesis can be rejected.
+Thus, the evidence suggests there is a difference between the heights of
+male and female persons. 
+However the significance of the test for the @var{temperature}
+variable is greater than 0.05 so the null hypothesis cannot be
+rejected, and there is insufficient evidence to suggest a difference
+in body temperature.}
+@end float
+
+@node Linear Regression
+@subsection Linear Regression
+@cindex linear regression
+@vindex REGRESSION
+
+Linear regression is a technique used to investigate if and how a variable 
+is linearly related to others.
+If a variable is found to be linearly related, then this can be used to 
+predict future values of that variable.
+
+In example @ref{regression}, the service department of the company wanted to
+be able to predict the time to repair equipment, in order to improve
+the accuracy of their quotations.
+It was suggested that the time to repair might be related to the time
+between failures and the duty cycle of the equipment.
+The p-value of 0.1 was chosen for this investigation.
+In order to investigate this hypothesis, the @cmd{REGRESSION} command
+was used.
+This command not only tests if the variables are related, but also
+identifies the potential linear relationship. @xref{REGRESSION}.
+
+
+@float Example, regression 
+@cartouche
+@example
+@prompt{PSPP>} get file='@value{example-dir}/repairs.sav'.
+@prompt{PSPP>} regression /variables = mtbf duty_cycle /dependent = mttr.
+@prompt{PSPP>} regression /variables = mtbf /dependent = mttr.
+@end example
+Output:
+@example
+1.3(1) REGRESSION.  Coefficients
+#=============================================#====#==========#====#=====#
+#                                             #  B |Std. Error|Beta|  t  #
+#========#====================================#====#==========#====#=====#
+#        |(Constant)                          #9.81|      1.50| .00| 6.54#
+#        |Mean time between failures (months) #3.10|       .10| .99|32.43#
+#        |Ratio of working to non-working time#1.09|      1.78| .02|  .61#
+#        |                                    #    |          |    |     #
+#========#====================================#====#==========#====#=====#
+
+1.3(2) REGRESSION.  Coefficients
+#=============================================#============#
+#                                             #Significance#
+#========#====================================#============#
+#        |(Constant)                          #         .10#
+#        |Mean time between failures (months) #         .00#
+#        |Ratio of working to non-working time#         .55#
+#        |                                    #            #
+#========#====================================#============#
+2.3(1) REGRESSION.  Coefficients
+#============================================#=====#==========#====#=====#
+#                                            #  B  |Std. Error|Beta|  t  #
+#========#===================================#=====#==========#====#=====#
+#        |(Constant)                         #10.50|       .96| .00|10.96#
+#        |Mean time between failures (months)# 3.11|       .09| .99|33.39#
+#        |                                   #     |          |    |     #
+#========#===================================#=====#==========#====#=====#
+
+2.3(2) REGRESSION.  Coefficients
+#============================================#============#
+#                                            #Significance#
+#========#===================================#============#
+#        |(Constant)                         #         .06#
+#        |Mean time between failures (months)#         .00#
+#        |                                   #            #
+#========#===================================#============#
+@end example
+@end cartouche
+@caption{Linear regression analysis to find a predictor for
+@var{mttr}.
+The first attempt, including @var{duty_cycle}, produces some
+unacceptable high significance values.
+However the second attempt, which excludes @var{duty_cycle}, produces
+significance values no higher than 0.06.
+This suggests that @var{mtbf} alone may be a suitable predictor
+for @var{mttr}.}
+@end float
+
+The coefficients in the first table suggest that the formula
+@math{@var{mttr} = 9.81 + 3.1 \times @var{mtbf} + 1.09 \times @var{duty_cycle}}
+can be used to predict the time to repair.
+However, the significance value for the @var{duty_cycle} coefficient
+is very high, which would make this an unsafe predictor.
+For this reason, the test was repeated, but omitting the
+@var{duty_cycle} variable.
+This time, the significance of all coefficients no higher than 0.06,
+suggesting that at the 0.06 level, the formula
+@math{@var{mttr} = 10.5 + 3.11 \times @var{mtbf}} is a reliable
+predictor of the time to repair.
+
+
+@c  LocalWords:  PSPP dir itemize noindent var cindex dfn cartouche samp xref
+@c  LocalWords:  pxref ie sav Std Dev kilograms SYSMIS sansserif pre pspp emph
+@c  LocalWords:  Likert Cronbach's Cronbach mtbf npplot ln myfile cmd NPAR Sig
+@c  LocalWords:  vindex Levene Levene's df Diff clicksequence mydata dat ascii
+@c  LocalWords:  mttr outfile
index 35ea20e2131d42cb4e660db4d29973a2a7408cea..6882aa2165f525aa2b12ae466da5d762eabe95af 100644 (file)
@@ -374,8 +374,10 @@ SET
         /COMPRESSION=@{ON,OFF@}
         /SCOMPRESSION=@{ON,OFF@}
 
-(security)
+(miscellaneous)
         /SAFER=ON
+        /LOCALE='string'
+
 
 (obsolete settings accepted for compatibility, but ignored)
         /BOXSTRING=@{'xxx','xxxxxxxxxxx'@}
@@ -414,10 +416,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
@@ -698,6 +703,37 @@ Be aware that this setting does not guarantee safety (commands can still
 overwrite files, for instance) but it is an improvement.
 When set, this setting cannot be reset during the same session, for
 obvious security reasons.
+
+@item LOCALE
+@cindex locale
+@cindex encoding, characters
+This item is used to set the default character encoding.
+The encoding may be specified either as an encoding name or alias
+(see @url{http://www.iana.org/assignments/character-sets}), or
+as a locale name.
+If given as a locale name, only the character encoding of the 
+locale is relevant.
+
+System files written by PSPP will use this encoding.
+System files read by PSPP, for which the encoding is unknown, will be
+interpreted using this encoding.
+
+The full list of valid encodings and locale names/alias are operating system
+dependent.
+The following are all examples of acceptable syntax on common GNU/Linux
+systems.
+@example
+
+SET LOCALE='iso-8859-1'.
+
+SET LOCALE='ru_RU.cp1251'.
+
+SET LOCALE='japanese'.
+
+@end example
+
+Contrary to the intuition, this command does not affect any aspect 
+of the system's locale.
 @end table
 
 @node SHOW
@@ -784,4 +820,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..c9e270bfc242b6d8fb5e5fa6df3c12d6d097e7a9 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
@@ -200,9 +212,10 @@ As part of a range, LO or LOWEST may take the place of num1;
 HI or HIGHEST may take the place of num2.
 @end display
 
-@cmd{MISSING VALUES} sets user-missing values for numeric and
-short string variables.  Long string variables may not have missing
-values.
+@cmd{MISSING VALUES} sets user-missing values for numeric and string
+variables.  Long string variables may have missing values, but
+characters after the first 8 bytes of the missing value must be
+spaces.
 
 Specify a list of variables, followed by a list of their user-missing
 values in parentheses.  Up to three discrete values may be given, or,
@@ -330,8 +343,7 @@ stand for a long value.
 
 To set up value labels for a set of variables, specify the
 variable names after a slash (@samp{/}), followed by a list of values
-and their associated labels, separated by spaces.  Long string
-variables may not be specified.
+and their associated labels, separated by spaces.
 
 Before @cmd{VALUE LABELS} is executed, any existing value labels
 are cleared from the variables specified.  Use @cmd{ADD VALUE LABELS}
@@ -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
index cdbec4bbf35bcf6542c806dc2f1651d8f9695182..b69470a41b82fe2b1e432fd513da856d41bdc433 100644 (file)
@@ -1,8 +1,14 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-EXTRA_DIST += \
+
+examplesdir = $(pkgdatadir)/examples
+
+examples_DATA = \
        examples/descript.stat \
+       examples/hotel.sav \
+       examples/physiology.sav \
+       examples/repairs.sav \
        examples/regress.stat \
        examples/regress_categorical.stat
 
-EXTRA_DIST += examples/OChangeLog
+EXTRA_DIST += examples/OChangeLog $(examples_DATA)
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.
diff --git a/examples/hotel.sav b/examples/hotel.sav
new file mode 100644 (file)
index 0000000..49f6318
Binary files /dev/null and b/examples/hotel.sav differ
diff --git a/examples/physiology.sav b/examples/physiology.sav
new file mode 100644 (file)
index 0000000..005a88c
Binary files /dev/null and b/examples/physiology.sav differ
diff --git a/examples/repairs.sav b/examples/repairs.sav
new file mode 100644 (file)
index 0000000..a199162
Binary files /dev/null and b/examples/repairs.sav differ
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..29eba93
--- /dev/null
@@ -0,0 +1,5534 @@
+/*
+   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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
+    ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
+
+  if (sheet->select_status == 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, 3, 3};
+
+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->select_status = 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->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->select_status;
+
+  if (sheet->select_status == 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->select_status != PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_real_unselect_range (sheet, NULL);
+
+  sheet->select_status = 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;
+
+  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->select_status != PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_real_unselect_range (sheet, NULL);
+
+  sheet->select_status = 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;
+
+  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);
+}
+
+
+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 = MAX (sheet->cell_padding->left,
+                          MAX (sheet->cell_padding->right,
+                               MAX (sheet->cell_padding->top,
+                                    sheet->cell_padding->bottom)));
+
+  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->button->style = gtk_style_attach (sheet->button->style,
+                                          sheet->sheet_window);
+
+
+  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);
+
+  gtk_widget_unmap (sheet->entry_widget);
+  gtk_widget_unmap (sheet->button);
+  gtk_widget_unmap (sheet->hover_window->window);
+}
+
+/* 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->select_status != PSPPIRE_SHEET_NORMAL &&
+      psppire_sheet_range_isvisible (sheet, &sheet->range))
+    psppire_sheet_range_draw_selection (sheet, drawing_range);
+
+
+  if (sheet->select_status == 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->select_status;
+  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->select_status != 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->select_status != PSPPIRE_SHEET_NORMAL)
+    {
+      sheet->select_status = PSPPIRE_SHEET_NORMAL;
+      psppire_sheet_real_unselect_range (sheet, NULL);
+    }
+
+  old_row = sheet->active_cell.row;
+  old_col = sheet->active_cell.col;
+
+  entry_load_text (sheet);
+
+  /* Erase the old cell border */
+  psppire_sheet_draw_active_cell (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->select_status != 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);
+
+  area.width ++;
+  area.height ++;
+
+  gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
+
+  area.x += sheet->cell_padding->left / 2;
+  area.y += sheet->cell_padding->top / 2;
+  area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
+  area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
+
+  gdk_draw_rectangle (sheet->sheet_window,  sheet->xor_gc,
+                     FALSE,
+                     area.x,
+                     area.y,
+                     area.width,
+                     area.height);
+
+  gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
+}
+
+
+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->select_status;
+
+#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->select_status != 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->selection_cell.row = range->rowi;
+  sheet->selection_cell.col = range->coli;
+
+  sheet->select_status = 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->select_status = GTK_STATE_NORMAL;
+}
+
+
+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->select_status != 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;
+
+
+  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 (psppire_sheet_click_cell (sheet, row, column))
+       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))
+       {
+         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))
+       {
+         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->select_status == 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;
+      psppire_sheet_select_range (sheet, NULL);
+      return TRUE;
+    }
+
+  if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
+    {
+      sheet->select_status = PSPPIRE_SHEET_NORMAL;
+      psppire_sheet_real_unselect_range (sheet, NULL);
+    }
+  else
+    {
+      change_active_cell (sheet, row, 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->select_status = 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->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);
+
+      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->select_status == GTK_STATE_NORMAL) sheet->select_status = 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->select_status == 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;
+
+  gdk_threads_enter ();
+  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);
+       }
+    }
+
+  gdk_threads_leave ();
+  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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
+      if (sheet->select_status == 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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
+      if (sheet->select_status == 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->select_status == 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;
+
+  if (event->type == GDK_LEAVE_NOTIFY)
+    gtk_widget_hide (sheet->hover_window->window);
+
+  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->select_status;
+
+  switch (sheet->select_status)
+    {
+    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->select_status = 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->select_status = 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.x += sheet->cell_padding->left;
+  entry_alloc.y += sheet->cell_padding->right;
+  entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
+  entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
+
+
+  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),
+                NULL,
+                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),
+                  NULL,
+                  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),
+                        NULL,
+                        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..e3f77aa
--- /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->select_status */
+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 select_status;
+
+  /* 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..36300cc
--- /dev/null
@@ -0,0 +1,732 @@
+/* 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/vardict.h>
+#include <data/value-labels.h>
+#include <data/format.h>
+#include <data/data-in.h>
+#include <data/data-out.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);
+       int width = var_get_width (var);
+       value_set_missing (val, width);
+       memcpy (value_str_rw (val, width), 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
+    {
+      int width = var_get_width (var);
+      return newSVpvn (value_str (val, width), width);
+    }
+}
+
+
+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 void
+make_value_from_scalar (union value *uv, SV *val, const struct variable *var)
+{
+ value_init (uv, var_get_width (var));
+ scalar_to_value (uv, val, var);
+}
+
+
+MODULE = PSPP
+
+MODULE = PSPP          PACKAGE = PSPP
+
+void
+onBoot (ver)
+ const char *ver
+CODE:
+ assert (0 == strcmp (ver, bare_version));
+ i18n_init ();
+ 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);
+ const struct dictionary *dict = var_get_vardict (var)->dict;
+ union value uv;
+ char *s;
+ make_value_from_scalar (&uv, val, var);
+ s = data_out (&uv, dict_get_encoding (dict), fmt);
+ value_destroy (&uv, var_get_width (var));
+ 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;
+ int ret;
+ make_value_from_scalar (&uv, val, var);
+ ret = var_is_value_missing (var, &uv, MV_ANY);
+ value_destroy (&uv, var_get_width (var));
+ 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);
+
+SV *
+get_write_format (var)
+ struct variable *var
+CODE:
+ HV *fmthash = (HV *) sv_2mortal ((SV *) newHV());
+ const struct fmt_spec *fmt = var_get_write_format (var);
+
+ hv_store (fmthash, "fmt", 3, newSVnv (fmt->type), 0);
+ hv_store (fmthash, "decimals", 8, newSVnv (fmt->d), 0);
+ hv_store (fmthash, "width", 5, newSVnv (fmt->w), 0);
+
+ RETVAL = newRV ((SV *) fmthash);
+ OUTPUT:
+RETVAL
+
+SV *
+get_print_format (var)
+ struct variable *var
+CODE:
+ HV *fmthash = (HV *) sv_2mortal ((SV *) newHV());
+ const struct fmt_spec *fmt = var_get_print_format (var);
+
+ hv_store (fmthash, "fmt", 3, newSVnv (fmt->type), 0);
+ hv_store (fmthash, "decimals", 8, newSVnv (fmt->d), 0);
+ hv_store (fmthash, "width", 5, newSVnv (fmt->w), 0);
+
+ RETVAL = newRV ((SV *) fmthash);
+ OUTPUT:
+RETVAL
+
+
+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;
+ int width = var_get_width (var);
+ int ok;
+
+ value_init (&the_value, width);
+ if ( var_is_numeric (var))
+ {
+  if ( ! looks_like_number (key))
+    {
+      sv_setpv (errstr, "Cannot add label with string key to a numeric variable");
+      value_destroy (&the_value, width);
+      XSRETURN_IV (0);
+    }
+  the_value.f = SvNV (key);
+ }
+ else
+ {
+  value_copy_str_rpad (&the_value, width, SvPV_nolen(key), ' ');
+ }
+ ok = var_add_value_label (var, &the_value, label);
+ value_destroy (&the_value, width);
+ if (!ok)
+ {
+   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());
+ const 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);
+         vl;
+         vl = val_labs_next (labels, vl))
+       {
+        SV *sv = value_to_scalar (&vl->value, var);
+        STRLEN len;
+        const char *s = SvPV (sv, len);
+        hv_store (labelhash, s, len, newSVpv (val_lab_get_label (vl), 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_proto (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,
+                       sfi->dict,
+                       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);
+   value_set_missing (val, 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..c8e9484
--- /dev/null
@@ -0,0 +1,53 @@
+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 install
+   make test
+
+For "make test" to succeed, Perl must be able to find
+libpspp-core-$VERSION.so.  It can do so if "make install" has been run
+(as shown above), or if LD_LIBRARY_PATH points to it (e.g. in
+src/.libs).  Running "make check" from the top-level build directory
+will automatically set LD_LIBRARY_PATH.
+
+
+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..e559990
--- /dev/null
@@ -0,0 +1,560 @@
+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 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 get_write_format ()
+
+Returns a reference to a hash containing the write format for the variable.
+
+
+=head3 get_print_format ()
+
+Returns a reference to a hash containing the print format for the variable.
+
+=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 according 
+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..92180ab
--- /dev/null
@@ -0,0 +1,626 @@
+# -*-perl-*-
+# Before `make install' is performed this script should be runnable
+# with `make test' as long as libpspp-core-$VERSION.so is in
+# LD_LIBRARY_PATH.  After `make install' it should work as `perl
+# PSPP.t'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test::More tests => 36;
+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, "Value label for long string");
+
+      $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                             |        |
+|          +---------+-----------------------------------+        |
+|          |xxx      |xfoo                               |        |
++----------+---------+-----------------------------------+--------+
+
+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 517475d079be73d20a07768d4e6790f76392c042..ce3dc8da5ea9040e7ed46e0a63f6c30b6318b927 100644 (file)
@@ -1,2 +1,2 @@
 # Available languages
-en_GB
+en_GB nl
index 2c3bc865a77dfb02f95900927fc4bba92bdd3609..6d0a4a7556c090955a7906707ea9bc34db575992 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-07-17 15:23+0800\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"
@@ -50,156 +49,135 @@ msgid ""
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgstr ""
 
-#: src/data/case-tmpfile.c:57
-#, c-format
-msgid "failed to create temporary file"
-msgstr ""
-
-#: src/data/case-tmpfile.c:131
-#, c-format
-msgid "seeking in temporary file"
-msgstr ""
-
-#: src/data/case-tmpfile.c:153
-#, c-format
-msgid "reading temporary file"
-msgstr ""
-
-#: src/data/case-tmpfile.c:155
-#, c-format
-msgid "unexpected end of file reading temporary file"
-msgstr ""
-
-#: src/data/case-tmpfile.c:175
-#, c-format
-msgid "writing to temporary file"
-msgstr ""
-
-#: src/data/data-in.c:262 src/data/data-in.c:452
+#: src/data/data-in.c:263 src/data/data-in.c:453
 msgid "Field contents are not numeric."
 msgstr ""
 
-#: src/data/data-in.c:264 src/data/data-in.c:454
+#: src/data/data-in.c:265 src/data/data-in.c:455
 msgid "Number followed by garbage."
 msgstr ""
 
-#: src/data/data-in.c:275
+#: src/data/data-in.c:276
 msgid "Invalid numeric syntax."
 msgstr ""
 
-#: src/data/data-in.c:284 src/data/data-in.c:467
+#: src/data/data-in.c:285 src/data/data-in.c:468
 msgid "Too-large number set to system-missing."
 msgstr ""
 
-#: src/data/data-in.c:289 src/data/data-in.c:472
+#: src/data/data-in.c:290 src/data/data-in.c:473
 msgid "Too-small number set to zero."
 msgstr ""
 
-#: src/data/data-in.c:315
+#: src/data/data-in.c:316
 msgid "All characters in field must be digits."
 msgstr ""
 
-#: src/data/data-in.c:338
+#: src/data/data-in.c:339
 msgid "Unrecognized character in field."
-msgstr ""
+msgstr "Unrecognised character in field."
 
-#: src/data/data-in.c:362 src/data/data-in.c:636
+#: src/data/data-in.c:363 src/data/data-in.c:638
 msgid "Field must have even length."
 msgstr ""
 
-#: src/data/data-in.c:367 src/data/data-in.c:647
+#: src/data/data-in.c:368 src/data/data-in.c:649
 msgid "Field must contain only hex digits."
 msgstr ""
 
-#: src/data/data-in.c:686 src/data/data-in.c:733
+#: src/data/data-in.c:688 src/data/data-in.c:735
 msgid "Syntax error in date field."
 msgstr ""
 
-#: src/data/data-in.c:702
+#: src/data/data-in.c:704
 #, c-format
 msgid "Day (%ld) must be between 1 and 31."
 msgstr ""
 
-#: src/data/data-in.c:749
+#: src/data/data-in.c:751
 msgid "Delimiter expected between fields in date."
 msgstr ""
 
-#: src/data/data-in.c:823
+#: src/data/data-in.c:825
 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
+#: src/data/data-in.c:852
 #, c-format
 msgid "Year (%ld) must be between 1582 and 19999."
 msgstr ""
 
-#: src/data/data-in.c:862
+#: src/data/data-in.c:864
 #, c-format
 msgid "Trailing garbage \"%.*s\" following date."
 msgstr ""
 
-#: src/data/data-in.c:878
+#: src/data/data-in.c:880
 msgid "Julian day must have exactly three digits."
 msgstr ""
 
-#: src/data/data-in.c:883
+#: src/data/data-in.c:885
 #, c-format
 msgid "Julian day (%ld) must be between 1 and 366."
 msgstr ""
 
-#: src/data/data-in.c:907
+#: src/data/data-in.c:909
 #, c-format
 msgid "Quarter (%ld) must be between 1 and 4."
 msgstr ""
 
-#: src/data/data-in.c:927
+#: src/data/data-in.c:929
 #, c-format
 msgid "Week (%ld) must be between 1 and 53."
 msgstr ""
 
-#: src/data/data-in.c:940
+#: src/data/data-in.c:942
 msgid "Delimiter expected between fields in time."
 msgstr ""
 
-#: src/data/data-in.c:960
+#: src/data/data-in.c:962
 #, c-format
 msgid "Minute (%ld) must be between 0 and 59."
 msgstr ""
 
-#: src/data/data-in.c:1000
+#: src/data/data-in.c:1002
 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
+#: src/data/data-in.c:1140
 #, c-format
 msgid "`%c' expected in date field."
 msgstr ""
 
-#: src/data/data-in.c:1179
+#: src/data/data-in.c:1181
 #, c-format
 msgid "column %d"
 msgstr ""
 
-#: src/data/data-in.c:1181
+#: src/data/data-in.c:1183
 #, c-format
 msgid "columns %d-%d"
 msgstr ""
 
-#: src/data/data-in.c:1185
+#: src/data/data-in.c:1187
 #, c-format
 msgid "%s field) "
 msgstr ""
 
-#: src/data/data-out.c:446
+#: src/data/data-out.c:449
 #, c-format
 msgid "Weekday number %f is not between 1 and 7."
 msgstr ""
 
-#: src/data/data-out.c:467
+#: src/data/data-out.c:470
 #, c-format
 msgid "Month number %f is not between 1 and 12."
 msgstr ""
@@ -216,13 +194,13 @@ msgstr ""
 msgid "scratch"
 msgstr ""
 
-#: src/data/dictionary.c:882
+#: src/data/dictionary.c:940
 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:1263
 #, c-format
 msgid "Truncating document line to %d bytes."
 msgstr ""
@@ -247,16 +225,16 @@ msgstr ""
 msgid "searching for \"%s\" in path \"%s\""
 msgstr ""
 
-#: src/data/file-name.c:145
+#: src/data/file-name.c:146
 #, c-format
 msgid "...found \"%s\""
 msgstr ""
 
-#: src/data/file-name.c:152
+#: src/data/file-name.c:153
 msgid "...not found"
 msgstr ""
 
-#: src/data/file-name.c:242
+#: src/data/file-name.c:243
 #, c-format
 msgid "Not opening pipe file `%s' because SAFER option set."
 msgstr ""
@@ -319,33 +297,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:663
+#: src/ui/gui/psppire.glade:2009 src/ui/gui/psppire-var-store.c:584
+#: src/ui/gui/var-sheet-dialogs.glade:139
 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:663
+#: src/ui/gui/psppire.glade:2084 src/ui/gui/psppire-var-store.c:577
+#: src/ui/gui/var-sheet-dialogs.glade:28
 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:1228
+#: src/data/sys-file-reader.c:1230
 #: 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:490 src/language/xforms/recode.c:491
+#: src/language/xforms/recode.c:503 src/language/xforms/recode.c:504
 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:1228
+#: src/data/sys-file-reader.c:1230
 #: 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:490 src/language/xforms/recode.c:491
+#: src/language/xforms/recode.c:503 src/language/xforms/recode.c:504
 msgid "string"
 msgstr ""
 
@@ -354,27 +332,27 @@ msgstr ""
 msgid "String variable with width %d is not compatible with format %s."
 msgstr ""
 
-#: src/data/gnumeric-reader.c:33
+#: src/data/gnumeric-reader.c:36
 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:368
 #, c-format
-msgid "Error opening \"%s\" for reading as a gnumeric file: %s."
+msgid "Error opening \"%s\" for reading as a Gnumeric file: %s."
 msgstr ""
 
-#: src/data/gnumeric-reader.c:386
+#: src/data/gnumeric-reader.c:388
 #, 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:520 src/data/psql-reader.c:187
 #, c-format
 msgid "Cannot create variable name from %s"
 msgstr ""
 
-#: src/data/gnumeric-reader.c:535
+#: src/data/gnumeric-reader.c:532
 #, c-format
 msgid "Selected sheet or range of spreadsheet \"%s\" is empty."
 msgstr ""
@@ -445,118 +423,118 @@ msgstr ""
 
 #. TRANSLATORS: this fragment will be interpolated into
 #. messages in fh_lock() that identify types of files.
-#: src/data/por-file-reader.c:268 src/data/por-file-writer.c:148
+#: src/data/por-file-reader.c:267 src/data/por-file-writer.c:149
 msgid "portable file"
 msgstr ""
 
-#: src/data/por-file-reader.c:276
+#: src/data/por-file-reader.c:275
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
-#: src/data/por-file-reader.c:297
+#: src/data/por-file-reader.c:296
 msgid "Data record expected."
 msgstr ""
 
-#: src/data/por-file-reader.c:379
+#: src/data/por-file-reader.c:378
 msgid "Number expected."
 msgstr ""
 
-#: src/data/por-file-reader.c:407
+#: src/data/por-file-reader.c:406
 msgid "Missing numeric terminator."
 msgstr ""
 
-#: src/data/por-file-reader.c:430
+#: src/data/por-file-reader.c:429
 msgid "Invalid integer."
 msgstr ""
 
-#: src/data/por-file-reader.c:441
+#: src/data/por-file-reader.c:440
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:502
+#: src/data/por-file-reader.c:501
 #, c-format
 msgid "%s: Not a portable file."
 msgstr ""
 
-#: src/data/por-file-reader.c:519
+#: src/data/por-file-reader.c:518
 #, c-format
 msgid "Unrecognized version code `%c'."
-msgstr ""
+msgstr "Unrecognised version code `%c'."
 
-#: src/data/por-file-reader.c:528
+#: src/data/por-file-reader.c:527
 #, c-format
 msgid "Bad date string length %zu."
 msgstr ""
 
-#: src/data/por-file-reader.c:530
+#: src/data/por-file-reader.c:529
 #, c-format
 msgid "Bad time string length %zu."
 msgstr ""
 
-#: src/data/por-file-reader.c:572
+#: src/data/por-file-reader.c:571
 #, c-format
 msgid ""
 "%s: Bad format specifier byte (%d).  Variable will be assigned a default "
 "format."
 msgstr ""
 
-#: src/data/por-file-reader.c:593
+#: src/data/por-file-reader.c:592
 #, c-format
 msgid "Numeric variable %s has invalid format specifier %s."
 msgstr ""
 
-#: src/data/por-file-reader.c:597
+#: src/data/por-file-reader.c:596
 #, c-format
 msgid "String variable %s with width %d has invalid format specifier %s."
 msgstr ""
 
-#: src/data/por-file-reader.c:621
+#: src/data/por-file-reader.c:620
 msgid "Expected variable count record."
 msgstr ""
 
-#: src/data/por-file-reader.c:625
+#: src/data/por-file-reader.c:624
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:635
+#: src/data/por-file-reader.c:633
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr ""
 
-#: src/data/por-file-reader.c:650
+#: src/data/por-file-reader.c:648
 msgid "Expected variable record."
 msgstr ""
 
-#: src/data/por-file-reader.c:654
+#: src/data/por-file-reader.c:652
 #, c-format
 msgid "Invalid variable width %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:662
+#: src/data/por-file-reader.c:659
 #, c-format
 msgid "Invalid variable name `%s' in position %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:666
+#: src/data/por-file-reader.c:663 src/data/sys-file-reader.c:521
 #, c-format
 msgid "Bad width %d for variable %s."
 msgstr ""
 
-#: src/data/por-file-reader.c:681
+#: src/data/por-file-reader.c:678
 #, c-format
 msgid "Duplicate variable name %s in position %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:682
+#: src/data/por-file-reader.c:679
 #, c-format
 msgid "Duplicate variable name %s in position %d renamed to %s."
 msgstr ""
 
-#: src/data/por-file-reader.c:725
+#: src/data/por-file-reader.c:728
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr ""
@@ -572,56 +550,56 @@ msgid ""
 "Cannot assign value labels to %s and %s, which have different variable types."
 msgstr ""
 
-#: src/data/por-file-writer.c:140
+#: src/data/por-file-writer.c:141
 #, c-format
 msgid "Invalid decimal digits count %d.  Treating as %d."
 msgstr ""
 
-#: src/data/por-file-writer.c:160
+#: src/data/por-file-writer.c:161
 #, c-format
 msgid "Error opening \"%s\" for writing as a portable file: %s."
 msgstr ""
 
-#: src/data/por-file-writer.c:499
+#: src/data/por-file-writer.c:506
 #, c-format
 msgid "An I/O error occurred writing portable file \"%s\"."
 msgstr ""
 
-#: src/data/psql-reader.c:42
+#: src/data/psql-reader.c:46
 msgid ""
 "Support for reading postgres databases was not compiled into this "
 "installation of PSPP"
 msgstr ""
 
-#: src/data/psql-reader.c:239
+#: src/data/psql-reader.c:242
 msgid "Memory error whilst opening psql source"
 msgstr ""
 
-#: src/data/psql-reader.c:245
+#: src/data/psql-reader.c:248
 #, c-format
 msgid "Error opening psql source: %s."
 msgstr ""
 
-#: src/data/psql-reader.c:260
+#: src/data/psql-reader.c:263
 #, 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:283
 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:322 src/data/psql-reader.c:347
+#: src/data/psql-reader.c:357
 #, c-format
 msgid "Error from psql source: %s."
 msgstr ""
 
-#: src/data/psql-reader.c:437
+#: src/data/psql-reader.c:452
 #, c-format
 msgid "Unsupported OID %d.  SYSMIS values will be inserted."
 msgstr ""
@@ -635,11 +613,11 @@ msgstr ""
 
 #. TRANSLATORS: this fragment will be interpolated into
 #. messages in fh_lock() that identify types of files.
-#: src/data/scratch-writer.c:67 src/language/data-io/file-handle.q:181
+#: src/data/scratch-writer.c:66 src/language/data-io/file-handle.q:181
 msgid "scratch file"
 msgstr ""
 
-#: src/data/settings.c:685
+#: src/data/settings.c:686
 #, c-format
 msgid ""
 "%s: Custom currency string `%s' does not contain exactly three periods or "
@@ -652,359 +630,379 @@ 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:219 src/data/sys-file-writer.c:202
 msgid "system file"
 msgstr ""
 
-#: src/data/sys-file-reader.c:205
+#: src/data/sys-file-reader.c:226
 #, 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:265
 msgid "Misplaced type 4 record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:255
+#: src/data/sys-file-reader.c:276
 #, 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:315
 #, 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:355
 #, 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:420 src/data/sys-file-reader.c:430
 msgid "This is not an SPSS system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:428
+#: src/data/sys-file-reader.c:449
 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:517
 #, c-format
 msgid "Invalid variable name `%s'."
 msgstr ""
 
-#: src/data/sys-file-reader.c:500
-#, c-format
-msgid "Bad variable width %d."
-msgstr ""
-
-#: src/data/sys-file-reader.c:504
+#: src/data/sys-file-reader.c:525
 #, c-format
 msgid "Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:512
+#: src/data/sys-file-reader.c:533
 msgid "Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/data/sys-file-reader.c:520
+#: src/data/sys-file-reader.c:541
 #, 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:560
 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:578
 msgid "String missing value indicator field is not 0, 1, 2, or 3."
 msgstr ""
 
-#: src/data/sys-file-reader.c:557
-#, 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:610
 msgid "Missing string continuation record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:620
+#: src/data/sys-file-reader.c:644
 #, c-format
 msgid "Unknown variable format %<PRIu8>."
 msgstr ""
 
-#: src/data/sys-file-reader.c:638
+#: src/data/sys-file-reader.c:662
 #, 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:665
 msgid "print"
 msgstr ""
 
-#: src/data/sys-file-reader.c:641
+#: src/data/sys-file-reader.c:665
 msgid "write"
 msgstr ""
 
-#: src/data/sys-file-reader.c:645
+#: src/data/sys-file-reader.c:669
 msgid "Suppressing further invalid format warnings."
 msgstr ""
 
-#: src/data/sys-file-reader.c:663
+#: src/data/sys-file-reader.c:687
 msgid "Weighting variable must be numeric."
 msgstr ""
 
-#: src/data/sys-file-reader.c:677
+#: src/data/sys-file-reader.c:701
 msgid "Multiple type 6 (document) records."
 msgstr ""
 
-#: src/data/sys-file-reader.c:681
+#: src/data/sys-file-reader.c:705
 #, 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:713
 msgid "Document line contains null byte."
 msgstr ""
 
-#: src/data/sys-file-reader.c:763
-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:803
 #, c-format
-msgid "Unrecognized record type 7, subtype %d."
+msgid ""
+"Unrecognized record type 7, subtype %d.  Please send a copy of this file, "
+"and the syntax which created it to %s"
 msgstr ""
 
-#: src/data/sys-file-reader.c:793
+#: src/data/sys-file-reader.c:830
 #, 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:850
 #, 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:863
 msgid "little-endian"
 msgstr ""
 
-#: src/data/sys-file-reader.c:826
+#: src/data/sys-file-reader.c:863
 msgid "big-endian"
 msgstr ""
 
-#: src/data/sys-file-reader.c:827
+#: src/data/sys-file-reader.c:864
 #, 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:921
 #, c-format
 msgid "Bad size (%zu) or count (%zu) on extension 4."
 msgstr ""
 
-#: src/data/sys-file-reader.c:847
-#, c-format
-msgid "File specifies unexpected value %g as SYSMIS."
-msgstr ""
-
-#: src/data/sys-file-reader.c:849
-#, c-format
-msgid "File specifies unexpected value %g as HIGHEST."
-msgstr ""
-
-#: src/data/sys-file-reader.c:851
+#: src/data/sys-file-reader.c:925 src/data/sys-file-reader.c:929
+#: src/data/sys-file-reader.c:933
 #, c-format
-msgid "File specifies unexpected value %g as LOWEST."
+msgid "File specifies unexpected value %g as %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:867
+#: src/data/sys-file-reader.c:950
 #, c-format
 msgid "Bad size %zu on extension 11."
 msgstr ""
 
-#: src/data/sys-file-reader.c:879
+#: src/data/sys-file-reader.c:962
 #, 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:983
 #, 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:1027
 #, 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:1037
 #, 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:1090
 #, 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:1100
 #, 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:1106
 #, c-format
 msgid "Very long string %s overflows dictionary."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1041
+#: src/data/sys-file-reader.c:1120
 #, 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:1166
 #, c-format
 msgid "Invalid number of labels: %d.  Ignoring labels."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1117
+#: src/data/sys-file-reader.c:1197
 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:1204
 #, 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:1215
 #, c-format
-msgid "Value labels are not allowed on long string variables (%s)."
+msgid ""
+"Value labels may not be added to long string variables (e.g. %s) using "
+"records types 3 and 4."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1141
+#: src/data/sys-file-reader.c:1224
 #, 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:1258
 #, c-format
 msgid "Duplicate value label for %g on %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1177
+#: src/data/sys-file-reader.c:1261 src/data/sys-file-reader.c:1442
 #, c-format
 msgid "Duplicate value label for \"%.*s\" on %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1255
+#: src/data/sys-file-reader.c:1299
+#, c-format
+msgid "Error parsing attribute value %s[%d]"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1313
+#, c-format
+msgid "Attribute value %s[%d] is not quoted: %s"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1376
+#, c-format
+msgid ""
+"Variable name length in long string value label record (%d) exceeds %d-byte "
+"limit."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1386
+#, c-format
+msgid "Ignoring long string value record for unknown variable %s."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1393
+#, c-format
+msgid "Ignoring long string value record for numeric variable %s."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1400
+#, c-format
+msgid ""
+"Ignoring long string value record for variable %s because the record's width "
+"(%d) does not match the variable's width (%d)"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1422
+#, c-format
+msgid ""
+"Ignoring long string value %zu for variable %s, with width %d, that has bad "
+"value width %zu."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1537
 msgid "File ends in partial case."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1263
+#: src/data/sys-file-reader.c:1545
 #, 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:1642 src/data/sys-file-reader.c:1678
 msgid "Compressed data is corrupt."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1483
+#: src/data/sys-file-reader.c:1765
 #, 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:1770
 #, 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:1838
 #, 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:1879
 #, c-format
 msgid "Variable map refers to unknown variable %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1663
+#: src/data/sys-file-reader.c:1987
 #, c-format
 msgid "System error: %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1665
+#: src/data/sys-file-reader.c:1989
 msgid "Unexpected end of file."
 msgstr ""
 
-#: src/data/sys-file-writer.c:163
+#: src/data/sys-file-writer.c:175
 #, 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:214
 #, 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:917
 #, c-format
 msgid "An I/O error occurred writing system file \"%s\"."
 msgstr ""
 
-#: src/data/variable.c:209
+#: src/data/variable.c:242
 #, 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:254
 #, c-format
 msgid "Character `%c' (in %s) may not appear in a variable name."
 msgstr ""
 
-#: src/data/variable.c:249
+#: src/data/variable.c:282
 msgid "Variable name cannot be empty string."
 msgstr ""
 
-#: src/data/variable.c:255
+#: src/data/variable.c:288
 #, c-format
 msgid "Variable name %s exceeds %d-character limit."
 msgstr ""
 
-#: src/data/variable.c:263
+#: src/data/variable.c:296
 #, c-format
 msgid "`%s' may not be used as a variable name because it is a reserved word."
 msgstr ""
 
-#: src/language/command.c:208
+#: src/language/command.c:208 src/language/expressions/parse.c:1267
 #, c-format
-msgid "%s is unimplemented."
+msgid "%s is not yet implemented."
 msgstr ""
 
 #: src/language/command.c:214
@@ -1202,105 +1200,191 @@ msgid ""
 "commands."
 msgstr ""
 
-#: src/language/data-io/data-list.c:128
+#: 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:513
+msgid ""
+"Combining files with incompatible encodings. String data may not be "
+"represented correctly."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:545
+#, 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:551
+#, c-format
+msgid "In file %s, %s is numeric."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:554
+#, c-format
+msgid "In file %s, %s is a string variable with width %d."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:559
+#, c-format
+msgid "In an earlier file, %s was numeric."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:562
+#, c-format
+msgid "In an earlier file, %s was a string variable with width %d."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:601
+#, c-format
+msgid ""
+"Variable name %s specified on %s subcommand duplicates an existing variable "
+"name."
+msgstr ""
+
+#: src/language/data-io/combine-files.c:762
+#, c-format
+msgid "Encountered %zu sets of duplicate cases in the master file."
+msgstr ""
+
+#: src/language/data-io/data-list.c:137
 msgid "The END subcommand may only be used within INPUT PROGRAM."
 msgstr ""
 
-#: src/language/data-io/data-list.c:134
+#: src/language/data-io/data-list.c:143
 msgid "The END subcommand may only be specified once."
 msgstr ""
 
-#: src/language/data-io/data-list.c:172
+#: src/language/data-io/data-list.c:181
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
-#: src/language/data-io/data-list.c:237
+#: src/language/data-io/data-list.c:243
+msgid "Encoding should not be specified for inline data. It will be ignored."
+msgstr ""
+
+#: src/language/data-io/data-list.c:254
 msgid "The END subcommand may be used only with DATA LIST FIXED."
 msgstr ""
 
-#: src/language/data-io/data-list.c:252
+#: src/language/data-io/data-list.c:269
 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/get-data.c:528
+#: src/language/data-io/data-list.c:368 src/language/data-io/data-list.c:457
+#: src/language/data-io/get-data.c:530
 #, 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:375
 #, 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:382
 #, 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:390
 #, 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:169
+#: src/language/dictionary/sys-file-info.c:393
+#: src/language/dictionary/sys-file-info.c:725
+#: src/language/stats/descriptives.c:885 src/ui/gui/psppire-dictview.c:502
 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:534
+#: src/ui/gui/psppire-var-store.c:800
 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 +1401,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,138 +1511,53 @@ 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
+#: src/language/data-io/get-data.c:64
 #, c-format
 msgid "Unsupported TYPE %s"
 msgstr ""
 
-#: src/language/data-io/get-data.c:258
+#: src/language/data-io/get-data.c:260
 #, c-format
 msgid ""
 "%s is allowed only with %s arrangement, but %s arrangement was stated or "
 "implied earlier in this command."
 msgstr ""
 
-#: src/language/data-io/get-data.c:313
+#: src/language/data-io/get-data.c:315
 msgid "expecting FIXED or DELIMITED"
 msgstr ""
 
-#: src/language/data-io/get-data.c:326
+#: src/language/data-io/get-data.c:328
 msgid "Value of FIRSTCASE must be 1 or greater."
 msgstr ""
 
-#: src/language/data-io/get-data.c:351
+#: src/language/data-io/get-data.c:353
 msgid "expecting LINE or VARIABLES"
 msgstr ""
 
-#: src/language/data-io/get-data.c:364
+#: src/language/data-io/get-data.c:366
 msgid "Value of FIXCASE must be at least 1."
 msgstr ""
 
-#: src/language/data-io/get-data.c:384
+#: src/language/data-io/get-data.c:386
 msgid "Value of FIRST must be at least 1."
 msgstr ""
 
-#: src/language/data-io/get-data.c:396
+#: src/language/data-io/get-data.c:398
 msgid "Value of PERCENT must be between 1 and 100."
 msgstr ""
 
-#: src/language/data-io/get-data.c:445
+#: src/language/data-io/get-data.c:447
 msgid ""
 "In compatible syntax mode, the QUALIFIER string must contain exactly one "
 "character."
 msgstr ""
 
-#: src/language/data-io/get-data.c:460
+#: src/language/data-io/get-data.c:462
 msgid "expecting VARIABLES"
 msgstr ""
 
-#: src/language/data-io/get-data.c:482
+#: src/language/data-io/get-data.c:484
 #: src/language/data-io/placement-parser.c:378
 #, c-format
 msgid ""
@@ -1566,26 +1565,26 @@ msgid ""
 "Data fields must be listed in order of increasing record number."
 msgstr ""
 
-#: src/language/data-io/get-data.c:491
+#: src/language/data-io/get-data.c:493
 #, c-format
 msgid ""
 "The record number specified, %ld, exceeds the number of records per case "
 "specified on FIXCASE, %d."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:129
+#: src/language/data-io/inpt-pgm.c:130
 msgid "Unexpected end-of-file within INPUT PROGRAM."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:142
+#: src/language/data-io/inpt-pgm.c:143
 msgid "Input program did not create any variables."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:287
+#: src/language/data-io/inpt-pgm.c:288
 msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:337
+#: src/language/data-io/inpt-pgm.c:338
 msgid ""
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
@@ -1622,7 +1621,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,26 +1661,30 @@ 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."
 msgstr ""
 
-#: src/language/data-io/print.c:436
+#: src/language/data-io/print.c:437
 #, c-format
 msgid "Writing %d record to %s."
 msgid_plural "Writing %d records to %s."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/language/data-io/print.c:440
+#: src/language/data-io/print.c:441
 #, c-format
 msgid "Writing %d record."
 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,26 +1698,61 @@ msgstr ""
 msgid "The expression on PRINT SPACE evaluated to %g."
 msgstr ""
 
-#: src/language/dictionary/apply-dictionary.c:75
+#: src/language/data-io/save.c:223 src/language/data-io/save.c:238
+#: src/language/data-io/save.c:266
 #, c-format
-msgid "Variable %s is %s in target file, but %s in source file."
+msgid "expecting %s or %s"
 msgstr ""
 
-#: src/language/dictionary/apply-dictionary.c:99
+#: src/language/data-io/trim.c:88
 #, c-format
-msgid "Cannot add value labels from source file to long string variable %s."
+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/dictionary/apply-dictionary.c:113
+#: src/language/data-io/trim.c:114
+msgid "`=' expected after variable list."
+msgstr ""
+
+#: src/language/data-io/trim.c:121
 #, c-format
 msgid ""
-"Cannot apply missing values from source file to long string variable %s."
+"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."
 msgstr ""
 
-#: src/language/dictionary/apply-dictionary.c:126
+#: src/language/dictionary/apply-dictionary.c:115
 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 "
@@ -1731,12 +1769,12 @@ msgstr ""
 msgid "`(' expected after variable list."
 msgstr ""
 
-#: src/language/dictionary/formats.c:100 src/language/dictionary/numeric.c:70
+#: src/language/dictionary/formats.c:100 src/language/dictionary/numeric.c:74
 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 ""
 
@@ -1747,12 +1785,12 @@ msgid ""
 "a single list."
 msgstr ""
 
-#: src/language/dictionary/missing-values.c:117
+#: src/language/dictionary/missing-values.c:116
 #, c-format
-msgid "Truncating missing value to short string length (%d characters)."
+msgid "Truncating missing value to maximum acceptable length (%d bytes)."
 msgstr ""
 
-#: src/language/dictionary/missing-values.c:139
+#: src/language/dictionary/missing-values.c:138
 #, c-format
 msgid "Missing values provided are too long to assign to variable of width %d."
 msgstr ""
@@ -1820,7 +1858,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."
@@ -1830,17 +1868,17 @@ msgstr ""
 msgid "`/' or `.' expected."
 msgstr ""
 
-#: src/language/dictionary/numeric.c:63
+#: src/language/dictionary/numeric.c:67
 #, c-format
 msgid "Format type %s may not be used with a numeric variable."
 msgstr ""
 
-#: src/language/dictionary/numeric.c:82 src/language/dictionary/numeric.c:151
+#: src/language/dictionary/numeric.c:86 src/language/dictionary/numeric.c:155
 #, c-format
 msgid "There is already a variable named %s."
 msgstr ""
 
-#: src/language/dictionary/numeric.c:136
+#: src/language/dictionary/numeric.c:140
 #, c-format
 msgid "Format type %s may not be used with a string variable."
 msgstr ""
@@ -1869,261 +1907,253 @@ 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:486
+#: src/language/dictionary/sys-file-info.c:641
+#: src/language/stats/crosstabs.q:1231 src/language/stats/crosstabs.q:1258
+#: src/language/stats/crosstabs.q:1282 src/language/stats/crosstabs.q:1307
+#: src/language/stats/examine.q:1959 src/language/stats/frequencies.q:1048
+#: src/language/stats/frequencies.q:1173 src/language/stats/reliability.q:582
+#: src/language/stats/reliability.q:593
 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:642 src/ui/gui/crosstabs.glade:275
+#: src/ui/gui/psppire.glade:1974 src/ui/gui/psppire-var-sheet.c:531
+#: src/ui/gui/psppire-var-store.c:797
 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:1913
 #: 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
+#: src/language/dictionary/sys-file-info.c:160
 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:158
+msgid "Charset:"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:170
+#: src/language/dictionary/sys-file-info.c:397
 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:171
+#: src/language/dictionary/sys-file-info.c:399
+#: src/language/dictionary/sys-file-info.c:724
 msgid "Position"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:222
+#: src/language/dictionary/sys-file-info.c:220
 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:223
 msgid "File label:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:288
+#: src/language/dictionary/sys-file-info.c:298
 msgid "No variables to display."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:307
+#: src/language/dictionary/sys-file-info.c:313
 msgid "Macros not supported."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:317
+#: src/language/dictionary/sys-file-info.c:323
 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:331
 msgid "Documents in the active file:"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:477
+#: src/language/dictionary/sys-file-info.c:485
+msgid "Attribute"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:543
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:485
+#: src/language/dictionary/sys-file-info.c:550
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:488
+#: src/language/dictionary/sys-file-info.c:554
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:494
+#: src/language/dictionary/sys-file-info.c:567
 #, 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:568
+#: src/ui/gui/psppire-var-sheet.c:111
 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:569
+#: src/ui/gui/psppire-var-sheet.c:112
 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:570
+#: src/ui/gui/psppire-var-sheet.c:113
 msgid "Scale"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:500
+#: src/language/dictionary/sys-file-info.c:573
 #, 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:574
+#: src/ui/gui/psppire-var-sheet.c:104
 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:575
+#: src/ui/gui/psppire-var-sheet.c:106
 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:576
+#: src/ui/gui/psppire-var-sheet.c:105
 msgid "Right"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:506
+#: src/language/dictionary/sys-file-info.c:579
 #, c-format
 msgid "Display Width: %d"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:517
+#: src/language/dictionary/sys-file-info.c:593
 msgid "Missing Values: "
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:611
+#: src/language/dictionary/sys-file-info.c:702
 msgid "No vectors defined."
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:632
+#: src/language/dictionary/sys-file-info.c:723
 msgid "Vector"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:635
+#: src/language/dictionary/sys-file-info.c:726
 msgid "Print Format"
 msgstr ""
 
-#: src/language/dictionary/value-labels.c:121
-#, c-format
-msgid ""
-"It is not possible to assign value labels to long string variables such as %"
-"s."
-msgstr ""
-
-#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:629
-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."
-msgstr ""
-
-#: src/language/dictionary/value-labels.c:184
+#: src/language/dictionary/value-labels.c:150
 msgid "Truncating value label to 60 characters."
 msgstr ""
 
-#: src/language/dictionary/variable-display.c:119
+#: src/language/dictionary/variable-display.c:120
 msgid "Variable display width must be a positive integer."
 msgstr ""
 
@@ -2175,11 +2205,11 @@ msgstr ""
 msgid "The weighting variable may not be scratch."
 msgstr ""
 
-#: src/language/expressions/evaluate.c:154
+#: src/language/expressions/evaluate.c:155
 msgid "expecting number or string"
 msgstr ""
 
-#: src/language/expressions/evaluate.c:168
+#: src/language/expressions/evaluate.c:169
 #, c-format
 msgid "Duplicate variable name %s."
 msgstr ""
@@ -2226,6 +2256,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 +2308,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,12 +2376,6 @@ 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
-#, c-format
-msgid "%s is not yet implemented."
-msgstr ""
-
 #: src/language/expressions/parse.c:1273
 #, c-format
 msgid "%s may not appear after TEMPORARY."
@@ -2361,114 +2389,117 @@ 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
-#, c-format
-msgid "Bad character in input: `%c'."
-msgstr ""
-
-#: src/language/lexer/lexer.c:388
+#: src/language/lexer/lexer.c:389
 #, c-format
-msgid "Bad character in input: `\\%o'."
+msgid "Bad character in input: `%s'."
 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
+#: src/language/lexer/lexer.c:631
+msgid "expecting string"
+msgstr ""
+
+#: src/language/lexer/lexer.c:645
+msgid "expecting integer"
+msgstr ""
+
+#: src/language/lexer/lexer.c:658
 msgid "expecting number"
 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 ""
 
-#: src/language/lexer/range-parser.c:60
+#: src/language/lexer/value-parser.c:60
 #, c-format
 msgid ""
 "Low end of range (%g) is below high end (%g).  The range will be treated as "
 "reversed."
 msgstr ""
 
-#: src/language/lexer/range-parser.c:68
+#: src/language/lexer/value-parser.c:68
 #, c-format
 msgid "Ends of range are equal (%g)."
 msgstr ""
 
-#: src/language/lexer/range-parser.c:76
+#: src/language/lexer/value-parser.c:76
 msgid "LO or LOWEST must be part of a range."
 msgstr ""
 
-#: src/language/lexer/range-parser.c:108
+#: src/language/lexer/value-parser.c:108
 msgid "System-missing value is not valid here."
 msgstr ""
 
-#: src/language/lexer/range-parser.c:116
+#: src/language/lexer/value-parser.c:116
 msgid "expecting number or data string"
 msgstr ""
 
@@ -2546,54 +2577,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 "
@@ -2615,385 +2646,388 @@ msgstr ""
 msgid "Duplicate variable name %s among target variables."
 msgstr ""
 
-#: src/language/stats/binomial.c:132
+#: src/language/stats/binomial.c:141
 #, c-format
 msgid "Variable %s is not dichotomous"
 msgstr ""
 
-#: src/language/stats/binomial.c:177
+#: src/language/stats/binomial.c:194
 msgid "Binomial Test"
 msgstr ""
 
-#: src/language/stats/binomial.c:201
+#: src/language/stats/binomial.c:224
 msgid "Group1"
 msgstr ""
 
-#: src/language/stats/binomial.c:202
+#: src/language/stats/binomial.c:225
 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:226 src/language/stats/chisquare.c:202
+#: src/language/stats/chisquare.c:262 src/language/stats/crosstabs.q:843
+#: src/language/stats/crosstabs.q:1170 src/language/stats/crosstabs.q:1594
+#: src/language/stats/examine.q:1216 src/language/stats/frequencies.q:1125
+#: src/language/stats/oneway.q:305 src/language/stats/oneway.q:476
+#: src/language/stats/regression.q:309 src/language/stats/reliability.q:718
+#: src/language/stats/sign.c:94 src/language/stats/wilcoxon.c:262
+#: 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:259 src/language/stats/chisquare.c:225
+#: src/language/stats/crosstabs.q:1256 src/language/stats/crosstabs.q:1304
 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:260 src/language/stats/crosstabs.q:850
+#: src/language/stats/examine.q:1289 src/language/stats/frequencies.q:1396
+#: src/language/stats/npar-summary.c:123 src/language/stats/oneway.q:389
+#: src/language/stats/reliability.q:721 src/language/stats/sign.c:74
+#: src/language/stats/t-test.q:506 src/language/stats/t-test.q:526
+#: src/language/stats/t-test.q:626 src/language/stats/t-test.q:1105
+#: src/language/stats/wilcoxon.c:245
 msgid "N"
 msgstr ""
 
-#: src/language/stats/binomial.c:237
+#: src/language/stats/binomial.c:261
 msgid "Observed Prop."
 msgstr ""
 
-#: src/language/stats/binomial.c:238
+#: src/language/stats/binomial.c:262
 msgid "Test Prop."
 msgstr ""
 
-#: src/language/stats/binomial.c:241
+#: src/language/stats/binomial.c:265
 #, c-format
 msgid "Exact Sig. (%d-tailed)"
 msgstr ""
 
-#: src/language/stats/chisquare.c:193
+#: src/language/stats/chisquare.c:172
 #, c-format
 msgid ""
 "CHISQUARE test specified %d expected values, but %d distinct values were "
 "encountered in variable %s."
 msgstr ""
 
-#: src/language/stats/chisquare.c:207 src/language/stats/chisquare.c:247
+#: src/language/stats/chisquare.c:186 src/language/stats/chisquare.c:226
 msgid "Observed N"
 msgstr ""
 
-#: src/language/stats/chisquare.c:208 src/language/stats/chisquare.c:248
+#: src/language/stats/chisquare.c:187 src/language/stats/chisquare.c:227
 msgid "Expected N"
 msgstr ""
 
-#: src/language/stats/chisquare.c:209 src/language/stats/chisquare.c:249
+#: src/language/stats/chisquare.c:188 src/language/stats/chisquare.c:228
 #: src/language/stats/regression.q:308 src/ui/gui/crosstabs-dialog.c:61
 msgid "Residual"
 msgstr ""
 
-#: src/language/stats/chisquare.c:242
+#: src/language/stats/chisquare.c:221 src/language/stats/sign.c:62
 msgid "Frequencies"
 msgstr ""
 
-#: src/language/stats/chisquare.c:297
+#: src/language/stats/chisquare.c:276 src/language/stats/sign.c:115
+#: src/language/stats/wilcoxon.c:313
 msgid "Test Statistics"
 msgstr ""
 
-#: src/language/stats/chisquare.c:311
+#: src/language/stats/chisquare.c:290
 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/regression.q:302 src/language/stats/t-test.q:1001
-#: src/language/stats/t-test.q:1193 src/language/stats/t-test.q:1286
+#: src/language/stats/chisquare.c:291 src/language/stats/crosstabs.q:1232
+#: src/language/stats/oneway.q:278 src/language/stats/oneway.q:691
+#: src/language/stats/regression.q:302 src/language/stats/t-test.q:753
+#: src/language/stats/t-test.q:924 src/language/stats/t-test.q:1011
 msgid "df"
 msgstr ""
 
-#: src/language/stats/chisquare.c:313
+#: src/language/stats/chisquare.c:292
 msgid "Asymp. Sig."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:277
+#: src/language/stats/crosstabs.q:325
 msgid ""
 "Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:287
-msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
-msgstr ""
-
-#: src/language/stats/crosstabs.q:363
+#: src/language/stats/crosstabs.q:414
 msgid "Too many cross-tabulation variables or dimensions."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:373
+#: src/language/stats/crosstabs.q:424
 msgid "expecting BY"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:440
+#: src/language/stats/crosstabs.q:484
 msgid "VARIABLES must be specified before TABLES."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:478
+#: src/language/stats/crosstabs.q:522
 #, c-format
 msgid "Maximum value (%ld) less than minimum value (%ld)."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:857
+#: src/language/stats/crosstabs.q:838
 msgid "Summary."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:859 src/language/stats/examine.q:981
+#: src/language/stats/crosstabs.q:840 src/language/stats/examine.q:1277
+#: src/language/stats/reliability.q:709
 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:841 src/language/stats/examine.q:1214
+#: src/language/stats/frequencies.q:1046 src/language/stats/frequencies.q:1397
+#: src/language/stats/reliability.q:712
 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:842 src/language/stats/examine.q:1215
+#: src/language/stats/frequencies.q:1116 src/language/stats/frequencies.q:1398
+#: src/ui/gui/psppire-var-sheet.c:533 src/ui/gui/psppire-var-store.c:799
 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:851 src/language/stats/examine.q:1292
+#: src/language/stats/frequencies.q:1050 src/language/stats/frequencies.q:1051
+#: src/language/stats/frequencies.q:1052
 msgid "Percent"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1114
+#: src/language/stats/crosstabs.q:1131
 msgid "count"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1115
+#: src/language/stats/crosstabs.q:1132
 msgid "row %"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1116
+#: src/language/stats/crosstabs.q:1133
 msgid "column %"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1117
+#: src/language/stats/crosstabs.q:1134
 msgid "total %"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1118
+#: src/language/stats/crosstabs.q:1135
 msgid "expected"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1119
+#: src/language/stats/crosstabs.q:1136
 msgid "residual"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1120
+#: src/language/stats/crosstabs.q:1137
 msgid "std. resid."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1121
+#: src/language/stats/crosstabs.q:1138
 msgid "adj. resid."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1151
+#: src/language/stats/crosstabs.q:1227
 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:1230 src/language/stats/crosstabs.q:1257
+#: src/language/stats/crosstabs.q:1281 src/language/stats/crosstabs.q:1305
+#: src/language/stats/examine.q:1753 src/ui/gui/checkbox-treeview.c:92
 msgid "Statistic"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1158
+#: src/language/stats/crosstabs.q:1234
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1160
-msgid "Exact. Sig. (2-sided)"
+#: src/language/stats/crosstabs.q:1236
+msgid "Exact Sig. (2-sided)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1162
-msgid "Exact. Sig. (1-sided)"
+#: src/language/stats/crosstabs.q:1238
+msgid "Exact Sig. (1-sided)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1177
+#: src/language/stats/crosstabs.q:1253
 msgid "Symmetric measures."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1183 src/language/stats/crosstabs.q:1225
+#: src/language/stats/crosstabs.q:1259 src/language/stats/crosstabs.q:1308
 msgid "Asymp. Std. Error"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1184 src/language/stats/crosstabs.q:1226
+#: src/language/stats/crosstabs.q:1260 src/language/stats/crosstabs.q:1309
 msgid "Approx. T"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1185 src/language/stats/crosstabs.q:1227
+#: src/language/stats/crosstabs.q:1261 src/language/stats/crosstabs.q:1310
 msgid "Approx. Sig."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1196
+#: src/language/stats/crosstabs.q:1276
 msgid "Risk estimate."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1200
+#: src/language/stats/crosstabs.q:1280
 #, c-format
 msgid "95%% Confidence Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1203 src/language/stats/t-test.q:1005
-#: src/language/stats/t-test.q:1190 src/language/stats/t-test.q:1289
+#: src/language/stats/crosstabs.q:1283 src/language/stats/t-test.q:757
+#: src/language/stats/t-test.q:921 src/language/stats/t-test.q:1014
 msgid "Lower"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1204 src/language/stats/t-test.q:1006
-#: src/language/stats/t-test.q:1191 src/language/stats/t-test.q:1290
+#: src/language/stats/crosstabs.q:1284 src/language/stats/t-test.q:758
+#: src/language/stats/t-test.q:922 src/language/stats/t-test.q:1015
 msgid "Upper"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1218
+#: src/language/stats/crosstabs.q:1301
 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:1306 src/ui/gui/psppire.glade:2099
+#: src/ui/gui/psppire-var-sheet.c:528 src/ui/gui/psppire-var-store.c:794
 msgid "Type"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1977
+#: src/language/stats/crosstabs.q:1774
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1978
+#: src/language/stats/crosstabs.q:1775
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1979
+#: src/language/stats/crosstabs.q:1776
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1980
+#: src/language/stats/crosstabs.q:1777
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1981
+#: src/language/stats/crosstabs.q:1778
 msgid "Linear-by-Linear Association"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2018 src/language/stats/crosstabs.q:2088
-#: src/language/stats/crosstabs.q:2147
+#: src/language/stats/crosstabs.q:1813 src/language/stats/crosstabs.q:1888
+#: src/language/stats/crosstabs.q:1953
 msgid "N of Valid Cases"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2034 src/language/stats/crosstabs.q:2163
+#: src/language/stats/crosstabs.q:1832 src/language/stats/crosstabs.q:1971
 msgid "Nominal by Nominal"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2035 src/language/stats/crosstabs.q:2164
+#: src/language/stats/crosstabs.q:1833 src/language/stats/crosstabs.q:1972
 msgid "Ordinal by Ordinal"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2036
+#: src/language/stats/crosstabs.q:1834
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2037
+#: src/language/stats/crosstabs.q:1835
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2042 src/ui/gui/crosstabs-dialog.c:41
+#: src/language/stats/crosstabs.q:1840 src/ui/gui/crosstabs-dialog.c:41
 msgid "Phi"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2043
+#: src/language/stats/crosstabs.q:1841
 msgid "Cramer's V"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2044
+#: src/language/stats/crosstabs.q:1842
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2045
+#: src/language/stats/crosstabs.q:1843
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2046
+#: src/language/stats/crosstabs.q:1844
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2047 src/ui/gui/crosstabs-dialog.c:48
+#: src/language/stats/crosstabs.q:1845 src/ui/gui/crosstabs-dialog.c:48
 msgid "Gamma"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2048
+#: src/language/stats/crosstabs.q:1846
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2049
+#: src/language/stats/crosstabs.q:1847
 msgid "Pearson's R"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2050 src/ui/gui/crosstabs-dialog.c:50
+#: src/language/stats/crosstabs.q:1848 src/ui/gui/crosstabs-dialog.c:50
 msgid "Kappa"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2120
+#: src/language/stats/crosstabs.q:1926
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2123
+#: src/language/stats/crosstabs.q:1929
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2131
+#: src/language/stats/crosstabs.q:1937
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2134
+#: src/language/stats/crosstabs.q:1940
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2165
+#: src/language/stats/crosstabs.q:1973
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2170 src/ui/gui/crosstabs-dialog.c:43
+#: src/language/stats/crosstabs.q:1978 src/ui/gui/crosstabs-dialog.c:43
 msgid "Lambda"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2171
+#: src/language/stats/crosstabs.q:1979
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2172
+#: src/language/stats/crosstabs.q:1980
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2173
+#: src/language/stats/crosstabs.q:1981
 msgid "Somers' d"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2174 src/ui/gui/crosstabs-dialog.c:51
+#: src/language/stats/crosstabs.q:1982 src/ui/gui/crosstabs-dialog.c:51
 msgid "Eta"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2179
+#: src/language/stats/crosstabs.q:1987
 msgid "Symmetric"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2180 src/language/stats/crosstabs.q:2181
+#: src/language/stats/crosstabs.q:1988 src/language/stats/crosstabs.q:1989
 #, c-format
 msgid "%s Dependent"
 msgstr ""
 
-#: src/language/stats/descriptives.c:102 src/language/stats/examine.q:1556
-#: 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/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/language/stats/descriptives.c:102 src/language/stats/examine.q:1559
+#: src/language/stats/frequencies.q:123 src/language/stats/npar-summary.c:126
+#: src/language/stats/oneway.q:390 src/language/stats/t-test.q:507
+#: src/language/stats/t-test.q:527 src/language/stats/t-test.q:625
+#: src/language/stats/t-test.q:918 src/ui/gui/descriptives-dialog.c:39
 #: src/ui/gui/frequencies-dialog.c:40
 msgid "Mean"
 msgstr ""
@@ -3006,13 +3040,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:1589
 #: 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:1625
 #: src/language/stats/frequencies.q:129 src/ui/gui/descriptives-dialog.c:47
 #: src/ui/gui/frequencies-dialog.c:50
 msgid "Kurtosis"
@@ -3022,7 +3056,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:1620
 #: src/language/stats/frequencies.q:131 src/ui/gui/descriptives-dialog.c:48
 #: src/ui/gui/frequencies-dialog.c:46
 msgid "Skewness"
@@ -3032,21 +3066,21 @@ 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:1609
 #: 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/frequencies.q:134 src/language/stats/npar-summary.c:131
+#: src/language/stats/descriptives.c:111 src/language/stats/examine.q:1599
+#: src/language/stats/frequencies.q:134 src/language/stats/npar-summary.c:132
 #: src/language/stats/oneway.q:404 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/frequencies.q:135 src/language/stats/npar-summary.c:134
+#: src/language/stats/descriptives.c:112 src/language/stats/examine.q:1604
+#: src/language/stats/frequencies.q:135 src/language/stats/npar-summary.c:135
 #: src/language/stats/oneway.q:405 src/ui/gui/descriptives-dialog.c:42
 #: src/ui/gui/frequencies-dialog.c:43
 msgid "Maximum"
@@ -3084,137 +3118,142 @@ 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:917
 #, 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:346 src/language/stats/examine.q:499
+#: src/language/stats/examine.q:1060
+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:356
 #, 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:357 src/language/stats/examine.q:362
+msgid "Observed Value"
 msgstr ""
 
-#: src/language/stats/examine.q:1183
-msgid "Extreme Values"
+#: src/language/stats/examine.q:358
+msgid "Expected Normal"
 msgstr ""
 
-#: src/language/stats/examine.q:1199
-msgid "Case Number"
+#: src/language/stats/examine.q:360
+#, c-format
+msgid "Detrended Normal Q-Q Plot of %s"
 msgstr ""
 
-#: src/language/stats/examine.q:1297
-msgid "Highest"
+#: src/language/stats/examine.q:363
+msgid "Dev from Normal"
 msgstr ""
 
-#: src/language/stats/examine.q:1302
-msgid "Lowest"
+#: src/language/stats/examine.q:516
+#, 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:520
+#, 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"
+#: src/language/stats/examine.q:756 src/language/stats/examine.q:769
+#, c-format
+msgid "%s and %s are mutually exclusive"
+msgstr ""
+
+#: src/language/stats/examine.q:1272 src/language/stats/reliability.q:686
+msgid "Case Processing Summary"
 msgstr ""
 
-#: src/language/stats/examine.q:1574 src/language/stats/oneway.q:399
+#: src/language/stats/examine.q:1564 src/language/stats/oneway.q:398
 #, 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:1570 src/language/stats/oneway.q:401
 msgid "Lower Bound"
 msgstr ""
 
-#: src/language/stats/examine.q:1591 src/language/stats/oneway.q:402
+#: src/language/stats/examine.q:1575 src/language/stats/oneway.q:402
 msgid "Upper Bound"
 msgstr ""
 
-#: src/language/stats/examine.q:1603
-#, c-format
-msgid "5%% Trimmed Mean"
+#: src/language/stats/examine.q:1579
+msgid "5% Trimmed Mean"
 msgstr ""
 
-#: src/language/stats/examine.q:1614 src/language/stats/frequencies.q:125
+#: src/language/stats/examine.q:1584 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/t-test.q:718 src/language/stats/t-test.q:851
-#: src/language/stats/t-test.q:1188
+#: src/language/stats/examine.q:1594 src/language/stats/npar-summary.c:129
+#: src/language/stats/oneway.q:391 src/language/stats/t-test.q:508
+#: src/language/stats/t-test.q:528 src/language/stats/t-test.q:627
+#: src/language/stats/t-test.q:919
 msgid "Std. Deviation"
 msgstr ""
 
-#: src/language/stats/examine.q:1696
+#: src/language/stats/examine.q:1614
 msgid "Interquartile Range"
 msgstr ""
 
-#: src/language/stats/examine.q:1850
-#, c-format
-msgid "Boxplot of %s vs. %s"
+#: src/language/stats/examine.q:1750 src/language/stats/oneway.q:408
+#: src/ui/gui/examine.glade:310
+msgid "Descriptives"
 msgstr ""
 
-#: src/language/stats/examine.q:1877
-msgid "Boxplot"
+#: src/language/stats/examine.q:1756 src/language/stats/oneway.q:392
+#: src/language/stats/oneway.q:689 src/language/stats/regression.q:203
+msgid "Std. Error"
 msgstr ""
 
-#: src/language/stats/examine.q:1919
-#, c-format
-msgid "Normal Q-Q Plot of %s"
+#: src/language/stats/examine.q:1939
+msgid "Highest"
 msgstr ""
 
-#: src/language/stats/examine.q:1920 src/language/stats/examine.q:1926
-msgid "Observed Value"
+#: src/language/stats/examine.q:1944
+msgid "Lowest"
 msgstr ""
 
-#: src/language/stats/examine.q:1921
-msgid "Expected Normal"
+#: src/language/stats/examine.q:1951
+msgid "Extreme Values"
 msgstr ""
 
-#: src/language/stats/examine.q:1924
-#, c-format
-msgid "Detrended Normal Q-Q Plot of %s"
+#: src/language/stats/examine.q:1955
+msgid "Case Number"
 msgstr ""
 
-#: src/language/stats/examine.q:1927
-msgid "Dev from Normal"
+#: src/language/stats/examine.q:2077
+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:2117 src/language/stats/examine.q:2134
+#: src/language/stats/frequencies.q:1407 src/language/stats/npar-summary.c:142
+#: src/ui/gui/examine.glade:333
 msgid "Percentiles"
 msgstr ""
 
-#: src/language/stats/examine.q:2204
-msgid "Tukey's Hinges"
+#: src/language/stats/examine.q:2124
+#, c-format
+msgid "%g"
 msgstr ""
 
 #: src/language/stats/flip.c:96
@@ -3222,68 +3261,54 @@ msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/language/stats/flip.c:151
+#: src/language/stats/flip.c:147
 msgid "Could not create temporary file for FLIP."
 msgstr ""
 
-#: src/language/stats/flip.c:162
-#, c-format
-msgid "Error writing FLIP file: %s."
-msgstr ""
-
-#: src/language/stats/flip.c:262
-#, c-format
-msgid "Could not create acceptable variant for variable %s."
-msgstr ""
-
-#: src/language/stats/flip.c:278
-msgid "Cannot create more than 99999 variable names."
-msgstr ""
-
-#: src/language/stats/flip.c:394
+#: src/language/stats/flip.c:324
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:401
+#: src/language/stats/flip.c:331
 msgid "Error creating FLIP source file."
 msgstr ""
 
-#: src/language/stats/flip.c:414
+#: src/language/stats/flip.c:344
 #, c-format
 msgid "Error reading FLIP file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:416
+#: src/language/stats/flip.c:346
 msgid "Unexpected end of file reading FLIP file."
 msgstr ""
 
-#: src/language/stats/flip.c:432
+#: src/language/stats/flip.c:362
 #, c-format
 msgid "Error seeking FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:440
+#: src/language/stats/flip.c:370
 #, c-format
 msgid "Error writing FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:451
+#: src/language/stats/flip.c:381
 #, c-format
 msgid "Error closing FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:459
+#: src/language/stats/flip.c:389
 #, c-format
 msgid "Error rewinding FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:488
+#: src/language/stats/flip.c:419
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:491
+#: src/language/stats/flip.c:422
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
@@ -3303,74 +3328,74 @@ msgstr ""
 msgid "S.E. Skew"
 msgstr ""
 
-#: src/language/stats/frequencies.q:409
+#: src/language/stats/frequencies.q:405
 msgid ""
 "At most one of BARCHART, HISTOGRAM, or HBAR should be given.  HBAR will be "
 "assumed.  Argument values will be given precedence increasing along the "
 "order given."
 msgstr ""
 
-#: src/language/stats/frequencies.q:492
+#: src/language/stats/frequencies.q:488
 #, c-format
 msgid ""
 "MAX must be greater than or equal to MIN, if both are specified.  However, "
 "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:752
 #, c-format
 msgid "Variable %s specified multiple times on VARIABLES subcommand."
 msgstr ""
 
-#: src/language/stats/frequencies.q:822
+#: src/language/stats/frequencies.q:809
 msgid "`)' expected after GROUPED interval list."
 msgstr ""
 
-#: src/language/stats/frequencies.q:834
+#: src/language/stats/frequencies.q:821
 #, c-format
 msgid "Variables %s specified on GROUPED but not on VARIABLES."
 msgstr ""
 
-#: src/language/stats/frequencies.q:841
+#: src/language/stats/frequencies.q:828
 #, 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:1047 src/language/stats/frequencies.q:1140
+#: src/language/stats/frequencies.q:1141 src/language/stats/frequencies.q:1176
 msgid "Cum"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1061 src/output/charts/plot-hist.c:126
+#: src/language/stats/frequencies.q:1049 src/output/charts/plot-hist.c:140
 msgid "Frequency"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1082
+#: src/language/stats/frequencies.q:1070
 msgid "Value Label"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1185
+#: src/language/stats/frequencies.q:1174
 msgid "Freq"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1186 src/language/stats/frequencies.q:1188
+#: src/language/stats/frequencies.q:1175 src/language/stats/frequencies.q:1177
 msgid "Pct"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1379
+#: src/language/stats/frequencies.q:1370
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/language/stats/frequencies.q:1421
+#: src/language/stats/frequencies.q:1411
 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,138 +3407,138 @@ msgstr ""
 msgid "TABLES subcommand may not appear more than once."
 msgstr ""
 
-#: src/language/stats/npar.q:98
+#: src/language/stats/npar.q:109
 msgid "NPAR subcommand not currently implemented."
 msgstr ""
 
-#: src/language/stats/npar.q:236
+#: src/language/stats/npar.q:252
 #, 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:307
 #, 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:441 src/language/stats/t-test.q:379
 #, c-format
 msgid ""
 "PAIRED was specified but the number of variables preceding WITH (%zu) did "
 "not match the number following (%zu)."
 msgstr ""
 
-#: src/language/stats/npar-summary.c:108
+#: src/language/stats/npar-summary.c:109
 msgid "Descriptive Statistics"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:145
+#: src/language/stats/npar-summary.c:146
 msgid "25th"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:148
+#: src/language/stats/npar-summary.c:149
 msgid "50th (Median)"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:151
+#: src/language/stats/npar-summary.c:152
 msgid "75th"
 msgstr ""
 
-#: src/language/stats/oneway.q:169
+#: src/language/stats/oneway.q:171
 msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
-#: src/language/stats/oneway.q:178
+#: src/language/stats/oneway.q:180
 #, c-format
 msgid "Coefficients for contrast %zu do not total zero"
 msgstr ""
 
-#: src/language/stats/oneway.q:244
+#: src/language/stats/oneway.q:243
 #, 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:277 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:279 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/t-test.q:998
+#: src/language/stats/oneway.q:280 src/language/stats/regression.q:304
+#: src/language/stats/t-test.q:750
 msgid "F"
 msgstr ""
 
-#: src/language/stats/oneway.q:282 src/language/stats/oneway.q:542
+#: src/language/stats/oneway.q:281 src/language/stats/oneway.q:539
 #: 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:303
 msgid "Between Groups"
 msgstr ""
 
-#: src/language/stats/oneway.q:305
+#: src/language/stats/oneway.q:304
 msgid "Within Groups"
 msgstr ""
 
-#: src/language/stats/oneway.q:352 src/language/stats/regression.q:330
+#: src/language/stats/oneway.q:348 src/language/stats/regression.q:330
 msgid "ANOVA"
 msgstr ""
 
-#: src/language/stats/oneway.q:539
+#: src/language/stats/oneway.q:536
 msgid "Levene Statistic"
 msgstr ""
 
-#: src/language/stats/oneway.q:540
+#: src/language/stats/oneway.q:537
 msgid "df1"
 msgstr ""
 
-#: src/language/stats/oneway.q:541
+#: src/language/stats/oneway.q:538
 msgid "df2"
 msgstr ""
 
-#: src/language/stats/oneway.q:545
+#: src/language/stats/oneway.q:541
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/language/stats/oneway.q:613
+#: src/language/stats/oneway.q:608
 msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/language/stats/oneway.q:615 src/language/stats/oneway.q:690
+#: src/language/stats/oneway.q:610 src/language/stats/oneway.q:687
 msgid "Contrast"
 msgstr ""
 
-#: src/language/stats/oneway.q:688
+#: src/language/stats/oneway.q:685
 msgid "Contrast Tests"
 msgstr ""
 
-#: src/language/stats/oneway.q:691
+#: src/language/stats/oneway.q:688
 msgid "Value of Contrast"
 msgstr ""
 
-#: src/language/stats/oneway.q:693 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
+#: src/language/stats/oneway.q:690 src/language/stats/regression.q:205
+#: src/language/stats/t-test.q:752 src/language/stats/t-test.q:923
+#: src/language/stats/t-test.q:1010
 msgid "t"
 msgstr ""
 
-#: src/language/stats/oneway.q:695 src/language/stats/t-test.q:1002
-#: src/language/stats/t-test.q:1194 src/language/stats/t-test.q:1287
+#: src/language/stats/oneway.q:692 src/language/stats/t-test.q:754
+#: src/language/stats/t-test.q:925 src/language/stats/t-test.q:1012
 msgid "Sig. (2-tailed)"
 msgstr ""
 
-#: src/language/stats/oneway.q:739
+#: src/language/stats/oneway.q:736
 msgid "Assume equal variances"
 msgstr ""
 
-#: src/language/stats/oneway.q:743
+#: src/language/stats/oneway.q:740
 msgid "Does not assume equal"
 msgstr ""
 
@@ -3527,50 +3552,50 @@ 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:728
 #, c-format
 msgid "%s into %s(%s of %s BY %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:744
+#: src/language/stats/rank.q:741
 #, c-format
 msgid "%s into %s(%s of %s using %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:754
+#: src/language/stats/rank.q:750
 #, c-format
 msgid "%s into %s(%s of %s)"
 msgstr ""
 
-#: src/language/stats/rank.q:767
+#: src/language/stats/rank.q:762
 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:853
 #, c-format
 msgid "Variable %s already exists."
 msgstr ""
 
-#: src/language/stats/rank.q:865
+#: src/language/stats/rank.q:858
 msgid "Too many variables in INTO clause."
 msgstr ""
 
-#: src/language/stats/regression.q:159 src/ui/gui/regression-dialog.c:41
+#: src/language/stats/regression.q:159 src/ui/gui/regression-dialog.c:42
 msgid "R"
 msgstr ""
 
@@ -3622,191 +3647,296 @@ 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:433
+msgid "Reliability Statistics"
+msgstr ""
+
+#: src/language/stats/reliability.q:476
+msgid "Item-Total Statistics"
+msgstr ""
+
+#: src/language/stats/reliability.q:498
+msgid "Scale Mean if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:501
+msgid "Scale Variance if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:504
+msgid "Corrected Item-Total Correlation"
+msgstr ""
+
+#: src/language/stats/reliability.q:507
+msgid "Cronbach's Alpha if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:557 src/language/stats/reliability.q:576
+msgid "Cronbach's Alpha"
+msgstr ""
+
+#: src/language/stats/reliability.q:560
+msgid "N of items"
+msgstr ""
+
+#: src/language/stats/reliability.q:579
+msgid "Part 1"
+msgstr ""
+
+#: src/language/stats/reliability.q:585 src/language/stats/reliability.q:596
+msgid "N of Items"
+msgstr ""
+
+#: src/language/stats/reliability.q:590
+msgid "Part 2"
+msgstr ""
+
+#: src/language/stats/reliability.q:601
+msgid "Total N of Items"
+msgstr ""
+
+#: src/language/stats/reliability.q:604
+msgid "Correlation Between Forms"
+msgstr ""
+
+#: src/language/stats/reliability.q:608
+msgid "Spearman-Brown Coefficient"
+msgstr ""
+
+#: src/language/stats/reliability.q:611
+msgid "Equal Length"
+msgstr ""
+
+#: src/language/stats/reliability.q:614
+msgid "Unequal Length"
+msgstr ""
+
+#: src/language/stats/reliability.q:618
+msgid "Guttman Split-Half Coefficient"
+msgstr ""
+
+#: src/language/stats/reliability.q:715
+msgid "Excluded"
+msgstr ""
+
+#: src/language/stats/reliability.q:723
+msgid "%"
+msgstr ""
+
+#: src/language/stats/sign.c:91
+msgid "Negative Differences"
+msgstr ""
+
+#: src/language/stats/sign.c:92
+msgid "Positive Differences"
+msgstr ""
+
+#: src/language/stats/sign.c:93 src/language/stats/wilcoxon.c:261
+msgid "Ties"
+msgstr ""
+
+#: src/language/stats/sign.c:134 src/language/stats/wilcoxon.c:331
+msgid "Exact Sig. (2-tailed)"
+msgstr ""
+
+#: src/language/stats/sign.c:137 src/language/stats/wilcoxon.c:332
+msgid "Exact Sig. (1-tailed)"
+msgstr ""
+
+#: src/language/stats/sign.c:140 src/language/stats/wilcoxon.c:335
+msgid "Point Probability"
+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 ""
 
-#: src/language/stats/t-test.q:275
-msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
+#: src/language/stats/t-test.q:189
+msgid "Exactly one of TESTVAL, GROUPS and PAIRS subcommands must be specified."
 msgstr ""
 
-#: src/language/stats/t-test.q:293
-msgid "VARIABLES subcommand is not appropriate with PAIRS"
+#: src/language/stats/t-test.q:210
+msgid "VARIABLES subcommand may not be used with PAIRS."
 msgstr ""
 
-#: src/language/stats/t-test.q:331
+#: src/language/stats/t-test.q:229
 msgid "One or more VARIABLES must be specified."
 msgstr ""
 
-#: src/language/stats/t-test.q:381
-#, c-format
-msgid "Long string variable %s is not valid here."
-msgstr ""
-
-#: src/language/stats/t-test.q:401 src/language/stats/t-test.q:415
+#: src/language/stats/t-test.q:323
 msgid ""
 "When applying GROUPS to a string variable, two values must be specified."
 msgstr ""
 
-#: src/language/stats/t-test.q:513
+#: src/language/stats/t-test.q:394
 msgid "At least two variables must be specified on PAIRS."
 msgstr ""
 
-#: src/language/stats/t-test.q:691
+#: src/language/stats/t-test.q:504
 msgid "One-Sample Statistics"
 msgstr ""
 
-#: src/language/stats/t-test.q:696 src/language/stats/t-test.q:719
-#: src/language/stats/t-test.q:852
+#: src/language/stats/t-test.q:509 src/language/stats/t-test.q:529
+#: src/language/stats/t-test.q:628
 msgid "SE. Mean"
 msgstr ""
 
-#: src/language/stats/t-test.q:714
+#: src/language/stats/t-test.q:523
 msgid "Group Statistics"
 msgstr ""
 
-#: src/language/stats/t-test.q:846
+#: src/language/stats/t-test.q:622
 msgid "Paired Sample Statistics"
 msgstr ""
 
-#: src/language/stats/t-test.q:868 src/language/stats/t-test.q:1213
-#: src/language/stats/t-test.q:1404
+#: src/language/stats/t-test.q:642 src/language/stats/t-test.q:945
+#: src/language/stats/t-test.q:1119
 #, c-format
 msgid "Pair %d"
 msgstr ""
 
-#: src/language/stats/t-test.q:986
+#: src/language/stats/t-test.q:738
 msgid "Independent Samples Test"
 msgstr ""
 
-#: src/language/stats/t-test.q:994
+#: src/language/stats/t-test.q:746
 msgid "Levene's Test for Equality of Variances"
 msgstr ""
 
-#: src/language/stats/t-test.q:996
+#: src/language/stats/t-test.q:748
 msgid "t-test for Equality of Means"
 msgstr ""
 
-#: src/language/stats/t-test.q:999 src/language/stats/t-test.q:1389
+#: src/language/stats/t-test.q:751 src/language/stats/t-test.q:1107
 msgid "Sig."
 msgstr ""
 
-#: src/language/stats/t-test.q:1003 src/language/stats/t-test.q:1288
+#: src/language/stats/t-test.q:755 src/language/stats/t-test.q:1013
 msgid "Mean Difference"
 msgstr ""
 
-#: src/language/stats/t-test.q:1004
+#: src/language/stats/t-test.q:756
 msgid "Std. Error Difference"
 msgstr ""
 
-#: src/language/stats/t-test.q:1009 src/language/stats/t-test.q:1184
-#: src/language/stats/t-test.q:1280
+#: src/language/stats/t-test.q:761 src/language/stats/t-test.q:915
+#: src/language/stats/t-test.q:1005
 #, c-format
 msgid "%g%% Confidence Interval of the Difference"
 msgstr ""
 
-#: src/language/stats/t-test.q:1064
+#: src/language/stats/t-test.q:815
 msgid "Equal variances assumed"
 msgstr ""
 
-#: src/language/stats/t-test.q:1116
+#: src/language/stats/t-test.q:861
 msgid "Equal variances not assumed"
 msgstr ""
 
-#: src/language/stats/t-test.q:1174
+#: src/language/stats/t-test.q:905
 msgid "Paired Samples Test"
 msgstr ""
 
-#: src/language/stats/t-test.q:1177
+#: src/language/stats/t-test.q:908
 msgid "Paired Differences"
 msgstr ""
 
-#: src/language/stats/t-test.q:1189
+#: src/language/stats/t-test.q:920
 msgid "Std. Error Mean"
 msgstr ""
 
-#: src/language/stats/t-test.q:1269
+#: src/language/stats/t-test.q:994
 msgid "One-Sample Test"
 msgstr ""
 
-#: src/language/stats/t-test.q:1274
+#: src/language/stats/t-test.q:999
 #, c-format
 msgid "Test Value = %f"
 msgstr ""
 
-#: src/language/stats/t-test.q:1384
+#: src/language/stats/t-test.q:1102
 msgid "Paired Samples Correlations"
 msgstr ""
 
-#: src/language/stats/t-test.q:1388
+#: src/language/stats/t-test.q:1106
 msgid "Correlation"
 msgstr ""
 
-#: src/language/stats/t-test.q:1407
+#: src/language/stats/t-test.q:1121
 #, c-format
 msgid "%s & %s"
 msgstr ""
 
-#: src/language/syntax-file.c:88
-#, c-format
-msgid "opening \"%s\" as syntax file"
+#: src/language/stats/wilcoxon.c:232
+msgid "Ranks"
 msgstr ""
 
-#: src/language/syntax-file.c:93
-#, c-format
-msgid "Opening `%s': %s."
+#: src/language/stats/wilcoxon.c:246
+msgid "Mean Rank"
 msgstr ""
 
-#: src/language/syntax-file.c:106
-#, c-format
-msgid "Reading `%s': %s."
+#: src/language/stats/wilcoxon.c:247
+msgid "Sum of Ranks"
 msgstr ""
 
-#: src/language/syntax-file.c:126
-#, c-format
-msgid "Closing `%s': %s."
+#: src/language/stats/wilcoxon.c:259
+msgid "Negative Ranks"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:260
+msgid "Positive Ranks"
 msgstr ""
 
-#: src/language/tests/check-model.q:138
-msgid "PATH and SEARCH subcommands are mutually exclusive.  Ignoring PATH."
+#: src/language/stats/wilcoxon.c:326
+msgid "Z"
 msgstr ""
 
-#: src/language/tests/check-model.q:156
-msgid "At least one value must be specified on PATH."
+#: src/language/stats/wilcoxon.c:327
+msgid "Asymp. Sig. (2-tailed)"
+msgstr ""
+
+#: src/language/syntax-file.c:88
+#, c-format
+msgid "opening \"%s\" as syntax file"
+msgstr ""
+
+#: src/language/syntax-file.c:93
+#, c-format
+msgid "Opening `%s': %s."
 msgstr ""
 
-#: src/language/tests/check-model.q:167
+#: src/language/syntax-file.c:107
 #, c-format
-msgid "Hash bits adjusted to %d."
+msgid "Reading `%s': %s."
 msgstr ""
 
-#: src/language/tests/check-model.q:208
+#: src/language/syntax-file.c:127
 #, c-format
-msgid "error opening \"%s\" for writing"
+msgid "Closing `%s': %s."
 msgstr ""
 
 #: src/language/tests/float-format.c:124
@@ -3842,32 +3972,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."
@@ -3883,138 +4018,95 @@ msgstr ""
 msgid "Cannot change mode of %s: %s"
 msgstr ""
 
-#: src/language/utilities/set.q:200
+#: src/language/utilities/set.q:202
 msgid "WORKSPACE must be at least 1MB"
 msgstr ""
 
-#: src/language/utilities/set.q:206 src/language/utilities/set.q:208
-#: src/language/utilities/set.q:210 src/language/utilities/set.q:212
-#: src/language/utilities/set.q:214 src/language/utilities/set.q:216
-#: src/language/utilities/set.q:218 src/language/utilities/set.q:220
-#: src/language/utilities/set.q:222
+#: src/language/utilities/set.q:208 src/language/utilities/set.q:210
+#: src/language/utilities/set.q:212 src/language/utilities/set.q:214
+#: src/language/utilities/set.q:216 src/language/utilities/set.q:218
+#: src/language/utilities/set.q:220 src/language/utilities/set.q:222
+#: src/language/utilities/set.q:224
 #, c-format
 msgid "%s is obsolete."
 msgstr ""
 
-#: src/language/utilities/set.q:225
+#: src/language/utilities/set.q:227
 #, c-format
 msgid "%s is not implemented."
 msgstr ""
 
-#: src/language/utilities/set.q:228
+#: src/language/utilities/set.q:230
 msgid "Active file compression is not implemented."
 msgstr ""
 
-#: src/language/utilities/set.q:323
+#: src/language/utilities/set.q:325
 msgid "EPOCH must be 1500 or later."
 msgstr ""
 
-#: src/language/utilities/set.q:330
+#: src/language/utilities/set.q:332
 msgid "expecting AUTOMATIC or year"
 msgstr ""
 
-#: src/language/utilities/set.q:351
+#: src/language/utilities/set.q:353
 msgid "LENGTH must be at least 1."
 msgstr ""
 
-#: src/language/utilities/set.q:395
+#: src/language/utilities/set.q:389
+#, c-format
+msgid "%s is not a recognised encoding or locale name"
+msgstr ""
+
+#: src/language/utilities/set.q:432
 msgid "WIDTH must be at least 40."
 msgstr ""
 
-#: src/language/utilities/set.q:418
+#: src/language/utilities/set.q:455
 #, c-format
 msgid ""
 "FORMAT requires numeric output format as an argument.  Specified format %s "
 "is of type string."
 msgstr ""
 
-#: src/language/utilities/set.q:485
-msgid "BLANKS is SYSMIS."
-msgstr ""
-
-#: src/language/utilities/set.q:487
-#, c-format
-msgid "BLANKS is %g."
-msgstr ""
-
-#: src/language/utilities/set.q:522
-#, c-format
-msgid "%s is \"%s\"."
-msgstr ""
-
-#: src/language/utilities/set.q:558
-#, c-format
-msgid "DECIMAL is \"%c\"."
-msgstr ""
-
-#: src/language/utilities/set.q:564
-#, c-format
-msgid "ENDCMD is \"%c\"."
-msgstr ""
-
-#: src/language/utilities/set.q:572
-#, c-format
-msgid "ERRORS is \"%s\"."
-msgstr ""
-
-#: src/language/utilities/set.q:583
-#, c-format
-msgid "FORMAT is %s."
-msgstr ""
-
-#: src/language/utilities/set.q:589
-#, c-format
-msgid "LENGTH is %d."
-msgstr ""
-
-#: src/language/utilities/set.q:595
-#, c-format
-msgid "MXERRS is %d."
-msgstr ""
-
-#: src/language/utilities/set.q:601
-#, c-format
-msgid "MXLOOPS is %d."
+#: src/language/utilities/set.q:668
+msgid "ISL (32-bit IEEE 754 single, little-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:607
-#, c-format
-msgid "MXWARNS is %d."
+#: src/language/utilities/set.q:671
+msgid "ISB (32-bit IEEE 754 single, big-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:614 src/language/utilities/set.q:665
-#, c-format
-msgid "%s is %s (%s)."
+#: src/language/utilities/set.q:674
+msgid "IDL (64-bit IEEE 754 double, little-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:686
-msgid "SCOMPRESSION is ON."
+#: src/language/utilities/set.q:677
+msgid "IDB (64-bit IEEE 754 double, big-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:688
-msgid "SCOMPRESSION is OFF."
+#: src/language/utilities/set.q:681
+msgid "VF (32-bit VAX F, VAX-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:695
-msgid "UNDEFINED is WARN."
+#: src/language/utilities/set.q:684
+msgid "VD (64-bit VAX D, VAX-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:697
-msgid "UNDEFINED is NOWARN."
+#: src/language/utilities/set.q:687
+msgid "VG (64-bit VAX G, VAX-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:705
-msgid "WEIGHT is off."
+#: src/language/utilities/set.q:691
+msgid "ZS (32-bit IBM Z hexadecimal short, big-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:707
-#, c-format
-msgid "WEIGHT is variable %s."
+#: src/language/utilities/set.q:694
+msgid "ZL (64-bit IBM Z hexadecimal long, big-endian)"
 msgstr ""
 
-#: src/language/utilities/set.q:725
+#: src/language/utilities/set.q:793
 #, c-format
-msgid "WIDTH is %d."
+msgid "%s is %s."
 msgstr ""
 
 #: src/language/utilities/title.c:68
@@ -4027,20 +4119,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:353
 #, c-format
 msgid "There is no vector named %s."
 msgstr ""
@@ -4049,44 +4141,44 @@ msgstr ""
 msgid "Destination cannot be a string variable."
 msgstr ""
 
-#: src/language/xforms/recode.c:246
+#: src/language/xforms/recode.c:245
 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:266
 msgid "CONVERT requires string input values and numeric output values."
 msgstr ""
 
-#: src/language/xforms/recode.c:317
+#: src/language/xforms/recode.c:321
 msgid "THRU is not allowed with string variables."
 msgstr ""
 
-#: src/language/xforms/recode.c:391
+#: src/language/xforms/recode.c:400
 msgid "expecting output value"
 msgstr ""
 
-#: src/language/xforms/recode.c:440
+#: src/language/xforms/recode.c:457
 #, 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:472
 #, 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:488
 #, 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:501
 #, c-format
 msgid "Type mismatch.  Cannot store %s data in %s variable %s."
 msgstr ""
@@ -4112,28 +4204,53 @@ 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/libpspp/tmpfile.c:55
+#, c-format
+msgid "failed to create temporary file"
+msgstr ""
+
+#: src/libpspp/tmpfile.c:96
+#, c-format
+msgid "seeking in temporary file"
+msgstr ""
+
+#: src/libpspp/tmpfile.c:115
+#, c-format
+msgid "reading temporary file"
+msgstr ""
+
+#: src/libpspp/tmpfile.c:117
+#, c-format
+msgid "unexpected end of file reading temporary file"
+msgstr ""
+
+#: src/libpspp/tmpfile.c:136
+#, c-format
+msgid "writing to temporary file"
+msgstr ""
+
+#: 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 +4400,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 +4418,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 +4458,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:99
 #, c-format
 msgid "syntax error"
 msgstr ""
@@ -4402,7 +4519,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
@@ -4528,17 +4645,25 @@ msgstr ""
 msgid "closing Postscript encoding \"%s\""
 msgstr ""
 
-#: src/output/table.c:234
+#: src/output/table.c:236
 #, c-format
 msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
-#: src/output/table.c:305
+#: src/output/table.c:307
 #, c-format
 msgid ""
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
+#: src/ui/gui/about.c:64
+msgid "A program for the analysis of sampled data"
+msgstr ""
+
+#: src/ui/gui/about.c:73
+msgid "translator-credits"
+msgstr "John Darrington"
+
 #: src/ui/gui/comments-dialog.c:58
 #, c-format
 msgid "Column Number: %d"
@@ -4578,7 +4703,8 @@ 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:568 src/ui/gui/var-display.c:16
+#: src/ui/gui/variable-info-dialog.c:40
 msgid "None"
 msgstr ""
 
@@ -4614,7 +4740,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 +4757,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:756
 msgid "Ascending"
 msgstr ""
 
@@ -4660,1258 +4786,1233 @@ 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:10 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:25 src/ui/gui/data-editor.glade:51
+#: 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:32 src/ui/gui/data-editor.glade:58
+#: src/ui/gui/data-editor.glade:311 src/ui/gui/data-editor.glade:329
+#: 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:70
+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:103
+msgid "D_isplay Data File Information"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:362
-msgid "Delete the variables at the selected position(s)"
+#: src/ui/gui/data-editor.glade:112
+msgid "Working File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:377
-msgid "Insert _Variable"
+#: src/ui/gui/data-editor.glade:119
+msgid "External File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:378
-msgid "Create a new variable at the current position"
+#: src/ui/gui/data-editor.glade:135
+msgid "Recently Used Da_ta"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:396
-msgid "Insert Ca_se"
+#: src/ui/gui/data-editor.glade:142
+msgid "Recently Used _Files"
 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/output-viewer.glade:55
+#: src/ui/gui/syntax-editor.glade:118
+msgid "_Edit"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:417
-msgid "_Goto Case"
+#: src/ui/gui/data-editor.glade:174 src/ui/gui/data-editor.glade:843
+#: src/ui/gui/psppire-data-window.c:843 src/ui/gui/psppire-data-window.c:933
+msgid "Insert Variable"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:418
-msgid "Jump to a Case in the Data Sheet"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:437
-msgid "_Weights"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:438
-msgid "Weight cases by variable"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:447 src/ui/gui/data-editor.glade:319
-msgid "_Transpose"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:448
-msgid "Transpose the cases with the variables"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:459
-msgid "S_plit"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:460
-msgid "Split the active file"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:470
-msgid "_Sort"
+#: src/ui/gui/data-editor.glade:182
+msgid "Insert Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:471
-msgid "Sort cases in the active file"
+#: src/ui/gui/data-editor.glade:190 src/ui/gui/data-editor.glade:780
+msgid "Go To Case"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:479 src/ui/gui/data-editor.glade:340
-msgid "Select _Cases"
+#: src/ui/gui/data-editor.glade:231
+msgid "Cl_ear Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:480
-msgid "Select cases from the active file"
+#: src/ui/gui/data-editor.glade:239
+msgid "_Clear Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:489 src/ui/gui/data-editor.glade:369
-msgid "_Compute"
+#: src/ui/gui/data-editor.glade:252
+msgid "gtk-find"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:490
-msgid "Compute new values for a variable"
+#: src/ui/gui/data-editor.glade:264
+msgid "_View"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:498
-msgid "Oneway _ANOVA"
+#: src/ui/gui/data-editor.glade:271
+msgid "_Status Bar"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:499
-msgid "Perform one way analysis of variance"
+#: src/ui/gui/data-editor.glade:284
+msgid "_Fonts"
 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:291
+msgid "_Grid Lines"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:508
-msgid "Calculate T Test for samples from independent groups"
+#: src/ui/gui/data-editor.glade:299
+msgid "Value _Labels"
 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:318 src/ui/gui/data-editor.glade:613
+msgid "_Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:518
-msgid "Calculate T Test for paired samples"
+#: src/ui/gui/data-editor.glade:336
+msgid "_Sort Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:527
-msgid "One _Sample T Test"
+#: src/ui/gui/data-editor.glade:350
+msgid "_Transpose"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:528
-msgid "Calculate T Test for sample from a single distribution"
+#: src/ui/gui/data-editor.glade:363
+msgid "S_plit File"
 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:371
+msgid "Select _Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:538
-msgid "Commentary text for the data file"
+#: src/ui/gui/data-editor.glade:378
+msgid "_Weight Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:546 src/ui/gui/data-editor.glade:228
-msgid "_Find"
+#: src/ui/gui/data-editor.glade:390
+msgid "_Transform"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:547
-msgid "Find Case"
+#: src/ui/gui/data-editor.glade:400
+msgid "_Compute"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:556 src/ui/gui/data-editor.glade:377
+#: src/ui/gui/data-editor.glade:408
 msgid "Ran_k Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:557
-msgid "Rank Cases"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:566 src/ui/gui/data-editor.glade:389
+#: src/ui/gui/data-editor.glade:420
 msgid "Recode into _Same Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:567
-msgid "Recode values into the same Variables"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:576 src/ui/gui/data-editor.glade:396
+#: src/ui/gui/data-editor.glade:427
 msgid "Recode into _Different Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:577
-msgid "Recode values into different Variables"
+#: src/ui/gui/data-editor.glade:440
+msgid "_Run Pending Transforms"
 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:453
+msgid "_Analyze"
+msgstr "_Analyse"
+
+#: src/ui/gui/data-editor.glade:463
+msgid "_Descriptive Statistics"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:587
-msgid "Jump to Variable"
+#: src/ui/gui/data-editor.glade:473
+msgid "_Frequencies"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:595 src/ui/gui/data-editor.glade:450
-#: src/ui/gui/oneway.glade:179
+#: src/ui/gui/data-editor.glade:481 src/ui/gui/oneway.glade:179
 msgid "_Descriptives"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:596
-msgid "Calculate descriptive statistics (mean, variance, ...)"
+#: src/ui/gui/data-editor.glade:489
+msgid "_Explore"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:605 src/ui/gui/data-editor.glade:442
-msgid "_Frequencies"
+#: src/ui/gui/data-editor.glade:497
+msgid "_Crosstabs"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:606
-msgid "Generate frequency statistics"
+#: src/ui/gui/data-editor.glade:509
+msgid "Compare _Means"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:614 src/ui/gui/data-editor.glade:466
-msgid "_Crosstabs"
+#: src/ui/gui/data-editor.glade:519
+msgid "_One Sample T Test"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:615
-msgid "Generate crosstabulations"
+#: src/ui/gui/data-editor.glade:527
+msgid "_Independent Samples T Test"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:624 src/ui/gui/data-editor.glade:458
-msgid "_Explore"
+#: src/ui/gui/data-editor.glade:535
+msgid "_Paired Samples T Test"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:625
-msgid "Examine Data by Factors"
+#: src/ui/gui/data-editor.glade:543
+msgid "One Way _ANOVA"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:634 src/ui/gui/data-editor.glade:532
-msgid "Linear _Regression"
+#: src/ui/gui/data-editor.glade:554
+msgid "Re_liability"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:635
-msgid "Estimate parameters of the linear model"
+#: src/ui/gui/data-editor.glade:562
+msgid "Linear _Regression"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1027
-msgid "Font Selection"
+#: src/ui/gui/data-editor.glade:569
+msgid "_Non-Parametric Statistics"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1099
-msgid "No Split"
+#: src/ui/gui/data-editor.glade:579
+msgid "_Chi-Square"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1108
-msgid "Split by "
+#: src/ui/gui/data-editor.glade:587
+msgid "_Binomial"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1133
-msgid "Filter off"
+#: src/ui/gui/data-editor.glade:603
+msgid "_Utilities"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1145
-#, c-format
-msgid "Filter by %s"
+#: src/ui/gui/data-editor.glade:622
+msgid "Data File _Comments"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1163
-msgid "Weights off"
+#: src/ui/gui/data-editor.glade:633 src/ui/gui/output-viewer.glade:78
+#: src/ui/gui/syntax-editor.glade:209
+msgid "_Windows"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1175
-#, c-format
-msgid "Weight by %s"
-msgstr ""
+#: src/ui/gui/data-editor.glade:640 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.c:1198 src/ui/gui/data-editor.c:1440
-#: src/ui/gui/data-editor.glade:660
-msgid "Open"
+#: src/ui/gui/data-editor.glade:647
+msgid "_Split"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1199
-msgid "Open a data file"
+#: src/ui/gui/data-editor.glade:658 src/ui/gui/output-viewer.glade:99
+#: src/ui/gui/syntax-editor.glade:229
+msgid "_Help"
 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:665 src/ui/gui/output-viewer.glade:106
+#: src/ui/gui/syntax-editor.glade:237
+msgid "_Reference Manual"
 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:678 src/ui/gui/output-viewer.glade:113
+#: src/ui/gui/syntax-editor.glade:244
+msgid "_About"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1217
-msgid "Save As"
+#: src/ui/gui/data-editor.glade:702 src/ui/gui/psppire-data-window.c:379
+msgid "Open"
 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:712 src/ui/gui/psppire-data-window.c:581
+msgid "Save"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1227
-msgid "New data file"
+#: src/ui/gui/data-editor.glade:722
+msgid "Print"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1235
-msgid "_Import Text Data"
+#: src/ui/gui/data-editor.glade:732
+msgid "Recall"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1236
-msgid "Import text data file"
+#: src/ui/gui/data-editor.glade:750
+msgid "Undo"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1331 src/ui/gui/data-editor.c:1448
-msgid "System Files (*.sav)"
+#: src/ui/gui/data-editor.glade:760
+msgid "Redo"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1337 src/ui/gui/data-editor.c:1454
-msgid "Portable Files (*.por) "
+#: src/ui/gui/data-editor.glade:790
+msgid "Variables"
 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"
+#: src/ui/gui/data-editor.glade:811
+msgid "Find"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1351
-msgid "System File"
+#: src/ui/gui/data-editor.glade:831 src/ui/gui/psppire-data-window.c:897
+msgid "Insert Case"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1356
-msgid "Portable File"
+#: src/ui/gui/data-editor.glade:863
+msgid "Split File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1498
-msgid "Sort Ascending"
+#: src/ui/gui/data-editor.glade:874
+msgid "Weight Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1501
-msgid "Sort Descending"
+#: src/ui/gui/data-editor.glade:886
+msgid "Select Cases"
 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:906 src/ui/gui/var-sheet-dialogs.glade:401
+#: src/ui/gui/var-sheet-dialogs.glade:582
+msgid "Value Labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1558 src/ui/gui/data-editor.glade:789
-msgid "Insert Case"
+#: src/ui/gui/data-editor.glade:917
+msgid "Use Sets"
 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:938
+msgid "Information Area"
 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:957
+msgid "Processor Area"
 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:982
+msgid "Case Counter Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:78
-msgid "_Import Delimited Text Data"
+#: src/ui/gui/data-editor.glade:1007
+msgid "Filter Use Status Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:111
-msgid "Recently Used Da_ta"
+#: src/ui/gui/data-editor.glade:1033
+msgid "Weight Status Area"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:118
-msgid "Recently Used _Files"
+#: src/ui/gui/data-editor.glade:1059
+msgid "Split File Status Area"
 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/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
+msgid "Standard deviation"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:158
-msgid "Insert Cases"
+#: src/ui/gui/descriptives-dialog.c:45
+msgid "Standard error"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:166 src/ui/gui/data-editor.glade:738
-msgid "Go To Case"
+#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
+msgid "Statistics:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:207
-msgid "Cl_ear Variables"
+#: src/ui/gui/descriptives-dialog.glade:184
+msgid "Exclude entire case if any selected variable is missing"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:215
-msgid "_Clear Cases"
+#: src/ui/gui/descriptives-dialog.glade:194
+msgid "Include user-missing data in analysis"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:239
-msgid "_View"
+#: src/ui/gui/descriptives-dialog.glade:207
+msgid "Save Z-scores of selected variables as new variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:246
-msgid "_Status Bar"
+#: src/ui/gui/descriptives-dialog.glade:223
+msgid "Options:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:259
-msgid "_Fonts"
+#: src/ui/gui/examine.glade:49
+msgid "Label Cases by:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:266
-msgid "_Grid Lines"
+#: src/ui/gui/examine.glade:100
+msgid "Factor List:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:274
-msgid "Value _Labels"
+#: src/ui/gui/examine.glade:150
+msgid "Dependent List:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:311
-msgid "_Sort Cases"
+#: 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:332
-msgid "S_plit File"
+#: src/ui/gui/examine.glade:320
+msgid "Extremes"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:347
-msgid "_Weight Cases"
+#: src/ui/gui/examine.glade:388
+msgid "Exclude cases listwise"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:359
-msgid "_Transform"
+#: src/ui/gui/examine.glade:399
+msgid "Exclude cases pairwise"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:409
-msgid "_Run Pending Transforms"
+#: src/ui/gui/examine.glade:414
+msgid "Repeat values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:422
-msgid "_Analyze"
+#: src/ui/gui/examine.glade:432 src/ui/gui/t-test.glade:460
+#: src/ui/gui/var-sheet-dialogs.glade:649
+msgid "Missing Values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:432
-msgid "_Descriptive Statistics"
+#: src/ui/gui/find-dialog.c:658
+#, c-format
+msgid "Bad regular expression: %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:478
-msgid "Compare _Means"
+#: src/ui/gui/find.glade:80
+msgid "Variable:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:488
-msgid "_One Sample T Test"
+#: src/ui/gui/find.glade:111 src/ui/gui/recode.glade:185
+#: src/ui/gui/var-sheet-dialogs.glade:512
+msgid "Value:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:512
-msgid "One Way _ANOVA"
+#: src/ui/gui/find.glade:137
+msgid "Search value labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:524
-msgid "Bivariate _Correlation"
+#: src/ui/gui/find.glade:161
+msgid "Regular expression Match"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:540
-msgid "_Non-Parametric Statistics"
+#: src/ui/gui/find.glade:172
+msgid "Search substrings"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:550
-msgid "_Chi-Square"
+#: src/ui/gui/find.glade:185
+msgid "Wrap around"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:558
-msgid "_Binomial"
+#: src/ui/gui/find.glade:198
+msgid "Search backward"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:574
-msgid "_Utilities"
+#: src/ui/gui/frequencies-dialog.c:44
+msgid "Standard error of the mean"
 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/frequencies-dialog.c:47
+msgid "Standard error of the skewness"
 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/frequencies-dialog.c:51
+msgid "Standard error of the kurtosis"
 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/frequencies.glade:98 src/ui/gui/psppire.glade:252
+#: src/ui/gui/rank.glade:103
+msgid "Variable(s):"
 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/frequencies.glade:168
+msgid "Display Frequency Table"
 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/frequencies.glade:264
+msgid "Ascending Order"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:680
-msgid "Print"
+#: src/ui/gui/frequencies.glade:275
+msgid "Descending Order"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:690
-msgid "Recall"
+#: src/ui/gui/frequencies.glade:290
+msgid "Ascending Counts"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:708
-msgid "Undo"
+#: src/ui/gui/frequencies.glade:305
+msgid "Descending Counts"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:718
-msgid "Redo"
+#: src/ui/gui/frequencies.glade:323
+msgid "Order by"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:748
-msgid "Variables"
+#: src/ui/gui/frequencies.glade:355
+msgid "Supress tables with more than N categories"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:769
-msgid "Find"
+#: src/ui/gui/frequencies.glade:371
+msgid "Maximum no of categories"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:821
-msgid "Split File"
+#: src/ui/gui/helper.c:186
+msgid "Sorry. The help system hasn't yet been implemented."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:832
-msgid "Weight Cases"
+#: src/ui/gui/helper.c:231
+#, c-format
+msgid "Cannot open reference manual: %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:844
-msgid "Select Cases"
+#: src/ui/gui/main.c:43
+msgid "Don't show the splash screen"
 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/main.c:173
+msgid "PSPPIRE --- A user interface for PSPP"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:875
-msgid "Use Sets"
+#: src/ui/gui/main.c:175
+msgid "Miscellaneous options:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:907
-msgid "Information Area"
-msgstr ""
+#: src/ui/gui/main.c:177 src/ui/terminal/main.c:125
+msgid "Options affecting syntax and behavior:"
+msgstr "Options affecting syntax and behaviour:"
 
-#: src/ui/gui/data-editor.glade:926
-msgid "Processor Area"
+#: src/ui/gui/message-dialog.c:103
+msgid "data file error"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:951
-msgid "Case Counter Area"
+#: src/ui/gui/message-dialog.c:108
+msgid "PSPP error"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:976
-msgid "Filter Use Status Area"
+#: src/ui/gui/message-dialog.c:116
+msgid "syntax warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1002
-msgid "Weight Status Area"
+#: src/ui/gui/message-dialog.c:120
+msgid "data file warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1028
-msgid "Split File Status Area"
+#: src/ui/gui/message-dialog.c:125
+msgid "PSPP warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1058
-msgid "Variable Type"
+#: src/ui/gui/message-dialog.c:134
+msgid "syntax information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1094 src/ui/gui/psppire-var-store.c:599
-msgid "Comma"
+#: src/ui/gui/message-dialog.c:138
+msgid "data file information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1110 src/ui/gui/psppire-var-store.c:600
-msgid "Dot"
+#: src/ui/gui/message-dialog.c:143
+msgid "PSPP information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1126
-msgid "Scientific notation"
-msgstr ""
+#: src/ui/gui/message-dialog.c:222
+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:1142 src/ui/gui/psppire-var-store.c:602
-msgid "Date"
-msgstr ""
+#: src/ui/gui/message-dialog.c:229
+#, 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:1158 src/ui/gui/psppire-var-store.c:603
-msgid "Dollar"
-msgstr ""
+#: src/ui/gui/message-dialog.c:236
+#, 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:1174
-msgid "Custom currency"
+#: src/ui/gui/message-dialog.glade:10
+msgid "Messages Reported"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1268
-msgid "positive"
+#: src/ui/gui/message-dialog.glade:47
+msgid ""
+"The PSPP processor reported # errors.  The first # and last # are shown "
+"below:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1274
-msgid "negative"
+#: src/ui/gui/message-dialog.glade:101
+msgid "gtk-close"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1287
-msgid "Sample"
+#: 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:1337
-msgid "Width:"
+#: 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:1381
-msgid "Decimal Places:"
+#: src/ui/gui/oneway-anova-dialog.c:331
+#, c-format
+msgid "Contrast %d of %d"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1550
-msgid "Value Label:"
+#: src/ui/gui/oneway.glade:30
+msgid "_Factor:"
 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/oneway.glade:66
+msgid "Dependent _Variable(s):"
 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/oneway.glade:190
+msgid "_Homogeneity"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1718
-msgid "_Range plus one optional discrete missing value"
+#: src/ui/gui/oneway.glade:226
+msgid "_Contrasts..."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1743
-msgid "_Low:"
+#: src/ui/gui/oneway.glade:309
+msgid "gtk-go-back"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1772
-msgid "_High:"
+#: src/ui/gui/oneway.glade:320
+msgid "gtk-go-forward"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1813
-msgid "Di_screte value:"
+#: src/ui/gui/oneway.glade:343
+msgid "_Coefficients:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1860
-msgid "_No missing values"
+#: src/ui/gui/oneway.glade:389
+msgid "Coefficient Total: "
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1878
-msgid "_Discrete missing values"
+#: src/ui/gui/oneway.glade:422
+msgid "Contrast 1 of 1"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
-msgid "Standard deviation"
+#: src/ui/gui/output-viewer.glade:32
+msgid "gtk-save"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.c:45
-msgid "Standard error"
+#: src/ui/gui/output-viewer.glade:41
+msgid "gtk-save-as"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
-msgid "Statistics:"
+#: src/ui/gui/output-viewer.glade:65
+msgid "gtk-copy"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:184
-msgid "Exclude entire case if any selected variable is missing"
+#: src/ui/gui/psppire-buttonbox.c:143
+msgid "Buttons"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:194
-msgid "Include user-missing data in analysis"
+#: src/ui/gui/psppire-buttonbox.c:144
+msgid "The mask that decides what buttons appear in the button box"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:207
-msgid "Save Z-scores of selected variables as new variables"
+#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:435
+msgid "Continue"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:223
-msgid "Options:"
+#: src/ui/gui/psppire-buttonbox.c:433
+msgid "OK"
 msgstr ""
 
-#: src/ui/gui/examine.glade:132
-msgid "Dependent List:"
+#: src/ui/gui/psppire-buttonbox.c:434
+msgid "Go To"
 msgstr ""
 
-#: src/ui/gui/examine.glade:180
-msgid "Factor List:"
+#: src/ui/gui/psppire-buttonbox.c:436
+msgid "Cancel"
 msgstr ""
 
-#: src/ui/gui/examine.glade:218
-msgid "Label Cases by:"
+#: src/ui/gui/psppire-buttonbox.c:437
+msgid "Help"
 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:438
+msgid "Reset"
 msgstr ""
 
-#: src/ui/gui/examine.glade:316
-msgid "Extremes"
+#: src/ui/gui/psppire-buttonbox.c:439
+msgid "Paste"
 msgstr ""
 
-#: src/ui/gui/examine.glade:382
-msgid "Exclude cases listwise"
+#: src/ui/gui/psppire.c:247
+msgid "_Reset"
 msgstr ""
 
-#: src/ui/gui/examine.glade:392
-msgid "Exclude cases pairwise"
+#: src/ui/gui/psppire.c:248
+msgid "_Select"
 msgstr ""
 
-#: src/ui/gui/examine.glade:406
-msgid "Repeat values"
+#: src/ui/gui/psppire-data-editor.c:956
+msgid "Data View"
 msgstr ""
 
-#: src/ui/gui/find-dialog.c:659
-#, c-format
-msgid "Bad regular expression: %s"
+#: src/ui/gui/psppire-data-editor.c:959
+msgid "Variable View"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:44
-msgid "Standard error of the mean"
+#: src/ui/gui/psppire-data-store.c:761
+msgid "var"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:47
-msgid "Standard error of the skewness"
+#: src/ui/gui/psppire-data-store.c:771 src/ui/gui/psppire-var-store.c:655
+#: src/ui/gui/psppire-var-store.c:665 src/ui/gui/psppire-var-store.c:675
+#: src/ui/gui/psppire-var-store.c:786
+#, c-format
+msgid "%d"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:51
-msgid "Standard error of the kurtosis"
+#: src/ui/gui/psppire-data-window.c:213
+msgid "Transformations Pending"
 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-data-window.c:229
+msgid "Filter off"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:168
-msgid "Display Frequency Table"
+#: src/ui/gui/psppire-data-window.c:241
+#, c-format
+msgid "Filter by %s"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:264
-msgid "Ascending Order"
+#: src/ui/gui/psppire-data-window.c:262
+msgid "No Split"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:275
-msgid "Descending Order"
+#: src/ui/gui/psppire-data-window.c:271
+msgid "Split by "
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:290
-msgid "Ascending Counts"
+#: src/ui/gui/psppire-data-window.c:299
+msgid "Weights off"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:305
-msgid "Descending Counts"
+#: src/ui/gui/psppire-data-window.c:311
+#, c-format
+msgid "Weight by %s"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:323
-msgid "Order by"
+#: src/ui/gui/psppire-data-window.c:387 src/ui/gui/psppire-data-window.c:589
+msgid "System Files (*.sav)"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:355
-msgid "Supress tables with more than N categories"
+#: src/ui/gui/psppire-data-window.c:393 src/ui/gui/psppire-data-window.c:595
+msgid "Portable Files (*.por) "
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:371
-msgid "Maximum no of categories"
+#: src/ui/gui/psppire-data-window.c:399 src/ui/gui/psppire-data-window.c:601
+#: src/ui/gui/psppire-syntax-window.c:298
+#: src/ui/gui/psppire-syntax-window.c:385
+msgid "All Files"
 msgstr ""
 
-#: src/ui/gui/helper.c:139
-msgid "Sorry. The help system hasn't yet been implemented."
+#: src/ui/gui/psppire-data-window.c:609
+msgid "System File"
 msgstr ""
 
-#: src/ui/gui/helper.c:165
-#, c-format
-msgid "Cannot open reference manual: %s"
+#: src/ui/gui/psppire-data-window.c:614
+msgid "Portable File"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:101
-msgid "data file error"
+#: src/ui/gui/psppire-data-window.c:764
+msgid "Font Selection"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:106
-msgid "PSPP error"
+#: src/ui/gui/psppire-data-window.c:832
+msgid "Sort Ascending"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:114
-msgid "syntax warning"
+#: src/ui/gui/psppire-data-window.c:838
+msgid "Sort Descending"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:118
-msgid "data file warning"
+#: src/ui/gui/psppire-data-window.c:846 src/ui/gui/psppire-data-window.c:900
+#: src/ui/gui/psppire-data-window.c:936 src/ui/gui/psppire-data-window.c:1301
+#: src/ui/gui/psppire-data-window.c:1319
+msgid "Clear"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:123
-msgid "PSPP warning"
+#: src/ui/gui/psppire-data-window.c:1178
+msgid "Open a data file"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:132
-msgid "syntax information"
+#: src/ui/gui/psppire-data-window.c:1196
+msgid "New data file"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:136
-msgid "data file information"
+#: src/ui/gui/psppire-data-window.c:1211
+msgid "Import text data file"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:141
-msgid "PSPP information"
+#: src/ui/gui/psppire-data-window.c:1227 src/ui/gui/psppire-data-window.c:1244
+msgid "Save data to file"
 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/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/message-dialog.glade:8
-msgid "Messages Reported"
+#: src/ui/gui/psppire-data-window.c:1243
+msgid "Save As"
 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:1282
+msgid "Show/hide value labels"
 msgstr ""
 
-#: src/ui/gui/message-dialog.glade:94
-msgid "gtk-close"
+#: src/ui/gui/psppire-data-window.c:1302
+msgid "Delete the cases at the selected position(s)"
 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:1320
+msgid "Delete the variables at the selected position(s)"
 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:1338
+msgid "Create a new variable at the current position"
 msgstr ""
 
-#: src/ui/gui/oneway-anova-dialog.c:335
-#, c-format
-msgid "Contrast %d of %d"
+#: src/ui/gui/psppire-data-window.c:1353
+msgid "Create a new case at the current position"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:30
-msgid "_Factor:"
+#: src/ui/gui/psppire-data-window.c:1369
+msgid "Jump to a Case in the Data Sheet"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:66
-msgid "Dependent _Variable(s):"
+#: src/ui/gui/psppire-data-window.c:1385
+msgid "Weight cases by variable"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:190
-msgid "_Homogeneity"
+#: src/ui/gui/psppire-data-window.c:1399
+msgid "Transpose the cases with the variables"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:226
-msgid "_Contrasts..."
+#: src/ui/gui/psppire-data-window.c:1413
+msgid "Split the active file"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:309
-msgid "gtk-go-back"
+#: src/ui/gui/psppire-data-window.c:1428
+msgid "Sort cases in the active file"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:320
-msgid "gtk-go-forward"
+#: src/ui/gui/psppire-data-window.c:1442
+msgid "Select cases from the active file"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:343
-msgid "_Coefficients:"
+#: src/ui/gui/psppire-data-window.c:1456
+msgid "Compute new values for a variable"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:389
-msgid "Coefficient Total: "
+#: src/ui/gui/psppire-data-window.c:1470
+msgid "Perform one way analysis of variance"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:422
-msgid "Contrast 1 of 1"
+#: src/ui/gui/psppire-data-window.c:1485
+msgid "Calculate T Test for samples from independent groups"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:32
-msgid "gtk-save"
+#: src/ui/gui/psppire-data-window.c:1499
+msgid "Calculate T Test for paired samples"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:41
-msgid "gtk-save-as"
+#: src/ui/gui/psppire-data-window.c:1513
+msgid "Calculate T Test for sample from a single distribution"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:65
-msgid "gtk-copy"
+#: src/ui/gui/psppire-data-window.c:1528
+msgid "Commentary text for the data file"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:143
-msgid "Buttons"
+#: src/ui/gui/psppire-data-window.c:1554
+msgid "Rank Cases"
 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:1568
+msgid "Recode values into the same variables"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:429
-msgid "Continue"
+#: src/ui/gui/psppire-data-window.c:1582
+msgid "Recode values into different variables"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:427
-msgid "OK"
+#: src/ui/gui/psppire-data-window.c:1596
+msgid "Jump to variable"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:428
-msgid "Go To"
+#: src/ui/gui/psppire-data-window.c:1609
+msgid "Calculate descriptive statistics (mean, variance, ...)"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:430
-msgid "Cancel"
+#: src/ui/gui/psppire-data-window.c:1623
+msgid "Generate frequency statistics"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:431
-msgid "Help"
+#: src/ui/gui/psppire-data-window.c:1637
+msgid "Generate crosstabulations"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:432
-msgid "Reset"
+#: src/ui/gui/psppire-data-window.c:1652
+msgid "Examine Data by Factors"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:433
-msgid "Paste"
+#: src/ui/gui/psppire-data-window.c:1666
+msgid "Estimate parameters of the linear model"
 msgstr ""
 
-#: src/ui/gui/psppire.c:194
-msgid "_Reset"
+#: src/ui/gui/psppire-data-window.c:1680 src/ui/gui/reliability.glade:7
+msgid "Reliability Analysis"
 msgstr ""
 
-#: src/ui/gui/psppire.c:195
-msgid "_Select"
+#: src/ui/gui/psppire-data-window.c:1843
+msgid "Split the window vertically and horizontally"
 msgstr ""
 
-#: src/ui/gui/psppire-data-editor.c:604
-msgid "Data View"
+#: src/ui/gui/psppire-data-window.c:1885
+msgid "Data Editor"
 msgstr ""
 
-#: src/ui/gui/psppire-data-editor.c:607
-msgid "Variable View"
+#: src/ui/gui/psppire-dictview.c:207
+msgid "The dictionary to be displayed by this widget"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:828
-msgid "var"
+#: src/ui/gui/psppire-dictview.c:214
+msgid "A predicate function"
 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-dictview.c:221
+msgid "How many things can be selected"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:11
-msgid ""
-"This is beta status software.  Please report bugs to bug-gnu-pspp@gnu.org"
+#: src/ui/gui/psppire-dictview.c:539
+msgid "Prefer variable labels"
 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:47 src/ui/gui/psppire.glade:130
+#: src/ui/gui/weight-cases-dialog.c:79
 msgid "Do not weight cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:83
+#: src/ui/gui/psppire.glade:58
 msgid "Weight cases by"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:107
+#: src/ui/gui/psppire.glade:83
 msgid "Frequency Variable"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:147
+#: src/ui/gui/psppire.glade:123
 msgid "Current Status: "
 msgstr ""
 
-#: src/ui/gui/psppire.glade:314
+#: src/ui/gui/psppire.glade:219
 msgid "Name Variable:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:429
+#: src/ui/gui/psppire.glade:404
 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:415
 msgid "Compare groups."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:452
+#: src/ui/gui/psppire.glade:429
 msgid "Organize output by groups."
-msgstr ""
+msgstr "Organise output by groups."
 
-#: src/ui/gui/psppire.glade:499
+#: src/ui/gui/psppire.glade:477
 msgid "Groups based on:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:563
+#: src/ui/gui/psppire.glade:540
 msgid "Sort the file by grouping variables."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:574
+#: src/ui/gui/psppire.glade:552
 msgid "File is already sorted."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:618
+#: src/ui/gui/psppire.glade:597
 msgid "Current Status : "
 msgstr ""
 
-#: src/ui/gui/psppire.glade:626
+#: src/ui/gui/psppire.glade:605
 msgid "Analysis by groups is off"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:725
+#: src/ui/gui/psppire.glade:704
 msgid "Sort by:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:788
+#: src/ui/gui/psppire.glade:767
 msgid "Descending"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:804
+#: src/ui/gui/psppire.glade:784
 msgid "Sort Order"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:873
+#: src/ui/gui/psppire.glade:853
 msgid "Target Variable:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:904
+#: src/ui/gui/psppire.glade:884
 msgid "Type & Label"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:943
+#: src/ui/gui/psppire.glade:924
 msgid "="
 msgstr ""
 
-#: src/ui/gui/psppire.glade:989
+#: src/ui/gui/psppire.glade:970
 msgid "Numeric Expressions:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1043
+#: src/ui/gui/psppire.glade:1024
 msgid "Functions:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1107 src/ui/gui/psppire.glade:1253
+#: src/ui/gui/psppire.glade:1087 src/ui/gui/psppire.glade:1491
 #: src/ui/gui/recode.glade:731
 msgid "If..."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1223
-msgid "All Cases"
+#: src/ui/gui/psppire.glade:1320
+msgid "Use filter variable"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1239
-msgid "If condition is satisfied"
+#: src/ui/gui/psppire.glade:1373
+msgid "Based on time or case range"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1291
-msgid "Random sample of cases"
+#: src/ui/gui/psppire.glade:1386
+msgid "Range..."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1305
-msgid "Sample..."
+#: src/ui/gui/psppire.glade:1425
+msgid "Random sample of cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1343
-msgid "Based on time or case range"
+#: src/ui/gui/psppire.glade:1439
+msgid "Sample..."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1356
-msgid "Range..."
+#: src/ui/gui/psppire.glade:1477
+msgid "If condition is satisfied"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1394
-msgid "Use filter variable"
+#: src/ui/gui/psppire.glade:1526
+msgid "All Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1556
+#: src/ui/gui/psppire.glade:1541
 msgid "Select"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1586
+#: src/ui/gui/psppire.glade:1570
 msgid "Filtered"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1596
+#: src/ui/gui/psppire.glade:1581
 msgid "Deleted"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1613
+#: src/ui/gui/psppire.glade:1599
 msgid "Unselected Cases Are"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1678
+#: src/ui/gui/psppire.glade:1664
 msgid "Comments:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1720
+#: src/ui/gui/psppire.glade:1706
 msgid "Display comments in output"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1734
+#: src/ui/gui/psppire.glade:1721
 msgid "Column Number: 0"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1810
-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:1804
+msgid "First case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1910
+#: src/ui/gui/psppire.glade:1817
 msgid "Last case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1923
-msgid "First case"
+#: src/ui/gui/psppire.glade:1830
+msgid "Observation"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2081
+#: src/ui/gui/psppire.glade:1894
 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:2020 src/ui/gui/psppire-var-sheet.c:529
+#: src/ui/gui/psppire-var-store.c:795
 msgid "Width"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2274
+#: src/ui/gui/psppire.glade:2150
 msgid "Goto Case Number:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2410
+#: src/ui/gui/psppire.glade:2287
 msgid "Sample Size"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2513
-msgid "Variable:"
+#: src/ui/gui/psppire-output-window.c:269
+msgid "Output Viewer"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2570
-msgid "Search value labels"
+#: src/ui/gui/psppire-syntax-window.c:265
+#, c-format
+msgid "Saved file \"%s\""
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2593
-msgid "Regular expression Match"
+#: src/ui/gui/psppire-syntax-window.c:284
+msgid "Save Syntax"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2603
-msgid "Search substrings"
+#: src/ui/gui/psppire-syntax-window.c:292
+#: src/ui/gui/psppire-syntax-window.c:379
+msgid "Syntax Files (*.sps) "
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2615
-msgid "Wrap around"
+#: src/ui/gui/psppire-syntax-window.c:371
+msgid "Open Syntax"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2627
-msgid "Search backward"
+#: src/ui/gui/psppire-syntax-window.c:551
+msgid "Syntax Editor"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:100
+#: src/ui/gui/psppire-syntax-window.c:565
+#, c-format
+msgid "Cannot load syntax file '%s'"
+msgstr ""
+
+#: src/ui/gui/psppire-var-sheet.c:527 src/ui/gui/psppire-var-store.c:793
 msgid "Name"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:103
+#: src/ui/gui/psppire-var-sheet.c:530 src/ui/gui/psppire-var-store.c:796
 msgid "Decimals"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:105
+#: src/ui/gui/psppire-var-sheet.c:532 src/ui/gui/psppire-var-store.c:798
 msgid "Values"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:108
+#: src/ui/gui/psppire-var-sheet.c:535 src/ui/gui/psppire-var-store.c:801
 msgid "Align"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:109
+#: src/ui/gui/psppire-var-sheet.c:536 src/ui/gui/psppire-var-store.c:802
 msgid "Measure"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:601
+#: src/ui/gui/psppire-var-store.c:578 src/ui/gui/var-sheet-dialogs.glade:43
+msgid "Comma"
+msgstr ""
+
+#: src/ui/gui/psppire-var-store.c:579 src/ui/gui/var-sheet-dialogs.glade:59
+msgid "Dot"
+msgstr ""
+
+#: src/ui/gui/psppire-var-store.c:580
 msgid "Scientific"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:604
+#: src/ui/gui/psppire-var-store.c:581 src/ui/gui/var-sheet-dialogs.glade:91
+msgid "Date"
+msgstr ""
+
+#: src/ui/gui/psppire-var-store.c:582 src/ui/gui/var-sheet-dialogs.glade:107
+msgid "Dollar"
+msgstr ""
+
+#: src/ui/gui/psppire-var-store.c:583
 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:97
 #, c-format
-msgid "%d"
+msgid "%s %s PSPPIRE %s"
+msgstr ""
+
+#: src/ui/gui/psppire-window.c:480
+#, c-format
+msgid "Save the changes to \"%s\" before closing?"
 msgstr ""
 
-#: src/ui/gui/rank.glade:111
+#: src/ui/gui/psppire-window.c:487
+#, c-format
+msgid ""
+"If you don't save, changes from the last %ld seconds will be permanently "
+"lost."
+msgstr ""
+
+#: src/ui/gui/psppire-window.c:491
+msgid "Close _without saving"
+msgstr ""
+
+#: 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 +6023,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:881
 msgid "Recode into Different Variables"
 msgstr ""
 
-#: src/ui/gui/recode-dialog.c:882
+#: src/ui/gui/recode-dialog.c:884
 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 ""
 
@@ -6062,15 +6167,15 @@ msgstr ""
 msgid "Old and New Values"
 msgstr ""
 
-#: src/ui/gui/regression-dialog.c:40
+#: src/ui/gui/regression-dialog.c:41
 msgid "Coeff"
 msgstr ""
 
-#: src/ui/gui/regression-dialog.c:42
+#: src/ui/gui/regression-dialog.c:43
 msgid "Anova"
 msgstr ""
 
-#: src/ui/gui/regression-dialog.c:43
+#: src/ui/gui/regression-dialog.c:44
 msgid "Bcov"
 msgstr ""
 
@@ -6094,99 +6199,90 @@ msgstr ""
 msgid "Residuals"
 msgstr ""
 
-#: src/ui/gui/select-cases-dialog.c:81
-#, c-format
-msgid "Approximately %3d%% of all cases."
-msgstr ""
-
-#: src/ui/gui/select-cases-dialog.c:82
-#, c-format
-msgid "Exactly %3d cases from the first %3d cases."
+#: src/ui/gui/reliability.glade:89
+msgid "_Items:"
 msgstr ""
 
-#: src/ui/gui/select-cases-dialog.c:222
-#, c-format
-msgid "%d thru %d"
+#: src/ui/gui/reliability.glade:111
+msgid "Model:\t"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:77
-#, c-format
-msgid "Save contents of syntax editor to %s?"
+#: src/ui/gui/reliability.glade:122
+msgid ""
+"Alpha\n"
+"Split"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:124
-msgid "Save Syntax"
+#: src/ui/gui/reliability.glade:144
+msgid "Variables in first split:"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:132 src/ui/gui/syntax-editor.c:516
-msgid "Syntax Files (*.sps) "
+#: src/ui/gui/select-cases-dialog.c:82
+#, c-format
+msgid "Approximately %3d%% of all cases."
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:508
-msgid "Open Syntax"
+#: src/ui/gui/select-cases-dialog.c:83
+#, c-format
+msgid "Exactly %3d cases from the first %3d cases."
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:10
-msgid "Psppire Syntax Editor"
+#: src/ui/gui/select-cases-dialog.c:223
+#, c-format
+msgid "%d thru %d"
 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 ""
 
-#: src/ui/gui/text-data-import-dialog.c:70
-msgid ""
-"The text import assistant has not been compiled into this build of PSPPIRE, "
-"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:461
 #, 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:477
 #, 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:480
 #, 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:494
 #, c-format
 msgid "\"%s\" is empty."
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:566
+#: src/ui/gui/text-data-import-dialog.c:539
 msgid "Import Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:617
+#: src/ui/gui/text-data-import-dialog.c:590
 msgid "Importing Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:768
+#: src/ui/gui/text-data-import-dialog.c:749
 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 +6290,21 @@ msgid ""
 "\n"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:774
+#: src/ui/gui/text-data-import-dialog.c:755
 #, 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:763
 #, 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:769
 #, c-format
 msgid ""
 "Only the first %zu line of the file will be shown for preview purposes in "
@@ -6219,25 +6315,25 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/ui/gui/text-data-import-dialog.c:795
+#: src/ui/gui/text-data-import-dialog.c:776
 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:1523
+#: src/ui/gui/text-data-import-dialog.c:1765
 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:1756
 #, c-format
 msgid "Field content \"%.*s\" cannot be parsed in format %s."
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:7
+#: src/ui/gui/text-data-import.glade:8
 msgid "Importing Textual Data"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:16
+#: src/ui/gui/text-data-import.glade:18
 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 "
@@ -6248,124 +6344,117 @@ msgid ""
 "below how much of the file should actually be imported."
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:47
+#: src/ui/gui/text-data-import.glade:50
 msgid "All cases"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:62 src/ui/gui/text-data-import.glade:117
+#: src/ui/gui/text-data-import.glade:66 src/ui/gui/text-data-import.glade:122
 msgid "Only first "
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:93
+#: src/ui/gui/text-data-import.glade:97
 msgid " cases"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:147
+#: src/ui/gui/text-data-import.glade:152
 msgid "% of file (approximately)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:168
+#: src/ui/gui/text-data-import.glade:173
 msgid "<b>Amount to Import</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:189
+#: src/ui/gui/text-data-import.glade:195
 msgid "Select Data to Import"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:198
+#: src/ui/gui/text-data-import.glade:205
 msgid "Select the first line of the data file that contains data."
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:209
+#: src/ui/gui/text-data-import.glade:236
 msgid "Line above selected line contains variable names"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:243
+#: src/ui/gui/text-data-import.glade:251
 msgid "Choose Separators"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:274
-msgid "_Space"
-msgstr ""
-
-#: src/ui/gui/text-data-import.glade:285
-msgid "Ta_b"
+#: src/ui/gui/text-data-import.glade:299
+msgid "C_ustom"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:300
-msgid "Ban_g (!)"
+#: src/ui/gui/text-data-import.glade:314
+msgid "Slas_h (/)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:315
-msgid "_Colon (:)"
+#: src/ui/gui/text-data-import.glade:331
+msgid "Semicolo_n (;)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:330
-msgid "Co_mma (,)"
+#: src/ui/gui/text-data-import.glade:348
+msgid "P_ipe (|)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:347
+#: src/ui/gui/text-data-import.glade:363
 msgid "H_yphen (-)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:364
-msgid "P_ipe (|)"
+#: src/ui/gui/text-data-import.glade:380
+msgid "Co_mma (,)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:379
-msgid "Semicolo_n (;)"
+#: src/ui/gui/text-data-import.glade:397
+msgid "_Colon (:)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:396
-msgid "Slas_h (/)"
+#: src/ui/gui/text-data-import.glade:412
+msgid "Ban_g (!)"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:413
-msgid "C_ustom"
+#: src/ui/gui/text-data-import.glade:427
+msgid "Ta_b"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:444
-msgid "<b>Separators</b>"
+#: src/ui/gui/text-data-import.glade:442
+msgid "_Space"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:475
-msgid "Quote separator characters with"
+#: src/ui/gui/text-data-import.glade:456
+msgid "<b>Separators</b>"
 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:526
+msgid "Quote separator characters with"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:533
+#: src/ui/gui/text-data-import.glade:543
 msgid "<b>Quoting</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:584
+#: src/ui/gui/text-data-import.glade:594
 msgid "<b>Fields Preview</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:601
+#: src/ui/gui/text-data-import.glade:612
 msgid "Adjust Variable Formats"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:610
+#: src/ui/gui/text-data-import.glade:622
 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:665
 msgid "<b>Variables</b>"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:700
+#: src/ui/gui/text-data-import.glade:712
 msgid "<b>Data Preview</b>"
 msgstr ""
 
@@ -6411,132 +6500,168 @@ msgstr ""
 msgid "Confidence Interval: %2d %%"
 msgstr ""
 
-#: src/ui/gui/t-test-paired-samples.c:228
+#: src/ui/gui/t-test-paired-samples.c:227
 msgid "Var 1"
 msgstr ""
 
-#: src/ui/gui/t-test-paired-samples.c:229
+#: src/ui/gui/t-test-paired-samples.c:228
 msgid "Var 2"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:89
+#: src/ui/gui/variable-info-dialog.c:92
 #, c-format
 msgid "Label: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:98
+#: src/ui/gui/variable-info-dialog.c:101
 #, c-format
 msgid "Type: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:102
+#: src/ui/gui/variable-info-dialog.c:105
 #, c-format
 msgid "Missing Values: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:107
+#: src/ui/gui/variable-info-dialog.c:110
 #, c-format
 msgid "Measurement Level: %s\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:121
+#: src/ui/gui/variable-info-dialog.c:125
 msgid "Value Labels:\n"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:133
+#: src/ui/gui/variable-info-dialog.c:138
 #, c-format
 msgid "%s %s\n"
 msgstr ""
 
-#: src/ui/gui/weight-cases-dialog.c:86
+#: src/ui/gui/variable-info-dialog.glade:49
+msgid "Variable Information:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:7
+msgid "Variable Type"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:75
+msgid "Scientific notation"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:123
+msgid "Custom currency"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:217
+msgid "positive"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:223
+msgid "negative"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:236
+msgid "Sample"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:286
+msgid "Width:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:330
+msgid "Decimal Places:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:499
+msgid "Value Label:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:677
+msgid "_No missing values"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:747
+msgid "_Discrete missing values"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:782
+msgid "_Low:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:801
+msgid "_High:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:826
+msgid "Di_screte value:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:856
+msgid "_Range plus one optional discrete missing value"
+msgstr ""
+
+#: 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/terminal/command-line.c:230
+#: src/ui/source-init-opts.c:48
+msgid "Set to `compatible' if you want only to accept SPSS compatible syntax"
+msgstr ""
+
+#: 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:119 src/ui/terminal/terminal-opts.c:177
+msgid "Options affecting input and output locations:"
+msgstr ""
+
+#: src/ui/terminal/main.c:122 src/ui/terminal/terminal-opts.c:178
+msgid "Diagnostic options:"
 msgstr ""
 
-#: src/ui/terminal/main.c:130
+#: src/ui/terminal/main.c:156
 msgid ""
 "Stopping syntax file processing here to avoid a cascade of dependent command "
 "failures."
@@ -6574,6 +6699,29 @@ 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 ""
+
+#~ msgid "Unrecognized record type 7, subtype %d."
+#~ msgstr "Unrecognised record type 7, subtype %d."
+
 #~ 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"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644 (file)
index 0000000..db3c9d3
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,7133 @@
+# translation of nl.po to Dutch
+# translation of nl.po to
+# Dutch translations for PSPP
+# Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+# This file is distributed under the same licence as the PSPP package.
+#
+#
+#
+# Vertaalde woorden:
+#  Bad        = Foutief
+#  Case       = Case
+#  Cell       = Cel
+#  Command    = Opdracht
+#  Display    = Toon
+#  Dictionary = Woordenboek
+#  Invalid    = Ongeldig
+#  Missing    = Ontbrekende
+#  Required   = vereist
+#  Ranking    = Ordenen
+#  Rename     = Hernoemd
+#  Range      = Range
+#  Rank       = Rang/Rangschik
+#  Specififed = Opgegeven
+#  Stream     = Stream
+#  String     = String
+#  Ties       = ??
+#  View       = Beeld/Weergave
+#  Window     = venster
+#  Weighting  = Weging
+#
+# pspp <pspp@sjpaes.nl>, 2009.
+msgid ""
+msgstr ""
+"Project-Id-Version: nl\n"
+"Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
+"POT-Creation-Date: 2009-07-17 15:23+0800\n"
+"PO-Revision-Date: 2009-05-15 18:24+0200\n"
+"Last-Translator:  <pspp@sjpaes.nl>\n"
+"Language-Team: Dutch\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: KBabel 1.11.4\n"
+
+#: src/data/any-reader.c:57
+#, c-format
+msgid "An error occurred while opening \"%s\": %s."
+msgstr "Er is een fout opgetreden tijdens het openen van \"%s\": %s."
+
+#: src/data/any-reader.c:93
+#, c-format
+msgid "\"%s\" is not a system or portable file."
+msgstr "\"%s\" is geen systeem of overdraagbaar (portable) bestand."
+
+#: src/data/any-reader.c:99 src/data/any-writer.c:63
+msgid "The inline file is not allowed here."
+msgstr "Het 'inline' bestand is hier niet toegestaan."
+
+#: src/data/calendar.c:81
+#, c-format
+msgid "Month %d is not in acceptable range of 0 to 13."
+msgstr "Maand %d valt niet in de acceptabele range van 0 tot 13."
+
+#: src/data/calendar.c:89
+#, c-format
+msgid "Day %d is not in acceptable range of 0 to 31."
+msgstr "Dag %d valt niet in de acceptabele range van 0 tot 31."
+
+#: src/data/calendar.c:96
+#, c-format
+msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
+msgstr "Datum %04d-%d-%d is voor de eerste acceptabele datum van 1582-10-15."
+
+#: src/data/casereader-filter.c:221
+msgid ""
+"At least one case in the data read had a weight value that was user-missing, "
+"system-missing, zero, or negative.  These case(s) were ignored."
+msgstr ""
+"Ten minste een case in de gelezen data heeft een gewicht waarde 'user-"
+"missing, system-missing, zero, of negatief.  Deze case(s) zijn genegeerd."
+
+#: src/data/data-in.c:263 src/data/data-in.c:453
+msgid "Field contents are not numeric."
+msgstr "Veld inhoud is niet numeriek."
+
+#: src/data/data-in.c:265 src/data/data-in.c:455
+msgid "Number followed by garbage."
+msgstr "Nummer gevolgd door rommel."
+
+#: src/data/data-in.c:276
+msgid "Invalid numeric syntax."
+msgstr "Ongeldige numerieke syntax."
+
+#: src/data/data-in.c:285 src/data/data-in.c:468
+msgid "Too-large number set to system-missing."
+msgstr "Te groot getal, is op system-missing gezet."
+
+#: src/data/data-in.c:290 src/data/data-in.c:473
+msgid "Too-small number set to zero."
+msgstr "Te klein getal, is op nul gezet."
+
+#: src/data/data-in.c:316
+msgid "All characters in field must be digits."
+msgstr "Alle karakters in veld moeten cijfers zijn."
+
+#: src/data/data-in.c:339
+msgid "Unrecognized character in field."
+msgstr "Onherkenbaar karakter in veld."
+
+#: src/data/data-in.c:363 src/data/data-in.c:638
+msgid "Field must have even length."
+msgstr "Veld moet een even lengte hebben."
+
+#: src/data/data-in.c:368 src/data/data-in.c:649
+msgid "Field must contain only hex digits."
+msgstr "Veld mag alleen hex cijfers bevatten."
+
+#: src/data/data-in.c:688 src/data/data-in.c:735
+msgid "Syntax error in date field."
+msgstr "Syntax fout in datum veld."
+
+#: src/data/data-in.c:704
+#, c-format
+msgid "Day (%ld) must be between 1 and 31."
+msgstr "Dag (%ld) moet tussen 1 en 31 zijn."
+
+#: src/data/data-in.c:751
+msgid "Delimiter expected between fields in date."
+msgstr "Veldscheider verwacht tussen velden in datum."
+
+#: src/data/data-in.c:825
+msgid ""
+"Unrecognized month format.  Months may be specified as Arabic or Roman "
+"numerals or as at least 3 letters of their English names."
+msgstr ""
+"Niet herkend maand formaat.  Maanden mogen gespecificeerd zijn als Arabisch "
+"of Romeins numeriek of als tenminste de eerste 3 letters van hun Engelse "
+"naam."
+
+#: src/data/data-in.c:852
+#, c-format
+msgid "Year (%ld) must be between 1582 and 19999."
+msgstr "Jaar (%ld) moet tussen 1582 en 19999 zijn."
+
+#: src/data/data-in.c:864
+#, c-format
+msgid "Trailing garbage \"%.*s\" following date."
+msgstr "Afsluitende rommel \"%.*s\" achter datum."
+
+#: src/data/data-in.c:880
+msgid "Julian day must have exactly three digits."
+msgstr "Juliaanse datum moet bestaan uit precies 3 cijfers."
+
+#: src/data/data-in.c:885
+#, c-format
+msgid "Julian day (%ld) must be between 1 and 366."
+msgstr "Juliaanse dag (%ld) moet tussen 1 en 366 zijn."
+
+#: src/data/data-in.c:909
+#, c-format
+msgid "Quarter (%ld) must be between 1 and 4."
+msgstr "Kwartaal (%ld) moet tussen 1 en 4 zijn."
+
+#: src/data/data-in.c:929
+#, c-format
+msgid "Week (%ld) must be between 1 and 53."
+msgstr "Week (%ld) moet tussen 1 en 53 zijn."
+
+#: src/data/data-in.c:942
+msgid "Delimiter expected between fields in time."
+msgstr "Veldscheider verwacht tussen velden in tijd."
+
+#: src/data/data-in.c:962
+#, c-format
+msgid "Minute (%ld) must be between 0 and 59."
+msgstr "Minuut (%ld) moet tussen 0 en 59 zijn."
+
+#: src/data/data-in.c:1002
+msgid ""
+"Unrecognized weekday name.  At least the first two letters of an English "
+"weekday name must be specified."
+msgstr ""
+"Niet herkende weekdag naam.  Tenminste de eerste 2 letters van een Engelse "
+"weekdag naam moeten opgegeven worden."
+
+#: src/data/data-in.c:1140
+#, c-format
+msgid "`%c' expected in date field."
+msgstr "`%c' verwacht in datum veld."
+
+#: src/data/data-in.c:1181
+#, c-format
+msgid "column %d"
+msgstr "kolom %d"
+
+#: src/data/data-in.c:1183
+#, c-format
+msgid "columns %d-%d"
+msgstr "kolommen %d-%d"
+
+#: src/data/data-in.c:1187
+#, c-format
+msgid "%s field) "
+msgstr "%s veld) "
+
+#: src/data/data-out.c:449
+#, c-format
+msgid "Weekday number %f is not between 1 and 7."
+msgstr "Weekdag nummer %f is niet tussen 1 en 7."
+
+#: src/data/data-out.c:470
+#, c-format
+msgid "Month number %f is not between 1 and 12."
+msgstr "Maand nummer %f is niet tussen 1 en 12."
+
+#: src/data/dict-class.c:52
+msgid "ordinary"
+msgstr "gewoon"
+
+#: src/data/dict-class.c:54
+msgid "system"
+msgstr "systeem"
+
+#: src/data/dict-class.c:56
+msgid "scratch"
+msgstr ""
+
+#: src/data/dictionary.c:940
+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 ""
+"Op zijn minst een case in het data bestand heeft een gewicht waarde user-"
+"missing, system-missing, nul, of negatief.  Deze case(s) zijn genegeerd."
+
+#: src/data/dictionary.c:1263
+#, c-format
+msgid "Truncating document line to %d bytes."
+msgstr "Document regel afgekapt tot %d bytes."
+
+#: src/data/file-handle-def.c:462
+#, c-format
+msgid "Can't read from %s as a %s because it is already being read as a %s."
+msgstr ""
+"Kan niet lezen van %s als een %s omdat het al gelezen wordt als een %s."
+
+#: src/data/file-handle-def.c:466
+#, c-format
+msgid "Can't write to %s as a %s because it is already being written as a %s."
+msgstr ""
+"Kan niet schrijven naar %s als een %s omdat het al geschreven wordt als een %"
+"s."
+
+#: src/data/file-handle-def.c:473
+#, c-format
+msgid "Can't re-open %s as a %s."
+msgstr "Kan %s niet heropenen als een %s."
+
+#: src/data/file-name.c:131
+#, c-format
+msgid "searching for \"%s\" in path \"%s\""
+msgstr "zoeken naar \"%s\" in pad \"%s\""
+
+#: src/data/file-name.c:146
+#, c-format
+msgid "...found \"%s\""
+msgstr "...gevonden \"%s\""
+
+#: src/data/file-name.c:153
+msgid "...not found"
+msgstr "...niet gevonden"
+
+#: src/data/file-name.c:243
+#, c-format
+msgid "Not opening pipe file `%s' because SAFER option set."
+msgstr ""
+
+#: src/data/format.c:235
+msgid "Input format"
+msgstr "Invoer formaat"
+
+#: src/data/format.c:235
+msgid "Output format"
+msgstr "Uitvoer formaat"
+
+#: src/data/format.c:244
+#, c-format
+msgid "Format %s may not be used for input."
+msgstr "Formaat %s mag niet gebruikt worden voor invoer."
+
+#: src/data/format.c:251
+#, c-format
+msgid "%s specifies width %d, but %s requires an even width."
+msgstr "%s specificeert breedte %d, maar %s vereist een even breedte."
+
+#: src/data/format.c:260
+#, c-format
+msgid "%s %s specifies width %d, but %s requires a width between %d and %d."
+msgstr ""
+"%s %s specificeert breedte %d, maar %s vereist een breedte tussen %d en %d."
+
+#: src/data/format.c:269
+#, c-format
+msgid "%s %s specifies %d decimal place, but %s does not allow any decimals."
+msgid_plural ""
+"%s %s specifies %d decimal places, but %s does not allow any decimals."
+msgstr[0] ""
+"%s %s specificeert %d decima(a)l(en), maar %s staat geen decimalen toe."
+msgstr[1] "%s %s specificeert %d decimalen, maar %s staat geen decimalen toe."
+
+#: src/data/format.c:280
+#, c-format
+msgid ""
+"%s %s specifies %d decimal place, but the given width allows at most %d "
+"decimals."
+msgid_plural ""
+"%s %s specifies %d decimal places, but the given width allows at most %d "
+"decimals."
+msgstr[0] ""
+"%s %s specificeert %d decimalen, maar de opgegeven breedte staat maximaal %d "
+"decimalentoe."
+msgstr[1] ""
+"%s %s specificeert %d decimalen, maar de opgegeven breedte staat maximaal %d "
+"toe."
+
+#: src/data/format.c:287
+#, c-format
+msgid ""
+"%s %s specifies %d decimal place, but the given width does not allow for any "
+"decimals."
+msgid_plural ""
+"%s %s specifies %d decimal places, but the given width does not allow for "
+"any decimals."
+msgstr[0] ""
+"%s %s specificeert %d decima(a)l(en), maar de opgegeven breedte staat geen "
+"decimalen toe."
+msgstr[1] ""
+"%s %s specificeert %d decimalen, maar de opgegeven breedte staat geen "
+"decimalen toe."
+
+#: src/data/format.c:326
+#, c-format
+msgid "%s variables are not compatible with %s format %s."
+msgstr "%s variabelen zijn niet compatibel met %s formaat %s."
+
+#: src/data/format.c:327 src/data/sys-file-reader.c:663
+#: src/ui/gui/psppire.glade:2009 src/ui/gui/psppire-var-store.c:584
+#: src/ui/gui/var-sheet-dialogs.glade:139
+msgid "String"
+msgstr ""
+
+#: src/data/format.c:327 src/data/sys-file-reader.c:663
+#: src/ui/gui/psppire.glade:2084 src/ui/gui/psppire-var-store.c:577
+#: src/ui/gui/var-sheet-dialogs.glade:28
+msgid "Numeric"
+msgstr "Numeriek"
+
+#: src/data/format.c:328 src/data/sys-file-reader.c:1228
+#: src/data/sys-file-reader.c:1230
+#: src/language/dictionary/apply-dictionary.c:78
+#: src/language/dictionary/apply-dictionary.c:79
+#: src/language/xforms/recode.c:490 src/language/xforms/recode.c:491
+#: src/language/xforms/recode.c:503 src/language/xforms/recode.c:504
+msgid "numeric"
+msgstr "numeriek"
+
+#: src/data/format.c:328 src/data/sys-file-reader.c:1228
+#: src/data/sys-file-reader.c:1230
+#: src/language/dictionary/apply-dictionary.c:78
+#: src/language/dictionary/apply-dictionary.c:79
+#: src/language/xforms/recode.c:490 src/language/xforms/recode.c:491
+#: src/language/xforms/recode.c:503 src/language/xforms/recode.c:504
+msgid "string"
+msgstr ""
+
+#: src/data/format.c:346
+#, c-format
+msgid "String variable with width %d is not compatible with format %s."
+msgstr "String variabele met breedte %d is niet compatibel met formaat %s."
+
+#: src/data/gnumeric-reader.c:36
+msgid ""
+"Support for Gnumeric files was not compiled into this installation of PSPP"
+msgstr ""
+"Ondersteuning voor Gnumeric bestanden is niet gecompileerd in deze "
+"installatie van PSPP"
+
+#: src/data/gnumeric-reader.c:368
+#, fuzzy, c-format
+msgid "Error opening \"%s\" for reading as a Gnumeric file: %s."
+msgstr ""
+"Fout bij het openen van \"%s\" voor het lezen als een gnumeric bestand: %s."
+
+#: src/data/gnumeric-reader.c:388
+#, c-format
+msgid "Invalid cell range \"%s\""
+msgstr "Ongeldige cel range \"%s\""
+
+#: src/data/gnumeric-reader.c:520 src/data/psql-reader.c:187
+#, c-format
+msgid "Cannot create variable name from %s"
+msgstr "Kan geen variabele naam creëren van %s"
+
+#: src/data/gnumeric-reader.c:532
+#, c-format
+msgid "Selected sheet or range of spreadsheet \"%s\" is empty."
+msgstr "Geselecteerd blad of range van werkblad \"%s\" is leeg."
+
+#: src/data/make-file.c:64
+#, c-format
+msgid "%s: Creating temporary file: %s."
+msgstr "%s: Aanmaken tijdelijk bestand: %s."
+
+#: src/data/make-file.c:106
+#, c-format
+msgid "%s: Creating file: %s."
+msgstr "%s: Aanmaken bestand: %s."
+
+#: src/data/make-file.c:144
+#, c-format
+msgid "Opening %s for writing: %s."
+msgstr "Openen %s voor schrijven: %s."
+
+#: src/data/make-file.c:153
+#, c-format
+msgid "Opening stream for %s: %s."
+msgstr "Openen stream voor %s: %s."
+
+#: src/data/make-file.c:182
+#, c-format
+msgid "Creating temporary file to replace %s: %s."
+msgstr "Aanmaken tijdelijk bestand voor het vervangen van %s: %s."
+
+#: src/data/make-file.c:193
+#, c-format
+msgid "Creating temporary file %s: %s."
+msgstr "Aanmaken tijdelijk bestand %s: %s."
+
+#: src/data/make-file.c:205
+#, c-format
+msgid "Opening stream for temporary file %s: %s."
+msgstr "Openen stream voor tijdelijk bestand %s: %s."
+
+#: src/data/make-file.c:246
+#, c-format
+msgid "Replacing %s by %s: %s."
+msgstr "Vervangen %s door %s: %s."
+
+#: src/data/make-file.c:274
+#, c-format
+msgid "Removing %s: %s."
+msgstr "Verwijderen %s: %s."
+
+#: src/data/por-file-reader.c:99
+#, c-format
+msgid "portable file %s corrupt at offset 0x%lx: "
+msgstr "overdraagbaar (portable) bestand %s corrupt op offset 0x%lx: "
+
+#: src/data/por-file-reader.c:128
+#, c-format
+msgid "reading portable file %s at offset 0x%lx: "
+msgstr "lezen overdraagbaar (portable) bestand %s op offset 0x%lx: "
+
+#: src/data/por-file-reader.c:156
+#, c-format
+msgid "Error closing portable file \"%s\": %s."
+msgstr ""
+"Fout bij het afsluiten van overdraagbaar (portable) bestand \"%s\": %s."
+
+#: src/data/por-file-reader.c:208
+msgid "unexpected end of file"
+msgstr "onverwacht einde bestand"
+
+#. TRANSLATORS: this fragment will be interpolated into
+#. messages in fh_lock() that identify types of files.
+#: src/data/por-file-reader.c:267 src/data/por-file-writer.c:149
+msgid "portable file"
+msgstr "overdraagbaar (portable) bestand"
+
+#: src/data/por-file-reader.c:275
+#, c-format
+msgid ""
+"An error occurred while opening \"%s\" for reading as a portable file: %s."
+msgstr ""
+"Er is een fout opgetreden tijdens het openen van \"%s\" voor het lezen als "
+"overdraagbaar (portable) bestand: %s."
+
+#: src/data/por-file-reader.c:296
+msgid "Data record expected."
+msgstr "Data record verwacht."
+
+#: src/data/por-file-reader.c:378
+msgid "Number expected."
+msgstr "Nummer verwacht."
+
+#: src/data/por-file-reader.c:406
+msgid "Missing numeric terminator."
+msgstr "Ontbrekende numerieke afsluiter."
+
+#: src/data/por-file-reader.c:429
+msgid "Invalid integer."
+msgstr "Ongeldige integer."
+
+#: src/data/por-file-reader.c:440
+#, c-format
+msgid "Bad string length %d."
+msgstr "Foutieve string lengte %d."
+
+#: src/data/por-file-reader.c:501
+#, c-format
+msgid "%s: Not a portable file."
+msgstr "%s: Geen overdraagbaar (portable) bestand."
+
+#: src/data/por-file-reader.c:518
+#, c-format
+msgid "Unrecognized version code `%c'."
+msgstr "Niet herkende versie code `%c'."
+
+#: src/data/por-file-reader.c:527
+#, c-format
+msgid "Bad date string length %zu."
+msgstr "Foutieve datum string lengte %zu."
+
+#: src/data/por-file-reader.c:529
+#, c-format
+msgid "Bad time string length %zu."
+msgstr "Foutieve tijd string lengte %zu."
+
+#: src/data/por-file-reader.c:571
+#, c-format
+msgid ""
+"%s: Bad format specifier byte (%d).  Variable will be assigned a default "
+"format."
+msgstr ""
+"%s: Foutief formaat specificatie byte (%d). Variabele krijgt een default "
+"formaat."
+
+#: src/data/por-file-reader.c:592
+#, c-format
+msgid "Numeric variable %s has invalid format specifier %s."
+msgstr "Numerieke variabele %s heeft een ongeldige formaat specificatie %s."
+
+#: src/data/por-file-reader.c:596
+#, c-format
+msgid "String variable %s with width %d has invalid format specifier %s."
+msgstr ""
+"String variabele %s met breedte %d heeft ongeldige formaat specificatie %s."
+
+#: src/data/por-file-reader.c:620
+msgid "Expected variable count record."
+msgstr "Variabele teller record verwacht."
+
+#: src/data/por-file-reader.c:624
+#, c-format
+msgid "Invalid number of variables %d."
+msgstr "Ongeldig aantal variabelen %d."
+
+#: src/data/por-file-reader.c:633
+#, c-format
+msgid "Weight variable name (%s) truncated."
+msgstr "Weging variabele naam (%s) afgekapt."
+
+#: src/data/por-file-reader.c:648
+msgid "Expected variable record."
+msgstr "Variabel record verwacht."
+
+#: src/data/por-file-reader.c:652
+#, c-format
+msgid "Invalid variable width %d."
+msgstr "Ongeldige variabele breedte %d."
+
+#: src/data/por-file-reader.c:659
+#, c-format
+msgid "Invalid variable name `%s' in position %d."
+msgstr "Ongeldige variabele naam '%s' in positie %d."
+
+#: src/data/por-file-reader.c:663 src/data/sys-file-reader.c:521
+#, c-format
+msgid "Bad width %d for variable %s."
+msgstr "Foutieve breedte %d voor variabele %s."
+
+#: src/data/por-file-reader.c:678
+#, c-format
+msgid "Duplicate variable name %s in position %d."
+msgstr "Dubbele variabele naam %s in positie %d."
+
+#: src/data/por-file-reader.c:679
+#, c-format
+msgid "Duplicate variable name %s in position %d renamed to %s."
+msgstr "Dubbele variabele naam %s in positie %d hernoemd naar %s."
+
+#: src/data/por-file-reader.c:728
+#, c-format
+msgid "Weighting variable %s not present in dictionary."
+msgstr "Weging variabele %s niet aanwezig in woordenboek."
+
+#: src/data/por-file-reader.c:772
+#, c-format
+msgid "Unknown variable %s while parsing value labels."
+msgstr "Onbekende variabele %s tijdens het ontleden van waarde labels."
+
+#: src/data/por-file-reader.c:775
+#, c-format
+msgid ""
+"Cannot assign value labels to %s and %s, which have different variable types."
+msgstr ""
+"Kan geen waarde labels toekennen aan %s en %s, die verschillende variabele "
+"types hebben."
+
+#: src/data/por-file-writer.c:141
+#, c-format
+msgid "Invalid decimal digits count %d.  Treating as %d."
+msgstr "Ongeldige decimaal cijfers teller %d. Behandeld als %d."
+
+#: src/data/por-file-writer.c:161
+#, c-format
+msgid "Error opening \"%s\" for writing as a portable file: %s."
+msgstr ""
+"Fout tijdens openen \"%s\" voor het schrijven als een overdraagbaar "
+"(portable) bestand: %s."
+
+#: src/data/por-file-writer.c:506
+#, c-format
+msgid "An I/O error occurred writing portable file \"%s\"."
+msgstr ""
+"Een I/O fout opgetreden tijdens het schrijven van overdraagbaar (portable) "
+"bestand \"%s\"."
+
+#: src/data/psql-reader.c:46
+msgid ""
+"Support for reading postgres databases was not compiled into this "
+"installation of PSPP"
+msgstr ""
+"Ondersteuning voor het lezen van postgres databases was niet gecompileerd in "
+"deze installatie van PSPP"
+
+#: src/data/psql-reader.c:242
+msgid "Memory error whilst opening psql source"
+msgstr "Geheugen fout tijdens het openen van psql source"
+
+#: src/data/psql-reader.c:248
+#, c-format
+msgid "Error opening psql source: %s."
+msgstr "Fout tijdens openen psql source: %s."
+
+#: src/data/psql-reader.c:263
+#, c-format
+msgid ""
+"Postgres server is version %s. Reading from versions earlier than 8.0 is not "
+"supported."
+msgstr ""
+"Postgres server is versie %s. Lezen van versies ouder dan 8.0 wordt niet "
+"ondersteund."
+
+#: src/data/psql-reader.c:283
+msgid ""
+"Connection is unencrypted, but unencrypted connections have not been "
+"permitted."
+msgstr ""
+"Connectie is niet geëncrypt, maar niet geëncrypte connecties zijn niet "
+"toegestaan."
+
+#: src/data/psql-reader.c:322 src/data/psql-reader.c:347
+#: src/data/psql-reader.c:357
+#, c-format
+msgid "Error from psql source: %s."
+msgstr "Fout van psql source: %s."
+
+#: src/data/psql-reader.c:452
+#, c-format
+msgid "Unsupported OID %d.  SYSMIS values will be inserted."
+msgstr "Niet ondersteunde OID %d. SYSMIS waarde wordt ingevoegd."
+
+#: src/data/scratch-reader.c:54
+#, c-format
+msgid ""
+"Scratch file handle %s has not yet been written, using SAVE or another "
+"procedure, so it cannot yet be used for reading."
+msgstr ""
+
+#. TRANSLATORS: this fragment will be interpolated into
+#. messages in fh_lock() that identify types of files.
+#: src/data/scratch-writer.c:66 src/language/data-io/file-handle.q:181
+msgid "scratch file"
+msgstr "scratch bestand"
+
+#: src/data/settings.c:686
+#, c-format
+msgid ""
+"%s: Custom currency string `%s' does not contain exactly three periods or "
+"commas (or it contains both)."
+msgstr ""
+"%s: Aangepaste waarde string `%s' bevat niet exact drie punten of komma's "
+"(of het bevat beiden). "
+
+#: src/data/short-names.c:66
+msgid "Variable suffix too large."
+msgstr "Variabele achtervoegsel te lang."
+
+#. TRANSLATORS: this fragment will be interpolated into
+#. messages in fh_lock() that identify types of files.
+#: src/data/sys-file-reader.c:219 src/data/sys-file-writer.c:202
+msgid "system file"
+msgstr "systeem bestand"
+
+#: src/data/sys-file-reader.c:226
+#, c-format
+msgid "Error opening \"%s\" for reading as a system file: %s."
+msgstr ""
+"Fout bij het openen van \"%s\" voor het lezen als een systeem file: %s."
+
+#: src/data/sys-file-reader.c:265
+msgid "Misplaced type 4 record."
+msgstr "Verkeerd geplaatst type 4 record. "
+
+#: src/data/sys-file-reader.c:276
+#, c-format
+msgid "Unrecognized record type %d."
+msgstr "Niet herkend record type %d."
+
+#: src/data/sys-file-reader.c:315
+#, c-format
+msgid "File header claims %d variable positions but %d were read from file."
+msgstr ""
+"Bestand kop claimt %d variabele posities maar er zijn er %d gelezen van het "
+"bestand."
+
+#: src/data/sys-file-reader.c:355
+#, c-format
+msgid "Error closing system file \"%s\": %s."
+msgstr "Fout bij het sluiten van system file \"%s\": %s."
+
+#: src/data/sys-file-reader.c:420 src/data/sys-file-reader.c:430
+msgid "This is not an SPSS system file."
+msgstr "Dit is geen SPSS systeem bestand."
+
+#: src/data/sys-file-reader.c:449
+msgid ""
+"Compression bias is not the usual value of 100, or system file uses "
+"unrecognized floating-point format."
+msgstr ""
+
+#: src/data/sys-file-reader.c:517
+#, c-format
+msgid "Invalid variable name `%s'."
+msgstr "Ongeldige variabele naam '%s'."
+
+#: src/data/sys-file-reader.c:525
+#, c-format
+msgid "Duplicate variable name `%s' within system file."
+msgstr "Dubbele variabele naam '%s' binnen system file."
+
+#: src/data/sys-file-reader.c:533
+msgid "Variable label indicator field is not 0 or 1."
+msgstr "Variabel label indicator veld is niet 0 of 1."
+
+#: src/data/sys-file-reader.c:541
+#, c-format
+msgid "Variable %s has label of invalid length %zu."
+msgstr "Variabele %s heeft label van ongeldige lengte %zu."
+
+#: src/data/sys-file-reader.c:560
+msgid "Numeric missing value indicator field is not -3, -2, 0, 1, 2, or 3."
+msgstr ""
+"Numeriek ontbrekende waarde indicator veld is niet -3, -2, 0, 1, 2, of 3."
+
+#: src/data/sys-file-reader.c:578
+msgid "String missing value indicator field is not 0, 1, 2, or 3."
+msgstr "String missing waarde indicator veld is niet 0, 1, 2, of 3."
+
+#: src/data/sys-file-reader.c:610
+msgid "Missing string continuation record."
+msgstr "Mis string continuering record."
+
+#: src/data/sys-file-reader.c:644
+#, c-format
+msgid "Unknown variable format %<PRIu8>."
+msgstr "Onbekend variabele formaat %<PRIu8>."
+
+#: src/data/sys-file-reader.c:662
+#, c-format
+msgid "%s variable %s has invalid %s format %s."
+msgstr "%s variabele %s heeft ongeldig %s formaat %s."
+
+#: src/data/sys-file-reader.c:665
+msgid "print"
+msgstr "afdrukken"
+
+#: src/data/sys-file-reader.c:665
+msgid "write"
+msgstr "schrijf"
+
+#: src/data/sys-file-reader.c:669
+msgid "Suppressing further invalid format warnings."
+msgstr "Onderdrukt verdere ongeldige formaat waarschuwingen."
+
+#: src/data/sys-file-reader.c:687
+msgid "Weighting variable must be numeric."
+msgstr "Weging variabele moet numeriek zijn."
+
+#: src/data/sys-file-reader.c:701
+msgid "Multiple type 6 (document) records."
+msgstr "Meerdere type 6 (document) records."
+
+#: src/data/sys-file-reader.c:705
+#, c-format
+msgid "Number of document lines (%d) must be greater than 0."
+msgstr "Aantal document regels (%d) moet groter dan 0 zijn."
+
+#: src/data/sys-file-reader.c:713
+msgid "Document line contains null byte."
+msgstr "Document regel bevat null byte."
+
+#: src/data/sys-file-reader.c:803
+#, c-format
+msgid ""
+"Unrecognized record type 7, subtype %d.  Please send a copy of this file, "
+"and the syntax which created it to %s"
+msgstr ""
+"Niet herkend type 7, subtype %d.  Stuur s.v.p. een kopie van dit bestand en "
+"de syntax waarmee het is aangemaakt naar %s "
+
+#: src/data/sys-file-reader.c:830
+#, c-format
+msgid "Bad size (%zu) or count (%zu) field on record type 7, subtype 3."
+msgstr ""
+"Foutieve lengte (%zu) of aantal (%zu) veld in record type 7, subtype 3."
+
+#: src/data/sys-file-reader.c:850
+#, c-format
+msgid ""
+"Floating-point representation indicated by system file (%d) differs from "
+"expected (%d)."
+msgstr ""
+"Drijvende komma representatie aangegeven door systeem bestand %d verschilt "
+"van verwachting (%d)."
+
+#: src/data/sys-file-reader.c:863
+msgid "little-endian"
+msgstr ""
+
+#: src/data/sys-file-reader.c:863
+msgid "big-endian"
+msgstr ""
+
+#: src/data/sys-file-reader.c:864
+#, c-format
+msgid ""
+"Integer format indicated by system file (%s) differs from expected (%s)."
+msgstr ""
+"Integer formaat aangegeven door systeem bestand (%s) verschilt van verwacht "
+"(%s). "
+
+#: src/data/sys-file-reader.c:921
+#, c-format
+msgid "Bad size (%zu) or count (%zu) on extension 4."
+msgstr "Foutieve lengte (%zu) of aantal (%zu) bij extensie 4."
+
+#: src/data/sys-file-reader.c:925 src/data/sys-file-reader.c:929
+#: src/data/sys-file-reader.c:933
+#, fuzzy, c-format
+msgid "File specifies unexpected value %g as %s."
+msgstr "Bestand specificeert onverwachte waarde %g als SYSMIS."
+
+#: src/data/sys-file-reader.c:950
+#, c-format
+msgid "Bad size %zu on extension 11."
+msgstr "Foutieve lengte %zu voor extensie 11."
+
+#: src/data/sys-file-reader.c:962
+#, c-format
+msgid "Extension 11 has bad count %zu (for %zu variables)."
+msgstr "Extensie 11 heeft een foutief aantal %zu (voor %zu variabelen)."
+
+#: src/data/sys-file-reader.c:983
+#, c-format
+msgid ""
+"Invalid variable display parameters for variable %zu (%s).  Default "
+"parameters substituted."
+msgstr ""
+"Ongeldige variabele toon parameters voor variabele %zu (%s).  Default "
+"parameters ingevuld."
+
+#: src/data/sys-file-reader.c:1027
+#, c-format
+msgid "Long variable mapping from %s to invalid variable name `%s'."
+msgstr "Lange variabele afbeelding van %s tot ongeldige naam '%s'. "
+
+#: src/data/sys-file-reader.c:1037
+#, c-format
+msgid "Duplicate long variable name `%s' within system file."
+msgstr "Dubbele lange variabele naam `%s' binnen systeem bestand."
+
+#: src/data/sys-file-reader.c:1090
+#, c-format
+msgid "%s listed as string of invalid length %s in very length string record."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1100
+#, c-format
+msgid ""
+"%s listed in very long string record with width %s, which requires only one "
+"segment."
+msgstr ""
+"%s vermeld in erg lang string record met breedte %s, dat slechts een segment "
+"vereist."
+
+#: src/data/sys-file-reader.c:1106
+#, c-format
+msgid "Very long string %s overflows dictionary."
+msgstr "Erg lange string %s is te groot voor woordenboek."
+
+#: src/data/sys-file-reader.c:1120
+#, c-format
+msgid ""
+"Very long string with width %ld has segment %d of width %d (expected %d)"
+msgstr ""
+"Erg lange string met breedte %ld heeft segment %d van breedte %d (verwacht %"
+"d)"
+
+#: src/data/sys-file-reader.c:1166
+#, c-format
+msgid "Invalid number of labels: %d.  Ignoring labels."
+msgstr "Ongeldig aantal labels: %d. Labels worden genegeerd."
+
+#: src/data/sys-file-reader.c:1197
+msgid ""
+"Variable index record (type 4) does not immediately follow value label "
+"record (type 3) as it should."
+msgstr ""
+"Variabele index record (type 4) volgt niet onmiddellijk waarde label record "
+"(type 3) zoals het moet."
+
+#: src/data/sys-file-reader.c:1204
+#, c-format
+msgid ""
+"Number of variables associated with a value label (%d) is not between 1 and "
+"the number of variables (%zu)."
+msgstr ""
+"Aantal variabelen geassocieerd aan waarde label (%d) is niet tussen 1 en het "
+"aantal variabelen (%zu)."
+
+#: src/data/sys-file-reader.c:1215
+#, fuzzy, c-format
+msgid ""
+"Value labels may not be added to long string variables (e.g. %s) using "
+"records types 3 and 4."
+msgstr "Waarde labels zijn niet toegestaan bij lange string variabelen (%s)."
+
+#: src/data/sys-file-reader.c:1224
+#, c-format
+msgid ""
+"Variables associated with value label are not all of identical type.  "
+"Variable %s is %s, but variable %s is %s."
+msgstr ""
+"Variabelen geassocieerd met waarde label zijn niet allemaal van het "
+"identieke type.  Variabele %s is %s, maar variabele %s is %s."
+
+#: src/data/sys-file-reader.c:1258
+#, c-format
+msgid "Duplicate value label for %g on %s."
+msgstr "Dubbel waarde label voor %g op %s."
+
+#: src/data/sys-file-reader.c:1261 src/data/sys-file-reader.c:1442
+#, c-format
+msgid "Duplicate value label for \"%.*s\" on %s."
+msgstr "Dubbel waarde label voor \"%.*s\" on %s."
+
+#: src/data/sys-file-reader.c:1299
+#, c-format
+msgid "Error parsing attribute value %s[%d]"
+msgstr "Fout bij het ontleden van attribuut waarde %s[%d]"
+
+#: src/data/sys-file-reader.c:1313
+#, c-format
+msgid "Attribute value %s[%d] is not quoted: %s"
+msgstr "Attribuut waarde %s[%d] is niet geciteerd: %s"
+
+#: src/data/sys-file-reader.c:1376
+#, c-format
+msgid ""
+"Variable name length in long string value label record (%d) exceeds %d-byte "
+"limit."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1386
+#, fuzzy, c-format
+msgid "Ignoring long string value record for unknown variable %s."
+msgstr "Variabele afbeelding refereert aan onbekende variabele %s."
+
+#: src/data/sys-file-reader.c:1393
+#, fuzzy, c-format
+msgid "Ignoring long string value record for numeric variable %s."
+msgstr ""
+"Kan missing values uit bron bestand niet toepassen op lange string variabele "
+"%s."
+
+#: src/data/sys-file-reader.c:1400
+#, c-format
+msgid ""
+"Ignoring long string value record for variable %s because the record's width "
+"(%d) does not match the variable's width (%d)"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1422
+#, c-format
+msgid ""
+"Ignoring long string value %zu for variable %s, with width %d, that has bad "
+"value width %zu."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1537
+msgid "File ends in partial case."
+msgstr "Bestand eindigt in gedeeltelijke case."
+
+#: src/data/sys-file-reader.c:1545
+#, c-format
+msgid "Error reading case from file %s."
+msgstr "Fout tijdens lezen case van bestand %s."
+
+#: src/data/sys-file-reader.c:1642 src/data/sys-file-reader.c:1678
+msgid "Compressed data is corrupt."
+msgstr "Gecomprimeerde data is corrupt."
+
+#: src/data/sys-file-reader.c:1765
+#, c-format
+msgid "Variable index %d not in valid range 1...%d."
+msgstr "Variabele index %d niet in geldige range 1...%d."
+
+#: src/data/sys-file-reader.c:1770
+#, c-format
+msgid "Variable index %d refers to long string continuation."
+msgstr "Variabele index %d verwijst naar lange string voortzetting."
+
+#: src/data/sys-file-reader.c:1838
+#, c-format
+msgid "Suppressed %d additional related warnings."
+msgstr "Onderdrukt %d extra gerelateerde waarschuwingen."
+
+#: src/data/sys-file-reader.c:1879
+#, c-format
+msgid "Variable map refers to unknown variable %s."
+msgstr "Variabele afbeelding refereert aan onbekende variabele %s."
+
+#: src/data/sys-file-reader.c:1987
+#, c-format
+msgid "System error: %s."
+msgstr "Systeem fout: %s."
+
+#: src/data/sys-file-reader.c:1989
+msgid "Unexpected end of file."
+msgstr "Onverwacht bestand einde."
+
+#: src/data/sys-file-writer.c:175
+#, c-format
+msgid "Unknown system file version %d. Treating as version %d."
+msgstr "Onbekende systeem bestand versie %d. Behandeld als versie %d."
+
+#: src/data/sys-file-writer.c:214
+#, c-format
+msgid "Error opening \"%s\" for writing as a system file: %s."
+msgstr ""
+"Fout bij het openen van \"%s\" voor het schrijven als een systeem bestand: %"
+"s."
+
+#: src/data/sys-file-writer.c:917
+#, c-format
+msgid "An I/O error occurred writing system file \"%s\"."
+msgstr ""
+"Een I/O fout is opgetreden tijdens het schrijven van systeem bestand \"%s\"."
+
+#: src/data/variable.c:242
+#, c-format
+msgid ""
+"Character `%c' (in %s) may not appear as the first character in a variable "
+"name."
+msgstr ""
+"Karakter '%c' (in %s) mag niet als eerste karakter in een variabele naam "
+"voorkomen. "
+
+#: src/data/variable.c:254
+#, c-format
+msgid "Character `%c' (in %s) may not appear in a variable name."
+msgstr "Karakter '%c' (in %s) mag niet in een variabele naam voorkomen."
+
+#: src/data/variable.c:282
+msgid "Variable name cannot be empty string."
+msgstr "Variabele naam kan geen lege string zijn."
+
+#: src/data/variable.c:288
+#, c-format
+msgid "Variable name %s exceeds %d-character limit."
+msgstr "Variabele naam %s overschrijdt de limiet van %d-karakters."
+
+#: src/data/variable.c:296
+#, c-format
+msgid "`%s' may not be used as a variable name because it is a reserved word."
+msgstr ""
+"'%s' mag niet gebruikt worden als variabele naam omdat het een gereserveerd "
+"woord is."
+
+#: src/language/command.c:208 src/language/expressions/parse.c:1267
+#, c-format
+msgid "%s is not yet implemented."
+msgstr "%s is nog niet geïmplementeerd."
+
+#: src/language/command.c:214
+#, c-format
+msgid "%s may be used only in testing mode."
+msgstr "%s mag alleen in test modus gebruikt worden."
+
+#: src/language/command.c:220
+#, c-format
+msgid "%s may be used only in enhanced syntax mode."
+msgstr "%s mag alleen in uitgebreide syntax modus gebruikt worden."
+
+#: src/language/command.c:248
+msgid "Error encountered while ERROR=STOP is effective."
+msgstr "Fout tegengekomen terwijl ERROR=STOP is actief."
+
+#: src/language/command.c:489
+msgid "expecting command name"
+msgstr "opdracht naam verwacht"
+
+#: src/language/command.c:503
+#, c-format
+msgid "Unknown command %s."
+msgstr "Onbekende opdracht %s."
+
+#: src/language/command.c:628
+#, c-format
+msgid "%s is allowed only before the active file has been defined."
+msgstr "%s is alleen toegestaan voordat het actieve bestand is gedefinieerd."
+
+#: src/language/command.c:632
+#, c-format
+msgid "%s is allowed only after the active file has been defined."
+msgstr "%s is alleen toegestaan nadat het actieve bestand is gedefinieerd."
+
+#: src/language/command.c:636
+#, c-format
+msgid "%s is allowed only inside INPUT PROGRAM."
+msgstr "%s is alleen toegestaan binnen INPUT PROGRAM."
+
+#: src/language/command.c:640
+#, c-format
+msgid "%s is allowed only inside FILE TYPE."
+msgstr "%s is alleen toegestaan binnen FILE TYPE."
+
+#: src/language/command.c:647
+#, c-format
+msgid ""
+"%s is allowed only before the active file has been defined or inside INPUT "
+"PROGRAM."
+msgstr ""
+"%s is alleen toegestaan voordat het actieve bestand is gedefinieerd of "
+"binnen INPUT PROGRAMMA."
+
+#: src/language/command.c:651
+#, c-format
+msgid ""
+"%s is allowed only before the active file has been defined or inside FILE "
+"TYPE."
+msgstr ""
+"%s is alleen toegestaan voordat het actieve bestand is gedefinieerd of "
+"binnen FILE TYPE."
+
+#: src/language/command.c:655
+#, c-format
+msgid ""
+"%s is allowed only after the active file has been defined or inside INPUT "
+"PROGRAM."
+msgstr ""
+"%s is alleen toegestaan nadat het actieve bestand is gedefinieerd of binnen "
+"INPUT PROGRAMMA."
+
+#: src/language/command.c:659
+#, c-format
+msgid ""
+"%s is allowed only after the active file has been defined or inside FILE "
+"TYPE."
+msgstr ""
+"%s is alleen toegestaan nadat het actieve bestand is gedefinieerd of binnen "
+"FILE TYPE."
+
+#: src/language/command.c:663
+#, c-format
+msgid "%s is allowed only inside INPUT PROGRAM or inside FILE TYPE."
+msgstr "%s is alleen toegestaan binnen INPUT PROGRAM of binnen FILE TYPE."
+
+#: src/language/command.c:669
+#, c-format
+msgid ""
+"%s is allowed only after the active file has been defined, inside INPUT "
+"PROGRAM, or inside FILE TYPE."
+msgstr ""
+"%s is alleen toegestaan nadat het actieve bestand is gedefinieerd, binnen "
+"INPUT PROGRAM of binnen FILE TYPE."
+
+#: src/language/command.c:674
+#, c-format
+msgid ""
+"%s is allowed only before the active file has been defined, inside INPUT "
+"PROGRAM, or inside FILE TYPE."
+msgstr ""
+"%s is alleen toegestaan voordat het actieve bestand is gedefinieerd, binnen "
+"INPUT PROGRAM of binnen FILE TYPE."
+
+#: src/language/command.c:692
+#, c-format
+msgid "%s is not allowed inside INPUT PROGRAM."
+msgstr "%s is niet toegestaan binnen INPUT PROGRAM."
+
+#: src/language/command.c:694
+#, c-format
+msgid "%s is not allowed inside FILE TYPE."
+msgstr "%s is niet toegestaan binnen FILE TYPE."
+
+#: src/language/command.c:773 src/language/command.c:881
+#: src/language/utilities/permissions.c:98
+msgid "This command not allowed when the SAFER option is set."
+msgstr "Deze opdracht is niet toegestaan als de SAFER optie is gezet."
+
+#: src/language/command.c:785
+#, c-format
+msgid "Error removing `%s': %s."
+msgstr "Fout bij verwijderen '%s': %s."
+
+#: src/language/command.c:835
+#, c-format
+msgid "Couldn't fork: %s."
+msgstr ""
+
+#: src/language/command.c:850
+msgid "Interactive shell not supported on this platform."
+msgstr "Interactieve shell niet ondersteunt op dit platform."
+
+#: src/language/command.c:862
+msgid "Command shell not supported on this platform."
+msgstr "Opdracht shell niet ondersteunt op dit platform."
+
+#: src/language/command.c:868
+#, c-format
+msgid "Error executing command: %s."
+msgstr "Fout tijdens uitvoeren opdracht: %s."
+
+#: src/language/control/control-stack.c:27
+#, c-format
+msgid "%s without %s."
+msgstr "%s zonder %s."
+
+#: src/language/control/control-stack.c:55
+#, c-format
+msgid "This command must appear inside %s...%s, without intermediate %s...%s."
+msgstr ""
+"Deze opdracht moet binnen %s...%s voorkomen, zonder tussenliggende %s...%s."
+
+#: src/language/control/control-stack.c:72
+#, c-format
+msgid "This command cannot appear outside %s...%s."
+msgstr "Deze opdracht kan niet voorkomen buiten %s...%s."
+
+#: src/language/control/do-if.c:177
+msgid "This command may not follow ELSE in DO IF...END IF."
+msgstr "Deze opdracht mag niet volgen op ELSE in DO IF...END IF."
+
+#: src/language/control/loop.c:214
+msgid "Only one index clause may be specified."
+msgstr "Slechts een index clausule mag gespecificeerd worden."
+
+#: src/language/control/repeat.c:171
+#, c-format
+msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
+msgstr "Dummy variabele naam \"%s\" verbergt woordenboek variabele \"%s\"."
+
+#: src/language/control/repeat.c:176
+#, c-format
+msgid "Dummy variable name \"%s\" is given twice."
+msgstr "Dummy variabele naam \"%s\"is 2 keer opgegeven."
+
+#: src/language/control/repeat.c:222
+#, c-format
+msgid ""
+"Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
+"were specified."
+msgstr ""
+"Dummy variabele \"%.*s\" heeft %d substituties, dus \"%.*s\" moet dat ook, "
+"maar %d zijn er gespecificeerd."
+
+#: src/language/control/repeat.c:334
+msgid "DO REPEAT may not nest in compatibility mode."
+msgstr "DO REPEAT mag niet nesten in compatibiliteit mode."
+
+#: src/language/control/repeat.c:436
+msgid "Ranges may only have integer bounds"
+msgstr "Ranges mogen alleen integer grenzen hebben"
+
+#: src/language/control/repeat.c:445
+#, c-format
+msgid "%g TO %g is an invalid range."
+msgstr "%g TO %g is een ongeldige range."
+
+#: src/language/control/repeat.c:480
+msgid "String expected."
+msgstr "String verwacht."
+
+#: src/language/control/repeat.c:499
+msgid "No matching DO REPEAT."
+msgstr "Geen overeenkomende DO REPEAT."
+
+#: src/language/control/temporary.c:46
+msgid ""
+"This command may only appear once between procedures and procedure-like "
+"commands."
+msgstr ""
+"Deze opdracht mag slechts 1 keer voorkomen tussen procedures en procedure-"
+"achtige opdrachten."
+
+#: src/language/data-io/combine-files.c:210
+msgid "Cannot specify the active file since no active file has been defined."
+msgstr ""
+"Kan het actieve bestand niet specificeren omdat er geen actief bestand is "
+"gedefinieerd."
+
+#: 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 ""
+"Deze opdracht mag niet gebruikt worden na TEMPORARY als het actieve bestand "
+"een invoer bron is. Tijdelijke transformaties zullen permanent worden."
+
+#: src/language/data-io/combine-files.c:250
+msgid "Multiple IN subcommands for a single FILE or TABLE."
+msgstr "Meerdere IN subopdrachten voor een enkele FILE of TABLE."
+
+#: src/language/data-io/combine-files.c:302
+#, c-format
+msgid "File %s lacks BY variable %s."
+msgstr "Bestand %s mist BY variabele %s."
+
+#: src/language/data-io/combine-files.c:305
+#, c-format
+msgid "Active file lacks BY variable %s."
+msgstr "Actief bestand mist BY variabele %s."
+
+#: src/language/data-io/combine-files.c:376
+msgid "The BY subcommand is required."
+msgstr "De BY subopdracht is nodig."
+
+#: src/language/data-io/combine-files.c:381
+msgid "BY is required when TABLE is specified."
+msgstr "BY is noodzakelijk als TABLE is gespecificeerd."
+
+#: src/language/data-io/combine-files.c:386
+msgid "BY is required when SORT is specified."
+msgstr "BY is noodzakelijk als SORT is gespecificeerd."
+
+#: src/language/data-io/combine-files.c:513
+msgid ""
+"Combining files with incompatible encodings. String data may not be "
+"represented correctly."
+msgstr ""
+"Combineren bestanden met incompatibele codering. String data wordt misschien "
+"niet correct weergegeven."
+
+#: src/language/data-io/combine-files.c:545
+#, c-format
+msgid ""
+"Variable %s in file %s has different type or width from the same variable in "
+"earlier file."
+msgstr ""
+"Variabele %s in bestand %s heeft een ander type of de breedte dan dezelfde "
+"variabele in eerder bestand."
+
+#: src/language/data-io/combine-files.c:551
+#, c-format
+msgid "In file %s, %s is numeric."
+msgstr "In bestand %s, %s is numeriek."
+
+#: src/language/data-io/combine-files.c:554
+#, c-format
+msgid "In file %s, %s is a string variable with width %d."
+msgstr "In bestand %s, %s is een string variabele met breedte %d."
+
+#: src/language/data-io/combine-files.c:559
+#, c-format
+msgid "In an earlier file, %s was numeric."
+msgstr "In eerder bestand, %s was numeriek."
+
+#: src/language/data-io/combine-files.c:562
+#, c-format
+msgid "In an earlier file, %s was a string variable with width %d."
+msgstr "In een eerder bestand, %s was een string variabele met breedte %d."
+
+#: src/language/data-io/combine-files.c:601
+#, c-format
+msgid ""
+"Variable name %s specified on %s subcommand duplicates an existing variable "
+"name."
+msgstr ""
+"Variabele naam %s gespecificeerd op %s subopdracht dupliceert een bestaande "
+"variabele naam."
+
+#: src/language/data-io/combine-files.c:762
+#, c-format
+msgid "Encountered %zu sets of duplicate cases in the master file."
+msgstr "Ontmoet %zu sets van dubbele cases in het master bestand. "
+
+#: src/language/data-io/data-list.c:137
+msgid "The END subcommand may only be used within INPUT PROGRAM."
+msgstr "De END subopdracht mag alleen binnen INPUT PROGRAM gebruikt worden."
+
+#: src/language/data-io/data-list.c:143
+msgid "The END subcommand may only be specified once."
+msgstr "De END subopdracht mag slechts 1 keer gespecificeerd worden."
+
+#: src/language/data-io/data-list.c:181
+msgid "Only one of FIXED, FREE, or LIST may be specified."
+msgstr "Slechts 1 van FIXED, FREE of LIST mag gespecificeerd worden."
+
+#: src/language/data-io/data-list.c:243
+msgid "Encoding should not be specified for inline data. It will be ignored."
+msgstr ""
+"Coderen dient niet opgegeven te worden voor inline date. Het wordt genegeerd."
+
+#: src/language/data-io/data-list.c:254
+msgid "The END subcommand may be used only with DATA LIST FIXED."
+msgstr "De END subopdracht mag allen gebruikt worden met DATA LIST FIXED."
+
+#: src/language/data-io/data-list.c:269
+msgid "At least one variable must be specified."
+msgstr "Tenminste 1 variabele moet gespecificeerd worden."
+
+#: src/language/data-io/data-list.c:368 src/language/data-io/data-list.c:457
+#: src/language/data-io/get-data.c:530
+#, c-format
+msgid "%s is a duplicate variable name."
+msgstr "%s is een dubbele variabele naam."
+
+#: src/language/data-io/data-list.c:375
+#, c-format
+msgid "There is already a variable %s of a different type."
+msgstr "Er is al een variabele %s van een ander type."
+
+#: src/language/data-io/data-list.c:382
+#, c-format
+msgid "There is already a string variable %s of a different width."
+msgstr "Er is al een string variabele %s van een andere breedte."
+
+#: src/language/data-io/data-list.c:390
+#, c-format
+msgid "Cannot place variable %s on record %d when RECORDS=%d is specified."
+msgstr ""
+"Kan variabele %s niet plaatsen in record %d als RECORDS=%d is gespecificeerd."
+
+#: 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 "Geciteerde string loopt door na regeleinde."
+
+#: src/language/data-io/data-parser.c:522
+#, c-format
+msgid "Partial case of %d of %d records discarded."
+msgstr "Gedeeltelijke case van %d van %d records genegeerd."
+
+#: src/language/data-io/data-parser.c:568
+#, c-format
+msgid "Partial case discarded.  The first variable missing was %s."
+msgstr "Gedeeltelijke case overgeslagen. De eerste gemiste variabele was %s."
+
+#: 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 ""
+"Missing value(s) voor alle variabelen vanaf %s. Deze worden gevuld met de "
+"geschikte system-missing waarde of spatie."
+
+#: src/language/data-io/data-parser.c:624
+msgid "Record ends in data not part of any field."
+msgstr "Record eindigd in data die geen onderdeel is van een veld."
+
+#: 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:169
+#: src/language/dictionary/sys-file-info.c:393
+#: src/language/dictionary/sys-file-info.c:725
+#: src/language/stats/descriptives.c:885 src/ui/gui/psppire-dictview.c:502
+msgid "Variable"
+msgstr "Variabele"
+
+#: 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:646 src/language/data-io/print.c:405
+#: src/ui/gui/crosstabs.glade:92 src/ui/gui/psppire-var-sheet.c:534
+#: src/ui/gui/psppire-var-store.c:800
+msgid "Columns"
+msgstr "Kolommen"
+
+#: 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 "Formaat"
+
+#: 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] "Lezen %d record van %s."
+msgstr[1] "Lezen %d records van %s."
+
+#: src/language/data-io/data-parser.c:702
+#, c-format
+msgid "Reading free-form data from %s."
+msgstr "Lezen vrij-formaat data van %s."
+
+#. TRANSLATORS: this fragment will be interpolated into
+#. messages in fh_lock() that identify types of files.
+#: src/language/data-io/data-reader.c:122
+#: src/language/data-io/data-writer.c:58
+msgid "data file"
+msgstr "data bestand"
+
+#: src/language/data-io/data-reader.c:149
+#, c-format
+msgid "Could not open \"%s\" for reading as a data file: %s."
+msgstr "Kon \"%s\"niet openen voor het lezen als data bestand: %s."
+
+#: 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 ""
+"Onverwacht einde-bestand tijdens het lezen van data in BEGIN DATA. Dit geeft "
+"waarschijnlijk aan dat de END DATA opdracht ontbreekt of verkeerd geschreven "
+"is. END DATA dient alleen op 1 regel met precies 1 spatie tussen de woorden "
+"voor te komen."
+
+#: src/language/data-io/data-reader.c:216
+#, c-format
+msgid "Error reading file %s: %s."
+msgstr "Fout tijdens lezen bestand %s: %s."
+
+#: src/language/data-io/data-reader.c:219
+#, c-format
+msgid "Unexpected end of file reading %s."
+msgstr "Onverwacht einde tijdens lezen van bestand %s."
+
+#: 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:288
+#, c-format
+msgid "Corrupt block descriptor word at offset 0x%lx in %s."
+msgstr ""
+
+#: 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:302
+#, c-format
+msgid "Corrupt record size at offset 0x%lx in %s."
+msgstr ""
+
+#: src/language/data-io/data-reader.c:444
+msgid "Record exceeds remaining block length."
+msgstr "Record overschrijdt resterend blok lengte."
+
+#: src/language/data-io/data-reader.c:518
+#, c-format
+msgid "Attempt to read beyond end-of-file on file %s."
+msgstr "Poging om te lezen na einde-bestand op bestand %s."
+
+#: src/language/data-io/data-reader.c:521
+msgid "Attempt to read beyond END DATA."
+msgstr "Poging om te lezen na END DATA."
+
+#: 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."
+msgstr ""
+"Deze opdracht is hier niet geldig omdat het huidige invoer programma het "
+"inline bestand niet benaderd."
+
+#: src/language/data-io/data-writer.c:74
+#, c-format
+msgid "An error occurred while opening \"%s\" for writing as a data file: %s."
+msgstr ""
+"Een fout is opgetreden tijdens het openen van \"%s\" voor schrijven als data "
+"bestand: %s."
+
+#: src/language/data-io/data-writer.c:191
+#, c-format
+msgid "I/O error occurred writing data file \"%s\"."
+msgstr "I/O fout opgetreden tijdens schrijven data bestand \"%s\"."
+
+#: src/language/data-io/file-handle.q:65
+#, c-format
+msgid ""
+"File handle %s is already defined.  Use CLOSE FILE HANDLE before redefining "
+"a file handle."
+msgstr ""
+"Bestand 'handle' %s is al gedefinieerd. Gebruik CLOSE FILE HANDLE voor het "
+"opnieuw definiëren van een bestand 'handle'."
+
+#: src/language/data-io/file-handle.q:120
+msgid "RECFORM must be specified with MODE=360."
+msgstr "RECFORM moet opgegeven worden met MODE=360."
+
+#: src/language/data-io/file-handle.q:131
+#, c-format
+msgid "The specified file mode requires LRECL.  Assuming %d-character records."
+msgstr ""
+"De gespecificeerd bestandsmodus vereist LRECL. %d-karakter records "
+"veronderstelt."
+
+#: src/language/data-io/file-handle.q:135
+#, c-format
+msgid ""
+"Record length (%ld) must be between 1 and %lu bytes.  Assuming %d-character "
+"records."
+msgstr ""
+"Record lengte (%ld) moet tussen 1 en %lu bytes zijn. Veronderstel %d-"
+"karakters records."
+
+#: src/language/data-io/file-handle.q:177
+msgid "file"
+msgstr "bestand"
+
+#: src/language/data-io/file-handle.q:179
+msgid "inline file"
+msgstr "inline bestand"
+
+#: src/language/data-io/file-handle.q:205
+msgid "expecting a file name or handle name"
+msgstr "file naam of 'handle' naam verwacht"
+
+#: src/language/data-io/file-handle.q:225
+#, c-format
+msgid "Handle for %s not allowed here."
+msgstr "'Handle' voor %s is hier niet toegestaan."
+
+#: src/language/data-io/get.c:99
+msgid "expecting COMM or TAPE"
+msgstr "COMM of TAPE verwacht"
+
+#: src/language/data-io/get-data.c:64
+#, c-format
+msgid "Unsupported TYPE %s"
+msgstr "Niet ondersteunt TYPE %s"
+
+#: src/language/data-io/get-data.c:260
+#, c-format
+msgid ""
+"%s is allowed only with %s arrangement, but %s arrangement was stated or "
+"implied earlier in this command."
+msgstr ""
+"%s is alleen toegestaan met %s regeling, maar %s regeling was eerder "
+"opgegeven of geïmpliceerd in deze opdracht."
+
+#: src/language/data-io/get-data.c:315
+msgid "expecting FIXED or DELIMITED"
+msgstr "FIXED of DELIMITED verwacht"
+
+#: src/language/data-io/get-data.c:328
+msgid "Value of FIRSTCASE must be 1 or greater."
+msgstr "Waarde van FIRSTCASE moet 1 of groter zijn."
+
+#: src/language/data-io/get-data.c:353
+msgid "expecting LINE or VARIABLES"
+msgstr "LINE of VARIABLES verwacht"
+
+#: src/language/data-io/get-data.c:366
+msgid "Value of FIXCASE must be at least 1."
+msgstr "Waarde van FIXCASE moet tenminste 1 zijn."
+
+#: src/language/data-io/get-data.c:386
+msgid "Value of FIRST must be at least 1."
+msgstr "Waarde van FIRST moet tenminste 1 zijn."
+
+#: src/language/data-io/get-data.c:398
+msgid "Value of PERCENT must be between 1 and 100."
+msgstr "Waarde van PERCENT moet tussen 1 en 100 zijn."
+
+#: src/language/data-io/get-data.c:447
+msgid ""
+"In compatible syntax mode, the QUALIFIER string must contain exactly one "
+"character."
+msgstr ""
+"In compatibele syntax modus, the QUALIFIER string moet precies 1 karakter "
+"bevatten."
+
+#: src/language/data-io/get-data.c:462
+msgid "expecting VARIABLES"
+msgstr "VARIABLES verwacht"
+
+#: src/language/data-io/get-data.c:484
+#: src/language/data-io/placement-parser.c:378
+#, c-format
+msgid ""
+"The record number specified, %ld, is at or before the previous record, %d.  "
+"Data fields must be listed in order of increasing record number."
+msgstr ""
+"Het opgegeven record nummer, %ld, is op of voor het huidige record, %d. Data "
+"velden dienen opgegeven te worden in oplopende recordnummer volgorde."
+
+#: src/language/data-io/get-data.c:493
+#, c-format
+msgid ""
+"The record number specified, %ld, exceeds the number of records per case "
+"specified on FIXCASE, %d."
+msgstr ""
+"Het gespecificeerde record nummer, %ld, overschrijdt het aantal records per "
+"case zoals gespecificeerd in FIXCASE, %d."
+
+#: src/language/data-io/inpt-pgm.c:130
+msgid "Unexpected end-of-file within INPUT PROGRAM."
+msgstr "Onverwacht einde-bestand binnen INPUT PROGRAM."
+
+#: src/language/data-io/inpt-pgm.c:143
+msgid "Input program did not create any variables."
+msgstr "Input program heeft geen variabelen gecreëerd."
+
+#: src/language/data-io/inpt-pgm.c:288
+msgid "COLUMN subcommand multiply specified."
+msgstr "COLUMN subopdracht meerdere keren gespecificeerd."
+
+#: src/language/data-io/inpt-pgm.c:338
+msgid ""
+"REREAD: Column numbers must be positive finite numbers.  Column set to 1."
+msgstr ""
+"REREAD: Kolom nummers moeten positieve eindige nummers zijn. Kolom is op 1 "
+"gezet."
+
+#: src/language/data-io/list.q:157 src/language/stats/descriptives.c:362
+msgid "No variables specified."
+msgstr "Geen variabelen gespecificeerd."
+
+#: src/language/data-io/list.q:165
+#, c-format
+msgid ""
+"The first case (%ld) specified precedes the last case (%ld) specified.  The "
+"values will be swapped."
+msgstr ""
+"De eerste gespecificeerde case (%ld) gaat vooraf aan de laatste "
+"gespecificeerde case (%ld). De waardes worden verwisseld."
+
+#: src/language/data-io/list.q:173
+#, c-format
+msgid ""
+"The first case (%ld) to list is less than 1.  The value is being reset to 1."
+msgstr ""
+"De eerste case (%ld) om weer te geven is kleiner dan 1. De waarde is "
+"teruggezet op 1."
+
+#: src/language/data-io/list.q:179
+#, c-format
+msgid ""
+"The last case (%ld) to list is less than 1.  The value is being reset to 1."
+msgstr ""
+"De laatste case (%ld) om weer te geven is kleiner dan 1. De waarde is "
+"teruggezet op 1."
+
+#: src/language/data-io/list.q:185
+#, c-format
+msgid "The step value %ld is less than 1.  The value is being reset to 1."
+msgstr "De stap waarde %ld is kleiner dan 1. De waarde is teruggezet op 1."
+
+#: src/language/data-io/list.q:211
+msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
+msgstr "'/FORMAT WEIGHT' gespecificeerd, maar weging is niet aan."
+
+#: src/language/data-io/list.q:468
+msgid "Line"
+msgstr "Regel"
+
+#: src/language/data-io/placement-parser.c:87
+#, c-format
+msgid ""
+"Number of variables specified (%zu) differs from number of variable formats "
+"(%zu)."
+msgstr ""
+"Aantal gespecificeerde variabelen (%zu) verschilt van aantal variabele "
+"formaten (%zu)."
+
+#: src/language/data-io/placement-parser.c:97
+msgid ""
+"SPSS-like or Fortran-like format specification expected after variable names."
+msgstr ""
+"SPSS-achtig of Fortran-achtig formaat specificatie verwacht na variabele "
+"naam."
+
+#: src/language/data-io/placement-parser.c:119
+#, c-format
+msgid "The %d columns %d-%d can't be evenly divided into %zu fields."
+msgstr "De %d kolommen %d-%d kunnen niet gelijk verdeeld worden in %zu velden."
+
+#: src/language/data-io/placement-parser.c:226
+#: src/language/lexer/format-parser.c:107
+#: src/language/lexer/format-parser.c:126
+#, c-format
+msgid "Unknown format type \"%s\"."
+msgstr "Onbekend formaat type \"%s\"."
+
+#: src/language/data-io/placement-parser.c:305
+msgid "Column positions for fields must be positive."
+msgstr "Kolom posities voor velden moet positief zijn."
+
+#: src/language/data-io/placement-parser.c:307
+msgid "Column positions for fields must not be negative."
+msgstr "Kolom posities voor velden mogen niet negatief zijn."
+
+#: src/language/data-io/placement-parser.c:344
+msgid "The ending column for a field must be greater than the starting column."
+msgstr "De eind kolom van een veld moet groter zijn dan de start kolom."
+
+#: src/language/data-io/print.c:178 src/language/data-io/trim.c:54
+msgid "expecting a valid subcommand"
+msgstr "een geldig subopdracht verwacht"
+
+#: src/language/data-io/print.c:266
+#, c-format
+msgid "Output calls for %d records but %zu specified on RECORDS subcommand."
+msgstr ""
+"De output vraagt %d records maar %zu gespecificeerd bij RECORDS subopdracht. "
+
+#: src/language/data-io/print.c:437
+#, c-format
+msgid "Writing %d record to %s."
+msgid_plural "Writing %d records to %s."
+msgstr[0] "Schrijven van %d record naar %s."
+msgstr[1] "Schrijven van %d records naar %s."
+
+#: src/language/data-io/print.c:441
+#, c-format
+msgid "Writing %d record."
+msgid_plural "Writing %d records."
+msgstr[0] "Schrijven van %d record."
+msgstr[1] "Schrijven van %d records."
+
+#: 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 "verwacht einde van opdracht "
+
+#: src/language/data-io/print-space.c:116
+msgid "The expression on PRINT SPACE evaluated to the system-missing value."
+msgstr "De expressie bij PRINT SPACE evalueerde tot de system-missing waarde."
+
+#: src/language/data-io/print-space.c:119
+#, c-format
+msgid "The expression on PRINT SPACE evaluated to %g."
+msgstr "De expressie bij PRINT SPACE evalueerde tot %g."
+
+#: 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 "%s of %s verwacht"
+
+#: 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 ""
+"Kan %s niet hernoemen naar  %s omdat er al een variabele met de naam %s "
+"bestaat.  Om variabelen met overlappende naam te hernoemen gebruik een enkel "
+"RENAME subopdracht zoals \"/RENAME (A=B)(B=C)(C=A)\", of equivalent achtig, "
+"\"/RENAME (A B C=B C A)\"."
+
+#: src/language/data-io/trim.c:114
+msgid "`=' expected after variable list."
+msgstr "'=' verwacht na variabele lijst."
+
+#: 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 ""
+"Aantal variabelen aan linker zijde van `=' (%zu) komt niet overeen met het "
+"aantal variabelen aan rechter zijde (%zu), in tussengevoegde groep %d van "
+"RENAME subopdracht."
+
+#: src/language/data-io/trim.c:134
+#, c-format
+msgid "Requested renaming duplicates variable name %s."
+msgstr "Gevraagde hernoeming dupliceert variabele naam %s."
+
+#: src/language/data-io/trim.c:165
+msgid "Cannot DROP all variables from dictionary."
+msgstr "Kan niet alle variabelen DROP-en uit woordenboek."
+
+#: src/language/dictionary/apply-dictionary.c:75
+#, c-format
+msgid "Variable %s is %s in target file, but %s in source file."
+msgstr "Variabele %s is %s in doel bestand, maar %s in bron bestand."
+
+#: src/language/dictionary/apply-dictionary.c:115
+msgid "No matching variables found between the source and target files."
+msgstr ""
+"Geen overeenkomende variabelen gevonden tussen het bron en het doel bestand."
+
+#: src/language/dictionary/attributes.c:108
+msgid "Attribute array index must be between 1 and 65535."
+msgstr "Attribuut array index moet tussen 1 en 65535 liggen."
+
+#: src/language/dictionary/attributes.c:189
+msgid "expecting ATTRIBUTE= or DELETE="
+msgstr "ATTRIBUTE= of DELETE= verwacht"
+
+#: src/language/dictionary/delete-variables.c:40
+msgid ""
+"DELETE VARIABLES may not be used after TEMPORARY.  Temporary transformations "
+"will be made permanent."
+msgstr ""
+"DELETE VARIABLES mag niet gebruikt worden na TEMPORARY. Tijdelijke "
+"transformaties worden permanent gemaakt."
+
+#: src/language/dictionary/delete-variables.c:48
+msgid ""
+"DELETE VARIABLES may not be used to delete all variables from the active "
+"file dictionary.  Use NEW FILE instead."
+msgstr ""
+"DELETE VARIABLES mag niet gebruikt om alle variabelen van het actieve "
+"bestand woordenboek te verwijderen. Gebruik NEW FILE in de plaats."
+
+#: src/language/dictionary/formats.c:90
+msgid "`(' expected after variable list."
+msgstr "'(' verwacht na variabele lijst."
+
+#: src/language/dictionary/formats.c:100 src/language/dictionary/numeric.c:74
+msgid "`)' expected after output format."
+msgstr "')' verwacht na output formaat."
+
+#: src/language/dictionary/missing-values.c:56
+#: src/language/stats/aggregate.c:458
+msgid "expecting `('"
+msgstr "'(' verwacht"
+
+#: src/language/dictionary/missing-values.c:72
+#, c-format
+msgid ""
+"Cannot mix numeric variables (e.g. %s) and string variables (e.g. %s) within "
+"a single list."
+msgstr ""
+"Kan numerieke variabelen (b.v. %s) en string variabelen (b.v. %s) niet mixen "
+"binnen een enkele lijst."
+
+#: src/language/dictionary/missing-values.c:116
+#, fuzzy, c-format
+msgid "Truncating missing value to maximum acceptable length (%d bytes)."
+msgstr "Afkappen missing value naar short string lengte (%d karakters)."
+
+#: src/language/dictionary/missing-values.c:138
+#, c-format
+msgid "Missing values provided are too long to assign to variable of width %d."
+msgstr ""
+"De opgegeven missing values zijn te lang om toe te kennen aan een variabele "
+"van breedte %d."
+
+#: src/language/dictionary/modify-variables.c:92
+msgid ""
+"MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
+"be made permanent."
+msgstr ""
+"MODIFY VARS mag niet gebruikt worden na TEMPORARY. Tijdelijke transformaties "
+"zullen permanent gemaakt worden."
+
+#: src/language/dictionary/modify-variables.c:114
+msgid "REORDER subcommand may be given at most once."
+msgstr "REORDER subopdracht mag maximaal 1 keer gegeven worden."
+
+#: src/language/dictionary/modify-variables.c:137
+msgid "Cannot specify ALL after specifying a set of variables."
+msgstr "Kan niet ALL opgeven na het specificeren van een set van variabelen."
+
+#: src/language/dictionary/modify-variables.c:147
+msgid "`(' expected on REORDER subcommand."
+msgstr "'(' verwacht bij REORDER subopdracht."
+
+#: src/language/dictionary/modify-variables.c:159
+msgid "`)' expected following variable names on REORDER subcommand."
+msgstr "')' verwacht achter variabele namen bij REORDER subopdracht."
+
+#: src/language/dictionary/modify-variables.c:177
+msgid "RENAME subcommand may be given at most once."
+msgstr "RENAME subopdracht mag maximaal 1 keer gegeven worden."
+
+#: src/language/dictionary/modify-variables.c:190
+msgid "`(' expected on RENAME subcommand."
+msgstr "'(' verwacht bij RENAME subopdracht."
+
+#: src/language/dictionary/modify-variables.c:199
+msgid ""
+"`=' expected between lists of new and old variable names on RENAME "
+"subcommand."
+msgstr ""
+"'=' verwacht tussen lijst van nieuwe en oude variabele namen bij RENAME "
+"subopdracht. "
+
+#: src/language/dictionary/modify-variables.c:208
+#: src/language/dictionary/rename-variables.c:76
+#, c-format
+msgid ""
+"Differing number of variables in old name list (%zu) and in new name list (%"
+"zu)."
+msgstr ""
+"Verschillend aantal variabelen in oude naam lijst (%zu) en in de nieuwe naam "
+"lijst (%zu)."
+
+#: src/language/dictionary/modify-variables.c:219
+msgid "`)' expected after variable lists on RENAME subcommand."
+msgstr "')' verwacht na variabele lijst bij RENAME subopdracht."
+
+#: src/language/dictionary/modify-variables.c:233
+msgid ""
+"KEEP subcommand may be given at most once.  It may not be given in "
+"conjunction with the DROP subcommand."
+msgstr ""
+"KEEP subopdracht mag slechts eenmaal gegeven worden. Het mag niet gegeven "
+"worden in combinatie met de DROP subopdracht."
+
+#: src/language/dictionary/modify-variables.c:276
+msgid ""
+"DROP subcommand may be given at most once.  It may not be given in "
+"conjunction with the KEEP subcommand."
+msgstr ""
+"DROP subopdracht mag slechts eenmaal gegeven worden. Het mag niet gegeven "
+"worden in combinatie met de KEEP subopdracht."
+
+#: src/language/dictionary/modify-variables.c:302
+#, c-format
+msgid "Unrecognized subcommand name `%s'."
+msgstr "Niet herkende subopdracht naam `%s'."
+
+#: src/language/dictionary/modify-variables.c:304
+msgid "Subcommand name expected."
+msgstr "Subopdracht naam verwacht."
+
+#: src/language/dictionary/modify-variables.c:312
+msgid "`/' or `.' expected."
+msgstr "'/' of '.' verwacht."
+
+#: src/language/dictionary/numeric.c:67
+#, c-format
+msgid "Format type %s may not be used with a numeric variable."
+msgstr "Formaat type %s mag niet gebruikt worden met een numerieke variabele."
+
+#: src/language/dictionary/numeric.c:86 src/language/dictionary/numeric.c:155
+#, c-format
+msgid "There is already a variable named %s."
+msgstr "Er bestaat al een variabele genaamd %s."
+
+#: src/language/dictionary/numeric.c:140
+#, c-format
+msgid "Format type %s may not be used with a string variable."
+msgstr "Formaat type %s mag niet gebruikt worden met een string variabele."
+
+#: src/language/dictionary/rename-variables.c:49
+msgid ""
+"RENAME VARS may not be used after TEMPORARY.  Temporary transformations will "
+"be made permanent."
+msgstr ""
+"RENAME VARS mag niet gebruikt worden na TEMPORARY. Tijdelijke transformaties "
+"zullen permanent gemaakt worden."
+
+#: src/language/dictionary/rename-variables.c:59
+msgid "`(' expected."
+msgstr "'(' verwacht."
+
+#: src/language/dictionary/rename-variables.c:67
+msgid "`=' expected between lists of new and old variable names."
+msgstr "'=' verwacht tussen lijst met nieuwe en oude variabele namen."
+
+#: src/language/dictionary/rename-variables.c:87
+msgid "`)' expected after variable names."
+msgstr "')' verwacht achter variabele namen."
+
+#: src/language/dictionary/rename-variables.c:97
+#, c-format
+msgid "Renaming would duplicate variable name %s."
+msgstr "Hernoemen zou variabele naam %s dupliceren."
+
+#: src/language/dictionary/split-file.c:85
+#: src/language/dictionary/sys-file-info.c:486
+#: src/language/dictionary/sys-file-info.c:641
+#: src/language/stats/crosstabs.q:1231 src/language/stats/crosstabs.q:1258
+#: src/language/stats/crosstabs.q:1282 src/language/stats/crosstabs.q:1307
+#: src/language/stats/examine.q:1959 src/language/stats/frequencies.q:1048
+#: src/language/stats/frequencies.q:1173 src/language/stats/reliability.q:582
+#: src/language/stats/reliability.q:593
+msgid "Value"
+msgstr "Waarde"
+
+#: src/language/dictionary/split-file.c:86
+#: src/language/dictionary/sys-file-info.c:397
+#: src/language/dictionary/sys-file-info.c:642 src/ui/gui/crosstabs.glade:275
+#: src/ui/gui/psppire.glade:1974 src/ui/gui/psppire-var-sheet.c:531
+#: src/ui/gui/psppire-var-store.c:797
+msgid "Label"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:113
+msgid "File:"
+msgstr "Bestand:"
+
+#: src/language/dictionary/sys-file-info.c:115 src/ui/gui/psppire.glade:1913
+#: src/ui/gui/recode.glade:841
+msgid "Label:"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:119
+msgid "No label."
+msgstr "Geen label."
+
+#: src/language/dictionary/sys-file-info.c:122
+msgid "Created:"
+msgstr "Aangemaakt:"
+
+#: src/language/dictionary/sys-file-info.c:125
+msgid "Integer Format:"
+msgstr "Integer Formaat:"
+
+#: src/language/dictionary/sys-file-info.c:127
+msgid "Big Endian."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:128
+msgid "Little Endian."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:129
+#: src/language/dictionary/sys-file-info.c:137
+msgid "Unknown."
+msgstr "Onbekend."
+
+#: src/language/dictionary/sys-file-info.c:130
+msgid "Real Format:"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:132
+msgid "IEEE 754 LE."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:133
+msgid "IEEE 754 BE."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:134
+msgid "VAX D."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:135
+msgid "VAX G."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:136
+msgid "IBM 390 Hex Long."
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:138
+#: src/ui/gui/descriptives-dialog.glade:79 src/ui/gui/recode.glade:940
+msgid "Variables:"
+msgstr "Variabelen:"
+
+#: src/language/dictionary/sys-file-info.c:140
+msgid "Cases:"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:142
+#: src/language/dictionary/sys-file-info.c:160
+msgid "Unknown"
+msgstr "Onbekend"
+
+#: src/language/dictionary/sys-file-info.c:144
+msgid "Type:"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:145
+msgid "System File."
+msgstr "Systeem Bestand."
+
+#: src/language/dictionary/sys-file-info.c:146
+msgid "Weight:"
+msgstr "Gewicht:"
+
+#: src/language/dictionary/sys-file-info.c:151
+msgid "Not weighted."
+msgstr "Niet gewogen."
+
+#: src/language/dictionary/sys-file-info.c:153
+msgid "Mode:"
+msgstr "Modus:"
+
+#: src/language/dictionary/sys-file-info.c:155
+#, c-format
+msgid "Compression %s."
+msgstr "Compressie %s."
+
+#: src/language/dictionary/sys-file-info.c:155
+msgid "on"
+msgstr "aan"
+
+#: src/language/dictionary/sys-file-info.c:155
+msgid "off"
+msgstr "uit"
+
+#: src/language/dictionary/sys-file-info.c:158
+msgid "Charset:"
+msgstr "Karakterset:"
+
+#: src/language/dictionary/sys-file-info.c:170
+#: src/language/dictionary/sys-file-info.c:397
+msgid "Description"
+msgstr "Omschrijving"
+
+#: src/language/dictionary/sys-file-info.c:171
+#: src/language/dictionary/sys-file-info.c:399
+#: src/language/dictionary/sys-file-info.c:724
+msgid "Position"
+msgstr "Positie"
+
+#: src/language/dictionary/sys-file-info.c:220
+msgid "The active file does not have a file label."
+msgstr "Het actieve bestand heeft geen bestand label."
+
+#: src/language/dictionary/sys-file-info.c:223
+msgid "File label:"
+msgstr "Bestand label:"
+
+#: src/language/dictionary/sys-file-info.c:298
+msgid "No variables to display."
+msgstr "Geen variabelen om te tonen."
+
+#: src/language/dictionary/sys-file-info.c:313
+msgid "Macros not supported."
+msgstr "Macros worden niet ondersteunt."
+
+#: src/language/dictionary/sys-file-info.c:323
+msgid "The active file dictionary does not contain any documents."
+msgstr "Het actieve bestand woordenboek bevat geen documenten."
+
+#: src/language/dictionary/sys-file-info.c:331
+msgid "Documents in the active file:"
+msgstr "Documenten in het actieve bestand:"
+
+#: src/language/dictionary/sys-file-info.c:485
+msgid "Attribute"
+msgstr "Attribuut"
+
+#: src/language/dictionary/sys-file-info.c:543
+#, c-format
+msgid "Format: %s"
+msgstr "Formaat: %s"
+
+#: src/language/dictionary/sys-file-info.c:550
+#, c-format
+msgid "Print Format: %s"
+msgstr "Print Formaat: %s"
+
+#: src/language/dictionary/sys-file-info.c:554
+#, c-format
+msgid "Write Format: %s"
+msgstr "Schrijf Formaat: %s"
+
+#: src/language/dictionary/sys-file-info.c:567
+#, c-format
+msgid "Measure: %s"
+msgstr "Meting: %s"
+
+#: src/language/dictionary/sys-file-info.c:568
+#: src/ui/gui/psppire-var-sheet.c:111
+msgid "Nominal"
+msgstr "Nominaal"
+
+#: src/language/dictionary/sys-file-info.c:569
+#: src/ui/gui/psppire-var-sheet.c:112
+msgid "Ordinal"
+msgstr "Ordinaal"
+
+#: src/language/dictionary/sys-file-info.c:570
+#: src/ui/gui/psppire-var-sheet.c:113
+msgid "Scale"
+msgstr "Schaal"
+
+#: src/language/dictionary/sys-file-info.c:573
+#, c-format
+msgid "Display Alignment: %s"
+msgstr "Toon Groepering: %s"
+
+#: src/language/dictionary/sys-file-info.c:574
+#: src/ui/gui/psppire-var-sheet.c:104
+msgid "Left"
+msgstr "Links"
+
+#: src/language/dictionary/sys-file-info.c:575
+#: src/ui/gui/psppire-var-sheet.c:106
+msgid "Center"
+msgstr "Centreer"
+
+#: src/language/dictionary/sys-file-info.c:576
+#: src/ui/gui/psppire-var-sheet.c:105
+msgid "Right"
+msgstr "Rechts"
+
+#: src/language/dictionary/sys-file-info.c:579
+#, c-format
+msgid "Display Width: %d"
+msgstr "Toonbreedte: %d"
+
+#: src/language/dictionary/sys-file-info.c:593
+msgid "Missing Values: "
+msgstr "Ontbrekende Waardes:"
+
+#: src/language/dictionary/sys-file-info.c:702
+msgid "No vectors defined."
+msgstr "Geen vectoren gedefinieerd."
+
+#: src/language/dictionary/sys-file-info.c:723
+msgid "Vector"
+msgstr ""
+
+#: src/language/dictionary/sys-file-info.c:726
+msgid "Print Format"
+msgstr "Print Formaat"
+
+#: src/language/dictionary/value-labels.c:150
+msgid "Truncating value label to 60 characters."
+msgstr "Afkappen waarde label tot 60 karakters."
+
+#: src/language/dictionary/variable-display.c:120
+msgid "Variable display width must be a positive integer."
+msgstr "Variabele toonbreedte moet een positieve integer zijn."
+
+#: src/language/dictionary/variable-label.c:51
+msgid "String expected for variable label."
+msgstr "String verwacht voor variabele label."
+
+#: src/language/dictionary/variable-label.c:59
+msgid "Truncating variable label to 255 characters."
+msgstr "Afkappen variabele label tot 255 karakters."
+
+#: src/language/dictionary/vector.c:64
+#, c-format
+msgid "A vector named %s already exists."
+msgstr "Een vector genaamd %s bestaat al."
+
+#: src/language/dictionary/vector.c:72
+#, c-format
+msgid "Vector name %s is given twice."
+msgstr "Vector genaamd %s is 2 keer opgegeven."
+
+#: src/language/dictionary/vector.c:96
+msgid "A slash must separate each vector specification in VECTOR's long form."
+msgstr "Een '/' moet elke vector specificatie scheiden in VECTOR's lange vorm."
+
+#: src/language/dictionary/vector.c:129
+msgid "Vectors must have at least one element."
+msgstr "Vectoren moeten ten minste 1 element bevatten."
+
+#: src/language/dictionary/vector.c:150
+msgid "expecting vector length"
+msgstr "vector lengte verwacht"
+
+#: src/language/dictionary/vector.c:166
+#, c-format
+msgid "%s is too long for a variable name."
+msgstr "%s is te lang voor een variabele naam."
+
+#: src/language/dictionary/vector.c:171
+#, c-format
+msgid "%s is an existing variable name."
+msgstr "%s is een bestaande variabele naam."
+
+#: src/language/dictionary/weight.c:49
+msgid "The weighting variable must be numeric."
+msgstr "De weging variabele moet numeriek zijn."
+
+#: src/language/dictionary/weight.c:54
+msgid "The weighting variable may not be scratch."
+msgstr "De weging variabele mag geen scratch zijn."
+
+#: src/language/expressions/evaluate.c:155
+msgid "expecting number or string"
+msgstr "verwacht nummer of string"
+
+#: src/language/expressions/evaluate.c:169
+#, c-format
+msgid "Duplicate variable name %s."
+msgstr "Dubbele variabele naam %s."
+
+#: src/language/expressions/helpers.c:51
+msgid ""
+"One of the arguments to a DATE function is not an integer.  The result will "
+"be system-missing."
+msgstr ""
+"Een van de variabelen voor een DATE functie is geen integer. Het resultaat "
+"zal system-missing zijn."
+
+#: src/language/expressions/helpers.c:73
+msgid ""
+"The week argument to DATE.WKYR is not an integer.  The result will be system-"
+"missing."
+msgstr ""
+"Het week argument voor DATE.WKYR is geen integer.  Het resultaat zal system-"
+"missing zijn."
+
+#: src/language/expressions/helpers.c:79
+msgid ""
+"The week argument to DATE.WKYR is outside the acceptable range of 1 to 53.  "
+"The result will be system-missing."
+msgstr ""
+"Het week argument voor DATE.WKYR is buiten de acceptabele range van 1 tot "
+"53.  Het resultaat zal system-missing zijn."
+
+#: src/language/expressions/helpers.c:101
+msgid ""
+"The day argument to DATE.YRDAY is not an integer.  The result will be system-"
+"missing."
+msgstr ""
+"Het dag argument voor DATE.WKYR is geen integer.  Het resultaat zal system-"
+"missing zijn."
+
+#: src/language/expressions/helpers.c:107
+msgid ""
+"The day argument to DATE.YRDAY is outside the acceptable range of 1 to 366.  "
+"The result will be system-missing."
+msgstr ""
+"Het dag argument voor DATE.WKYR is buiten de acceptabele range van 1 tot "
+"366.  Het resultaat zal system-missing zijn."
+
+#: src/language/expressions/helpers.c:129
+msgid ""
+"The year argument to YRMODA is greater than 47516.  The result will be "
+"system-missing."
+msgstr ""
+"Het jaar argument voor YRMODA is groter dan 47516.  Het resultaat zal system-"
+"missing zijn."
+
+#: src/language/expressions/helpers.c:182
+#, c-format
+msgid ""
+"Unrecognized date unit \"%.*s\".  Valid date units are \"years\", \"quarters"
+"\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", and \"seconds\"."
+msgstr ""
+"Niet herkende datum eenheid \"%.*s\".  Geldige datum eenheden zijn \"years"
+"\", \"quarters\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", "
+"en \"seconds\"."
+
+#: src/language/expressions/helpers.c:332
+msgid ""
+"Invalid DATESUM method.  Valid choices are \"closest\" and \"rollover\"."
+msgstr ""
+"Ongeldige DATESUM methode. Geldige keuzes zijn \"closest\" en \"rollover\"."
+
+#: src/language/expressions/parse.c:259
+#, c-format
+msgid ""
+"Type mismatch: expression has %s type, but a numeric value is required here."
+msgstr ""
+"Type ongelijk: expressie heeft type %s, maar een numerieke waarde is hier "
+"noodzakelijk."
+
+#: src/language/expressions/parse.c:271
+#, c-format
+msgid ""
+"Type mismatch: expression has %s type, but a string value is required here."
+msgstr ""
+"Type ongelijk: expressie heeft type %s, maar een string waarde is hier "
+"noodzakelijk."
+
+#: src/language/expressions/parse.c:427
+#, c-format
+msgid "Type mismatch while applying %s operator: cannot convert %s to %s."
+msgstr ""
+"Type ongelijk tijdens het uitvoeren van %s operator: kan %s niet naar %s "
+"converteren."
+
+#: src/language/expressions/parse.c:643
+msgid ""
+"Chaining relational operators (e.g. \"a < b < c\") will not produce the "
+"mathematically expected result.  Use the AND logical operator to fix the "
+"problem (e.g. \"a < b AND b < c\").  If chaining is really intended, "
+"parentheses will disable this warning (e.g. \"(a < b) < c\".)"
+msgstr ""
+
+#: src/language/expressions/parse.c:744
+msgid ""
+"The exponentiation operator (\"**\") is left-associative, even though right-"
+"associative semantics are more useful.  That is, \"a**b**c\" equals \"(a**b)"
+"**c\", not as \"a**(b**c)\".  To disable this warning, insert parentheses."
+msgstr ""
+
+#: src/language/expressions/parse.c:809
+#, c-format
+msgid "Unknown system variable %s."
+msgstr "Onbekende systeem variabele %s."
+
+#: src/language/expressions/parse.c:857
+#, c-format
+msgid "Unknown identifier %s."
+msgstr "Onbekende herkenningsteken %s."
+
+#: src/language/expressions/parse.c:885 src/language/stats/aggregate.c:516
+msgid "expecting `)'"
+msgstr "')' verwacht"
+
+#: src/language/expressions/parse.c:892
+msgid "in expression"
+msgstr "in expressie"
+
+#: src/language/expressions/parse.c:1073
+#, c-format
+msgid "%s must have at least %d arguments in list."
+msgstr "%s heeft tenminste %d argumenten nodig in lijst."
+
+#: src/language/expressions/parse.c:1082
+#, c-format
+msgid "%s must have even number of arguments in list."
+msgstr "%s heeft een even aantal argumenten in lijst nodig."
+
+#: src/language/expressions/parse.c:1085
+#, c-format
+msgid "%s must have multiple of %d arguments in list."
+msgstr "%s heeft meerdere %d argumenten in lijst nodig."
+
+#: src/language/expressions/parse.c:1095
+#, c-format
+msgid "%s function does not accept a minimum valid argument count."
+msgstr "%s functie accepteert geen minimaal geldige argumenten teller."
+
+#: src/language/expressions/parse.c:1104
+#, c-format
+msgid "%s requires at least %d valid arguments in list."
+msgstr "%s vereist tenminste %d geldige argumenten in lijst."
+
+#: src/language/expressions/parse.c:1110
+#, c-format
+msgid ""
+"With %s, using minimum valid argument count of %d does not make sense when "
+"passing only %d arguments in list."
+msgstr ""
+"Met %s, heeft het gebruik van de minimum geldige argumenttelling van %d geen "
+"zin wanneer een lijst van slechts %d wordt doorgegeven. "
+
+#: src/language/expressions/parse.c:1164
+#, c-format
+msgid "Type mismatch invoking %s as "
+msgstr "Type ongelijk bij aanroep %s als "
+
+#: src/language/expressions/parse.c:1169
+msgid "Function invocation "
+msgstr "Functie aanroep "
+
+#: src/language/expressions/parse.c:1171
+msgid " does not match any known function.  Candidates are:"
+msgstr " komt niet overeen met een geldige functie. Kandidaten zijn:"
+
+#: src/language/expressions/parse.c:1201
+#, c-format
+msgid "No function or vector named %s."
+msgstr "Geen functie of vector genaamd %s."
+
+#: src/language/expressions/parse.c:1244
+#, c-format
+msgid "expecting `,' or `)' invoking %s function"
+msgstr "verwacht ',' of ')' bij aanroep %s functie"
+
+#: src/language/expressions/parse.c:1264
+#, c-format
+msgid "%s is a PSPP extension."
+msgstr "%s is een PSPP extensie."
+
+#: src/language/expressions/parse.c:1273
+#, c-format
+msgid "%s may not appear after TEMPORARY."
+msgstr "%s mag niet voorkomen na TEMPORARY."
+
+#: src/language/lexer/format-parser.c:88
+msgid "expecting valid format specifier"
+msgstr "verwacht geldige formaat specificator"
+
+#: src/language/lexer/format-parser.c:121
+msgid "expecting format type"
+msgstr "verwacht formaat type"
+
+#: src/language/lexer/lexer.c:283
+#, c-format
+msgid "%s does not form a valid number."
+msgstr "%s vormt geen geldig nummer."
+
+#: src/language/lexer/lexer.c:389
+#, fuzzy, c-format
+msgid "Bad character in input: `%s'."
+msgstr "Fout karakter in input: '%c'."
+
+#: src/language/lexer/lexer.c:426
+#, c-format
+msgid "Subcommand %s may only be specified once."
+msgstr "Subopdracht %s mag slechts een keer gespecificeerd worden."
+
+#: src/language/lexer/lexer.c:434
+#, c-format
+msgid "missing required subcommand %s"
+msgstr "mis verplichte subopdracht %s"
+
+#: src/language/lexer/lexer.c:463
+#, c-format
+msgid "Syntax error %s at %s."
+msgstr "Syntax fout %s op %s."
+
+#: src/language/lexer/lexer.c:466
+#, c-format
+msgid "Syntax error at %s."
+msgstr "Syntax fout op %s."
+
+#: src/language/lexer/lexer.c:600 src/language/lexer/lexer.c:617
+#, c-format
+msgid "expecting `%s'"
+msgstr "verwacht '%s'"
+
+#: src/language/lexer/lexer.c:631
+msgid "expecting string"
+msgstr "string verwacht"
+
+#: src/language/lexer/lexer.c:645
+msgid "expecting integer"
+msgstr "verwacht integer"
+
+#: src/language/lexer/lexer.c:658
+msgid "expecting number"
+msgstr "nummer verwacht"
+
+#: src/language/lexer/lexer.c:670
+msgid "expecting identifier"
+msgstr "verwacht herkenningsteken"
+
+#: src/language/lexer/lexer.c:1064
+msgid "binary"
+msgstr "binair"
+
+#: src/language/lexer/lexer.c:1069
+msgid "octal"
+msgstr "octaal"
+
+#: src/language/lexer/lexer.c:1074
+msgid "hex"
+msgstr ""
+
+#: src/language/lexer/lexer.c:1084
+#, c-format
+msgid "String of %s digits has %zu characters, which is not a multiple of %d."
+msgstr ""
+"String van %s cijfers heeft %zu karakters, wat geen meervoud van %d is."
+
+#: src/language/lexer/lexer.c:1113
+#, c-format
+msgid "`%c' is not a valid %s digit."
+msgstr "'%c' is geen geldig %s cijfer."
+
+#: src/language/lexer/lexer.c:1147
+msgid "Unterminated string constant."
+msgstr "Geen einde aan string constante."
+
+#: src/language/lexer/lexer.c:1201
+msgid "Unexpected end of file in string concatenation."
+msgstr "Onverwacht bestandseinde in string samenvoeging."
+
+#: src/language/lexer/lexer.c:1209
+msgid "String expected following `+'."
+msgstr "String verwacht achter '+'."
+
+#: src/language/lexer/lexer.c:1222
+#, c-format
+msgid "String exceeds 255 characters in length (%zu characters)."
+msgstr "String overschrijdt de lengte van 255 karakters (%zu karakters)."
+
+#: src/language/lexer/value-parser.c:60
+#, c-format
+msgid ""
+"Low end of range (%g) is below high end (%g).  The range will be treated as "
+"reversed."
+msgstr ""
+"Ondergrens van range (%g) is lager dan bovengrens (%g). De range wordt "
+"behandeld als omgekeerd."
+
+#: src/language/lexer/value-parser.c:68
+#, c-format
+msgid "Ends of range are equal (%g)."
+msgstr "Eindes van range zijn gelijk (%g)."
+
+#: src/language/lexer/value-parser.c:76
+msgid "LO or LOWEST must be part of a range."
+msgstr "LO of LOWEST moet een onderdeel van een range zijn."
+
+#: src/language/lexer/value-parser.c:108
+msgid "System-missing value is not valid here."
+msgstr "System-missing waarde is hier niet geldig."
+
+#: src/language/lexer/value-parser.c:116
+msgid "expecting number or data string"
+msgstr "nummer of data string verwacht"
+
+#: src/language/lexer/variable-parser.c:63
+msgid "expecting variable name"
+msgstr "variabele naam verwacht"
+
+#: src/language/lexer/variable-parser.c:73
+#, c-format
+msgid "%s is not a variable name."
+msgstr "%s is geen variabele naam."
+
+#: src/language/lexer/variable-parser.c:176
+#, c-format
+msgid ""
+"%s is not a numeric variable.  It will not be included in the variable list."
+msgstr ""
+"%s is geen numerieke variabele. Het wordt niet opgenomen in de variabele "
+"lijst."
+
+#: src/language/lexer/variable-parser.c:179
+#, c-format
+msgid ""
+"%s is not a string variable.  It will not be included in the variable list."
+msgstr ""
+"%s is geen string variabele. Het wordt niet opgenomen in de variabele lijst."
+
+#: src/language/lexer/variable-parser.c:183
+#, c-format
+msgid "Scratch variables (such as %s) are not allowed here."
+msgstr "Scratch variabelen (zoals %s) zijn hier niet toegestaan."
+
+#: src/language/lexer/variable-parser.c:187
+#, c-format
+msgid ""
+"%s and %s are not the same type.  All variables in this variable list must "
+"be of the same type.  %s will be omitted from the list."
+msgstr ""
+"%s en %s zijn niet van hetzelfde type. Alle variabelen in deze variabele "
+"lijst dienen van hetzelfde type te zijn.  %s wordt overgeslagen voor de "
+"lijst."
+
+#: src/language/lexer/variable-parser.c:193
+#, c-format
+msgid ""
+"%s and %s are string variables with different widths.  All variables in this "
+"variable list must have the same width.  %s will be omitted from the list."
+msgstr ""
+"%s en %s hebben verschillende breedtes. Alle variabelen in deze variabele "
+"lijst dienen dezelfde breedte te hebben.  %s wordt overgeslagen voor de "
+"lijst."
+
+#: src/language/lexer/variable-parser.c:198
+#, c-format
+msgid "Variable %s appears twice in variable list."
+msgstr "Variabele %s komt 2 keer in de variabele lijst voor."
+
+#: src/language/lexer/variable-parser.c:311
+#, c-format
+msgid "%s TO %s is not valid syntax since %s precedes %s in the dictionary."
+msgstr ""
+"%s TO %s is geen geldige syntax omdat %s voor %s in het woordenboek staat."
+
+#: src/language/lexer/variable-parser.c:319
+#, c-format
+msgid ""
+"When using the TO keyword to specify several variables, both variables must "
+"be from the same variable dictionaries, of either ordinary, scratch, or "
+"system variables.  %s is a %s variable, whereas %s is %s."
+msgstr ""
+"Wanneer het sleutelwoord TO wordt gebruikt om verscheidene variabelen te "
+"specificeren, moeten beide variabelen van het zelfde variabele "
+"woordenboeken, of gewone, scratch, of systeem variabelen zijn. %s is een %s "
+"variabele, terwijl %s %s. is. "
+
+#: src/language/lexer/variable-parser.c:393
+msgid "incorrect use of TO convention"
+msgstr "foutief gebruik van TO conventie"
+
+#: src/language/lexer/variable-parser.c:436
+msgid "Scratch variables not allowed here."
+msgstr "Scratch variabelen niet toegestaan hier."
+
+#: src/language/lexer/variable-parser.c:458
+msgid "Prefixes don't match in use of TO convention."
+msgstr "Prefixen komen niet overeen in het gebruik van TO conventie. "
+
+#: src/language/lexer/variable-parser.c:463
+msgid "Bad bounds in use of TO convention."
+msgstr "Slechte grenzen in het gebruik van TO conventie."
+
+#: src/language/stats/aggregate.c:219
+msgid "while expecting COLUMNWISE"
+msgstr "terwijl COLUMNWISE verwacht werd"
+
+#: src/language/stats/aggregate.c:247
+msgid "expecting BREAK"
+msgstr "BREAK verwacht"
+
+#: 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 ""
+"Als PRESORTED is gespecificeerd, heeft specificeren van sorteer volgorde met "
+"(A) of (D) geen effect. Uitvoer data is hetzelfde gesorteerd als de invoer "
+"data."
+
+#: src/language/stats/aggregate.c:423
+msgid "expecting aggregation function"
+msgstr "aggregatie functie verwacht"
+
+#: src/language/stats/aggregate.c:441
+#, c-format
+msgid "Unknown aggregation function %s."
+msgstr "Onbekende aggregatie functie %s."
+
+#: src/language/stats/aggregate.c:497
+#, c-format
+msgid "Missing argument %zu to %s."
+msgstr "Missend argument %zu naar %s."
+
+#: src/language/stats/aggregate.c:506
+#, c-format
+msgid "Arguments to %s must be of same type as source variables."
+msgstr "Argumenten naar %s moeten van hetzelfde type zijn als bron variabelen."
+
+#: src/language/stats/aggregate.c:528
+#, c-format
+msgid ""
+"Number of source variables (%zu) does not match number of target variables (%"
+"zu)."
+msgstr ""
+"Aantal bron variabelen (%zu) komt niet overeen met aantal doel variabelen (%"
+"zu)."
+
+#: 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 ""
+"De volgorde van de geldige argumenten doorgegeven aan de %s functie klopt "
+"niet. Ze worden behandeld alsof ze in de correcte volgorde waren opgegeven."
+
+#: src/language/stats/aggregate.c:614
+#, c-format
+msgid ""
+"Variable name %s is not unique within the aggregate file dictionary, which "
+"contains the aggregate variables and the break variables."
+msgstr ""
+"Variabele naam %s is niet uniek binnen het aggregate bestand woordenboek, "
+"dat de aggregate variabelen en de break variabelen bevat."
+
+#: src/language/stats/autorecode.c:136
+#, c-format
+msgid "Source variable count (%zu) does not match target variable count (%zu)."
+msgstr ""
+"Bron variabele teller (%zu) komt niet overeen met doel variabele teller (%"
+"zu)."
+
+#: src/language/stats/autorecode.c:164
+#, c-format
+msgid "Target variable %s duplicates existing variable %s."
+msgstr "Doel variabele %s dupliceert bestaande variabele %s."
+
+#: src/language/stats/autorecode.c:171
+#, c-format
+msgid "Duplicate variable name %s among target variables."
+msgstr "Dubbele variabele naam %s tussen doel variabelen."
+
+#: src/language/stats/binomial.c:141
+#, c-format
+msgid "Variable %s is not dichotomous"
+msgstr "Variabele %s is niet dichotomisch "
+
+#: src/language/stats/binomial.c:194
+msgid "Binomial Test"
+msgstr ""
+
+#: src/language/stats/binomial.c:224
+msgid "Group1"
+msgstr "Groep1 "
+
+#: src/language/stats/binomial.c:225
+msgid "Group2"
+msgstr "Groep2"
+
+#: src/language/stats/binomial.c:226 src/language/stats/chisquare.c:202
+#: src/language/stats/chisquare.c:262 src/language/stats/crosstabs.q:843
+#: src/language/stats/crosstabs.q:1170 src/language/stats/crosstabs.q:1594
+#: src/language/stats/examine.q:1216 src/language/stats/frequencies.q:1125
+#: src/language/stats/oneway.q:305 src/language/stats/oneway.q:476
+#: src/language/stats/regression.q:309 src/language/stats/reliability.q:718
+#: src/language/stats/sign.c:94 src/language/stats/wilcoxon.c:262
+#: src/ui/gui/crosstabs-dialog.c:59
+msgid "Total"
+msgstr "Totaal"
+
+#: src/language/stats/binomial.c:259 src/language/stats/chisquare.c:225
+#: src/language/stats/crosstabs.q:1256 src/language/stats/crosstabs.q:1304
+msgid "Category"
+msgstr "Categorie"
+
+#: src/language/stats/binomial.c:260 src/language/stats/crosstabs.q:850
+#: src/language/stats/examine.q:1289 src/language/stats/frequencies.q:1396
+#: src/language/stats/npar-summary.c:123 src/language/stats/oneway.q:389
+#: src/language/stats/reliability.q:721 src/language/stats/sign.c:74
+#: src/language/stats/t-test.q:506 src/language/stats/t-test.q:526
+#: src/language/stats/t-test.q:626 src/language/stats/t-test.q:1105
+#: src/language/stats/wilcoxon.c:245
+msgid "N"
+msgstr ""
+
+#: src/language/stats/binomial.c:261
+msgid "Observed Prop."
+msgstr ""
+
+#: src/language/stats/binomial.c:262
+msgid "Test Prop."
+msgstr ""
+
+#: src/language/stats/binomial.c:265
+#, c-format
+msgid "Exact Sig. (%d-tailed)"
+msgstr ""
+
+#: src/language/stats/chisquare.c:172
+#, c-format
+msgid ""
+"CHISQUARE test specified %d expected values, but %d distinct values were "
+"encountered in variable %s."
+msgstr ""
+"CHISQUARE test specificeert %d verwachte waardes, maar %d verschillende "
+"waardes werden gevonden in variabele %s."
+
+#: src/language/stats/chisquare.c:186 src/language/stats/chisquare.c:226
+msgid "Observed N"
+msgstr "Waargenomen N"
+
+#: src/language/stats/chisquare.c:187 src/language/stats/chisquare.c:227
+msgid "Expected N"
+msgstr "Verwacht N"
+
+#: src/language/stats/chisquare.c:188 src/language/stats/chisquare.c:228
+#: src/language/stats/regression.q:308 src/ui/gui/crosstabs-dialog.c:61
+msgid "Residual"
+msgstr "Overblijvend"
+
+#: src/language/stats/chisquare.c:221 src/language/stats/sign.c:62
+msgid "Frequencies"
+msgstr "Frequenties"
+
+#: src/language/stats/chisquare.c:276 src/language/stats/sign.c:115
+#: src/language/stats/wilcoxon.c:313
+msgid "Test Statistics"
+msgstr ""
+
+#: src/language/stats/chisquare.c:290
+msgid "Chi-Square"
+msgstr ""
+
+#: src/language/stats/chisquare.c:291 src/language/stats/crosstabs.q:1232
+#: src/language/stats/oneway.q:278 src/language/stats/oneway.q:691
+#: src/language/stats/regression.q:302 src/language/stats/t-test.q:753
+#: src/language/stats/t-test.q:924 src/language/stats/t-test.q:1011
+msgid "df"
+msgstr ""
+
+#: src/language/stats/chisquare.c:292
+msgid "Asymp. Sig."
+msgstr ""
+
+#: src/language/stats/crosstabs.q:325
+msgid ""
+"Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
+msgstr ""
+"Missing modus REPORT niet toegestaan in algemene modus.  MISSING=TABLE "
+"aangenomen."
+
+#: src/language/stats/crosstabs.q:414
+msgid "Too many cross-tabulation variables or dimensions."
+msgstr "Te veel cross-tabulation variabelen of dimensies."
+
+#: src/language/stats/crosstabs.q:424
+msgid "expecting BY"
+msgstr "BY verwacht"
+
+#: src/language/stats/crosstabs.q:484
+msgid "VARIABLES must be specified before TABLES."
+msgstr "VARIABLES dient voor TABLES gespecificeerd te worden."
+
+#: src/language/stats/crosstabs.q:522
+#, c-format
+msgid "Maximum value (%ld) less than minimum value (%ld)."
+msgstr "Maximum waarde (%ld) is kleiner dan minimum waarde (%ld)."
+
+#: src/language/stats/crosstabs.q:838
+msgid "Summary."
+msgstr "Overzicht."
+
+#: src/language/stats/crosstabs.q:840 src/language/stats/examine.q:1277
+#: src/language/stats/reliability.q:709
+msgid "Cases"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:841 src/language/stats/examine.q:1214
+#: src/language/stats/frequencies.q:1046 src/language/stats/frequencies.q:1397
+#: src/language/stats/reliability.q:712
+msgid "Valid"
+msgstr "Geldig"
+
+#: src/language/stats/crosstabs.q:842 src/language/stats/examine.q:1215
+#: src/language/stats/frequencies.q:1116 src/language/stats/frequencies.q:1398
+#: src/ui/gui/psppire-var-sheet.c:533 src/ui/gui/psppire-var-store.c:799
+msgid "Missing"
+msgstr "Ontbrekend"
+
+#: src/language/stats/crosstabs.q:851 src/language/stats/examine.q:1292
+#: src/language/stats/frequencies.q:1050 src/language/stats/frequencies.q:1051
+#: src/language/stats/frequencies.q:1052
+msgid "Percent"
+msgstr "Procent"
+
+#: src/language/stats/crosstabs.q:1131
+msgid "count"
+msgstr "aantal"
+
+#: src/language/stats/crosstabs.q:1132
+msgid "row %"
+msgstr "rij %"
+
+#: src/language/stats/crosstabs.q:1133
+msgid "column %"
+msgstr "kolom %"
+
+#: src/language/stats/crosstabs.q:1134
+msgid "total %"
+msgstr "totaal %"
+
+#: src/language/stats/crosstabs.q:1135
+msgid "expected"
+msgstr "verwacht"
+
+#: src/language/stats/crosstabs.q:1136
+msgid "residual"
+msgstr "overblijvend"
+
+#: src/language/stats/crosstabs.q:1137
+msgid "std. resid."
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1138
+msgid "adj. resid."
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1227
+msgid "Chi-square tests."
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1230 src/language/stats/crosstabs.q:1257
+#: src/language/stats/crosstabs.q:1281 src/language/stats/crosstabs.q:1305
+#: src/language/stats/examine.q:1753 src/ui/gui/checkbox-treeview.c:92
+msgid "Statistic"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1234
+msgid "Asymp. Sig. (2-sided)"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1236
+msgid "Exact Sig. (2-sided)"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1238
+msgid "Exact Sig. (1-sided)"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1253
+msgid "Symmetric measures."
+msgstr "Symmetrische metingen."
+
+#: src/language/stats/crosstabs.q:1259 src/language/stats/crosstabs.q:1308
+msgid "Asymp. Std. Error"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1260 src/language/stats/crosstabs.q:1309
+msgid "Approx. T"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1261 src/language/stats/crosstabs.q:1310
+msgid "Approx. Sig."
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1276
+msgid "Risk estimate."
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1280
+#, c-format
+msgid "95%% Confidence Interval"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1283 src/language/stats/t-test.q:757
+#: src/language/stats/t-test.q:921 src/language/stats/t-test.q:1014
+msgid "Lower"
+msgstr "Lager"
+
+#: src/language/stats/crosstabs.q:1284 src/language/stats/t-test.q:758
+#: src/language/stats/t-test.q:922 src/language/stats/t-test.q:1015
+msgid "Upper"
+msgstr "Hoger"
+
+#: src/language/stats/crosstabs.q:1301
+msgid "Directional measures."
+msgstr "Directioneel metingen."
+
+#: src/language/stats/crosstabs.q:1306 src/ui/gui/psppire.glade:2099
+#: src/ui/gui/psppire-var-sheet.c:528 src/ui/gui/psppire-var-store.c:794
+msgid "Type"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1774
+msgid "Pearson Chi-Square"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1775
+msgid "Likelihood Ratio"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1776
+msgid "Fisher's Exact Test"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1777
+msgid "Continuity Correction"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1778
+msgid "Linear-by-Linear Association"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1813 src/language/stats/crosstabs.q:1888
+#: src/language/stats/crosstabs.q:1953
+msgid "N of Valid Cases"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1832 src/language/stats/crosstabs.q:1971
+msgid "Nominal by Nominal"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1833 src/language/stats/crosstabs.q:1972
+msgid "Ordinal by Ordinal"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1834
+msgid "Interval by Interval"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1835
+msgid "Measure of Agreement"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1840 src/ui/gui/crosstabs-dialog.c:41
+msgid "Phi"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1841
+msgid "Cramer's V"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1842
+msgid "Contingency Coefficient"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1843
+msgid "Kendall's tau-b"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1844
+msgid "Kendall's tau-c"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1845 src/ui/gui/crosstabs-dialog.c:48
+msgid "Gamma"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1846
+msgid "Spearman Correlation"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1847
+msgid "Pearson's R"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1848 src/ui/gui/crosstabs-dialog.c:50
+msgid "Kappa"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1926
+#, c-format
+msgid "Odds Ratio for %s (%g / %g)"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1929
+#, c-format
+msgid "Odds Ratio for %s (%.*s / %.*s)"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1937
+#, c-format
+msgid "For cohort %s = %g"
+msgstr "Voor cohort %s = %g"
+
+#: src/language/stats/crosstabs.q:1940
+#, c-format
+msgid "For cohort %s = %.*s"
+msgstr "Voor cohort %s = %.*s"
+
+#: src/language/stats/crosstabs.q:1973
+msgid "Nominal by Interval"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1978 src/ui/gui/crosstabs-dialog.c:43
+msgid "Lambda"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1979
+msgid "Goodman and Kruskal tau"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1980
+msgid "Uncertainty Coefficient"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1981
+msgid "Somers' d"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1982 src/ui/gui/crosstabs-dialog.c:51
+msgid "Eta"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1987
+msgid "Symmetric"
+msgstr ""
+
+#: src/language/stats/crosstabs.q:1988 src/language/stats/crosstabs.q:1989
+#, c-format
+msgid "%s Dependent"
+msgstr ""
+
+#: src/language/stats/descriptives.c:102 src/language/stats/examine.q:1559
+#: src/language/stats/frequencies.q:123 src/language/stats/npar-summary.c:126
+#: src/language/stats/oneway.q:390 src/language/stats/t-test.q:507
+#: src/language/stats/t-test.q:527 src/language/stats/t-test.q:625
+#: src/language/stats/t-test.q:918 src/ui/gui/descriptives-dialog.c:39
+#: src/ui/gui/frequencies-dialog.c:40
+msgid "Mean"
+msgstr "Gemiddeld"
+
+#: src/language/stats/descriptives.c:103
+msgid "S E Mean"
+msgstr ""
+
+#: src/language/stats/descriptives.c:104 src/language/stats/frequencies.q:127
+msgid "Std Dev"
+msgstr ""
+
+#: src/language/stats/descriptives.c:105 src/language/stats/examine.q:1589
+#: 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:1625
+#: src/language/stats/frequencies.q:129 src/ui/gui/descriptives-dialog.c:47
+#: src/ui/gui/frequencies-dialog.c:50
+msgid "Kurtosis"
+msgstr ""
+
+#: src/language/stats/descriptives.c:107
+msgid "S E Kurt"
+msgstr ""
+
+#: src/language/stats/descriptives.c:108 src/language/stats/examine.q:1620
+#: src/language/stats/frequencies.q:131 src/ui/gui/descriptives-dialog.c:48
+#: src/ui/gui/frequencies-dialog.c:46
+msgid "Skewness"
+msgstr ""
+
+#: src/language/stats/descriptives.c:109
+msgid "S E Skew"
+msgstr ""
+
+#: src/language/stats/descriptives.c:110 src/language/stats/examine.q:1609
+#: 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:1599
+#: src/language/stats/frequencies.q:134 src/language/stats/npar-summary.c:132
+#: src/language/stats/oneway.q:404 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:1604
+#: src/language/stats/frequencies.q:135 src/language/stats/npar-summary.c:135
+#: src/language/stats/oneway.q:405 src/ui/gui/descriptives-dialog.c:42
+#: src/ui/gui/frequencies-dialog.c:43
+msgid "Maximum"
+msgstr ""
+
+#: src/language/stats/descriptives.c:113 src/language/stats/frequencies.q:136
+#: src/ui/gui/descriptives-dialog.c:44 src/ui/gui/frequencies-dialog.c:53
+msgid "Sum"
+msgstr "Som"
+
+#: src/language/stats/descriptives.c:344
+#, c-format
+msgid "Z-score variable name %s would be a duplicate variable name."
+msgstr "Z-score variabele naam %s zou een dubbele variabele naam zijn."
+
+#: src/language/stats/descriptives.c:451
+msgid "expecting statistic name: reverting to default"
+msgstr "statistische naam verwacht: teruggezet op default"
+
+#: src/language/stats/descriptives.c:524
+msgid ""
+"Ran out of generic names for Z-score variables.  There are only 126 generic "
+"names: ZSC001-ZSC0999, STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."
+msgstr ""
+"Generieke namen voor Z-score variabelen zijn uitgeput.  Er zijn slechts 126 "
+"generieke namen: ZSC001-ZSC0999, STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."
+
+#: src/language/stats/descriptives.c:556
+msgid "Mapping of variables to corresponding Z-scores."
+msgstr "Mappen van variabelen naar corresponderen Z-scores."
+
+#: src/language/stats/descriptives.c:561
+msgid "Source"
+msgstr "Bron"
+
+#: src/language/stats/descriptives.c:562
+msgid "Target"
+msgstr "Doel"
+
+#: src/language/stats/descriptives.c:673
+#, c-format
+msgid "Z-score of %s"
+msgstr "Z-score van %s"
+
+#: src/language/stats/descriptives.c:888
+msgid "Valid N"
+msgstr ""
+
+#: src/language/stats/descriptives.c:889
+msgid "Missing N"
+msgstr ""
+
+#: src/language/stats/descriptives.c:917
+#, c-format
+msgid "Valid cases = %g; cases with missing value(s) = %g."
+msgstr "Geldige cases = %g; cases met missing value(s) = %g."
+
+#: src/language/stats/examine.q:346 src/language/stats/examine.q:499
+#: src/language/stats/examine.q:1060
+msgid "Not creating plot because data set is empty."
+msgstr "Er wordt geen plot aangemaakt omdat de data set leeg is."
+
+#: src/language/stats/examine.q:356
+#, c-format
+msgid "Normal Q-Q Plot of %s"
+msgstr "Normal Q-Q Plot van %s"
+
+#: src/language/stats/examine.q:357 src/language/stats/examine.q:362
+msgid "Observed Value"
+msgstr "Waargenomen Waarde"
+
+#: src/language/stats/examine.q:358
+msgid "Expected Normal"
+msgstr ""
+
+#: src/language/stats/examine.q:360
+#, c-format
+msgid "Detrended Normal Q-Q Plot of %s"
+msgstr "Detrended Normal Q-Q Plot van %s"
+
+#: src/language/stats/examine.q:363
+msgid "Dev from Normal"
+msgstr ""
+
+#: src/language/stats/examine.q:516
+#, c-format
+msgid "Boxplot of %s vs. %s"
+msgstr ""
+
+#: src/language/stats/examine.q:520
+#, c-format
+msgid "Boxplot of %s"
+msgstr ""
+
+#: src/language/stats/examine.q:756 src/language/stats/examine.q:769
+#, c-format
+msgid "%s and %s are mutually exclusive"
+msgstr "%s en %s zijn wederzijds exclusief"
+
+#: src/language/stats/examine.q:1272 src/language/stats/reliability.q:686
+msgid "Case Processing Summary"
+msgstr "Case Bewerkings Overzicht"
+
+#: src/language/stats/examine.q:1564 src/language/stats/oneway.q:398
+#, c-format
+msgid "%g%% Confidence Interval for Mean"
+msgstr ""
+
+#: src/language/stats/examine.q:1570 src/language/stats/oneway.q:401
+msgid "Lower Bound"
+msgstr "Beneden Grens"
+
+#: src/language/stats/examine.q:1575 src/language/stats/oneway.q:402
+msgid "Upper Bound"
+msgstr "Boven Grens"
+
+#: src/language/stats/examine.q:1579
+msgid "5% Trimmed Mean"
+msgstr ""
+
+#: src/language/stats/examine.q:1584 src/language/stats/frequencies.q:125
+#: src/ui/gui/frequencies-dialog.c:52
+msgid "Median"
+msgstr ""
+
+#: src/language/stats/examine.q:1594 src/language/stats/npar-summary.c:129
+#: src/language/stats/oneway.q:391 src/language/stats/t-test.q:508
+#: src/language/stats/t-test.q:528 src/language/stats/t-test.q:627
+#: src/language/stats/t-test.q:919
+msgid "Std. Deviation"
+msgstr ""
+
+#: src/language/stats/examine.q:1614
+msgid "Interquartile Range"
+msgstr ""
+
+#: src/language/stats/examine.q:1750 src/language/stats/oneway.q:408
+#: src/ui/gui/examine.glade:310
+msgid "Descriptives"
+msgstr ""
+
+#: src/language/stats/examine.q:1756 src/language/stats/oneway.q:392
+#: src/language/stats/oneway.q:689 src/language/stats/regression.q:203
+msgid "Std. Error"
+msgstr ""
+
+#: src/language/stats/examine.q:1939
+msgid "Highest"
+msgstr "Hoogste"
+
+#: src/language/stats/examine.q:1944
+msgid "Lowest"
+msgstr "Laagste"
+
+#: src/language/stats/examine.q:1951
+msgid "Extreme Values"
+msgstr "Extreme Waardes"
+
+#: src/language/stats/examine.q:1955
+msgid "Case Number"
+msgstr "Case Nummer"
+
+#: src/language/stats/examine.q:2077
+msgid "Tukey's Hinges"
+msgstr ""
+
+#: src/language/stats/examine.q:2117 src/language/stats/examine.q:2134
+#: src/language/stats/frequencies.q:1407 src/language/stats/npar-summary.c:142
+#: src/ui/gui/examine.glade:333
+msgid "Percentiles"
+msgstr ""
+
+#: src/language/stats/examine.q:2124
+#, c-format
+msgid "%g"
+msgstr ""
+
+#: src/language/stats/flip.c:96
+msgid ""
+"FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
+msgstr ""
+"FLIP negeert TEMPORARY. Tijdelijke transformaties worden permanent gemaakt."
+
+#: src/language/stats/flip.c:147
+msgid "Could not create temporary file for FLIP."
+msgstr "Kon geen tijdelijk bestand voor FLIP aanmaken."
+
+#: src/language/stats/flip.c:324
+#, c-format
+msgid "Error rewinding FLIP file: %s."
+msgstr "Fout tijdens terugdraaien FLIP bestand: %s."
+
+#: src/language/stats/flip.c:331
+msgid "Error creating FLIP source file."
+msgstr "Fout tijdens het creëren van FLIP bron bestand."
+
+#: src/language/stats/flip.c:344
+#, c-format
+msgid "Error reading FLIP file: %s."
+msgstr "Fout tijdens lezen FLIP bestand: %s."
+
+#: src/language/stats/flip.c:346
+msgid "Unexpected end of file reading FLIP file."
+msgstr "Onverwacht einde bestand tijdens lezen FLIP bestand."
+
+#: src/language/stats/flip.c:362
+#, c-format
+msgid "Error seeking FLIP source file: %s."
+msgstr "Fout tijdens zoeken FLIP bron bestand: %s."
+
+#: src/language/stats/flip.c:370
+#, c-format
+msgid "Error writing FLIP source file: %s."
+msgstr "Fout tijdens schrijven FLIP bron bestand: %s."
+
+#: src/language/stats/flip.c:381
+#, c-format
+msgid "Error closing FLIP source file: %s."
+msgstr "Fout tijdens sluiten FLIP bron bestand: %s."
+
+#: src/language/stats/flip.c:389
+#, c-format
+msgid "Error rewinding FLIP source file: %s."
+msgstr "Fout tijdens terugdraaien FLIP bron bestand: %s."
+
+#: src/language/stats/flip.c:419
+#, c-format
+msgid "Error reading FLIP temporary file: %s."
+msgstr "Fout tijdens lezen FLIP tijdelijk bestand: %s."
+
+#: src/language/stats/flip.c:422
+msgid "Unexpected end of file reading FLIP temporary file."
+msgstr "Onverwacht einde bestand tijdens lezen FLIP tijdelijk bestand."
+
+#: src/language/stats/frequencies.q:124
+msgid "S.E. Mean"
+msgstr ""
+
+#: src/language/stats/frequencies.q:126 src/ui/gui/frequencies-dialog.c:49
+msgid "Mode"
+msgstr ""
+
+#: src/language/stats/frequencies.q:130
+msgid "S.E. Kurt"
+msgstr ""
+
+#: src/language/stats/frequencies.q:132
+msgid "S.E. Skew"
+msgstr ""
+
+#: src/language/stats/frequencies.q:405
+msgid ""
+"At most one of BARCHART, HISTOGRAM, or HBAR should be given.  HBAR will be "
+"assumed.  Argument values will be given precedence increasing along the "
+"order given."
+msgstr ""
+"Op zijn meest een van BARCHART, HISTOGRAM, of HBAR moet opgegeven worden.  "
+"HBAR wordt aangenomen.  Argument waardes zullen gebruikt worden in opgegeven "
+"volgorde."
+
+#: src/language/stats/frequencies.q:488
+#, c-format
+msgid ""
+"MAX must be greater than or equal to MIN, if both are specified.  However, "
+"MIN was specified as %g and MAX as %g.  MIN and MAX will be ignored."
+msgstr ""
+"MAX moet groter of gelijk zijn aan MIN, als beiden zijn opgegeven. Maar, MIN "
+"was opgegeven als %g en MAX als %g. MIN en MAX worden genegeerd."
+
+#: src/language/stats/frequencies.q:752
+#, c-format
+msgid "Variable %s specified multiple times on VARIABLES subcommand."
+msgstr "Variabele %s is meerdere keren opgegeven bij VARIABLES subopdracht."
+
+#: src/language/stats/frequencies.q:809
+msgid "`)' expected after GROUPED interval list."
+msgstr "')' verwacht na GROUPED interval lijst."
+
+#: src/language/stats/frequencies.q:821
+#, c-format
+msgid "Variables %s specified on GROUPED but not on VARIABLES."
+msgstr "Variabele %s gespecificeerd bij GROUPED maar niet bij VARIABLES."
+
+#: src/language/stats/frequencies.q:828
+#, c-format
+msgid "Variables %s specified multiple times on GROUPED subcommand."
+msgstr "Variabele %s is meerdere keren gespecificeerd bij GROUPED subopdracht."
+
+#: src/language/stats/frequencies.q:1047 src/language/stats/frequencies.q:1140
+#: src/language/stats/frequencies.q:1141 src/language/stats/frequencies.q:1176
+msgid "Cum"
+msgstr ""
+
+#: src/language/stats/frequencies.q:1049 src/output/charts/plot-hist.c:140
+msgid "Frequency"
+msgstr "Frequenties"
+
+#: src/language/stats/frequencies.q:1070
+msgid "Value Label"
+msgstr "Waarde Label"
+
+#: src/language/stats/frequencies.q:1174
+msgid "Freq"
+msgstr ""
+
+#: src/language/stats/frequencies.q:1175 src/language/stats/frequencies.q:1177
+msgid "Pct"
+msgstr ""
+
+#: src/language/stats/frequencies.q:1370
+#, c-format
+msgid "No valid data for variable %s; statistics not displayed."
+msgstr "Geen geldige data voor variabele %s; statistieken worden niet getoond."
+
+#: src/language/stats/frequencies.q:1411
+msgid "50 (Median)"
+msgstr ""
+
+#: src/language/stats/glm.q:143
+msgid "Multivariate GLM not yet supported"
+msgstr ""
+
+#: src/language/stats/glm.q:262 src/language/stats/regression.q:1000
+msgid "No valid data found. This command was skipped."
+msgstr "Geen geldige data gevonden. Deze opdracht is overgeslagen."
+
+#: src/language/stats/means.q:100
+msgid "Missing required subcommand TABLES."
+msgstr "Missing vereist subopdracht TABLES."
+
+#: src/language/stats/means.q:134
+msgid "TABLES subcommand may not appear more than once."
+msgstr "TABLES subopdracht mag niet meer dan 1 keer voorkomen."
+
+#: src/language/stats/npar.q:109
+msgid "NPAR subcommand not currently implemented."
+msgstr ""
+
+#: src/language/stats/npar.q:252
+#, c-format
+msgid ""
+"The specified value of HI (%d) is lower than the specified value of LO (%d)"
+msgstr ""
+"De opgegeven waarde van HI (%d) is lager dan de opgegeven waarde van LO (%d)"
+
+#: src/language/stats/npar.q:307
+#, c-format
+msgid ""
+"%d expected values were given, but the specified range (%d-%d) requires "
+"exactly %d values."
+msgstr ""
+"%d verwachte waardes waren opgegeven, maar de opgegeven range (%d-%d) "
+"vereist precies %d waardes."
+
+#: src/language/stats/npar.q:441 src/language/stats/t-test.q:379
+#, c-format
+msgid ""
+"PAIRED was specified but the number of variables preceding WITH (%zu) did "
+"not match the number following (%zu)."
+msgstr ""
+"PAIRED was opgegeven maar het aantal variabelen voor WITH (%zu) komt niet "
+"overeen met het aantal er achter (%zu)."
+
+#: src/language/stats/npar-summary.c:109
+msgid "Descriptive Statistics"
+msgstr "Descriptive Statistieken"
+
+#: src/language/stats/npar-summary.c:146
+msgid "25th"
+msgstr ""
+
+#: src/language/stats/npar-summary.c:149
+msgid "50th (Median)"
+msgstr ""
+
+#: src/language/stats/npar-summary.c:152
+msgid "75th"
+msgstr ""
+
+#: src/language/stats/oneway.q:171
+msgid "Number of contrast coefficients must equal the number of groups"
+msgstr ""
+
+#: src/language/stats/oneway.q:180
+#, c-format
+msgid "Coefficients for contrast %zu do not total zero"
+msgstr ""
+
+#: src/language/stats/oneway.q:243
+#, c-format
+msgid "`%s' is not a variable name"
+msgstr "'%s' is geen variabele naam"
+
+#: src/language/stats/oneway.q:277 src/language/stats/regression.q:301
+msgid "Sum of Squares"
+msgstr ""
+
+#: src/language/stats/oneway.q:279 src/language/stats/regression.q:303
+msgid "Mean Square"
+msgstr ""
+
+#: src/language/stats/oneway.q:280 src/language/stats/regression.q:304
+#: src/language/stats/t-test.q:750
+msgid "F"
+msgstr ""
+
+#: src/language/stats/oneway.q:281 src/language/stats/oneway.q:539
+#: src/language/stats/regression.q:206 src/language/stats/regression.q:305
+msgid "Significance"
+msgstr "Significantie "
+
+#: src/language/stats/oneway.q:303
+msgid "Between Groups"
+msgstr "Tussen Groepen"
+
+#: src/language/stats/oneway.q:304
+msgid "Within Groups"
+msgstr "Binnen Groepen"
+
+#: src/language/stats/oneway.q:348 src/language/stats/regression.q:330
+msgid "ANOVA"
+msgstr ""
+
+#: src/language/stats/oneway.q:536
+msgid "Levene Statistic"
+msgstr ""
+
+#: src/language/stats/oneway.q:537
+msgid "df1"
+msgstr ""
+
+#: src/language/stats/oneway.q:538
+msgid "df2"
+msgstr ""
+
+#: src/language/stats/oneway.q:541
+msgid "Test of Homogeneity of Variances"
+msgstr ""
+
+#: src/language/stats/oneway.q:608
+msgid "Contrast Coefficients"
+msgstr ""
+
+#: src/language/stats/oneway.q:610 src/language/stats/oneway.q:687
+msgid "Contrast"
+msgstr ""
+
+#: src/language/stats/oneway.q:685
+msgid "Contrast Tests"
+msgstr ""
+
+#: src/language/stats/oneway.q:688
+msgid "Value of Contrast"
+msgstr ""
+
+#: src/language/stats/oneway.q:690 src/language/stats/regression.q:205
+#: src/language/stats/t-test.q:752 src/language/stats/t-test.q:923
+#: src/language/stats/t-test.q:1010
+msgid "t"
+msgstr ""
+
+#: src/language/stats/oneway.q:692 src/language/stats/t-test.q:754
+#: src/language/stats/t-test.q:925 src/language/stats/t-test.q:1012
+msgid "Sig. (2-tailed)"
+msgstr ""
+
+#: src/language/stats/oneway.q:736
+msgid "Assume equal variances"
+msgstr "Veronderstelt gelijke variantie"
+
+#: src/language/stats/oneway.q:740
+msgid "Does not assume equal"
+msgstr "Veronderstelt niet gelijk"
+
+#: src/language/stats/rank.q:221
+#, c-format
+msgid "%s of %s by %s"
+msgstr "%s van %s per %s"
+
+#: src/language/stats/rank.q:226
+#, c-format
+msgid "%s of %s"
+msgstr "%s van %s"
+
+#: src/language/stats/rank.q:601
+msgid "Cannot create new rank variable.  All candidates in use."
+msgstr "Kan geen rang variabele creëren. Alle kandidaten zijn in gebruik."
+
+#: src/language/stats/rank.q:694
+msgid "Variables Created By RANK"
+msgstr "Variabelen gecreëerd door RANK"
+
+#: src/language/stats/rank.q:718
+#, c-format
+msgid "%s into %s(%s of %s using %s BY %s)"
+msgstr "%s in %s(%s van %s gebruikt %s PER %s)"
+
+#: src/language/stats/rank.q:728
+#, c-format
+msgid "%s into %s(%s of %s BY %s)"
+msgstr "%s in %s(%s van %s PER %s)"
+
+#: src/language/stats/rank.q:741
+#, c-format
+msgid "%s into %s(%s of %s using %s)"
+msgstr "%s in %s(%s van %s gebruikt %s"
+
+#: src/language/stats/rank.q:750
+#, c-format
+msgid "%s into %s(%s of %s)"
+msgstr "%s in %s(%s van %s)"
+
+#: src/language/stats/rank.q:762
+msgid ""
+"FRACTION has been specified, but NORMAL and PROPORTION rank functions have "
+"not been requested.  The FRACTION subcommand will be ignored."
+msgstr ""
+"FRACTION is gespecificeerd maar NORMAL en PROPORTION rangschik functies ziin "
+"niet gevraagd. De FRACTION subopdracht wordt genegeerd."
+
+#: src/language/stats/rank.q:853
+#, c-format
+msgid "Variable %s already exists."
+msgstr "Variabele %s bestaat al."
+
+#: src/language/stats/rank.q:858
+msgid "Too many variables in INTO clause."
+msgstr "Te veel variabelen in INTO clause."
+
+#: src/language/stats/regression.q:159 src/ui/gui/regression-dialog.c:42
+msgid "R"
+msgstr ""
+
+#: src/language/stats/regression.q:160
+msgid "R Square"
+msgstr ""
+
+#: src/language/stats/regression.q:161
+msgid "Adjusted R Square"
+msgstr ""
+
+#: src/language/stats/regression.q:162
+msgid "Std. Error of the Estimate"
+msgstr ""
+
+#: src/language/stats/regression.q:167
+msgid "Model Summary"
+msgstr ""
+
+#: src/language/stats/regression.q:202
+msgid "B"
+msgstr ""
+
+#: src/language/stats/regression.q:204
+msgid "Beta"
+msgstr ""
+
+#: src/language/stats/regression.q:207
+msgid "(Constant)"
+msgstr ""
+
+#: src/language/stats/regression.q:271
+msgid "Coefficients"
+msgstr ""
+
+#: src/language/stats/regression.q:307
+msgid "Regression"
+msgstr ""
+
+#: src/language/stats/regression.q:389
+msgid "Model"
+msgstr ""
+
+#: src/language/stats/regression.q:390
+msgid "Covariances"
+msgstr ""
+
+#: src/language/stats/regression.q:405
+msgid "Coefficient Correlations"
+msgstr ""
+
+#: 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:904
+msgid "Dependent variable must be numeric."
+msgstr "Afhankelijke variabele moet numeriek zijn."
+
+#: src/language/stats/reliability.q:433
+msgid "Reliability Statistics"
+msgstr ""
+
+#: src/language/stats/reliability.q:476
+msgid "Item-Total Statistics"
+msgstr ""
+
+#: src/language/stats/reliability.q:498
+msgid "Scale Mean if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:501
+msgid "Scale Variance if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:504
+msgid "Corrected Item-Total Correlation"
+msgstr ""
+
+#: src/language/stats/reliability.q:507
+msgid "Cronbach's Alpha if Item Deleted"
+msgstr ""
+
+#: src/language/stats/reliability.q:557 src/language/stats/reliability.q:576
+msgid "Cronbach's Alpha"
+msgstr ""
+
+#: src/language/stats/reliability.q:560
+msgid "N of items"
+msgstr ""
+
+#: src/language/stats/reliability.q:579
+msgid "Part 1"
+msgstr "Deel 1"
+
+#: src/language/stats/reliability.q:585 src/language/stats/reliability.q:596
+msgid "N of Items"
+msgstr ""
+
+#: src/language/stats/reliability.q:590
+msgid "Part 2"
+msgstr "Deel 2"
+
+#: src/language/stats/reliability.q:601
+msgid "Total N of Items"
+msgstr ""
+
+#: src/language/stats/reliability.q:604
+msgid "Correlation Between Forms"
+msgstr ""
+
+#: src/language/stats/reliability.q:608
+msgid "Spearman-Brown Coefficient"
+msgstr ""
+
+#: src/language/stats/reliability.q:611
+msgid "Equal Length"
+msgstr "Gelijke Lengte"
+
+#: src/language/stats/reliability.q:614
+msgid "Unequal Length"
+msgstr "Ongelijke Lengte"
+
+#: src/language/stats/reliability.q:618
+msgid "Guttman Split-Half Coefficient"
+msgstr ""
+
+#: src/language/stats/reliability.q:715
+msgid "Excluded"
+msgstr "Uitgesloten"
+
+#: src/language/stats/reliability.q:723
+msgid "%"
+msgstr ""
+
+#: src/language/stats/sign.c:91
+msgid "Negative Differences"
+msgstr "Negatieve Verschillen"
+
+#: src/language/stats/sign.c:92
+msgid "Positive Differences"
+msgstr "Positieve Verschillen"
+
+#: src/language/stats/sign.c:93 src/language/stats/wilcoxon.c:261
+msgid "Ties"
+msgstr ""
+
+#: src/language/stats/sign.c:134 src/language/stats/wilcoxon.c:331
+msgid "Exact Sig. (2-tailed)"
+msgstr ""
+
+#: src/language/stats/sign.c:137 src/language/stats/wilcoxon.c:332
+msgid "Exact Sig. (1-tailed)"
+msgstr ""
+
+#: src/language/stats/sign.c:140 src/language/stats/wilcoxon.c:335
+msgid "Point Probability"
+msgstr ""
+
+#: src/language/stats/sort-cases.c:64
+msgid "Buffer limit must be at least 2."
+msgstr "Buffer limiet moet tenminste 2 zijn."
+
+#: src/language/stats/sort-criteria.c:74
+msgid "`A' or `D' expected inside parentheses."
+msgstr "'A' of 'D' verwacht binnen haakjes."
+
+#: src/language/stats/sort-criteria.c:79
+msgid "`)' expected."
+msgstr "')' verwacht."
+
+#: src/language/stats/sort-criteria.c:92
+#, c-format
+msgid "Variable %s specified twice in sort criteria."
+msgstr "Variabele %s 2 keer opgegeven in sort criteria."
+
+#: src/language/stats/t-test.q:189
+#, fuzzy
+msgid "Exactly one of TESTVAL, GROUPS and PAIRS subcommands must be specified."
+msgstr "TESTVAL, GROUPS en PAIRS subopdracht zijn wederzijds uitsluitend."
+
+#: src/language/stats/t-test.q:210
+#, fuzzy
+msgid "VARIABLES subcommand may not be used with PAIRS."
+msgstr "VARIABLES subprogramma is niet het juiste met PAIRS"
+
+#: src/language/stats/t-test.q:229
+msgid "One or more VARIABLES must be specified."
+msgstr "Een of meer VARIABLES moeten gespecificeerd zijn."
+
+#: src/language/stats/t-test.q:323
+msgid ""
+"When applying GROUPS to a string variable, two values must be specified."
+msgstr ""
+"By het toepassen van GROUPS op een string variabele moeten twee waardes "
+"opgegeven zijn."
+
+#: src/language/stats/t-test.q:394
+msgid "At least two variables must be specified on PAIRS."
+msgstr "Ten minste 2 variabelen moeten opgegeven worden bij PAIRS."
+
+#: src/language/stats/t-test.q:504
+msgid "One-Sample Statistics"
+msgstr ""
+
+#: src/language/stats/t-test.q:509 src/language/stats/t-test.q:529
+#: src/language/stats/t-test.q:628
+msgid "SE. Mean"
+msgstr ""
+
+#: src/language/stats/t-test.q:523
+msgid "Group Statistics"
+msgstr ""
+
+#: src/language/stats/t-test.q:622
+msgid "Paired Sample Statistics"
+msgstr ""
+
+#: src/language/stats/t-test.q:642 src/language/stats/t-test.q:945
+#: src/language/stats/t-test.q:1119
+#, c-format
+msgid "Pair %d"
+msgstr ""
+
+#: src/language/stats/t-test.q:738
+msgid "Independent Samples Test"
+msgstr ""
+
+#: src/language/stats/t-test.q:746
+msgid "Levene's Test for Equality of Variances"
+msgstr ""
+
+#: src/language/stats/t-test.q:748
+msgid "t-test for Equality of Means"
+msgstr ""
+
+#: src/language/stats/t-test.q:751 src/language/stats/t-test.q:1107
+msgid "Sig."
+msgstr ""
+
+#: src/language/stats/t-test.q:755 src/language/stats/t-test.q:1013
+msgid "Mean Difference"
+msgstr "Gemiddelde Verschil"
+
+#: src/language/stats/t-test.q:756
+msgid "Std. Error Difference"
+msgstr ""
+
+#: src/language/stats/t-test.q:761 src/language/stats/t-test.q:915
+#: src/language/stats/t-test.q:1005
+#, c-format
+msgid "%g%% Confidence Interval of the Difference"
+msgstr ""
+
+#: src/language/stats/t-test.q:815
+msgid "Equal variances assumed"
+msgstr ""
+
+#: src/language/stats/t-test.q:861
+msgid "Equal variances not assumed"
+msgstr ""
+
+#: src/language/stats/t-test.q:905
+msgid "Paired Samples Test"
+msgstr ""
+
+#: src/language/stats/t-test.q:908
+msgid "Paired Differences"
+msgstr ""
+
+#: src/language/stats/t-test.q:920
+msgid "Std. Error Mean"
+msgstr ""
+
+#: src/language/stats/t-test.q:994
+msgid "One-Sample Test"
+msgstr ""
+
+#: src/language/stats/t-test.q:999
+#, c-format
+msgid "Test Value = %f"
+msgstr ""
+
+#: src/language/stats/t-test.q:1102
+msgid "Paired Samples Correlations"
+msgstr ""
+
+#: src/language/stats/t-test.q:1106
+msgid "Correlation"
+msgstr "Correlatie"
+
+#: src/language/stats/t-test.q:1121
+#, c-format
+msgid "%s & %s"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:232
+msgid "Ranks"
+msgstr "Rangen"
+
+#: src/language/stats/wilcoxon.c:246
+msgid "Mean Rank"
+msgstr "Gemiddelde Rang"
+
+#: src/language/stats/wilcoxon.c:247
+msgid "Sum of Ranks"
+msgstr "Totaal van de Rangen"
+
+#: src/language/stats/wilcoxon.c:259
+msgid "Negative Ranks"
+msgstr "Negatieve Rangen"
+
+#: src/language/stats/wilcoxon.c:260
+msgid "Positive Ranks"
+msgstr "Positieve Rangen"
+
+#: src/language/stats/wilcoxon.c:326
+msgid "Z"
+msgstr ""
+
+#: src/language/stats/wilcoxon.c:327
+msgid "Asymp. Sig. (2-tailed)"
+msgstr ""
+
+#: src/language/syntax-file.c:88
+#, c-format
+msgid "opening \"%s\" as syntax file"
+msgstr "openen \"%s\" als syntax bestand"
+
+#: src/language/syntax-file.c:93
+#, c-format
+msgid "Opening `%s': %s."
+msgstr "Openen '%s': %s."
+
+#: src/language/syntax-file.c:107
+#, c-format
+msgid "Reading `%s': %s."
+msgstr "Lezen '%s': %s."
+
+#: src/language/syntax-file.c:127
+#, c-format
+msgid "Closing `%s': %s."
+msgstr "Sluiten '%s': %s."
+
+#: src/language/tests/float-format.c:124
+#, c-format
+msgid "%zu-byte string needed but %zu-byte string supplied."
+msgstr "%zu-byte string nodig maar %zu-byte string gegeven."
+
+#: src/language/tests/float-format.c:136
+msgid "Hexadecimal floating constant too long."
+msgstr ""
+
+#: src/language/tests/float-format.c:201
+#, c-format
+msgid ""
+"%s conversion of %s from %s to %s should have produced %s but actually "
+"produced %s."
+msgstr ""
+"%s conversie van %s van %s naar %s zou %s moeten produceren maar produceerde "
+"in werkelijkheid %s."
+
+#: src/language/tests/float-format.c:247
+msgid "Too many values in single command."
+msgstr "Te veel waardes in enkele opdracht."
+
+#: src/language/tests/moments-test.c:47
+msgid "expecting weight value"
+msgstr "verwacht weging waarde"
+
+#: src/language/utilities/cd.c:41
+#, c-format
+msgid "Cannot change directory to %s:  %s "
+msgstr "Kan map niet veranderen in %s: %s "
+
+#: src/language/utilities/date.c:32
+msgid "Only USE ALL is currently implemented."
+msgstr "Alleen USE ALL is op dit moment geïmplementeerd."
+
+#: src/language/utilities/include.c:92
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
+msgstr "BATCH of INTERACTIVE verwacht na SYNTAX."
+
+#: src/language/utilities/include.c:109
+msgid "Expecting YES or NO after CD."
+msgstr "YES of NO verwacht na CD."
+
+#: src/language/utilities/include.c:126
+msgid "Expecting CONTINUE or STOP after ERROR."
+msgstr "CONTINUE of STOP verwacht na ERROR."
+
+#: src/language/utilities/include.c:133
+#, c-format
+msgid "Unexpected token: `%s'."
+msgstr "Onverwacht symbool: '%s'."
+
+#: src/language/utilities/include.c:178
+msgid "expecting file name"
+msgstr "bestandsnaam verwacht"
+
+#: src/language/utilities/include.c:190
+#, c-format
+msgid "Can't find `%s' in include file search path."
+msgstr "Kan '%s' niet vinden in include bestand zoek pad."
+
+#: src/language/utilities/include.c:198
+#, c-format
+msgid "Unable to open `%s': %s."
+msgstr "Onmogelijk om te openen '%s': %s."
+
+#: src/language/utilities/permissions.c:73
+#, c-format
+msgid "Expecting %s or %s."
+msgstr "Verwacht %s of %s."
+
+#: src/language/utilities/permissions.c:106
+#, c-format
+msgid "Cannot stat %s: %s"
+msgstr ""
+
+#: src/language/utilities/permissions.c:119
+#, c-format
+msgid "Cannot change mode of %s: %s"
+msgstr "Kan mode van %s niet veranderen: %s"
+
+#: src/language/utilities/set.q:202
+msgid "WORKSPACE must be at least 1MB"
+msgstr "WORKSPACE moet minstens 1MB zijn"
+
+#: src/language/utilities/set.q:208 src/language/utilities/set.q:210
+#: src/language/utilities/set.q:212 src/language/utilities/set.q:214
+#: src/language/utilities/set.q:216 src/language/utilities/set.q:218
+#: src/language/utilities/set.q:220 src/language/utilities/set.q:222
+#: src/language/utilities/set.q:224
+#, c-format
+msgid "%s is obsolete."
+msgstr "%s is verouderd."
+
+#: src/language/utilities/set.q:227
+#, c-format
+msgid "%s is not implemented."
+msgstr "%s is niet geïmplementeerd."
+
+#: src/language/utilities/set.q:230
+msgid "Active file compression is not implemented."
+msgstr "Actief bestand compressie is niet geïmplementeerd."
+
+#: src/language/utilities/set.q:325
+msgid "EPOCH must be 1500 or later."
+msgstr "EPOCH moet 1500 of later zijn."
+
+#: src/language/utilities/set.q:332
+msgid "expecting AUTOMATIC or year"
+msgstr "AUTOMATIC of jaar verwacht"
+
+#: src/language/utilities/set.q:353
+msgid "LENGTH must be at least 1."
+msgstr "LENGTH moet tenminste 1 zijn."
+
+#: src/language/utilities/set.q:389
+#, c-format
+msgid "%s is not a recognised encoding or locale name"
+msgstr "%s is geen herkende codering of lokale naam"
+
+#: src/language/utilities/set.q:432
+msgid "WIDTH must be at least 40."
+msgstr "WIDTH moet tenminste 40 zijn."
+
+#: src/language/utilities/set.q:455
+#, c-format
+msgid ""
+"FORMAT requires numeric output format as an argument.  Specified format %s "
+"is of type string."
+msgstr ""
+"FORMAT vereist numeriek uitvoer formaat als een argument. Opgegeven formaat %"
+"s is van het type string."
+
+#: src/language/utilities/set.q:668
+msgid "ISL (32-bit IEEE 754 single, little-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:671
+msgid "ISB (32-bit IEEE 754 single, big-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:674
+msgid "IDL (64-bit IEEE 754 double, little-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:677
+msgid "IDB (64-bit IEEE 754 double, big-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:681
+msgid "VF (32-bit VAX F, VAX-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:684
+msgid "VD (64-bit VAX D, VAX-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:687
+msgid "VG (64-bit VAX G, VAX-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:691
+msgid "ZS (32-bit IBM Z hexadecimal short, big-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:694
+msgid "ZL (64-bit IBM Z hexadecimal long, big-endian)"
+msgstr ""
+
+#: src/language/utilities/set.q:793
+#, fuzzy, c-format
+msgid "%s is %s."
+msgstr "%s van %s"
+
+#: src/language/utilities/title.c:68
+#, c-format
+msgid "%s: `.' expected after string."
+msgstr "%s: `.' verwacht na string."
+
+#: src/language/utilities/title.c:108
+#, c-format
+msgid "   (Entered %s)"
+msgstr "   (Ingevoerd %s)"
+
+#: 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 ""
+"Tijdens uitvoeren van COMPUTE: SYSMIS is geen geldige waarde als een index "
+"in vector %s."
+
+#: 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 ""
+"Tijdens uitvoeren van COMPUTE: %g is geen geldige waarde als een index in "
+"vector %s."
+
+#: src/language/xforms/compute.c:353
+#, c-format
+msgid "There is no vector named %s."
+msgstr "Er is geen vector genaamd %s."
+
+#: src/language/xforms/count.c:123
+msgid "Destination cannot be a string variable."
+msgstr "Bestemming kan geen string variabele zijn."
+
+#: src/language/xforms/recode.c:245
+msgid ""
+"Inconsistent target variable types.  Target variables must be all numeric or "
+"all string."
+msgstr ""
+"Inconsistent doel variabele types.  Doel variabelen moeten allemaal numeriek "
+"of allemaal string zijn. "
+
+#: src/language/xforms/recode.c:266
+msgid "CONVERT requires string input values and numeric output values."
+msgstr "CONVERT vereist string invoer waardes en numerieke uitvoer waardes."
+
+#: src/language/xforms/recode.c:321
+msgid "THRU is not allowed with string variables."
+msgstr "THRU is niet toegestaan met string variabelen."
+
+#: src/language/xforms/recode.c:400
+msgid "expecting output value"
+msgstr "verwacht uitvoer waarde"
+
+#: src/language/xforms/recode.c:457
+#, 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 ""
+"%zu variabel(en) kunnen niet gehercodeerd worden in %zu variabel(en).  "
+"Specificeer hetzelfde aantal variabelen als bron en als doel variabelen."
+
+#: src/language/xforms/recode.c:472
+#, 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 ""
+"Er is geen variabele genaamd %s.  (Alle string variabelen gespecificeerd bij "
+"INTO dienen al te bestaan.  Gebruik de STRING opdracht om een string "
+"variabele aan te maken.)"
+
+#: src/language/xforms/recode.c:488
+#, c-format
+msgid "INTO is required with %s input values and %s output values."
+msgstr "INTO is vereist met %s invoer waardes en %s uitvoer waardes."
+
+#: src/language/xforms/recode.c:501
+#, c-format
+msgid "Type mismatch.  Cannot store %s data in %s variable %s."
+msgstr "Type fout. Kan %s data niet in %s variabele %s opslaan. "
+
+#: src/language/xforms/sample.c:76
+msgid "The sampling factor must be between 0 and 1 exclusive."
+msgstr "De steekproef factor moet exclusief tussen 0 en 1 liggen."
+
+#: src/language/xforms/sample.c:96
+#, c-format
+msgid "Cannot sample %d observations from a population of %d."
+msgstr "Kan niet %d observaties bemonsteren van een populatie van %d."
+
+#: src/language/xforms/select-if.c:100
+msgid "Syntax error expecting OFF or BY.  Turning off case filtering."
+msgstr "Syntax fout verwacht OFF of BY. Schakelt case filtering uit. "
+
+#: src/language/xforms/select-if.c:115
+msgid "The filter variable must be numeric."
+msgstr "De filter variabele moet numeriek zijn."
+
+#: src/language/xforms/select-if.c:121
+msgid "The filter variable may not be scratch."
+msgstr "De filter variabele mag niet scratch zijn."
+
+#: src/libpspp/hash.c:545
+#, c-format
+msgid "hash table:"
+msgstr "hash tabel:"
+
+#: src/libpspp/tmpfile.c:55
+#, c-format
+msgid "failed to create temporary file"
+msgstr "aanmaken van een tijdelijk bestand is mislukt"
+
+#: src/libpspp/tmpfile.c:96
+#, c-format
+msgid "seeking in temporary file"
+msgstr "zoeken in tijdelijk bestand"
+
+#: src/libpspp/tmpfile.c:115
+#, c-format
+msgid "reading temporary file"
+msgstr "lezen tijdelijk bestand"
+
+#: src/libpspp/tmpfile.c:117
+#, c-format
+msgid "unexpected end of file reading temporary file"
+msgstr "onverwacht einde bestand bij het lezen van tijdelijk bestand"
+
+#: src/libpspp/tmpfile.c:136
+#, c-format
+msgid "writing to temporary file"
+msgstr "schrijven naar tijdelijk bestand"
+
+#: src/math/percentiles.c:35
+msgid "HAverage"
+msgstr ""
+
+#: src/math/percentiles.c:36
+msgid "Weighted Average"
+msgstr "Gewogen Gemiddelde"
+
+#: src/math/percentiles.c:37
+msgid "Rounded"
+msgstr "Afgerond"
+
+#: src/math/percentiles.c:38
+msgid "Empirical"
+msgstr ""
+
+#: src/math/percentiles.c:39
+msgid "Empirical with averaging"
+msgstr ""
+
+#: src/output/afm.c:149
+#, c-format
+msgid "opening font metrics file \"%s\""
+msgstr ""
+
+#: src/output/afm.c:239
+msgid "first line must be StartFontMetrics"
+msgstr "eerste regel moet zijn StartFontMetrics"
+
+#: src/output/afm.c:266
+#, c-format
+msgid "unsupported MappingScheme %d"
+msgstr "niet ondersteunt MappingSchema %d"
+
+#: src/output/afm.c:287
+msgid "required FontName is missing"
+msgstr "vereiste FontNaam ontbreekt"
+
+#: src/output/afm.c:394
+msgid "CharMetrics line must start with C or CH"
+msgstr "CharMetrics regel moet beginnen met C of CH"
+
+#: src/output/afm.c:535
+#, c-format
+msgid "reference to unknown character \"%s\""
+msgstr "referentie naar onbekend karakter \"%s\""
+
+#: src/output/afm.c:593
+msgid "expected end of file"
+msgstr "einde bestand verwacht"
+
+#: src/output/afm.c:605
+msgid "syntax error expecting end of line"
+msgstr "syntax fout einde regel verwacht"
+
+#: src/output/afm.c:623 src/output/afm.c:660
+msgid "number out of valid range"
+msgstr "nummer buiten geldige range"
+
+#: src/output/afm.c:625 src/output/afm.c:662
+msgid "invalid numeric syntax"
+msgstr "ongeldige numerieke syntax"
+
+#: src/output/afm.c:641
+msgid "syntax error expecting integer"
+msgstr "syntax fout integer verwacht"
+
+#: src/output/afm.c:679
+msgid "syntax error expecting number"
+msgstr "syntax fout nummer verwacht"
+
+#: src/output/afm.c:692
+msgid "syntax error in hex constant"
+msgstr "syntax fout in hex constante"
+
+#: src/output/afm.c:707
+msgid "syntax error expecting hex constant"
+msgstr "syntax fout hex constante verwacht"
+
+#: src/output/afm.c:745
+msgid "unexpected end of line"
+msgstr "onverwacht regel einde"
+
+#: src/output/afm.c:795
+msgid "unexpected end of line expecting string"
+msgstr "onverwacht regeleinde string verwacht"
+
+#: src/output/ascii.c:251
+#, c-format
+msgid ""
+"ascii: page excluding margins and headers must be at least 59 characters "
+"wide by 15 lines long, but as configured is only %d characters by %d lines"
+msgstr ""
+"ascii: pagina exclusief marges en koppen moet tenminste 59 karakters breed "
+"en 15 regels lang zijn, maar geconfigureerd is slechts %d karakters bij %d "
+"regels"
+
+#: src/output/ascii.c:329
+#, c-format
+msgid ""
+"ascii: bad index value for `box' key: syntax is box[INDEX], 0 <= INDEX < %d "
+"decimal, with INDEX expressed in base 4"
+msgstr ""
+
+#: src/output/ascii.c:336
+#, c-format
+msgid "ascii: multiple values for %s"
+msgstr "ascii: meerdere waardes voor %s"
+
+#: src/output/ascii.c:344
+#, c-format
+msgid "ascii: unknown parameter `%s'"
+msgstr "ascii: onbekende parameter `%s'"
+
+#: src/output/ascii.c:360
+#, c-format
+msgid "ascii: only screen devices may have `auto' length or width"
+msgstr "ascii: alleen scherm apparaten mogen 'auto' lengte of breedte hebben"
+
+#: src/output/ascii.c:374
+#, c-format
+msgid "ascii: positive integer required as `%s' value"
+msgstr "ascii: positieve integer vereist als `%s' waarde"
+
+#: src/output/ascii.c:402
+#, c-format
+msgid "ascii: `emphasis' value must be `bold', `underline', or `none'"
+msgstr "ascii: `emphasis' waarde moet `bold', `underline', of `none' zijn"
+
+#: src/output/ascii.c:415
+#, c-format
+msgid "ascii: zero or positive integer required as `%s' value"
+msgstr "ascii: nul of positieve integer vereist als `%s' waarde"
+
+#: src/output/ascii.c:446
+#, c-format
+msgid "ascii: boolean value expected for `%s'"
+msgstr "ascii: boolean waarde verwacht voor `%s'"
+
+#: src/output/ascii.c:478 src/output/html.c:187
+#, c-format
+msgid "`chart-files' value must contain `#'"
+msgstr "`chart-files' waarde moet `#' bevatten"
+
+#: src/output/ascii.c:524
+#, c-format
+msgid "ascii: opening output file \"%s\""
+msgstr "ascii: openen uitvoer bestand \"%s\""
+
+#: src/output/ascii.c:587
+#, c-format
+msgid "ascii: bad line (%d,%d)-(%d,%d) out of (%d,%d)\n"
+msgstr ""
+
+#: src/output/ascii.c:809 src/output/postscript.c:826
+#, c-format
+msgid "%s - Page %d"
+msgstr "%s - Pagina %d"
+
+#: src/output/ascii.c:861
+#, c-format
+msgid "ascii: closing output file \"%s\""
+msgstr "ascii: sluiten uitvoer bestand \"%s\""
+
+#: src/output/chart.c:145
+#, c-format
+msgid "creating \"%s\""
+msgstr "aanmaken \"%s\""
+
+#: src/output/charts/plot-hist.c:138
+msgid "HISTOGRAM"
+msgstr ""
+
+#: src/output/html.c:71
+#, c-format
+msgid "opening HTML output file: %s"
+msgstr "openen HTML uitvoer bestand: %s"
+
+#: src/output/html.c:82
+msgid "PSPP Output"
+msgstr "PSPP Uitvoer"
+
+#: src/output/html.c:170
+#, c-format
+msgid "unknown configuration parameter `%s' for HTML device driver"
+msgstr "onbekende configuratie parameter '%s' voor HTML device driver"
+
+#: src/output/journal.c:69
+#, c-format
+msgid "error writing \"%s\""
+msgstr "fout bij schrijven \"%s\""
+
+#: src/output/journal.c:94
+#, c-format
+msgid "error creating \"%s\""
+msgstr "fout bij aanmaken \"%s\""
+
+#: src/output/output.c:168
+#, c-format
+msgid "unknown output driver `%s'"
+msgstr "onbekende uitvoer driver %s"
+
+#: src/output/output.c:170
+#, c-format
+msgid "output driver `%s' referenced but never defined"
+msgstr "uitvoer driver '%s' gerefereerd maar nooit gedefinieerd"
+
+#: src/output/output.c:261
+#, c-format
+msgid "using default output driver configuration"
+msgstr "gebruik default uitvoer driver configuratie"
+
+#: src/output/output.c:290
+#, c-format
+msgid "cannot find output initialization file (use `-vv' to view search path)"
+msgstr ""
+"kan uitvoer initialisatie bestand niet vinden (gebruik -vv om zoekpad te "
+"zien)"
+
+#: src/output/output.c:298
+#, c-format
+msgid "cannot open \"%s\""
+msgstr "kan \"%s\" niet openen"
+
+#: src/output/output.c:310
+#, c-format
+msgid "reading \"%s\""
+msgstr "lezen \"%s\""
+
+#: src/output/output.c:332 src/ui/gui/message-dialog.c:99
+#, c-format
+msgid "syntax error"
+msgstr "syntax fout"
+
+#: src/output/output.c:341
+#, c-format
+msgid "error closing \"%s\""
+msgstr "fout bij sluiten \"%s\""
+
+#: src/output/output.c:349
+#, c-format
+msgid "no active output drivers"
+msgstr "geen actieve uitvoer drivers"
+
+#: src/output/output.c:352
+#, c-format
+msgid "error reading device definition file"
+msgstr "fout tijdens lezen device definitie bestand"
+
+#: src/output/output.c:470
+#, c-format
+msgid ""
+"Driver classes:\n"
+"\t"
+msgstr ""
+
+#: src/output/output.c:502
+#, c-format
+msgid "syntax error parsing options for \"%s\" driver"
+msgstr "syntax fout bij het ontleden van opties voor \"%s\" driver"
+
+#: src/output/output.c:518
+#, c-format
+msgid ""
+"reached end of options inside quoted string parsing options for \"%s\" driver"
+msgstr ""
+"einde opties bereikt binnen geciteerde string tijdens ontleden van opties "
+"voor \"%s\" driver"
+
+#: src/output/output.c:588
+#, c-format
+msgid "syntax error in string constant parsing options for \"%s\" driver"
+msgstr ""
+"syntax fout in string constante tijdens ontleden opties voor \"%s\" driver"
+
+#: src/output/output.c:636
+#, c-format
+msgid "syntax error expecting `=' parsing options for driver \"%s\""
+msgstr "syntax fout verwacht `=' tijdens ontleden opties voor driver \"%s\""
+
+#: src/output/output.c:687
+#, c-format
+msgid "unknown output driver class `%.*s'"
+msgstr "onbekende uitvoer driver class `%.*s'"
+
+#: src/output/output.c:702
+#, c-format
+msgid "unknown device type `%.*s'"
+msgstr "onbekend apparaat type `%.*s'"
+
+#: src/output/output.c:719
+#, c-format
+msgid "cannot initialize output driver `%s' of class `%s'"
+msgstr ""
+
+#: src/output/output.c:765
+#, c-format
+msgid "driver definition line missing driver name or class name"
+msgstr "driver definitie regel mist driver naam of class naam"
+
+#: src/output/output.c:868
+#, c-format
+msgid "`%s' is not a valid length."
+msgstr "`%s' is geen geldige lengte."
+
+#: src/output/output.c:960
+#, c-format
+msgid "unknown paper type `%.*s'"
+msgstr "onbekend papier type `%.*s'"
+
+#: src/output/output.c:978
+#, c-format
+msgid "error opening \"%s\""
+msgstr "fout tijdens openen \"%s\""
+
+#: src/output/output.c:989
+#, c-format
+msgid "error reading \"%s\""
+msgstr "fout tijdens lezen \"%s\""
+
+#: src/output/output.c:1006
+#, c-format
+msgid "paper size file \"%s\" does not state a paper size"
+msgstr "papier grootte bestand \"%s\" geeft geen papier grootte aan"
+
+#: src/output/output.c:1066
+#, c-format
+msgid "syntax error in paper size `%s'"
+msgstr "syntax fout in papier grootte '%s' "
+
+#: src/output/postscript.c:158
+#, c-format
+msgid "opening PostScript output file \"%s\""
+msgstr "Openen PostScript uitvoer bestand \"%s\""
+
+#: src/output/postscript.c:196
+#, c-format
+msgid ""
+"The defined PostScript page is not long enough to hold margins and headers, "
+"plus least 15 lines of the default fonts.  In fact, there's only room for %d "
+"lines of each font at the default size of %d.%03d points."
+msgstr ""
+"De gedefinieerde PostScript pagina is niet lang genoeg om marges en koppen, "
+"plus tenminste 15 regels van het default font te bevatten.  In feite is er "
+"slechts plaats voor %d regels van elk font bij de default grootte van %d.%"
+"03d punten."
+
+#: src/output/postscript.c:246
+#, c-format
+msgid "closing PostScript output file \"%s\""
+msgstr "sluiten PostScript uitvoer bestand \"%s\""
+
+#: src/output/postscript.c:309
+#, c-format
+msgid "unknown configuration parameter `%s' for PostScript device driver"
+msgstr "onbekende configuratie parameter `%s' voor PostScript device driver"
+
+#: src/output/postscript.c:325
+#, c-format
+msgid ""
+"unknown orientation `%s' (valid orientations are `portrait' and `landscape')"
+msgstr ""
+"onbekende oriëntatie '%s' (geldige oriëntaties zijn 'portrait'en 'landscape')"
+
+#: src/output/postscript.c:337
+#, c-format
+msgid "boolean value expected for %s"
+msgstr "boolean waarde verwacht voor %s"
+
+#: src/output/postscript.c:350
+#, c-format
+msgid "positive integer value required for `%s'"
+msgstr "positieve integer waarde vereist voor '%s'"
+
+#: src/output/postscript.c:355
+#, c-format
+msgid "default font size must be at least 1 point (value of 1000 for key `%s')"
+msgstr ""
+"default font grootte moet tenminste 1 punt zijn (waarde 1000 voor sleutel '%"
+"s')"
+
+#: src/output/postscript.c:1176
+#, c-format
+msgid "\"%s\": bad font specification"
+msgstr "\"%s\": slechte font specificatie"
+
+#: src/output/postscript.c:1184
+#, c-format
+msgid "could not find AFM file \"%s\""
+msgstr "kon AFM bestand \"%s\" niet vinden"
+
+#: src/output/postscript.c:1198
+#, c-format
+msgid "could not find font \"%s\""
+msgstr "kon font \"%s\" niet vinden"
+
+#: src/output/postscript.c:1207
+#, c-format
+msgid "could not find encoding \"%s\""
+msgstr "kon codering \"%s\" niet vinden"
+
+#: src/output/postscript.c:1307
+#, c-format
+msgid "cannot open font file \"%s\""
+msgstr "kan font bestand \"%s\" niet openen"
+
+#: src/output/postscript.c:1348
+#, c-format
+msgid "reading font file \"%s\""
+msgstr "lezen font bestand \"%s\""
+
+#: src/output/postscript.c:1370
+#, c-format
+msgid "cannot open font encoding file \"%s\""
+msgstr "kan font codering bestand \"%s\" niet openen"
+
+#: src/output/postscript.c:1399
+#, c-format
+msgid "invalid numeric format"
+msgstr "ongeldig numeriek formaat"
+
+#: src/output/postscript.c:1421
+#, c-format
+msgid "closing Postscript encoding \"%s\""
+msgstr "sluiten Postscript codering \"%s\""
+
+#: src/output/table.c:236
+#, c-format
+msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
+msgstr ""
+
+#: src/output/table.c:307
+#, c-format
+msgid ""
+"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
+msgstr ""
+
+#: src/ui/gui/about.c:64
+msgid "A program for the analysis of sampled data"
+msgstr ""
+
+#: src/ui/gui/about.c:73
+msgid "translator-credits"
+msgstr ""
+
+#: src/ui/gui/comments-dialog.c:58
+#, c-format
+msgid "Column Number: %d"
+msgstr "Kolom Nummer: %d"
+
+#: src/ui/gui/crosstabs-dialog.c:40
+msgid "Chisq"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:42
+msgid "CC"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:44
+msgid "UC"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:45
+msgid "BTau"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:46
+msgid "CTau"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:47
+msgid "Risk"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:49
+msgid "D"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:52
+msgid "Corr"
+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:568 src/ui/gui/var-display.c:16
+#: src/ui/gui/variable-info-dialog.c:40
+msgid "None"
+msgstr "Geen"
+
+#: src/ui/gui/crosstabs-dialog.c:56
+msgid "Count"
+msgstr "Aantal"
+
+#: src/ui/gui/crosstabs-dialog.c:57
+msgid "Row"
+msgstr "Rij"
+
+#: src/ui/gui/crosstabs-dialog.c:58
+msgid "Column"
+msgstr "Kolom"
+
+#: src/ui/gui/crosstabs-dialog.c:60
+msgid "Expected"
+msgstr "Verwacht"
+
+#: src/ui/gui/crosstabs-dialog.c:62
+msgid "Std. Residual"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:63
+msgid "Adjusted Std. Residual"
+msgstr ""
+
+#: src/ui/gui/crosstabs.glade:50
+msgid "Rows"
+msgstr "Rijen"
+
+#: src/ui/gui/crosstabs.glade:131 src/ui/gui/frequencies.glade:185
+msgid "Format..."
+msgstr "Formaat..."
+
+#: src/ui/gui/crosstabs.glade:138 src/ui/gui/examine.glade:247
+#: src/ui/gui/regression.glade:31
+msgid "Statistics..."
+msgstr "Statistieken..."
+
+#: src/ui/gui/crosstabs.glade:148
+msgid "Cells..."
+msgstr "Cellen..."
+
+#: src/ui/gui/crosstabs.glade:230
+msgid "Print tables"
+msgstr "Print tabellen"
+
+#: src/ui/gui/crosstabs.glade:240
+msgid "Pivot"
+msgstr ""
+
+#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:756
+msgid "Ascending"
+msgstr "Oplopend"
+
+#: src/ui/gui/crosstabs.glade:283
+msgid "No label"
+msgstr "Geen label"
+
+#: src/ui/gui/crosstabs.glade:295
+msgid "Suppress value labels"
+msgstr "Onderdruk waarde labels"
+
+#: src/ui/gui/crosstabs.glade:311
+msgid "Labeling"
+msgstr ""
+
+#: src/ui/gui/crosstabs.glade:378
+msgid "Cell Display"
+msgstr ""
+
+#: src/ui/gui/crosstabs.glade:439 src/ui/gui/oneway.glade:207
+#: src/ui/gui/regression.glade:322
+msgid "Statistics"
+msgstr "Statistieken"
+
+#: src/ui/gui/customentry.c:334
+msgid "Style of bevel around the custom entry button"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:10 src/ui/gui/output-viewer.glade:22
+#: src/ui/gui/syntax-editor.glade:14
+msgid "_File"
+msgstr "_Bestand"
+
+#: src/ui/gui/data-editor.glade:25 src/ui/gui/data-editor.glade:51
+#: src/ui/gui/syntax-editor.glade:32 src/ui/gui/syntax-editor.glade:62
+msgid "_Syntax"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:32 src/ui/gui/data-editor.glade:58
+#: src/ui/gui/data-editor.glade:311 src/ui/gui/data-editor.glade:329
+#: src/ui/gui/syntax-editor.glade:41 src/ui/gui/syntax-editor.glade:71
+msgid "_Data"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:70
+msgid "_Import Delimited Text Data"
+msgstr "_Importeer Delimited Text Data"
+
+#: src/ui/gui/data-editor.glade:103
+msgid "D_isplay Data File Information"
+msgstr "_Toon Data Bestand Informatie"
+
+#: src/ui/gui/data-editor.glade:112
+msgid "Working File"
+msgstr "Werk Bestand"
+
+#: src/ui/gui/data-editor.glade:119
+msgid "External File"
+msgstr "Extern Bestand"
+
+#: src/ui/gui/data-editor.glade:135
+msgid "Recently Used Da_ta"
+msgstr "Recent Gebruikte Da_ta"
+
+#: src/ui/gui/data-editor.glade:142
+msgid "Recently Used _Files"
+msgstr "Recent Gebruikte _Bestanden"
+
+#: src/ui/gui/data-editor.glade:166 src/ui/gui/output-viewer.glade:55
+#: src/ui/gui/syntax-editor.glade:118
+msgid "_Edit"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:174 src/ui/gui/data-editor.glade:843
+#: src/ui/gui/psppire-data-window.c:843 src/ui/gui/psppire-data-window.c:933
+msgid "Insert Variable"
+msgstr "Invoegen Variabele"
+
+#: src/ui/gui/data-editor.glade:182
+msgid "Insert Cases"
+msgstr "Invoegen Cases"
+
+#: src/ui/gui/data-editor.glade:190 src/ui/gui/data-editor.glade:780
+msgid "Go To Case"
+msgstr "Ga Naar Case"
+
+#: src/ui/gui/data-editor.glade:231
+msgid "Cl_ear Variables"
+msgstr "V_erwijder Variabelen"
+
+#: src/ui/gui/data-editor.glade:239
+msgid "_Clear Cases"
+msgstr "_Verwijder Cases"
+
+#: src/ui/gui/data-editor.glade:252
+msgid "gtk-find"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:264
+msgid "_View"
+msgstr "Bee_ld"
+
+#: src/ui/gui/data-editor.glade:271
+msgid "_Status Bar"
+msgstr "_Status Balk"
+
+#: src/ui/gui/data-editor.glade:284
+msgid "_Fonts"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:291
+msgid "_Grid Lines"
+msgstr "_Grid Lijnen"
+
+#: src/ui/gui/data-editor.glade:299
+msgid "Value _Labels"
+msgstr "Waarde _Labels"
+
+#: src/ui/gui/data-editor.glade:318 src/ui/gui/data-editor.glade:613
+msgid "_Variables"
+msgstr "_Variabele"
+
+#: src/ui/gui/data-editor.glade:336
+msgid "_Sort Cases"
+msgstr "_Sorteer Cases"
+
+#: src/ui/gui/data-editor.glade:350
+msgid "_Transpose"
+msgstr "_Herschik"
+
+#: src/ui/gui/data-editor.glade:363
+msgid "S_plit File"
+msgstr "S_plits Bestand"
+
+#: src/ui/gui/data-editor.glade:371
+msgid "Select _Cases"
+msgstr "Selecteer _Cases"
+
+#: src/ui/gui/data-editor.glade:378
+msgid "_Weight Cases"
+msgstr "_Weeg Cases"
+
+#: src/ui/gui/data-editor.glade:390
+msgid "_Transform"
+msgstr "_Transformeer"
+
+#: src/ui/gui/data-editor.glade:400
+msgid "_Compute"
+msgstr "_Bereken"
+
+#: src/ui/gui/data-editor.glade:408
+msgid "Ran_k Cases"
+msgstr "Rangschi_k Cases"
+
+#: src/ui/gui/data-editor.glade:420
+msgid "Recode into _Same Variables"
+msgstr "Hercodeer in _Zelfde Variabelen"
+
+#: src/ui/gui/data-editor.glade:427
+msgid "Recode into _Different Variables"
+msgstr "Hercodeer in _Andere Variabelen"
+
+#: src/ui/gui/data-editor.glade:440
+msgid "_Run Pending Transforms"
+msgstr "_Run uitstaande Transformaties"
+
+#: src/ui/gui/data-editor.glade:453
+msgid "_Analyze"
+msgstr "_Analyseer"
+
+#: src/ui/gui/data-editor.glade:463
+msgid "_Descriptive Statistics"
+msgstr "_Descriptieve Statistieken"
+
+#: src/ui/gui/data-editor.glade:473
+msgid "_Frequencies"
+msgstr "_Frequenties"
+
+#: src/ui/gui/data-editor.glade:481 src/ui/gui/oneway.glade:179
+msgid "_Descriptives"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:489
+msgid "_Explore"
+msgstr "_Exploreer"
+
+#: src/ui/gui/data-editor.glade:497
+msgid "_Crosstabs"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:509
+msgid "Compare _Means"
+msgstr "_Vergelijk Gemiddelde"
+
+#: src/ui/gui/data-editor.glade:519
+msgid "_One Sample T Test"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:527
+msgid "_Independent Samples T Test"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:535
+msgid "_Paired Samples T Test"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:543
+msgid "One Way _ANOVA"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:554
+msgid "Re_liability"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:562
+msgid "Linear _Regression"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:569
+msgid "_Non-Parametric Statistics"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:579
+msgid "_Chi-Square"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:587
+msgid "_Binomial"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:603
+msgid "_Utilities"
+msgstr "E_xtra"
+
+#: src/ui/gui/data-editor.glade:622
+msgid "Data File _Comments"
+msgstr "Data Bestand _Commentaren"
+
+#: src/ui/gui/data-editor.glade:633 src/ui/gui/output-viewer.glade:78
+#: src/ui/gui/syntax-editor.glade:209
+msgid "_Windows"
+msgstr "_Vensters"
+
+#: src/ui/gui/data-editor.glade:640 src/ui/gui/output-viewer.glade:88
+#: src/ui/gui/syntax-editor.glade:218
+msgid "_Minimize All Windows"
+msgstr "_Minimalizeer Alle Vensters"
+
+#: src/ui/gui/data-editor.glade:647
+msgid "_Split"
+msgstr "_Splits"
+
+#: src/ui/gui/data-editor.glade:658 src/ui/gui/output-viewer.glade:99
+#: src/ui/gui/syntax-editor.glade:229
+msgid "_Help"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:665 src/ui/gui/output-viewer.glade:106
+#: src/ui/gui/syntax-editor.glade:237
+msgid "_Reference Manual"
+msgstr "_Handboek"
+
+#: src/ui/gui/data-editor.glade:678 src/ui/gui/output-viewer.glade:113
+#: src/ui/gui/syntax-editor.glade:244
+msgid "_About"
+msgstr "_Over"
+
+#: src/ui/gui/data-editor.glade:702 src/ui/gui/psppire-data-window.c:379
+msgid "Open"
+msgstr ""
+
+#: src/ui/gui/data-editor.glade:712 src/ui/gui/psppire-data-window.c:581
+msgid "Save"
+msgstr "Opslaan"
+
+#: src/ui/gui/data-editor.glade:722
+msgid "Print"
+msgstr "Afdrukken"
+
+#: src/ui/gui/data-editor.glade:732
+msgid "Recall"
+msgstr "Opnieuw"
+
+#: src/ui/gui/data-editor.glade:750
+msgid "Undo"
+msgstr "Ongedaan maken"
+
+#: src/ui/gui/data-editor.glade:760
+msgid "Redo"
+msgstr "Herstellen"
+
+#: src/ui/gui/data-editor.glade:790
+msgid "Variables"
+msgstr "Variabelen"
+
+#: src/ui/gui/data-editor.glade:811
+msgid "Find"
+msgstr "Vind"
+
+#: src/ui/gui/data-editor.glade:831 src/ui/gui/psppire-data-window.c:897
+msgid "Insert Case"
+msgstr "Invoegen Case"
+
+#: src/ui/gui/data-editor.glade:863
+msgid "Split File"
+msgstr "Splits Bestand"
+
+#: src/ui/gui/data-editor.glade:874
+msgid "Weight Cases"
+msgstr "Weeg Cases"
+
+#: src/ui/gui/data-editor.glade:886
+msgid "Select Cases"
+msgstr "Selecteer Cases"
+
+#: src/ui/gui/data-editor.glade:906 src/ui/gui/var-sheet-dialogs.glade:401
+#: src/ui/gui/var-sheet-dialogs.glade:582
+msgid "Value Labels"
+msgstr "Waarde Labels"
+
+#: src/ui/gui/data-editor.glade:917
+msgid "Use Sets"
+msgstr "Gebruik Sets"
+
+#: src/ui/gui/data-editor.glade:938
+msgid "Information Area"
+msgstr "Informatie Gebied"
+
+#: src/ui/gui/data-editor.glade:957
+msgid "Processor Area"
+msgstr "Processor Gebied"
+
+#: src/ui/gui/data-editor.glade:982
+msgid "Case Counter Area"
+msgstr "Case Teller Gebied"
+
+#: src/ui/gui/data-editor.glade:1007
+msgid "Filter Use Status Area"
+msgstr "Filter Gebruik Status Gebied"
+
+#: src/ui/gui/data-editor.glade:1033
+msgid "Weight Status Area"
+msgstr "Weging Status Gebied"
+
+#: src/ui/gui/data-editor.glade:1059
+msgid "Split File Status Area"
+msgstr "Splits Bestand Status Gebied"
+
+#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
+msgid "Standard deviation"
+msgstr "Standaard deviatie"
+
+#: src/ui/gui/descriptives-dialog.c:45
+msgid "Standard error"
+msgstr "Standaard fout"
+
+#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
+msgid "Statistics:"
+msgstr "Statistieken:"
+
+#: src/ui/gui/descriptives-dialog.glade:184
+msgid "Exclude entire case if any selected variable is missing"
+msgstr "Sluit gehele case uit indien een geselecteerde variabele ontbreekt"
+
+#: src/ui/gui/descriptives-dialog.glade:194
+msgid "Include user-missing data in analysis"
+msgstr "Inclusief user-missing data in analyse"
+
+#: src/ui/gui/descriptives-dialog.glade:207
+msgid "Save Z-scores of selected variables as new variables"
+msgstr "Sla Z_Scores van geselecteerde variabelen op als nieuwe variabelen"
+
+#: src/ui/gui/descriptives-dialog.glade:223
+msgid "Options:"
+msgstr "Opties:"
+
+#: src/ui/gui/examine.glade:49
+msgid "Label Cases by:"
+msgstr ""
+
+#: src/ui/gui/examine.glade:100
+msgid "Factor List:"
+msgstr ""
+
+#: src/ui/gui/examine.glade:150
+msgid "Dependent List:"
+msgstr ""
+
+#: 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 "Opties..."
+
+#: src/ui/gui/examine.glade:320
+msgid "Extremes"
+msgstr ""
+
+#: src/ui/gui/examine.glade:388
+msgid "Exclude cases listwise"
+msgstr ""
+
+#: src/ui/gui/examine.glade:399
+msgid "Exclude cases pairwise"
+msgstr ""
+
+#: src/ui/gui/examine.glade:414
+msgid "Repeat values"
+msgstr "Herhaal waardes"
+
+#: src/ui/gui/examine.glade:432 src/ui/gui/t-test.glade:460
+#: src/ui/gui/var-sheet-dialogs.glade:649
+msgid "Missing Values"
+msgstr "Ontbrekende Waardes"
+
+#: src/ui/gui/find-dialog.c:658
+#, c-format
+msgid "Bad regular expression: %s"
+msgstr "Foutieve regulaire expressie: %s"
+
+#: src/ui/gui/find.glade:80
+msgid "Variable:"
+msgstr "Variabele:"
+
+#: src/ui/gui/find.glade:111 src/ui/gui/recode.glade:185
+#: src/ui/gui/var-sheet-dialogs.glade:512
+msgid "Value:"
+msgstr "Waarde:"
+
+#: src/ui/gui/find.glade:137
+msgid "Search value labels"
+msgstr "Zoek waarde labels"
+
+#: src/ui/gui/find.glade:161
+msgid "Regular expression Match"
+msgstr ""
+
+#: src/ui/gui/find.glade:172
+msgid "Search substrings"
+msgstr "Zoek substrings"
+
+#: src/ui/gui/find.glade:185
+msgid "Wrap around"
+msgstr "Tekstterugloop"
+
+#: src/ui/gui/find.glade:198
+msgid "Search backward"
+msgstr "Zoek achterwaarts"
+
+#: src/ui/gui/frequencies-dialog.c:44
+msgid "Standard error of the mean"
+msgstr ""
+
+#: src/ui/gui/frequencies-dialog.c:47
+msgid "Standard error of the skewness"
+msgstr ""
+
+#: src/ui/gui/frequencies-dialog.c:51
+msgid "Standard error of the kurtosis"
+msgstr ""
+
+#: src/ui/gui/frequencies.glade:98 src/ui/gui/psppire.glade:252
+#: src/ui/gui/rank.glade:103
+msgid "Variable(s):"
+msgstr "Variabele(n):"
+
+#: src/ui/gui/frequencies.glade:168
+msgid "Display Frequency Table"
+msgstr "Toon Frequentie Tabel"
+
+#: src/ui/gui/frequencies.glade:264
+msgid "Ascending Order"
+msgstr "Oplopende Volgorde"
+
+#: src/ui/gui/frequencies.glade:275
+msgid "Descending Order"
+msgstr "Aflopende Volgorde"
+
+#: src/ui/gui/frequencies.glade:290
+msgid "Ascending Counts"
+msgstr "Oplopend Aantal"
+
+#: src/ui/gui/frequencies.glade:305
+msgid "Descending Counts"
+msgstr "Aflopend Aantal"
+
+#: src/ui/gui/frequencies.glade:323
+msgid "Order by"
+msgstr "Sorteer op"
+
+#: src/ui/gui/frequencies.glade:355
+msgid "Supress tables with more than N categories"
+msgstr "Onderdruk tabellen met meer dan N categorieën"
+
+#: src/ui/gui/frequencies.glade:371
+msgid "Maximum no of categories"
+msgstr "Maximaal aantal categorieën"
+
+#: src/ui/gui/helper.c:186
+msgid "Sorry. The help system hasn't yet been implemented."
+msgstr "Sorry. Het help systeem is nog niet geïmplementeerd."
+
+#: src/ui/gui/helper.c:231
+#, c-format
+msgid "Cannot open reference manual: %s"
+msgstr "Kan de handleiding niet openen: %s"
+
+#: src/ui/gui/main.c:43
+msgid "Don't show the splash screen"
+msgstr "Toon het splash scherm niet"
+
+#: src/ui/gui/main.c:173
+msgid "PSPPIRE --- A user interface for PSPP"
+msgstr "PSPPIRE --- Een gebruikers interface voor PSPP"
+
+#: src/ui/gui/main.c:175
+msgid "Miscellaneous options:"
+msgstr "Diverse opties:"
+
+#: src/ui/gui/main.c:177 src/ui/terminal/main.c:125
+msgid "Options affecting syntax and behavior:"
+msgstr "Opties die de syntax en het gedrag beinvloeden:"
+
+#: src/ui/gui/message-dialog.c:103
+msgid "data file error"
+msgstr "data bestand fout"
+
+#: src/ui/gui/message-dialog.c:108
+msgid "PSPP error"
+msgstr "PSPP fout"
+
+#: src/ui/gui/message-dialog.c:116
+msgid "syntax warning"
+msgstr "syntax waarschuwing"
+
+#: src/ui/gui/message-dialog.c:120
+msgid "data file warning"
+msgstr "data bestand waarschuwing"
+
+#: src/ui/gui/message-dialog.c:125
+msgid "PSPP warning"
+msgstr "PSPP waarschuwing"
+
+#: src/ui/gui/message-dialog.c:134
+msgid "syntax information"
+msgstr "syntax informatie"
+
+#: src/ui/gui/message-dialog.c:138
+msgid "data file information"
+msgstr "data bestand informatie"
+
+#: src/ui/gui/message-dialog.c:143
+msgid "PSPP information"
+msgstr "PSPP informatie"
+
+#: src/ui/gui/message-dialog.c:222
+msgid "The PSPP processing engine reported the following message:"
+msgid_plural "The PSPP processing engine reported the following messages:"
+msgstr[0] "De PSPP processor rapporteert de volgende melding:"
+msgstr[1] "De PSPP processor rapporteert de volgende meldingen:"
+
+#: src/ui/gui/message-dialog.c:229
+#, c-format
+msgid "The PSPP processing engine reported %d message."
+msgid_plural "The PSPP processing engine reported %d messages."
+msgstr[0] "De PSPP processor rapporteert %d melding."
+msgstr[1] "De PSPP processor rapporteert %d meldingen."
+
+#: src/ui/gui/message-dialog.c:236
+#, c-format
+msgid "%d of these messages are displayed below."
+msgid_plural "%d of these messages are displayed below."
+msgstr[0] "%d van deze meldingen worden hier onder getoond."
+msgstr[1] "%d van deze meldingen worden hier onder getoond."
+
+#: src/ui/gui/message-dialog.glade:10
+msgid "Messages Reported"
+msgstr "Meldingen Gerapporteerd"
+
+#: src/ui/gui/message-dialog.glade:47
+msgid ""
+"The PSPP processor reported # errors.  The first # and last # are shown "
+"below:"
+msgstr ""
+"De PSPP processor rapporteerde # fouten.  De eerste # en de laatste # worden "
+"hier onder getoond:"
+
+#: src/ui/gui/message-dialog.glade:101
+msgid "gtk-close"
+msgstr ""
+
+#: src/ui/gui/missing-val-dialog.c:114 src/ui/gui/missing-val-dialog.c:159
+msgid "Incorrect value for variable type"
+msgstr "Foutieve waarde voor variabele type"
+
+#: src/ui/gui/missing-val-dialog.c:135 src/ui/gui/missing-val-dialog.c:142
+msgid "Incorrect range specification"
+msgstr "Foutieve range specificatie"
+
+#: src/ui/gui/oneway-anova-dialog.c:331
+#, c-format
+msgid "Contrast %d of %d"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:30
+msgid "_Factor:"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:66
+msgid "Dependent _Variable(s):"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:190
+msgid "_Homogeneity"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:226
+msgid "_Contrasts..."
+msgstr ""
+
+#: src/ui/gui/oneway.glade:309
+msgid "gtk-go-back"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:320
+msgid "gtk-go-forward"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:343
+msgid "_Coefficients:"
+msgstr ""
+
+#: src/ui/gui/oneway.glade:389
+msgid "Coefficient Total: "
+msgstr ""
+
+#: src/ui/gui/oneway.glade:422
+msgid "Contrast 1 of 1"
+msgstr ""
+
+#: src/ui/gui/output-viewer.glade:32
+msgid "gtk-save"
+msgstr ""
+
+#: src/ui/gui/output-viewer.glade:41
+msgid "gtk-save-as"
+msgstr ""
+
+#: src/ui/gui/output-viewer.glade:65
+msgid "gtk-copy"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:143
+msgid "Buttons"
+msgstr "Knoppen"
+
+#: src/ui/gui/psppire-buttonbox.c:144
+msgid "The mask that decides what buttons appear in the button box"
+msgstr "Het masker dat beslist welke knoppen in de knop box zichtbaar zijn"
+
+#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:435
+msgid "Continue"
+msgstr "Verder"
+
+#: src/ui/gui/psppire-buttonbox.c:433
+msgid "OK"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:434
+msgid "Go To"
+msgstr "Ga Naar"
+
+#: src/ui/gui/psppire-buttonbox.c:436
+msgid "Cancel"
+msgstr "Afbreken"
+
+#: src/ui/gui/psppire-buttonbox.c:437
+msgid "Help"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:438
+msgid "Reset"
+msgstr "Standaard"
+
+#: src/ui/gui/psppire-buttonbox.c:439
+msgid "Paste"
+msgstr "Plak"
+
+#: src/ui/gui/psppire.c:247
+msgid "_Reset"
+msgstr "_Standaard"
+
+#: src/ui/gui/psppire.c:248
+msgid "_Select"
+msgstr "_Selecteer"
+
+#: src/ui/gui/psppire-data-editor.c:956
+msgid "Data View"
+msgstr "Data Weergave"
+
+#: src/ui/gui/psppire-data-editor.c:959
+msgid "Variable View"
+msgstr "Variabele Weergave"
+
+#: src/ui/gui/psppire-data-store.c:761
+msgid "var"
+msgstr ""
+
+#: src/ui/gui/psppire-data-store.c:771 src/ui/gui/psppire-var-store.c:655
+#: src/ui/gui/psppire-var-store.c:665 src/ui/gui/psppire-var-store.c:675
+#: src/ui/gui/psppire-var-store.c:786
+#, c-format
+msgid "%d"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:213
+msgid "Transformations Pending"
+msgstr "Transformaties Uitstaand"
+
+#: src/ui/gui/psppire-data-window.c:229
+msgid "Filter off"
+msgstr "Filter uit"
+
+#: src/ui/gui/psppire-data-window.c:241
+#, c-format
+msgid "Filter by %s"
+msgstr "Filter op %s"
+
+#: src/ui/gui/psppire-data-window.c:262
+msgid "No Split"
+msgstr "Geen Splits"
+
+#: src/ui/gui/psppire-data-window.c:271
+msgid "Split by "
+msgstr "Splits op "
+
+#: src/ui/gui/psppire-data-window.c:299
+msgid "Weights off"
+msgstr "Weging uit"
+
+#: src/ui/gui/psppire-data-window.c:311
+#, c-format
+msgid "Weight by %s"
+msgstr "Weeg op %s"
+
+#: src/ui/gui/psppire-data-window.c:387 src/ui/gui/psppire-data-window.c:589
+msgid "System Files (*.sav)"
+msgstr "Systeem Bestand (*.sav)"
+
+#: src/ui/gui/psppire-data-window.c:393 src/ui/gui/psppire-data-window.c:595
+msgid "Portable Files (*.por) "
+msgstr "Overdraagbaar (Portable) Bestand (*.por)"
+
+#: src/ui/gui/psppire-data-window.c:399 src/ui/gui/psppire-data-window.c:601
+#: src/ui/gui/psppire-syntax-window.c:298
+#: src/ui/gui/psppire-syntax-window.c:385
+msgid "All Files"
+msgstr "Alle bestanden"
+
+#: src/ui/gui/psppire-data-window.c:609
+msgid "System File"
+msgstr "Systeem Bestand"
+
+#: src/ui/gui/psppire-data-window.c:614
+msgid "Portable File"
+msgstr "Overdraagbaar (Portable) Bestand"
+
+#: src/ui/gui/psppire-data-window.c:764
+msgid "Font Selection"
+msgstr "Font Selectie"
+
+#: src/ui/gui/psppire-data-window.c:832
+msgid "Sort Ascending"
+msgstr "Sorteer oplopend"
+
+#: src/ui/gui/psppire-data-window.c:838
+msgid "Sort Descending"
+msgstr "Sorteer aflopend"
+
+#: src/ui/gui/psppire-data-window.c:846 src/ui/gui/psppire-data-window.c:900
+#: src/ui/gui/psppire-data-window.c:936 src/ui/gui/psppire-data-window.c:1301
+#: src/ui/gui/psppire-data-window.c:1319
+msgid "Clear"
+msgstr "Ruimop"
+
+#: src/ui/gui/psppire-data-window.c:1178
+msgid "Open a data file"
+msgstr "Open een data bestand"
+
+#: src/ui/gui/psppire-data-window.c:1196
+msgid "New data file"
+msgstr "Nieuw data bestand"
+
+#: src/ui/gui/psppire-data-window.c:1211
+msgid "Import text data file"
+msgstr "Importeer text data bestand"
+
+#: src/ui/gui/psppire-data-window.c:1227 src/ui/gui/psppire-data-window.c:1244
+msgid "Save data to file"
+msgstr "Data opslaan als bestand"
+
+#: src/ui/gui/psppire-data-window.c:1243
+msgid "Save As"
+msgstr "Opslaan Als"
+
+#: src/ui/gui/psppire-data-window.c:1282
+msgid "Show/hide value labels"
+msgstr "Show/verberg waarde labels"
+
+#: src/ui/gui/psppire-data-window.c:1302
+msgid "Delete the cases at the selected position(s)"
+msgstr "Verwijder de cases op de geselecteerde positie(s)"
+
+#: src/ui/gui/psppire-data-window.c:1320
+msgid "Delete the variables at the selected position(s)"
+msgstr "Verwijder de variabele op de geselecteerde positie(s)"
+
+#: src/ui/gui/psppire-data-window.c:1338
+msgid "Create a new variable at the current position"
+msgstr "Creëer een nieuwe variabele op de huidige positie"
+
+#: src/ui/gui/psppire-data-window.c:1353
+msgid "Create a new case at the current position"
+msgstr "Creëer een nieuwe case op de huidige positie"
+
+#: src/ui/gui/psppire-data-window.c:1369
+msgid "Jump to a Case in the Data Sheet"
+msgstr "Spring naar een Case in het Data Blad"
+
+#: src/ui/gui/psppire-data-window.c:1385
+msgid "Weight cases by variable"
+msgstr "Weeg cases per variabele"
+
+#: src/ui/gui/psppire-data-window.c:1399
+msgid "Transpose the cases with the variables"
+msgstr "Herschik de cases met de variabelen"
+
+#: src/ui/gui/psppire-data-window.c:1413
+msgid "Split the active file"
+msgstr "Splits het actieve bestand"
+
+#: src/ui/gui/psppire-data-window.c:1428
+msgid "Sort cases in the active file"
+msgstr "Sorteer cases in het actieve bestand"
+
+#: src/ui/gui/psppire-data-window.c:1442
+msgid "Select cases from the active file"
+msgstr "Selecteer cases van het actieve bestand"
+
+#: src/ui/gui/psppire-data-window.c:1456
+msgid "Compute new values for a variable"
+msgstr "Bereken nieuwe waardes voor een variabele"
+
+#: src/ui/gui/psppire-data-window.c:1470
+msgid "Perform one way analysis of variance"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1485
+msgid "Calculate T Test for samples from independent groups"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1499
+msgid "Calculate T Test for paired samples"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1513
+msgid "Calculate T Test for sample from a single distribution"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1528
+msgid "Commentary text for the data file"
+msgstr "Commentaar tekst voor het data bestand"
+
+#: src/ui/gui/psppire-data-window.c:1554
+msgid "Rank Cases"
+msgstr "Rangschik Cases"
+
+#: src/ui/gui/psppire-data-window.c:1568
+msgid "Recode values into the same variables"
+msgstr "Hercodeer waardes in dezelfde Variabelen"
+
+#: src/ui/gui/psppire-data-window.c:1582
+msgid "Recode values into different variables"
+msgstr "Hercodeer waardes in andere Variabelen"
+
+#: src/ui/gui/psppire-data-window.c:1596
+msgid "Jump to variable"
+msgstr "Spring naar Variabele"
+
+#: src/ui/gui/psppire-data-window.c:1609
+msgid "Calculate descriptive statistics (mean, variance, ...)"
+msgstr "Bereken descriptive statistieken (mean, variance, ...)"
+
+#: src/ui/gui/psppire-data-window.c:1623
+msgid "Generate frequency statistics"
+msgstr "Genereer frequentie statistieken"
+
+#: src/ui/gui/psppire-data-window.c:1637
+msgid "Generate crosstabulations"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1652
+msgid "Examine Data by Factors"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1666
+msgid "Estimate parameters of the linear model"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1680 src/ui/gui/reliability.glade:7
+msgid "Reliability Analysis"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1843
+msgid "Split the window vertically and horizontally"
+msgstr "Splits het venster verticaal en horizontaal"
+
+#: src/ui/gui/psppire-data-window.c:1885
+msgid "Data Editor"
+msgstr ""
+
+#: src/ui/gui/psppire-dictview.c:207
+msgid "The dictionary to be displayed by this widget"
+msgstr ""
+
+#: src/ui/gui/psppire-dictview.c:214
+msgid "A predicate function"
+msgstr ""
+
+#: src/ui/gui/psppire-dictview.c:221
+msgid "How many things can be selected"
+msgstr "Hoeveel dingen kunnen worden geselecteerd"
+
+#: src/ui/gui/psppire-dictview.c:539
+msgid "Prefer variable labels"
+msgstr "Prefereer variabele labels"
+
+#: src/ui/gui/psppire.glade:47 src/ui/gui/psppire.glade:130
+#: src/ui/gui/weight-cases-dialog.c:79
+msgid "Do not weight cases"
+msgstr "Weeg cases niet"
+
+#: src/ui/gui/psppire.glade:58
+msgid "Weight cases by"
+msgstr "Weeg cases op"
+
+#: src/ui/gui/psppire.glade:83
+msgid "Frequency Variable"
+msgstr "Frequencie Variabele"
+
+#: src/ui/gui/psppire.glade:123
+msgid "Current Status: "
+msgstr "Huidige Status:"
+
+#: src/ui/gui/psppire.glade:219
+msgid "Name Variable:"
+msgstr "Naam Variabele:"
+
+#: src/ui/gui/psppire.glade:404
+msgid "Analyze all cases.  Do not create groups."
+msgstr "Analyseer alle cases.  Creëer geen groepen."
+
+#: src/ui/gui/psppire.glade:415
+msgid "Compare groups."
+msgstr "Vergelijk groepen."
+
+#: src/ui/gui/psppire.glade:429
+msgid "Organize output by groups."
+msgstr "Organiseer uitvoer per groepen."
+
+#: src/ui/gui/psppire.glade:477
+msgid "Groups based on:"
+msgstr "Groepen gebaseerd op:"
+
+#: src/ui/gui/psppire.glade:540
+msgid "Sort the file by grouping variables."
+msgstr "Sorteer bestand op groepering variabelen."
+
+#: src/ui/gui/psppire.glade:552
+msgid "File is already sorted."
+msgstr "Bestand is al gesorteerd."
+
+#: src/ui/gui/psppire.glade:597
+msgid "Current Status : "
+msgstr "Huidige Status : "
+
+#: src/ui/gui/psppire.glade:605
+msgid "Analysis by groups is off"
+msgstr "Analyseer per groep is uit"
+
+#: src/ui/gui/psppire.glade:704
+msgid "Sort by:"
+msgstr "Sorteer op:"
+
+#: src/ui/gui/psppire.glade:767
+msgid "Descending"
+msgstr "Aflopend"
+
+#: src/ui/gui/psppire.glade:784
+msgid "Sort Order"
+msgstr "Sorteer Volgorde"
+
+#: src/ui/gui/psppire.glade:853
+msgid "Target Variable:"
+msgstr "Doel Variabele:"
+
+#: src/ui/gui/psppire.glade:884
+msgid "Type & Label"
+msgstr ""
+
+#: src/ui/gui/psppire.glade:924
+msgid "="
+msgstr ""
+
+#: src/ui/gui/psppire.glade:970
+msgid "Numeric Expressions:"
+msgstr "Numerieke Expressies:"
+
+#: src/ui/gui/psppire.glade:1024
+msgid "Functions:"
+msgstr "Functies:"
+
+#: src/ui/gui/psppire.glade:1087 src/ui/gui/psppire.glade:1491
+#: src/ui/gui/recode.glade:731
+msgid "If..."
+msgstr "Als..."
+
+#: src/ui/gui/psppire.glade:1320
+msgid "Use filter variable"
+msgstr "Gebruik filter variabele"
+
+#: src/ui/gui/psppire.glade:1373
+msgid "Based on time or case range"
+msgstr "Gebaseerd op tijd of case volgorde"
+
+#: src/ui/gui/psppire.glade:1386
+msgid "Range..."
+msgstr ""
+
+#: src/ui/gui/psppire.glade:1425
+msgid "Random sample of cases"
+msgstr "Random steekproef van cases"
+
+#: src/ui/gui/psppire.glade:1439
+msgid "Sample..."
+msgstr "Steekproef..."
+
+#: src/ui/gui/psppire.glade:1477
+msgid "If condition is satisfied"
+msgstr "Aan If conditie is voldaan"
+
+#: src/ui/gui/psppire.glade:1526
+msgid "All Cases"
+msgstr "Alle Cases"
+
+#: src/ui/gui/psppire.glade:1541
+msgid "Select"
+msgstr "Selecteer"
+
+#: src/ui/gui/psppire.glade:1570
+msgid "Filtered"
+msgstr "Gefilterd"
+
+#: src/ui/gui/psppire.glade:1581
+msgid "Deleted"
+msgstr "Verwijderd"
+
+#: src/ui/gui/psppire.glade:1599
+msgid "Unselected Cases Are"
+msgstr "Niet geselecteerde Cases zijn"
+
+#: src/ui/gui/psppire.glade:1664
+msgid "Comments:"
+msgstr "Commentaren:"
+
+#: src/ui/gui/psppire.glade:1706
+msgid "Display comments in output"
+msgstr "Toon commentaren in uitvoer"
+
+#: src/ui/gui/psppire.glade:1721
+msgid "Column Number: 0"
+msgstr "Kolom Nummer: 0"
+
+#: src/ui/gui/psppire.glade:1804
+msgid "First case"
+msgstr "Eerste case"
+
+#: src/ui/gui/psppire.glade:1817
+msgid "Last case"
+msgstr "Laatste case"
+
+#: src/ui/gui/psppire.glade:1830
+msgid "Observation"
+msgstr "Observatie"
+
+#: src/ui/gui/psppire.glade:1894
+msgid "Use expression as label"
+msgstr "Gebruik expressie als label"
+
+#: src/ui/gui/psppire.glade:2020 src/ui/gui/psppire-var-sheet.c:529
+#: src/ui/gui/psppire-var-store.c:795
+msgid "Width"
+msgstr "Breedte"
+
+#: src/ui/gui/psppire.glade:2150
+msgid "Goto Case Number:"
+msgstr "Ga naar Case Nummer:"
+
+#: src/ui/gui/psppire.glade:2287
+msgid "Sample Size"
+msgstr "Steekproef Grootte"
+
+#: src/ui/gui/psppire-output-window.c:269
+msgid "Output Viewer"
+msgstr "Uitvoer Viewer"
+
+#: src/ui/gui/psppire-syntax-window.c:265
+#, c-format
+msgid "Saved file \"%s\""
+msgstr "Opgeslagen bestand \"%s\""
+
+#: src/ui/gui/psppire-syntax-window.c:284
+msgid "Save Syntax"
+msgstr "Sla Syntax op"
+
+#: src/ui/gui/psppire-syntax-window.c:292
+#: src/ui/gui/psppire-syntax-window.c:379
+msgid "Syntax Files (*.sps) "
+msgstr "Syntax Bestand (*.sps)"
+
+#: src/ui/gui/psppire-syntax-window.c:371
+msgid "Open Syntax"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:551
+msgid "Syntax Editor"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:565
+#, c-format
+msgid "Cannot load syntax file '%s'"
+msgstr "Kan syntax bestand \"%s\" niet laden"
+
+#: src/ui/gui/psppire-var-sheet.c:527 src/ui/gui/psppire-var-store.c:793
+msgid "Name"
+msgstr "Naam"
+
+#: src/ui/gui/psppire-var-sheet.c:530 src/ui/gui/psppire-var-store.c:796
+msgid "Decimals"
+msgstr "Decimalen"
+
+#: src/ui/gui/psppire-var-sheet.c:532 src/ui/gui/psppire-var-store.c:798
+msgid "Values"
+msgstr "Waardes"
+
+#: src/ui/gui/psppire-var-sheet.c:535 src/ui/gui/psppire-var-store.c:801
+msgid "Align"
+msgstr "Uitlijnen"
+
+#: src/ui/gui/psppire-var-sheet.c:536 src/ui/gui/psppire-var-store.c:802
+msgid "Measure"
+msgstr "Meting"
+
+#: src/ui/gui/psppire-var-store.c:578 src/ui/gui/var-sheet-dialogs.glade:43
+msgid "Comma"
+msgstr "Komma"
+
+#: src/ui/gui/psppire-var-store.c:579 src/ui/gui/var-sheet-dialogs.glade:59
+msgid "Dot"
+msgstr "Punt"
+
+#: src/ui/gui/psppire-var-store.c:580
+msgid "Scientific"
+msgstr "Wetenschappelijk"
+
+#: src/ui/gui/psppire-var-store.c:581 src/ui/gui/var-sheet-dialogs.glade:91
+msgid "Date"
+msgstr "Datum"
+
+#: src/ui/gui/psppire-var-store.c:582 src/ui/gui/var-sheet-dialogs.glade:107
+msgid "Dollar"
+msgstr "Euro"
+
+#: src/ui/gui/psppire-var-store.c:583
+msgid "Custom"
+msgstr "Aangepast"
+
+#: src/ui/gui/psppire-window.c:97
+#, c-format
+msgid "%s %s PSPPIRE %s"
+msgstr ""
+
+#: src/ui/gui/psppire-window.c:480
+#, c-format
+msgid "Save the changes to \"%s\" before closing?"
+msgstr "De veranderingen van \"%s\" opslaan voor afsluiten?"
+
+#: src/ui/gui/psppire-window.c:487
+#, c-format
+msgid ""
+"If you don't save, changes from the last %ld seconds will be permanently "
+"lost."
+msgstr ""
+"Als je niet opslaat gaan de aanpassingen van de laatste %ld seconden "
+"definitief verloren."
+
+#: src/ui/gui/psppire-window.c:491
+msgid "Close _without saving"
+msgstr "Sluit _zonder opslaan"
+
+#: src/ui/gui/rank.glade:57
+msgid "By:"
+msgstr "Per:"
+
+#: src/ui/gui/rank.glade:196
+msgid "_Smallest Value"
+msgstr "_Kleinste Waarde"
+
+#: src/ui/gui/rank.glade:208
+msgid "_Largest Value"
+msgstr "_Grootste Waarde"
+
+#: src/ui/gui/rank.glade:227
+msgid "Assign rank 1 to:"
+msgstr "Ken rang 1 toe aan:"
+
+#: src/ui/gui/rank.glade:245
+msgid "_Display summary tables"
+msgstr "_Toon totalen tabellen"
+
+#: src/ui/gui/rank.glade:261
+msgid "Rank T_ypes"
+msgstr "Rangschik T_ypes"
+
+#: src/ui/gui/rank.glade:272
+msgid "_Ties..."
+msgstr ""
+
+#: src/ui/gui/rank.glade:339
+msgid "Sum of case weights"
+msgstr "Totaal van case gewichten"
+
+#: src/ui/gui/rank.glade:355
+msgid "Fractional rank as %"
+msgstr ""
+
+#: src/ui/gui/rank.glade:369
+msgid "Fractional rank"
+msgstr ""
+
+#: src/ui/gui/rank.glade:383
+msgid "Savage score"
+msgstr ""
+
+#: src/ui/gui/rank.glade:397
+msgid "Rank"
+msgstr ""
+
+#: src/ui/gui/rank.glade:411
+msgid "Ntiles"
+msgstr ""
+
+#: src/ui/gui/rank.glade:450
+msgid "Proportion Estimates"
+msgstr ""
+
+#: src/ui/gui/rank.glade:460
+msgid "Normal Scores"
+msgstr ""
+
+#: src/ui/gui/rank.glade:494
+msgid "Blom"
+msgstr ""
+
+#: src/ui/gui/rank.glade:505
+msgid "Tukey"
+msgstr ""
+
+#: src/ui/gui/rank.glade:519
+msgid "Rankit"
+msgstr ""
+
+#: src/ui/gui/rank.glade:533
+msgid "Van der Wärden"
+msgstr ""
+
+#: src/ui/gui/rank.glade:550
+msgid "Proportion Estimation Formula"
+msgstr ""
+
+#: src/ui/gui/rank.glade:612
+msgid "_Mean"
+msgstr "_Gemiddeld"
+
+#: src/ui/gui/rank.glade:624
+msgid "_Low"
+msgstr "_Laag"
+
+#: src/ui/gui/rank.glade:640
+msgid "_High"
+msgstr "_Hoog"
+
+#: src/ui/gui/rank.glade:658
+msgid "_Sequential ranks to unique values"
+msgstr "_Sequentiele rangen naar unieke waardes"
+
+#: src/ui/gui/rank.glade:678
+msgid "Rank Assigned to Ties"
+msgstr ""
+
+#: src/ui/gui/recode-dialog.c:881
+msgid "Recode into Different Variables"
+msgstr "Hercodeer in Andere Variabelen"
+
+#: src/ui/gui/recode-dialog.c:884
+msgid "Recode into Same Variables"
+msgstr "Hercodeer in Zelfde Variabelen"
+
+#: src/ui/gui/recode-dialog.c:912 src/ui/gui/recode-dialog.c:1014
+msgid "Old"
+msgstr "Oud"
+
+#: src/ui/gui/recode-dialog.c:927 src/ui/gui/recode-dialog.c:1022
+msgid "New"
+msgstr "Nieuw"
+
+#: src/ui/gui/recode-dialog.c:1270
+msgid "Recode into Different Variables: Old and New Values "
+msgstr "Hercodeer in Andere Variabelen: Oude en Nieuwe Waardes "
+
+#: src/ui/gui/recode-dialog.c:1271
+msgid "Recode into Same Variables: Old and New Values"
+msgstr "Hercodeer in Zelfde Variabelen: Oude en Nieuwe Waardes "
+
+#: src/ui/gui/recode.glade:197
+msgid "System-Missing"
+msgstr ""
+
+#: src/ui/gui/recode.glade:211
+msgid "System-or user-missing"
+msgstr ""
+
+#: src/ui/gui/recode.glade:245
+msgid "through"
+msgstr "tot"
+
+#: src/ui/gui/recode.glade:283
+msgid "Range, LOWEST thru value"
+msgstr "Range, LAAGSTE tot waarde"
+
+#: src/ui/gui/recode.glade:297
+msgid "Range, value thru HIGHEST"
+msgstr "Range, waarde tot HOOGSTE"
+
+#: src/ui/gui/recode.glade:327
+msgid "All other values"
+msgstr "Alle andere waardes"
+
+#: src/ui/gui/recode.glade:363
+msgid "Range:"
+msgstr ""
+
+#: src/ui/gui/recode.glade:380
+msgid "Old Value"
+msgstr "Oude Waarde"
+
+#: src/ui/gui/recode.glade:462
+msgid "System Missing"
+msgstr ""
+
+#: src/ui/gui/recode.glade:476
+msgid "Copy old values"
+msgstr "Kopieer oude waardes"
+
+#: src/ui/gui/recode.glade:500
+msgid "Value: "
+msgstr "Waarde: "
+
+#: src/ui/gui/recode.glade:530
+msgid "New Value"
+msgstr "Nieuwe Waarde"
+
+#: src/ui/gui/recode.glade:590
+msgid "Convert numeric strings to numbers ('5' -> 5)"
+msgstr "Converteer numerieke strings naar nummers ('5' -> 5)"
+
+#: src/ui/gui/recode.glade:608
+msgid "Output variables are strings"
+msgstr "Uitvoer variabelen zijn strings"
+
+#: src/ui/gui/recode.glade:620
+msgid "Width: "
+msgstr "Breedte: "
+
+#: src/ui/gui/recode.glade:743
+msgid "(optional case selection condition)"
+msgstr "(optionele case selectie conditie)"
+
+#: src/ui/gui/recode.glade:823
+msgid "Name:"
+msgstr "Naam:"
+
+#: src/ui/gui/recode.glade:867
+msgid "Change"
+msgstr "Wijzig"
+
+#: src/ui/gui/recode.glade:885
+msgid "Output Variable"
+msgstr "Uitvoer Variabele"
+
+#: src/ui/gui/recode.glade:965
+msgid "Old and New Values"
+msgstr "Oude en Nieuwe Waardes"
+
+#: src/ui/gui/regression-dialog.c:41
+msgid "Coeff"
+msgstr ""
+
+#: src/ui/gui/regression-dialog.c:43
+msgid "Anova"
+msgstr ""
+
+#: src/ui/gui/regression-dialog.c:44
+msgid "Bcov"
+msgstr ""
+
+#: src/ui/gui/regression.glade:40
+msgid "Save..."
+msgstr "Opslaan..."
+
+#: src/ui/gui/regression.glade:145
+msgid "Dependent"
+msgstr "Afhankelijk"
+
+#: src/ui/gui/regression.glade:193
+msgid "Independent"
+msgstr "Onafhankelijk"
+
+#: src/ui/gui/regression.glade:243
+msgid "Predicted values"
+msgstr "Voorspelde waardes"
+
+#: src/ui/gui/regression.glade:252
+msgid "Residuals"
+msgstr "Restant"
+
+#: src/ui/gui/reliability.glade:89
+msgid "_Items:"
+msgstr ""
+
+#: src/ui/gui/reliability.glade:111
+msgid "Model:\t"
+msgstr ""
+
+#: src/ui/gui/reliability.glade:122
+msgid ""
+"Alpha\n"
+"Split"
+msgstr ""
+"Alpha\n"
+"Splits"
+
+#: src/ui/gui/reliability.glade:144
+msgid "Variables in first split:"
+msgstr "Variabelen in eerste splits:"
+
+#: src/ui/gui/select-cases-dialog.c:82
+#, c-format
+msgid "Approximately %3d%% of all cases."
+msgstr "Ongeveer %3d%% van alle cases."
+
+#: src/ui/gui/select-cases-dialog.c:83
+#, c-format
+msgid "Exactly %3d cases from the first %3d cases."
+msgstr "Precies %3d cases van de eerste %3d cases."
+
+#: src/ui/gui/select-cases-dialog.c:223
+#, c-format
+msgid "%d thru %d"
+msgstr "%d tot %d"
+
+#: src/ui/gui/syntax-editor.glade:163
+msgid "_Run"
+msgstr ""
+
+#: src/ui/gui/syntax-editor.glade:172
+msgid "All"
+msgstr "Alles"
+
+#: src/ui/gui/syntax-editor.glade:180
+msgid "Selection"
+msgstr "Selectie"
+
+#: src/ui/gui/syntax-editor.glade:188
+msgid "Current Line"
+msgstr "Huidige Regel"
+
+#: src/ui/gui/syntax-editor.glade:197
+msgid "To End"
+msgstr "Naar Einde"
+
+#: src/ui/gui/text-data-import-dialog.c:461
+#, c-format
+msgid "Could not open \"%s\": %s"
+msgstr "Kon \"%s\": %s niet openen"
+
+#: src/ui/gui/text-data-import-dialog.c:477
+#, c-format
+msgid "Error reading \"%s\": %s"
+msgstr "Fout bij lezen \"%s\": %s"
+
+#: src/ui/gui/text-data-import-dialog.c:480
+#, 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 ""
+"Lezen van \"%s\" mislukt omdat het een regel bevat die meer dan %d bytes "
+"lang is en daarom is het geen tekst bestand."
+
+#: src/ui/gui/text-data-import-dialog.c:494
+#, c-format
+msgid "\"%s\" is empty."
+msgstr "\"%s\" is leeg."
+
+#: src/ui/gui/text-data-import-dialog.c:539
+msgid "Import Delimited Text Data"
+msgstr "Importeer Delimited Text Data"
+
+#: src/ui/gui/text-data-import-dialog.c:590
+msgid "Importing Delimited Text Data"
+msgstr "Importeren Delimited Text Data"
+
+#: src/ui/gui/text-data-import-dialog.c:749
+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 "
+"by tabs, commas, or other delimiters.\n"
+"\n"
+msgstr ""
+"De assistent zal je begeleiden door het proces van het importeren van data "
+"in PSPP van een tekst bestand met 1 regel per case, waarin velden zijn "
+"gescheiden door tabs, kommas of andere scheidingstekens.\n"
+
+#: src/ui/gui/text-data-import-dialog.c:755
+#, c-format
+msgid "The selected file contains %zu line of text.  "
+msgid_plural "The selected file contains %zu lines of text.  "
+msgstr[0] "Het geselecteerde bestand bevat %zu regel text.  "
+msgstr[1] "Het geselecteerde bestand bevat %zu regels text. "
+
+#: src/ui/gui/text-data-import-dialog.c:763
+#, 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] "Het geselecteerde bestand bevat ongeveer %lu regel text. "
+msgstr[1] "Het geselecteerde bestand bevat ongeveer %lu regels text. "
+
+#: src/ui/gui/text-data-import-dialog.c:769
+#, c-format
+msgid ""
+"Only the first %zu line of the file will be shown for preview purposes in "
+"the following screens.  "
+msgid_plural ""
+"Only the first %zu lines of the file will be shown for preview purposes in "
+"the following screens.  "
+msgstr[0] ""
+"Alleen de eerste %zu regel van het bestand worden getoond voor preview "
+"doeleinden in de volgende schermen."
+msgstr[1] ""
+"Alleen de eerste %zu regels van het bestand worden getoond voor preview "
+"doeleinden in de volgende schermen."
+
+#: src/ui/gui/text-data-import-dialog.c:776
+msgid "You may choose below how much of the file should actually be imported."
+msgstr ""
+"Hieronder kunt u kiezen hoeveel van het bestand daadwerkelijk geïmporteerd "
+"moet worden."
+
+#: src/ui/gui/text-data-import-dialog.c:1523
+#: src/ui/gui/text-data-import-dialog.c:1765
+msgid "This input line has too few separators to fill in this field."
+msgstr ""
+"Deze invoer regel heeft te weinig scheidingstekens om dit veld te vullen."
+
+#: src/ui/gui/text-data-import-dialog.c:1756
+#, c-format
+msgid "Field content \"%.*s\" cannot be parsed in format %s."
+msgstr "Veld inhoud \"%.*s\" kan niet ontleed worden in formaat %s."
+
+#: src/ui/gui/text-data-import.glade:8
+msgid "Importing Textual Data"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:18
+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 "
+"by tabs, commas, or other delimiters.\n"
+"\n"
+"The selected file contains N lines of text.  Only the first M of these will "
+"be shown for preview purposes in the following screens.  You may choose "
+"below how much of the file should actually be imported."
+msgstr ""
+"Deze assistant zal je assisteren bij het proces van het importeren van data "
+"in PSPP vanuit een text bestand met een regel per case en velden gescheiden "
+"met tabs, kommas of andere scheiders.\n"
+" \n"
+"Het geselecteerde bestand bevat N regels text.  Alleen de eerste  M hiervan "
+"zullen getoond worden voor voorbeeld doeleinden in de volgende schermen.  Je "
+"kunt hieronder kiezen hoeveel van het bestand daadwerkelijk geïmporteerd "
+"moet worden."
+
+#: src/ui/gui/text-data-import.glade:50
+msgid "All cases"
+msgstr "Alle cases"
+
+#: src/ui/gui/text-data-import.glade:66 src/ui/gui/text-data-import.glade:122
+msgid "Only first "
+msgstr "Alleen eerste "
+
+#: src/ui/gui/text-data-import.glade:97
+msgid " cases"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:152
+msgid "% of file (approximately)"
+msgstr "% van bestand (ongeveer)"
+
+#: src/ui/gui/text-data-import.glade:173
+msgid "<b>Amount to Import</b>"
+msgstr "<b>Aantal te Importeren</b>"
+
+#: src/ui/gui/text-data-import.glade:195
+msgid "Select Data to Import"
+msgstr "Selecteer Data om te Importeren"
+
+#: src/ui/gui/text-data-import.glade:205
+msgid "Select the first line of the data file that contains data."
+msgstr "Selecteer de eerste regel van het data bestand die data bevat."
+
+#: src/ui/gui/text-data-import.glade:236
+msgid "Line above selected line contains variable names"
+msgstr "De regel boven de geselecteerde data regel bevat de variabele namen"
+
+#: src/ui/gui/text-data-import.glade:251
+msgid "Choose Separators"
+msgstr "Kies scheidingstekens"
+
+#: src/ui/gui/text-data-import.glade:299
+msgid "C_ustom"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:314
+msgid "Slas_h (/)"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:331
+msgid "Semicolo_n (;)"
+msgstr "Pu_ntkomma(;)"
+
+#: src/ui/gui/text-data-import.glade:348
+msgid "P_ipe (|)"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:363
+msgid "H_yphen (-)"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:380
+msgid "Co_mma (,)"
+msgstr "Ko_mma (,)"
+
+#: src/ui/gui/text-data-import.glade:397
+msgid "_Colon (:)"
+msgstr "_Dubbele punt (:)"
+
+#: src/ui/gui/text-data-import.glade:412
+msgid "Ban_g (!)"
+msgstr "Uitroepteken(!)"
+
+#: src/ui/gui/text-data-import.glade:427
+msgid "Ta_b"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:442
+msgid "_Space"
+msgstr "_Spatie"
+
+#: src/ui/gui/text-data-import.glade:456
+msgid "<b>Separators</b>"
+msgstr "<b>Scheiders</b>"
+
+#: src/ui/gui/text-data-import.glade:489
+msgid "Doubled quote mark treated as escape"
+msgstr ""
+
+#: src/ui/gui/text-data-import.glade:526
+msgid "Quote separator characters with"
+msgstr "Citeer scheidingstekens met"
+
+#: src/ui/gui/text-data-import.glade:543
+msgid "<b>Quoting</b>"
+msgstr "<b>Citeren</b>"
+
+#: src/ui/gui/text-data-import.glade:594
+msgid "<b>Fields Preview</b>"
+msgstr "<b>Velden Voorbeeld</b>"
+
+#: src/ui/gui/text-data-import.glade:612
+msgid "Adjust Variable Formats"
+msgstr "Pas Variabele Formaat aan"
+
+#: src/ui/gui/text-data-import.glade:622
+msgid ""
+"Check the data formats displayed below and fix any that are incorrect.  You "
+"may set other variable properties now or later."
+msgstr ""
+"Controleer de data formaten hieronder en verbeter degene die foutief zijn. "
+"Je mag andere variabele opties nu of later zetten."
+
+#: src/ui/gui/text-data-import.glade:665
+msgid "<b>Variables</b>"
+msgstr "<b>Variabelen</b>"
+
+#: src/ui/gui/text-data-import.glade:712
+msgid "<b>Data Preview</b>"
+msgstr "<b>Data Voorbeeld</b>"
+
+#: src/ui/gui/t-test.glade:56 src/ui/gui/t-test.glade:165
+msgid "Define Groups"
+msgstr "Definieer Groepen"
+
+#: src/ui/gui/t-test.glade:123 src/ui/gui/t-test.glade:549
+#: src/ui/gui/t-test.glade:761
+msgid "Test Variable(s):"
+msgstr "Test Variabel(en):"
+
+#: src/ui/gui/t-test.glade:258
+msgid "Group_2 value:"
+msgstr "Groep_2 waarde:"
+
+#: src/ui/gui/t-test.glade:271
+msgid "Group_1 value:"
+msgstr "Groep_1 waarde:"
+
+#: src/ui/gui/t-test.glade:320
+msgid "_Cut point:"
+msgstr "_Knip punt:"
+
+#: src/ui/gui/t-test.glade:349
+msgid "_Use specified values:"
+msgstr "_Gebruik gespecificeerde waardes:"
+
+#: src/ui/gui/t-test.glade:431
+msgid "Exclude cases _analysis by analysis"
+msgstr "Sluit cases _analysis by analysis uit"
+
+#: src/ui/gui/t-test.glade:442
+msgid "Exclude cases _listwise"
+msgstr "Sluit cases _listwise uit"
+
+#: src/ui/gui/t-test.glade:594
+msgid "Test Value: "
+msgstr "Test Waarde:"
+
+#: src/ui/gui/t-test-options.c:60
+#, c-format
+msgid "Confidence Interval: %2d %%"
+msgstr ""
+
+#: src/ui/gui/t-test-paired-samples.c:227
+msgid "Var 1"
+msgstr ""
+
+#: src/ui/gui/t-test-paired-samples.c:228
+msgid "Var 2"
+msgstr ""
+
+#: src/ui/gui/variable-info-dialog.c:92
+#, c-format
+msgid "Label: %s\n"
+msgstr ""
+
+#: src/ui/gui/variable-info-dialog.c:101
+#, c-format
+msgid "Type: %s\n"
+msgstr ""
+
+#: src/ui/gui/variable-info-dialog.c:105
+#, c-format
+msgid "Missing Values: %s\n"
+msgstr "Ontbrekende Waardes: %s\n"
+
+#: src/ui/gui/variable-info-dialog.c:110
+#, c-format
+msgid "Measurement Level: %s\n"
+msgstr "Meetniveau: %s\n"
+
+#: src/ui/gui/variable-info-dialog.c:125
+msgid "Value Labels:\n"
+msgstr "Waarde Labels:\n"
+
+#: src/ui/gui/variable-info-dialog.c:138
+#, c-format
+msgid "%s %s\n"
+msgstr ""
+
+#: src/ui/gui/variable-info-dialog.glade:49
+msgid "Variable Information:"
+msgstr "Variabele Informatie:"
+
+#: src/ui/gui/var-sheet-dialogs.glade:7
+msgid "Variable Type"
+msgstr "Variabele Type"
+
+#: src/ui/gui/var-sheet-dialogs.glade:75
+msgid "Scientific notation"
+msgstr "Wetenschappelijke notatie"
+
+#: src/ui/gui/var-sheet-dialogs.glade:123
+msgid "Custom currency"
+msgstr "Aangepaste waarde"
+
+#: src/ui/gui/var-sheet-dialogs.glade:217
+msgid "positive"
+msgstr "positief"
+
+#: src/ui/gui/var-sheet-dialogs.glade:223
+msgid "negative"
+msgstr "negatief"
+
+#: src/ui/gui/var-sheet-dialogs.glade:236
+msgid "Sample"
+msgstr "Steekproef"
+
+#: src/ui/gui/var-sheet-dialogs.glade:286
+msgid "Width:"
+msgstr "Breedte:"
+
+#: src/ui/gui/var-sheet-dialogs.glade:330
+msgid "Decimal Places:"
+msgstr "Decimalen:"
+
+#: src/ui/gui/var-sheet-dialogs.glade:499
+msgid "Value Label:"
+msgstr "Waarde Label:"
+
+#: src/ui/gui/var-sheet-dialogs.glade:677
+msgid "_No missing values"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:747
+msgid "_Discrete missing values"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:782
+msgid "_Low:"
+msgstr "_Laag:"
+
+#: src/ui/gui/var-sheet-dialogs.glade:801
+msgid "_High:"
+msgstr "_Hoog:"
+
+#: src/ui/gui/var-sheet-dialogs.glade:826
+msgid "Di_screte value:"
+msgstr ""
+
+#: src/ui/gui/var-sheet-dialogs.glade:856
+msgid "_Range plus one optional discrete missing value"
+msgstr ""
+
+#: src/ui/gui/weight-cases-dialog.c:85
+#, c-format
+msgid "Weight cases by %s"
+msgstr "Weeg cases by %s"
+
+#: src/ui/source-init-opts.c:42
+msgid ""
+"set to `compatible' if you want output calculated from broken algorithms"
+msgstr ""
+"zet op 'compatible' als je uitvoer wilt die door 'broken algorithms' wordt "
+"berekend"
+
+#: src/ui/source-init-opts.c:43
+msgid "Append DIR to include path"
+msgstr "Voeg DIR toe aan include pad"
+
+#: src/ui/source-init-opts.c:44
+msgid "Clear include path"
+msgstr "Maak include pad leeg"
+
+#: src/ui/source-init-opts.c:45
+msgid "Disable execution of .pspp/rc at startup"
+msgstr "Schakel uitvoeren van .pspp/rc bij het opstarten uit"
+
+#: src/ui/source-init-opts.c:46
+msgid "Set configuration directory to DIR"
+msgstr "Zet configuratie directory op DIR"
+
+#: src/ui/source-init-opts.c:47
+msgid "Don't allow some unsafe operations"
+msgstr "Sta sommige onveilige operaties niet toe"
+
+#: src/ui/source-init-opts.c:48
+msgid "Set to `compatible' if you want only to accept SPSS compatible syntax"
+msgstr ""
+"Zet op 'compatible' als je alleen SPSS compatible syntax wilt accepteren"
+
+#: src/ui/source-init-opts.c:83
+#, c-format
+msgid "Algorithm must be either \"compatible\" or \"enhanced\"."
+msgstr "Algoritme moet zijn \"compatible\" of \"enhanced\"."
+
+#: src/ui/source-init-opts.c:124
+#, c-format
+msgid "Syntax must be either \"compatible\" or \"enhanced\"."
+msgstr "Syntax moet zijn \"compatible\" of \"enhanced\"."
+
+#: src/ui/terminal/main.c:115
+msgid "PSPP --- A program for statistical analysis"
+msgstr "PSPP -- Een programma voor statistische analyse"
+
+#: src/ui/terminal/main.c:116
+msgid "FILE1, FILE2 ... FILEn"
+msgstr ""
+
+#: src/ui/terminal/main.c:119 src/ui/terminal/terminal-opts.c:177
+msgid "Options affecting input and output locations:"
+msgstr "Opties die invoer en uitvoer locaties beinvloeden:"
+
+#: src/ui/terminal/main.c:122 src/ui/terminal/terminal-opts.c:178
+msgid "Diagnostic options:"
+msgstr "Diagnose opties:"
+
+#: src/ui/terminal/main.c:156
+msgid ""
+"Stopping syntax file processing here to avoid a cascade of dependent command "
+"failures."
+msgstr ""
+"Stop syntax bestand uitvoering hier om een cascade van afhankelijke opdracht "
+"fouten te voorkomen."
+
+#: src/ui/terminal/msg-ui.c:67
+#, c-format
+msgid "Cannot open %s (%s). Writing errors to stdout instead.\n"
+msgstr "Kan %s (%s) niet openen. Schrijf fouten naar stdout inplaats.\n"
+
+#: src/ui/terminal/msg-ui.c:94
+msgid "Terminating execution of syntax file due to error."
+msgstr "Breek uitvoering van syntax bestand af vanwege fout."
+
+#: src/ui/terminal/msg-ui.c:96
+#, c-format
+msgid "Errors (%d) exceeds limit (%d)."
+msgstr "Fouten (%d) overschrijdt limiet (%d)."
+
+#: src/ui/terminal/msg-ui.c:99
+#, c-format
+msgid "Warnings (%d) exceed limit (%d)."
+msgstr "Waarschuwings (%d) overschrijdt limiet (%d)."
+
+#: src/ui/terminal/msg-ui.c:150
+msgid "error"
+msgstr "fout"
+
+#: src/ui/terminal/msg-ui.c:151
+msgid "warning"
+msgstr "waarschuwing"
+
+#: src/ui/terminal/terminal.c:72
+#, c-format
+msgid "could not access definition for terminal `%s'"
+msgstr "kon definitie voor terminal '%s' niet benaderen"
+
+#: src/ui/terminal/terminal-opts.c:41
+msgid "Increase diagnostic verbosity level"
+msgstr "Verhoog diagnose zichtbaarheids niveau"
+
+#: src/ui/terminal/terminal-opts.c:68
+msgid "Send error messages to FILE (appended)"
+msgstr "Stuur fout meldingen naar FILE (aanvullend)"
+
+#: src/ui/terminal/terminal-opts.c:71
+msgid "Select output driver DEVICE and disable defaults"
+msgstr "Selecteer uitvoer driver DEVICE en schakel defaults uit"
+
+#: src/ui/terminal/terminal-opts.c:74
+msgid "Print a list of known driver classes, then exit"
+msgstr "Print een lijst van bekende driver classes en eindig daarna"
+
+#: src/ui/terminal/terminal-opts.c:76
+msgid "Start an interactive session"
+msgstr "Start een interactieve sessie"
+
+#~ msgid "Bad variable width %d."
+#~ msgstr "Foutieve variabele breedte %d."
+
+#~ msgid "File specifies unexpected value %g as HIGHEST."
+#~ msgstr "Bestand specificeert onverwachte waarde %g als HIGHEST."
+
+#~ msgid "File specifies unexpected value %g as LOWEST."
+#~ msgstr "Bestand specificeert onverwachte waarde %g als LOWEST."
+
+#~ msgid "%s is unimplemented."
+#~ msgstr "%s is niet geïmplementeerd."
+
+#~ msgid "Bad character in input: `\\%o'."
+#~ msgstr "Fout karakter in input: '\\%o'."
+
+#~ msgid "WEIGHT is off."
+#~ msgstr "WEGING is uit."
+
+#~ msgid "WEIGHT is variable %s."
+#~ msgstr "WEGING is variabele %s."
+
+#~ msgid "WIDTH is %d."
+#~ msgstr "BREEDTE is %d."
+
+#~ msgid ""
+#~ "Ignoring missing values on long string variable %s, which PSPP does not "
+#~ "yet support."
+#~ msgstr ""
+#~ "Negeren van missing values voor lange string variabele %s, wat PSPP nog "
+#~ "niet ondersteunt."
+
+#~ msgid ""
+#~ "Ignoring value labels for long string variables, which PSPP does not yet "
+#~ "support."
+#~ msgstr ""
+#~ "Negeer waarde labels voor lange string variabelen, die door PSPP nog niet "
+#~ "ondersteund worden."
+
+#~ msgid "Cannot add value labels from source file to long string variable %s."
+#~ msgstr ""
+#~ "Kan geen value labels van bron bestand toevoegen aan lange string "
+#~ "variabele %s."
+
+#~ msgid ""
+#~ "It is not possible to assign value labels to long string variables such "
+#~ "as %s."
+#~ msgstr ""
+#~ "Het is niet mogelijk om waarde labels aan lange string variabelen als %s "
+#~ "toe te kennen."
+
+#~ msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
+#~ msgstr ""
+#~ "Write modus ALL niet toegestaan in algemen modus.  WRITE=CELLS aangenomen."
+
+#~ msgid "Error writing FLIP file: %s."
+#~ msgstr "Fout tijdens het schrijven van FLIP bestand: %s."
+
+#~ msgid "Could not create acceptable variant for variable %s."
+#~ msgstr "Kon geen acceptabele variant voor variabele %s creëren."
+
+#~ msgid "Cannot create more than 99999 variable names."
+#~ msgstr "Kan niet meer dan 99999 variabele namen creëren."
+
+#~ msgid "Long string variable %s is not valid here."
+#~ msgstr "Lange string variabele %s is niet geldig hier."
+
+#~ msgid "PATH and SEARCH subcommands are mutually exclusive.  Ignoring PATH."
+#~ msgstr ""
+#~ "PATH en SEARCH subopdrachten zijn wederzijds uitsluitend. PATH genegeerd. "
+
+#~ msgid "At least one value must be specified on PATH."
+#~ msgstr "Tenminste 1 waarde dient bij PATH opgegeven te zijn."
+
+#~ msgid "Hash bits adjusted to %d."
+#~ msgstr "Hash bits aangepast naar %d."
+
+#~ msgid "error opening \"%s\" for writing"
+#~ msgstr "fout bij openen \"%s\" voor schrijven"
+
+#~ msgid ""
+#~ "This is beta status software.  Please report bugs to bug-gnu-pspp@gnu.org"
+#~ msgstr ""
+#~ "Dit is beta status software. Rapporteer bugs s.v.p. bij bug-gnu-pspp@gnu."
+#~ "org"
+
+#~ msgid "Diagnositic options:"
+#~ msgstr "Diagnostische opties:"
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..d99e945
--- /dev/null
@@ -0,0 +1,298 @@
+/* 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/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,
+                           hash_case_string (name, 0), &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, hash_case_string (name, 0));
+}
+
+/* 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..a249f5ad4e9557a8c0f5a6e28366dcc322182c1d 100644 (file)
@@ -1,20 +1,24 @@
+noinst_LTLIBRARIES += src/data/libdata.la
 
-noinst_LIBRARIES += src/data/libdata.a
+src_data_libdata_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(PG_CFLAGS) $(AM_CPPFLAGS) 
 
-src_data_libdata_a_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/caseproto.c \
+       src/data/caseproto.h \
        src/data/case.c \
        src/data/casegrouper.c \
        src/data/casegrouper.h \
@@ -83,8 +87,8 @@ src_data_libdata_a_SOURCES = \
        src/data/settings.h \
        src/data/short-names.c \
        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..411b9f0d49904cc118f3052c35c096c70c0e8376 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>
 /* A case map. */
 struct case_map
   {
-    size_t value_cnt;   /* Number of values in map. */
-    int *map;           /* For each destination index, the
-                           corresponding source index. */
+    struct caseproto *proto;   /* Prototype for output cases. */
+    int *map;                  /* For each destination index, the
+                                  corresponding source index. */
   };
 
-/* Creates and returns an empty map. */
+static struct ccase *translate_case (struct ccase *, void *map_);
+static bool destroy_case_map (void *map_);
+
+/* Creates and returns an empty map that outputs cases matching
+   PROTO. */
 static struct case_map *
-create_case_map (size_t n)
+create_case_map (const struct caseproto *proto)
 {
+  size_t n_values = caseproto_get_n_widths (proto);
   struct case_map *map;
   size_t i;
 
   map = xmalloc (sizeof *map);
-  map->value_cnt = n;
-  map->map = xnmalloc (n, sizeof *map->map);
-  for (i = 0; i < map->value_cnt; i++)
+  map->proto = caseproto_ref (proto);
+  map->map = xnmalloc (n_values, sizeof *map->map);
+  for (i = 0; i < n_values; i++)
     map->map[i] = -1;
 
   return map;
 }
 
-/* Inserts into MAP a mapping of the CNT values starting at FROM
-   to the CNT values starting at TO. */
+/* Inserts into MAP a mapping of the value at index FROM in the
+   source case to the value at index TO in the destination
+   case. */
 static void
-insert_mapping (struct case_map *map, size_t from, size_t to, size_t cnt)
+insert_mapping (struct case_map *map, size_t from, size_t to)
 {
-  size_t i;
-
-  assert (to + cnt <= map->value_cnt);
-  for (i = 0; i < cnt; i++)
-    {
-      assert (map->map[to + i] == -1);
-      map->map[to + i] = from + i;
-    }
+  assert (to < caseproto_get_n_widths (map->proto));
+  assert (map->map[to] == -1);
+  map->map[to] = from;
 }
 
 /* Destroys case map MAP. */
@@ -73,33 +76,101 @@ case_map_destroy (struct case_map *map)
 {
   if (map != NULL)
     {
+      caseproto_unref (map->proto);
       free (map->map);
       free (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);
+      size_t n_values = caseproto_get_n_widths (map->proto);
+      struct ccase *dst;
+      size_t dst_idx;
+
+      dst = case_create (map->proto);
+      for (dst_idx = 0; dst_idx < n_values; 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 prototype for output cases created by MAP.  The
+   caller must not unref the returned case prototype. */
+const struct caseproto *
+case_map_get_proto (const struct case_map *map)
+{
+  return map->proto;
+}
+
+/* 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_proto (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_proto (map),
+                                         translate_case,
+                                         destroy_case_map,
+                                         map);
 }
 
-/* Returns the number of `union value's in cases created by
-   MAP. */
-size_t
-case_map_get_value_cnt (const struct case_map *map)
+/* Casereader/casewriter translation callback. */
+static struct ccase *
+translate_case (struct ccase *input, void *map_)
 {
-  return map->value_cnt;
+  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
@@ -116,31 +187,25 @@ struct case_map *
 case_map_to_compact_dict (const struct dictionary *d,
                           unsigned int exclude_classes)
 {
-  size_t var_cnt;
+  size_t n_vars = dict_get_var_cnt (d);
+  struct caseproto *proto;
   struct case_map *map;
-  size_t value_idx;
+  size_t n_values;
   size_t i;
 
-  assert ((exclude_classes & ~((1u << DC_ORDINARY)
-                               | (1u << DC_SYSTEM)
-                               | (1u << DC_SCRATCH))) == 0);
+  /* Create the case mapping. */
+  proto = dict_get_compacted_proto (d, exclude_classes);
+  map = create_case_map (proto);
+  caseproto_unref (proto);
 
-  map = create_case_map (dict_count_values (d, exclude_classes));
-  var_cnt = dict_get_var_cnt (d);
-  value_idx = 0;
-  for (i = 0; i < var_cnt; i++)
+  /* Add the values to the case mapping. */
+  n_values = 0;
+  for (i = 0; i < n_vars; i++)
     {
       struct variable *v = dict_get_var (d, i);
-      enum dict_class class = dict_class_from_id (var_get_name (v));
-
-      if (!(exclude_classes & (1u << class)))
-        {
-          size_t value_cnt = var_get_value_cnt (v);
-          insert_mapping (map, var_get_case_index (v), value_idx, value_cnt);
-          value_idx += value_cnt;
-        }
+      if (!(exclude_classes & (1u << var_get_dict_class (v))))
+        insert_mapping (map, var_get_case_index (v), n_values++);
     }
-  assert (value_idx == map->value_cnt);
 
   return map;
 }
@@ -179,20 +244,20 @@ case_map_from_dict (const struct dictionary *d)
 {
   struct case_map *map;
   size_t var_cnt = dict_get_var_cnt (d);
+  size_t n_values;
   size_t i;
   bool identity_map = true;
 
-  map = create_case_map (dict_get_next_value_idx (d));
+  map = create_case_map (dict_get_proto (d));
   for (i = 0; i < var_cnt; i++)
     {
       struct variable *v = dict_get_var (d, i);
-      size_t value_cnt = var_get_value_cnt (v);
-      int *src_fv = (int *) var_detach_aux (v);
+      int *src_fv = var_detach_aux (v);
 
       if (var_get_case_index (v) != *src_fv)
         identity_map = false;
 
-      insert_mapping (map, *src_fv, var_get_case_index (v), value_cnt);
+      insert_mapping (map, *src_fv, var_get_case_index (v));
 
       free (src_fv);
     }
@@ -203,8 +268,9 @@ case_map_from_dict (const struct dictionary *d)
       return NULL;
     }
 
-  while (map->value_cnt > 0 && map->map[map->value_cnt - 1] == -1)
-    map->value_cnt--;
+  n_values = caseproto_get_n_widths (map->proto);
+  while (n_values > 0 && caseproto_get_width (map->proto, n_values - 1) == -1)
+    map->proto = caseproto_remove_widths (map->proto, --n_values, 1);
 
   return map;
 }
@@ -221,14 +287,13 @@ case_map_by_name (const struct dictionary *old,
   size_t var_cnt = dict_get_var_cnt (new);
   size_t i;
 
-  map = create_case_map (dict_get_next_value_idx (new));
+  map = create_case_map (dict_get_proto (new));
   for (i = 0; i < var_cnt; i++)
     {
       struct variable *nv = dict_get_var (new, i);
       struct variable *ov = dict_lookup_var_assert (old, var_get_name (nv));
       assert (var_get_width (nv) == var_get_width (ov));
-      insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv),
-                      var_get_value_cnt (ov));
+      insert_mapping (map, var_get_case_index (ov), var_get_case_index (nv));
     }
   return map;
 }
@@ -239,6 +304,6 @@ void
 case_map_dump (const struct case_map *cm)
 {
   int i;
-  for (i = 0 ; i < cm->value_cnt; ++i )
+  for (i = 0 ; i < caseproto_get_n_widths (cm->proto); ++i )
     printf ("%d -> %d\n", i, cm->map[i]);
 }
index 86d448ec9d8aefe6b45efdf9463313e212814cf4..cefbe7667a2fbf53a288aac0dc854cc68d0299f8 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 *);
+
+const struct caseproto *case_map_get_proto (const struct case_map *);
 
-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. */
diff --git a/src/data/case-matcher.c b/src/data/case-matcher.c
new file mode 100644 (file)
index 0000000..37cb4a6
--- /dev/null
@@ -0,0 +1,163 @@
+/* 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 (sizeof *cm->by_values
+                               * subcase_get_n_fields (by));
+      caseproto_init_values (subcase_get_proto (by), 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;
+
+      if (cm->by_values != NULL)
+        {
+          caseproto_destroy_values (subcase_get_proto (&cm->inputs[0].by_vars),
+                                    cm->by_values);
+          free (cm->by_values);
+        }
+      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..5744786611a737fb467f8f7c4c9673164696cc40 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 <libpspp/assertion.h>
 #include <libpspp/taint.h>
+#include <libpspp/tmpfile.h>
 
 #include "error.h"
 #include "xalloc.h"
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
 /* A temporary file that stores an array of cases. */
 struct case_tmpfile
   {
     struct taint *taint;        /* Taint. */
-    FILE *file;                 /* Underlying file. */
-    size_t value_cnt;           /* Number of `union value's per case. */
-
-    /* Current byte offset in file.  We track this manually,
-       instead of using ftello, because in glibc ftello flushes
-       the stream buffer, making the common case of sequential
-       access to cases unreasonably slow. */
-    off_t position;
+    struct caseproto *proto;    /* Format of cases in the tmpfile. */
+    size_t case_size;           /* Number of bytes per case. */
+    size_t *offsets;            /* Offset to each value. */
+    struct tmpfile *tmpfile;    /* Temporary file. */
   };
 
-/* Creates and returns a new case_tmpfile. */
+/* Returns the number of bytes needed to store a value with the
+   given WIDTH on disk. */
+static size_t
+width_to_n_bytes (int width)
+{
+  return width == 0 ? sizeof (double) : width;
+}
+
+/* Returns the address of the data in VALUE (for reading or
+   writing to/from disk).  VALUE must have the given WIDTH. */
+static void *
+value_to_data (const union value *value_, int width)
+{
+  union value *value = (union value *) value_;
+  assert (sizeof value->f == sizeof (double));
+  if (width == 0)
+    return &value->f;
+  else
+    return value_str_rw (value, width);
+}
+
+/* Creates and returns a new case_tmpfile that will store cases
+   that match case prototype PROTO.  The caller retains
+   ownership of PROTO. */
 struct case_tmpfile *
-case_tmpfile_create (size_t value_cnt)
+case_tmpfile_create (const struct caseproto *proto)
 {
-  struct case_tmpfile *ctf = xmalloc (sizeof *ctf);
+  struct case_tmpfile *ctf;
+  size_t n_values;
+  size_t i;
+
+  ctf = xmalloc (sizeof *ctf);
   ctf->taint = taint_create ();
-  ctf->file = tmpfile ();
-  if (ctf->file == NULL)
+  ctf->tmpfile = tmpfile_create ();
+  ctf->proto = caseproto_ref (proto);
+  ctf->case_size = 0;
+  n_values = caseproto_get_n_widths (proto);
+  ctf->offsets = xmalloc (n_values * sizeof *ctf->offsets);
+  for (i = 0; i < n_values; i++)
     {
-      error (0, errno, _("failed to create temporary file"));
-      taint_set_taint (ctf->taint);
+      size_t width = caseproto_get_width (proto, i);
+      ctf->offsets[i] = ctf->case_size;
+      ctf->case_size += width == -1 ? 0 : width == 0 ? sizeof (double) : width;
     }
-  ctf->value_cnt = value_cnt;
-  ctf->position = 0;
   return ctf;
 }
 
@@ -73,8 +97,9 @@ case_tmpfile_destroy (struct case_tmpfile *ctf)
   if (ctf != NULL)
     {
       struct taint *taint = ctf->taint;
-      if (ctf->file != NULL)
-        fclose (ctf->file);
+      tmpfile_destroy (ctf->tmpfile);
+      caseproto_unref (ctf->proto);
+      free (ctf->offsets);
       free (ctf);
       ok = taint_destroy (taint);
     }
@@ -104,137 +129,77 @@ case_tmpfile_get_taint (const struct case_tmpfile *ctf)
   return ctf->taint;
 }
 
-/* Seeks CTF's underlying file to the start of `union value'
-   VALUE_IDX within case CASE_IDX.
-   Returns true if the seek is successful and CTF is not
-   otherwise tainted, false otherwise. */
-static bool
-do_seek (const struct case_tmpfile *ctf_,
-         casenumber case_idx, size_t value_idx)
-{
-  struct case_tmpfile *ctf = (struct case_tmpfile *) ctf_;
-
-  if (!case_tmpfile_error (ctf))
-    {
-      off_t value_ofs = value_idx + (off_t) ctf->value_cnt * case_idx;
-      off_t byte_ofs = sizeof (union value) * value_ofs;
-
-      if (ctf->position == byte_ofs)
-        return true;
-      else if (fseeko (ctf->file, byte_ofs, SEEK_SET) == 0)
-        {
-          ctf->position = byte_ofs;
-          return true;
-        }
-      else
-        {
-          error (0, errno, _("seeking in temporary file"));
-          case_tmpfile_force_error (ctf);
-        }
-    }
-
-  return false;
-}
-
-/* Reads BYTES bytes from CTF's underlying file into BUFFER.
-   CTF must not be tainted upon entry into this function.
-   Returns true if successful, false upon an I/O error (in which
-   case CTF is marked tainted). */
-static bool
-do_read (const struct case_tmpfile *ctf_, size_t bytes, void *buffer)
-{
-  struct case_tmpfile *ctf = (struct case_tmpfile *) ctf_;
-
-  assert (!case_tmpfile_error (ctf));
-  if (fread (buffer, bytes, 1, ctf->file) != 1)
-    {
-      case_tmpfile_force_error (ctf);
-      if (ferror (ctf->file))
-        error (0, errno, _("reading temporary file"));
-      else if (feof (ctf->file))
-        error (0, 0, _("unexpected end of file reading temporary file"));
-      else
-        NOT_REACHED ();
-      return false;
-    }
-  ctf->position += bytes;
-  return true;
-}
-
-/* Writes BYTES bytes from BUFFER into CTF's underlying file.
-   CTF must not be tainted upon entry into this function.
-   Returns true if successful, false upon an I/O error (in which
-   case CTF is marked tainted). */
-static bool
-do_write (struct case_tmpfile *ctf, size_t bytes, const void *buffer)
-{
-  assert (!case_tmpfile_error (ctf));
-  if (fwrite (buffer, bytes, 1, ctf->file) != 1)
-    {
-      case_tmpfile_force_error (ctf);
-      error (0, errno, _("writing to temporary file"));
-      return false;
-    }
-  ctf->position += bytes;
-  return true;
-}
-
-/* Reads VALUE_CNT values into VALUES, from the case numbered
-   CASE_IDX starting START_VALUE values into that case.
-   Returns true if successful, false if CTF is tainted or an I/O
-   error occurs during the operation.
+/* Reads N_VALUES values into VALUES, from the case numbered
+   CASE_IDX starting START_VALUE values into that case.  Returns
+   true if successful, false if CTF is tainted or an I/O error
+   occurs during the operation.
 
    The results of this function are undefined if any of the
    values read have not been previously written to CTF. */
 bool
 case_tmpfile_get_values (const struct case_tmpfile *ctf,
                          casenumber case_idx, size_t start_value,
-                         union value values[], size_t value_cnt)
+                         union value values[], size_t n_values)
 {
-  assert (value_cnt <= ctf->value_cnt);
-  assert (value_cnt + start_value <= ctf->value_cnt);
+  off_t case_offset = (off_t) ctf->case_size * case_idx;
+  size_t i;
 
-  return (do_seek (ctf, case_idx, start_value)
-          && do_read (ctf, sizeof *values * value_cnt, values));
+  assert (caseproto_range_is_valid (ctf->proto, start_value, n_values));
+  for (i = start_value; i < start_value + n_values; i++)
+    {
+      int width = caseproto_get_width (ctf->proto, i);
+      if (width != -1
+          && !tmpfile_read (ctf->tmpfile, case_offset + ctf->offsets[i],
+                            width_to_n_bytes (width),
+                            value_to_data (&values[i], width)))
+          return false;
+    }
+  return true;
 }
 
-/* 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);
-  if (case_tmpfile_get_values (ctf, case_idx, 0,
-                               case_data_all_rw (c), ctf->value_cnt))
-    return true;
+  struct ccase *c = case_create (ctf->proto);
+  if (case_tmpfile_get_values (ctf, case_idx, 0, case_data_all_rw (c),
+                               caseproto_get_n_widths (ctf->proto)))
+    return c;
   else
     {
-      case_destroy (c);
-      case_nullify (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 }
 
-/* Writes VALUE_CNT values from VALUES, into the case numbered
+/* Writes N_VALUES values from VALUES, into the case numbered
    CASE_IDX starting START_VALUE values into that case.
    Returns true if successful, false if CTF is tainted or an I/O
    error occurs during the operation. */
 bool
 case_tmpfile_put_values (struct case_tmpfile *ctf,
                          casenumber case_idx, size_t start_value,
-                         const union value values[], size_t value_cnt)
-
+                         const union value values[], size_t n_values)
 {
-  assert (value_cnt <= ctf->value_cnt);
-  assert (value_cnt + start_value <= ctf->value_cnt);
+  off_t case_offset = (off_t) ctf->case_size * case_idx;
+  size_t i;
 
-  return (do_seek (ctf, case_idx, start_value)
-          && do_write (ctf, sizeof *values * value_cnt, values));
+  assert (caseproto_range_is_valid (ctf->proto, start_value, n_values));
+  for (i = start_value; i < start_value + n_values; i++)
+    {
+      int width = caseproto_get_width (ctf->proto, i);
+      if (width != -1
+          && !tmpfile_write (ctf->tmpfile, case_offset + ctf->offsets[i],
+                             width_to_n_bytes (width),
+                             value_to_data (values++, width)))
+          return false;
+    }
+  return true;
 }
 
 /* Writes C to CTF as the case numbered CASE_IDX.
@@ -244,9 +209,9 @@ bool
 case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx,
                        struct ccase *c)
 {
-  bool ok = case_tmpfile_put_values (ctf, case_idx, 0,
-                                     case_data_all (c), ctf->value_cnt);
-  case_destroy (c);
+  bool ok = case_tmpfile_put_values (ctf, case_idx, 0, case_data_all (c),
+                                     caseproto_get_n_widths (ctf->proto));
+  case_unref (c);
   return ok;
 }
 
index 3a92debc6e8e1b40be4e98342b96cd50a3487629..bbf736e3d13080f365c2932e4d65294ef63d2549 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
@@ -31,7 +31,9 @@
 
 #include <data/case.h>
 
-struct case_tmpfile *case_tmpfile_create (size_t value_cnt);
+struct caseproto;
+
+struct case_tmpfile *case_tmpfile_create (const struct caseproto *);
 bool case_tmpfile_destroy (struct case_tmpfile *);
 
 bool case_tmpfile_error (const struct case_tmpfile *);
@@ -41,8 +43,7 @@ const struct taint *case_tmpfile_get_taint (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..dc4029268ebf981bc10d96ecccf7c88eb53dd71a 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
 
 #include <data/case.h>
 
-#include <assert.h>
 #include <limits.h>
+#include <stddef.h>
 #include <stdlib.h>
 
 #include <data/value.h>
 #include <data/variable.h>
+#include <libpspp/assertion.h>
 #include <libpspp/str.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. */
-  };
+static size_t case_size (const struct caseproto *);
+static bool variable_matches_case (const struct ccase *,
+                                   const struct variable *);
+static void copy_forward (struct ccase *dst, size_t dst_idx,
+                          const struct ccase *src, size_t src_idx,
+                          size_t n_values);
+static void copy_backward (struct ccase *dst, size_t dst_idx,
+                           const struct ccase *src, size_t src_idx,
+                           size_t n_values);
+
+/* Creates and returns a new case that stores data of the form
+   specified by PROTO.  The data in the case have indeterminate
+   contents until explicitly written.
+
+   The caller retains ownership of PROTO. */
+struct ccase *
+case_create (const struct caseproto *proto)
+{
+  struct ccase *c = case_try_create (proto);
+  if (c == NULL)
+    xalloc_die ();
+  return c;
+}
 
-/* Ensures that C does not share data with any other case. */
-static void
-case_unshare (struct ccase *c)
+/* Like case_create, but returns a null pointer if not enough
+   memory is available. */
+struct ccase *
+case_try_create (const struct caseproto *proto)
 {
-  if (c->case_data->ref_cnt > 1)
+  struct ccase *c = malloc (case_size (proto));
+  if (c != NULL)
     {
-      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);
+      if (caseproto_try_init_values (proto, c->values))
+        {
+          c->proto = caseproto_ref (proto);
+          c->ref_cnt = 1;
+          return c;
+        }
+      free (c);
     }
+  return NULL;
 }
 
-/* Returns the number of bytes needed by a case with VALUE_CNT
-   values. */
-static size_t
-case_size (size_t value_cnt)
+/* Creates and returns an unshared copy of case C. */
+struct ccase *
+case_clone (const struct ccase *c)
 {
-  return (offsetof (struct case_data, values)
-          + value_cnt * sizeof (union value));
+  return case_unshare (case_ref (c));
 }
 
-/* Initializes C as a null case. */
-void
-case_nullify (struct ccase *c)
+/* Returns an estimate of the number of bytes of memory that
+   would be consumed in creating a case based on PROTO.  The
+   estimate includes typical overhead from malloc() in addition
+   to the actual size of data. */
+size_t
+case_get_cost (const struct caseproto *proto)
 {
-  c->case_data = NULL;
+  /* FIXME: improve approximation? */
+  return (1 + caseproto_get_n_widths (proto)
+          + 3 * caseproto_get_n_long_strings (proto)) * sizeof (union value);
 }
 
-/* Returns true iff C is a null case. */
-bool
-case_is_null (const struct ccase *c)
-{
-  return c->case_data == NULL;
-}
+/* Changes the prototype for case C, which must not be shared.
+   The new PROTO must be conformable with C's current prototype
+   (as defined by caseproto_is_conformable).
 
-/* Initializes C as a new case that can store VALUE_CNT values.
-   The values have indeterminate contents until explicitly
-   written. */
-void
-case_create (struct ccase *c, size_t value_cnt)
-{
-  if (!case_try_create (c, value_cnt))
-    xalloc_die ();
-}
+   Any new values created by this function have indeterminate
+   content that the caller is responsible for initializing.
 
-/* Initializes CLONE as a copy of ORIG. */
-void
-case_clone (struct ccase *clone, const struct ccase *orig)
-{
-  assert (orig->case_data->ref_cnt > 0);
-
-  if (clone != orig)
-    *clone = *orig;
-  orig->case_data->ref_cnt++;
-#ifdef DEBUGGING
-  case_unshare (clone);
-#endif
-}
+   The caller retains ownership of PROTO.
 
-/* 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)
+   Returns a new case that replaces C, which is freed. */
+struct ccase *
+case_resize (struct ccase *c, const struct caseproto *new_proto)
 {
-  assert (src->case_data->ref_cnt > 0);
+  struct caseproto *old_proto = c->proto;
+  size_t old_n_widths = caseproto_get_n_widths (old_proto);
+  size_t new_n_widths = caseproto_get_n_widths (new_proto);
 
-  if (dst != src)
+  assert (!case_is_shared (c));
+  expensive_assert (caseproto_is_conformable (old_proto, new_proto));
+
+  if (old_n_widths != new_n_widths)
     {
-      *dst = *src;
-      case_nullify (src);
+      if (new_n_widths < old_n_widths)
+        caseproto_reinit_values (old_proto, new_proto, c->values);
+      c = xrealloc (c, case_size (new_proto));
+      if (new_n_widths > old_n_widths)
+        caseproto_reinit_values (old_proto, new_proto, c->values);
+
+      caseproto_unref (old_proto);
+      c->proto = caseproto_ref (new_proto);
     }
+
+  return c;
 }
 
-/* Destroys case C. */
-void
-case_destroy (struct ccase *c)
-{
-  struct case_data *cd;
+/* case_unshare_and_resize(C, PROTO) is equivalent to
+   case_resize(case_unshare(C), PROTO), but it is faster if case
+   C is shared.
 
-  cd = c->case_data;
-  if (cd != NULL && --cd->ref_cnt == 0)
-    {
-      memset (cd->values, 0xcc, sizeof *cd->values * cd->value_cnt);
-      cd->value_cnt = 0xdeadbeef;
-      free (cd);
-    }
-}
+   Any new values created by this function have indeterminate
+   content that the caller is responsible for initializing.
 
-/* Returns the number of union values in C. */
-size_t
-case_get_value_cnt (const struct ccase *c)
-{
-  return c->case_data->value_cnt;
-}
+   The caller retains ownership of PROTO.
 
-/* Resizes case C to NEW_CNT union values. */
-void
-case_resize (struct ccase *c, size_t new_cnt)
+   Returns the new case that replaces C, which is freed. */
+struct ccase *
+case_unshare_and_resize (struct ccase *c, const struct caseproto *proto)
 {
-  size_t old_cnt = case_get_value_cnt (c);
-  if (old_cnt != new_cnt)
+  if (!case_is_shared (c))
+    return case_resize (c, proto);
+  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 (proto);
+      size_t old_n_values = caseproto_get_n_widths (c->proto);
+      size_t new_n_values = caseproto_get_n_widths (proto);
+      case_copy (new, 0, c, 0, MIN (old_n_values, new_n_values));
+      c->ref_cnt--;
+      return new;
     }
 }
 
-/* Swaps cases A and B. */
+/* Sets all of the numeric values in case C to the system-missing
+   value, and all of the string values to spaces. */
 void
-case_swap (struct ccase *a, struct ccase *b)
+case_set_missing (struct ccase *c)
 {
-  struct case_data *t = a->case_data;
-  a->case_data = b->case_data;
-  b->case_data = t;
-}
+  size_t i;
 
-/* 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;
+  assert (!case_is_shared (c));
+  for (i = 0; i < caseproto_get_n_widths (c->proto); i++)
+    value_set_missing (&c->values[i], caseproto_get_width (c->proto, i));
 }
 
-/* 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 N_VALUES values from SRC (starting at SRC_IDX) to DST
+   (starting at DST_IDX).  Each value that is copied into must
+   have the same width as the value that it is copied from.
 
-/* Copies VALUE_CNT values from SRC (starting at SRC_IDX) to DST
-   (starting at DST_IDX). */
+   Properly handles overlapping ranges when DST == SRC.
+
+   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 (src->case_data->ref_cnt > 0);
-  assert (src_idx + value_cnt <= src->case_data->value_cnt);
+  assert (!case_is_shared (dst));
+  assert (caseproto_range_is_valid (dst->proto, dst_idx, n_values));
+  assert (caseproto_range_is_valid (src->proto, src_idx, n_values));
+  assert (caseproto_equal (dst->proto, dst_idx, src->proto, src_idx,
+                           n_values));
 
-  if (dst->case_data != src->case_data || dst_idx != src_idx)
+  if (dst != src)
     {
-      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->proto->n_long_strings || !src->proto->n_long_strings)
+        memcpy (&dst->values[dst_idx], &src->values[src_idx],
+                sizeof dst->values[0] * n_values);
+      else
+        copy_forward (dst, dst_idx, src, src_idx, n_values);
+    }
+  else if (dst_idx != src_idx)
+    {
+      if (!dst->proto->n_long_strings)
+        memmove (&dst->values[dst_idx], &src->values[src_idx],
+                 sizeof dst->values[0] * n_values);
+      else if (dst_idx < src_idx)
+        copy_forward (dst, dst_idx, src, src_idx, n_values);
+      else /* dst_idx > src_idx */
+        copy_backward (dst, dst_idx, src, src_idx, 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);
+  size_t i;
 
-  memcpy (values, c->case_data->values + start_idx,
-          value_cnt * sizeof *values);
+  assert (caseproto_range_is_valid (c->proto, start_idx, n_values));
+
+  for (i = 0; i < n_values; i++)
+    value_copy (&values[i], &c->values[start_idx + i],
+                caseproto_get_width (c->proto, start_idx + i));
 }
 
-/* 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);
+  size_t i;
+
+  assert (!case_is_shared (c));
+  assert (caseproto_range_is_valid (c->proto, start_idx, n_values));
 
-  case_unshare (c);
-  memcpy (c->case_data->values + start_idx, values,
-          value_cnt * sizeof *values);
+  for (i = 0; i < n_values; i++)
+    value_copy (&c->values[start_idx + i], &values[i],
+                caseproto_get_width (c->proto, start_idx + i));
 }
 
 /* Returns a pointer to the `union value' used for the
@@ -244,49 +243,54 @@ case_copy_in (struct ccase *c,
 const union value *
 case_data (const struct ccase *c, const struct variable *v)
 {
-  return case_data_idx (c, var_get_case_index (v));
+  assert (variable_matches_case (c, v));
+  return &c->values[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->proto->n_widths);
+  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)
 {
-  return case_data_rw_idx (c, var_get_case_index (v));
+  assert (variable_matches_case (c, v));
+  assert (!case_is_shared (c));
+  return &c->values[var_get_case_index (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 (idx < c->proto->n_widths);
+  assert (!case_is_shared (c));
+  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)
+{
+  assert (variable_matches_case (c, v));
+  return c->values[var_get_case_index (v)].f;
 }
 
 /* Returns the numeric value of the `union value' in C numbered
@@ -294,107 +298,186 @@ 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);
+  assert (idx < c->proto->n_widths);
+  return c->values[idx].f;
+}
+
+/* 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.
 
-  return c->case_data->values[idx].f;
+   Like the strings embedded in all "union value"s, the return
+   value is not null-terminated. */
+const uint8_t *
+case_str (const struct ccase *c, const struct variable *v)
+{
+  size_t idx = var_get_case_index (v);
+  assert (variable_matches_case (c, v));
+  return value_str (&c->values[idx], caseproto_get_width (c->proto, idx));
 }
 
 /* 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. */
-const char *
+   IDX.  The caller must not modify the return value.
+
+   Like the strings embedded in all "union value"s, the return
+   value is not null-terminated. */
+const uint8_t *
 case_str_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].s;
+  assert (idx < c->proto->n_widths);
+  return value_str (&c->values[idx], caseproto_get_width (c->proto, idx));
 }
 
-/* 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)
+/* Returns the string value of the `union value' in C for
+   variable V.  Case C must be drawn from V's dictionary.  The
+   caller may modify the return value.
+
+   Case C must not be shared.
+
+   Like the strings embedded in all "union value"s, the return
+   value is not null-terminated. */
+uint8_t *
+case_str_rw (struct ccase *c, const struct variable *v)
 {
-  assert (c->case_data->ref_cnt > 0);
-  assert (idx < c->case_data->value_cnt);
+  size_t idx = var_get_case_index (v);
+  assert (variable_matches_case (c, v));
+  assert (!case_is_shared (c));
+  return value_str_rw (&c->values[idx], caseproto_get_width (c->proto, idx));
+}
+
+/* Returns the string value of the `union value' in C numbered
+   IDX.  The caller may modify the return value.
 
-  case_unshare (c);
-  return &c->case_data->values[idx];
+   Case C must not be shared.
+
+   Like the strings embedded in all "union value"s, the return
+   value is not null-terminated. */
+uint8_t *
+case_str_rw_idx (struct ccase *c, size_t idx)
+{
+  assert (idx < c->proto->n_widths);
+  assert (!case_is_shared (c));
+  return value_str_rw (&c->values[idx], caseproto_get_width (c->proto, idx));
 }
 
-/* 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++)
+  int cmp = 0;
+  for (; !cmp && n_vars-- > 0; vap++, vbp++)
     {
-      const struct variable *va = *vap;
-      const struct variable *vb = *vbp;
-
-      assert (var_get_width (va) == var_get_width (vb));
-
-      if (var_get_width (va) == 0)
-        {
-          double af = case_num (ca, va);
-          double bf = case_num (cb, vb);
-
-          if (af != bf)
-            return af > bf ? 1 : -1;
-        }
-      else
-        {
-          const char *as = case_str (ca, va);
-          const char *bs = case_str (cb, vb);
-          int cmp = memcmp (as, bs, var_get_width (va));
-
-          if (cmp != 0)
-            return cmp;
-        }
+      const union value *va = case_data (ca, *vap);
+      const union value *vb = case_data (cb, *vbp);
+      assert (var_get_width (*vap) == var_get_width (*vbp));
+      cmp = value_compare_3way (va, vb, var_get_width (*vap)); 
     }
-  return 0;
+  return cmp;
 }
 
 /* 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;
+}
+
+/* Internal helper function for case_unshare. */
+struct ccase *
+case_unshare__ (struct ccase *old)
+{
+  struct ccase *new = case_create (old->proto);
+  case_copy (new, 0, old, 0, caseproto_get_n_widths (new->proto));
+  --old->ref_cnt;
+  return new;
+}
 
-  case_unshare (c);
-  return c->case_data->values;
+/* Internal helper function for case_unref. */
+void
+case_unref__ (struct ccase *c)
+{
+  caseproto_destroy_values (c->proto, c->values);
+  caseproto_unref (c->proto);
+  free (c);
 }
+\f
+/* Returns the number of bytes needed by a case for case
+   prototype PROTO. */
+static size_t
+case_size (const struct caseproto *proto)
+{
+  return (offsetof (struct ccase, values)
+          + caseproto_get_n_widths (proto) * sizeof (union value));
+}
+
+/* Returns true if C contains a value at V's case index with the
+   same width as V; that is, if V may plausibly be used to read
+   or write data in C.
+
+   Useful in assertions. */
+static bool UNUSED
+variable_matches_case (const struct ccase *c, const struct variable *v)
+{
+  size_t case_idx = var_get_case_index (v);
+  return (case_idx < caseproto_get_n_widths (c->proto)
+          && caseproto_get_width (c->proto, case_idx) == var_get_width (v));
+}
+
+/* Internal helper function for case_copy(). */
+static void
+copy_forward (struct ccase *dst, size_t dst_idx,
+              const struct ccase *src, size_t src_idx,
+              size_t n_values)
+{
+  size_t i;
+
+  for (i = 0; i < n_values; i++)
+    value_copy (&dst->values[dst_idx + i], &src->values[src_idx + i],
+                caseproto_get_width (dst->proto, dst_idx + i));
+}
+
+/* Internal helper function for case_copy(). */
+static void
+copy_backward (struct ccase *dst, size_t dst_idx,
+               const struct ccase *src, size_t src_idx,
+               size_t n_values)
+{
+  size_t i;
+
+  for (i = n_values; i-- != 0; )
+    value_copy (&dst->values[dst_idx + i], &src->values[src_idx + i],
+                caseproto_get_width (dst->proto, dst_idx + i));
+}
+
index aa5b9dd0d7c99397ebcb00c7ef908f6ee6a4d719..0bfc62cdce96054fe461043ac2cb44bf39436c03 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,7 +20,9 @@
 #include <limits.h>
 #include <stddef.h>
 #include <stdbool.h>
-#include "value.h"
+#include <stdlib.h>
+#include <libpspp/compiler.h>
+#include <data/caseproto.h>
 
 struct variable;
 
@@ -29,57 +31,148 @@ 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. */
+    struct caseproto *proto;    /* Case prototype. */
+    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 (const struct caseproto *) MALLOC_LIKE;
+struct ccase *case_try_create (const struct caseproto *) MALLOC_LIKE;
+struct ccase *case_clone (const struct ccase *) 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 *);
+static inline const struct caseproto *case_get_proto (const struct ccase *);
 
-void case_resize (struct ccase *, size_t new_cnt);
-void case_swap (struct ccase *, struct ccase *);
+size_t case_get_cost (const struct caseproto *);
 
-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 *, const struct caseproto *)
+  WARN_UNUSED_RESULT;
+struct ccase *case_unshare_and_resize (struct ccase *,
+                                       const struct caseproto *)
+  WARN_UNUSED_RESULT;
+
+void case_set_missing (struct ccase *);
 
 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_idx (const struct ccase *, size_t idx);
-union value *case_data_rw_idx (struct ccase *, size_t idx);
+
+const uint8_t *case_str (const struct ccase *, const struct variable *);
+const uint8_t *case_str_idx (const struct ccase *, size_t idx);
+uint8_t *case_str_rw (struct ccase *, const struct variable *);
+uint8_t *case_str_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 *);
+void case_unref__ (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)
+    case_unref__ (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 caseproto_get_n_widths (c->proto);
+}
+
+/* Returns the prototype that describes the format of case C.
+   The caller must not unref the returned prototype. */
+static inline const struct caseproto *
+case_get_proto (const struct ccase *c)
+{
+  return c->proto;
+}
 
 #endif /* data/case.h */
index f5974a404b0a6200729548a62a2d67afde684323..9e7ab157e1921057ba8aed5a720658df74677067 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,27 @@ 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);
+      writer = autopaging_writer_create (
+        casereader_get_proto (grouper->reader));
+      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 +158,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 +172,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 +199,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..572f9160292e670f1a60ae99c226b2ad13b064de 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,8 +38,9 @@
 /* Binds a value with a place to put it. */
 struct init_value
   {
-    union value value;
     size_t case_index;
+    int width;
+    union value value;
   };
 
 /* A set of values to initialize in a case. */
@@ -68,6 +69,10 @@ init_list_create (struct init_list *list)
 static void
 init_list_destroy (struct init_list *list)
 {
+  struct init_value *iv;
+
+  for (iv = &list->values[0]; iv < &list->values[list->cnt]; iv++)
+    value_destroy (&iv->value, iv->width);
   free (list->values);
 }
 
@@ -111,14 +116,13 @@ init_list_mark (struct init_list *list, const struct init_list *exclude,
   size_t i;
 
   assert (list != exclude);
-  list->values = xnrealloc (list->values,
-                            list->cnt + dict_get_next_value_idx (d),
+  list->values = xnrealloc (list->values, list->cnt + dict_get_var_cnt (d),
                             sizeof *list->values);
   for (i = 0; i < var_cnt; i++)
     {
       struct variable *v = dict_get_var (d, i);
       size_t case_index = var_get_case_index (v);
-      int offset;
+      struct init_value *iv;
 
       /* Only include the correct class. */
       if (!(include & (var_get_leave (v) ? LEAVE_LEFT : LEAVE_REINIT)))
@@ -128,19 +132,14 @@ init_list_mark (struct init_list *list, const struct init_list *exclude,
       if (exclude != NULL && init_list_includes (exclude, case_index))
         continue;
 
-      offset = 0;
-      do
-        {
-          struct init_value *iv = &list->values[list->cnt++];
-          iv->case_index = case_index++;
-          if (var_is_numeric (v))
-            iv->value.f = var_get_leave (v) ? 0 : SYSMIS;
-          else
-            memset (iv->value.s, ' ', sizeof iv->value.s);
-
-          offset += sizeof iv->value.s;
-        }
-      while (offset < var_get_width (v));
+      iv = &list->values[list->cnt++];
+      iv->case_index = case_index;
+      iv->width = var_get_width (v);
+      value_init (&iv->value, iv->width);
+      if (var_is_numeric (v) && var_get_leave (v))
+        iv->value.f = 0;
+      else
+        value_set_missing (&iv->value, iv->width);
     }
 
   /* Drop duplicates. */
@@ -153,13 +152,10 @@ init_list_mark (struct init_list *list, const struct init_list *exclude,
 static void
 init_list_init (const struct init_list *list, struct ccase *c)
 {
-  size_t i;
+  const struct init_value *iv;
 
-  for (i = 0; i < list->cnt; i++)
-    {
-      const struct init_value *value = &list->values[i];
-      *case_data_rw_idx (c, value->case_index) = value->value;
-    }
+  for (iv = &list->values[0]; iv < &list->values[list->cnt]; iv++)
+    value_copy (case_data_rw_idx (c, iv->case_index), &iv->value, iv->width);
 }
 
 /* Updates the values in the initializer LIST from the data in
@@ -167,13 +163,10 @@ init_list_init (const struct init_list *list, struct ccase *c)
 static void
 init_list_update (const struct init_list *list, const struct ccase *c)
 {
-  size_t i;
+  struct init_value *iv;
 
-  for (i = 0; i < list->cnt; i++)
-    {
-      struct init_value *value = &list->values[i];
-      value->value = *case_data_idx (c, value->case_index);
-    }
+  for (iv = &list->values[0]; iv < &list->values[list->cnt]; iv++)
+    value_copy (&iv->value, case_data_idx (c, iv->case_index), iv->width);
 }
 \f
 /* A case initializer. */
@@ -247,7 +240,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)
 {
diff --git a/src/data/caseproto.c b/src/data/caseproto.c
new file mode 100644 (file)
index 0000000..1a40213
--- /dev/null
@@ -0,0 +1,411 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <data/caseproto.h>
+#include <data/val-type.h>
+#include <data/value.h>
+#include <libpspp/array.h>
+#include <libpspp/assertion.h>
+#include <libpspp/pool.h>
+
+#include "minmax.h"
+
+static struct caseproto *caseproto_unshare (struct caseproto *);
+static bool try_init_long_strings (const struct caseproto *,
+                                   size_t first, size_t last, union value[]);
+static void init_long_strings (const struct caseproto *,
+                               size_t first, size_t last, union value[]);
+static void destroy_long_strings (const struct caseproto *,
+                                  size_t first, size_t last, union value[]);
+static size_t count_long_strings (const struct caseproto *,
+                                  size_t idx, size_t count);
+
+/* Returns the number of bytes to allocate for a struct caseproto
+   with room for N_WIDTHS elements in its widths[] array. */
+static inline size_t
+caseproto_size (size_t n_widths)
+{
+  return (offsetof (struct caseproto, widths)
+          + n_widths * sizeof (((struct caseproto *) NULL)->widths[0]));
+}
+
+/* Creates and returns a case prototype that initially has no
+   widths. */
+struct caseproto *
+caseproto_create (void)
+{
+  enum { N_ALLOCATE = 4 };
+  struct caseproto *proto = xmalloc (caseproto_size (N_ALLOCATE));
+  proto->ref_cnt = 1;
+  proto->long_strings = NULL;
+  proto->n_long_strings = 0;
+  proto->n_widths = 0;
+  proto->allocated_widths = N_ALLOCATE;
+  return proto;
+}
+
+static void
+do_unref (void *proto_)
+{
+  struct caseproto *proto = proto_;
+  caseproto_unref (proto);
+}
+
+/* Creates and returns a new reference to PROTO.  When POOL is
+   destroyed, the new reference will be destroyed (unrefed).  */
+struct caseproto *
+caseproto_ref_pool (const struct caseproto *proto_, struct pool *pool)
+{
+  struct caseproto *proto = caseproto_ref (proto_);
+  pool_register (pool, do_unref, proto);
+  return proto;
+}
+
+/* Returns a replacement for PROTO that is unshared and has
+   enough room for at least N_WIDTHS widths before additional
+   memory is needed.  */
+struct caseproto *
+caseproto_reserve (struct caseproto *proto, size_t n_widths)
+{
+  proto = caseproto_unshare (proto);
+  if (n_widths > proto->allocated_widths)
+    {
+      proto->allocated_widths *= MAX (proto->allocated_widths * 2, n_widths);
+      proto = xrealloc (proto, caseproto_size (proto->allocated_widths));
+    }
+  return proto;
+}
+
+/* Returns a replacement for PROTO with WIDTH appended.  */
+struct caseproto *
+caseproto_add_width (struct caseproto *proto, int width)
+{
+  assert (width >= -1 && width <= MAX_STRING);
+
+  proto = caseproto_reserve (proto, proto->n_widths + 1);
+  proto->widths[proto->n_widths++] = width;
+  proto->n_long_strings += count_long_strings (proto, proto->n_widths - 1, 1);
+
+  return proto;
+}
+
+/* Returns a replacement for PROTO with the width at index IDX
+   replaced by WIDTH.  IDX may be greater than the current number
+   of widths in PROTO, in which case any gap is filled in by
+   widths of -1. */
+struct caseproto *
+caseproto_set_width (struct caseproto *proto, size_t idx, int width)
+{
+  assert (width >= -1 && width <= MAX_STRING);
+
+  proto = caseproto_reserve (proto, idx + 1);
+  while (idx >= proto->n_widths)
+    proto->widths[proto->n_widths++] = -1;
+  proto->n_long_strings -= count_long_strings (proto, idx, 1);
+  proto->widths[idx] = width;
+  proto->n_long_strings += count_long_strings (proto, idx, 1);
+
+  return proto;
+}
+
+/* Returns a replacement for PROTO with WIDTH inserted just
+   before index BEFORE, or just after the last element if BEFORE
+   is the number of widths in PROTO. */
+struct caseproto *
+caseproto_insert_width (struct caseproto *proto, size_t before, int width)
+{
+  assert (before <= proto->n_widths);
+
+  proto = caseproto_reserve (proto, proto->n_widths + 1);
+  proto->n_long_strings += value_needs_init (width);
+  insert_element (proto->widths, proto->n_widths, sizeof *proto->widths,
+                  before);
+  proto->widths[before] = width;
+  proto->n_widths++;
+
+  return proto;
+}
+
+/* Returns a replacement for PROTO with CNT widths removed
+   starting at index IDX. */
+struct caseproto *
+caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t cnt)
+{
+  assert (caseproto_range_is_valid (proto, idx, cnt));
+
+  proto = caseproto_unshare (proto);
+  proto->n_long_strings -= count_long_strings (proto, idx, cnt);
+  remove_range (proto->widths, proto->n_widths, sizeof *proto->widths,
+                idx, cnt);
+  proto->n_widths -= cnt;
+  return proto;
+}
+
+/* Returns a replacement for PROTO in which the CNT widths
+   starting at index OLD_WIDTH now start at index NEW_WIDTH, with
+   other widths shifting out of the way to make room. */
+struct caseproto *
+caseproto_move_widths (struct caseproto *proto,
+                       size_t old_start, size_t new_start,
+                       size_t cnt)
+{
+  assert (caseproto_range_is_valid (proto, old_start, cnt));
+  assert (caseproto_range_is_valid (proto, new_start, cnt));
+
+  proto = caseproto_unshare (proto);
+  move_range (proto->widths, proto->n_widths, sizeof *proto->widths,
+              old_start, new_start, cnt);
+  return proto;
+}
+
+/* Returns true if PROTO contains COUNT widths starting at index
+   OFS, false if any of those widths are out of range for
+   PROTO. */
+bool
+caseproto_range_is_valid (const struct caseproto *proto,
+                          size_t ofs, size_t count)
+{
+  return (count <= proto->n_widths
+          && ofs <= proto->n_widths
+          && ofs + count <= proto->n_widths);
+}
+
+/* Returns true if A and B have the same widths along their
+   common length.  (When this is so, a case with prototype A may
+   be extended or truncated to have prototype B without having to
+   change any existing values, and vice versa.) */
+bool
+caseproto_is_conformable (const struct caseproto *a, const struct caseproto *b)
+{
+  size_t min;
+  size_t i;
+
+  min = MIN (a->n_widths, b->n_widths);
+  for (i = 0; i < min; i++)
+    if (a->widths[i] != b->widths[i])
+      return false;
+  return true;
+}
+
+/* Returns true if the N widths starting at A_START in A are the
+   same as the N widths starting at B_START in B, false if any of
+   the corresponding widths differ. */
+bool
+caseproto_equal (const struct caseproto *a, size_t a_start,
+                 const struct caseproto *b, size_t b_start,
+                 size_t n)
+{
+  size_t i;
+
+  assert (caseproto_range_is_valid (a, a_start, n));
+  assert (caseproto_range_is_valid (b, b_start, n));
+  for (i = 0; i < n; i++)
+    if (a->widths[a_start + i] != b->widths[b_start + i])
+      return false;
+  return true;
+}
+
+/* Returns true if an array of values that is to be used for
+   data of the format specified in PROTO needs to be initialized
+   by calling caseproto_init_values, false if that step may be
+   skipped because such an initialization would be a no-op anyhow.
+
+   This optimization is useful only when a large number of
+   initializations of such arrays may be skipped as a group. */
+bool
+caseproto_needs_init_values (const struct caseproto *proto)
+{
+  return proto->n_long_strings > 0;
+}
+
+/* Initializes the values in VALUES as required by PROTO, by
+   calling value_init() on each value for which this is required.
+   The data in VALUES have indeterminate contents until
+   explicitly written.
+
+   VALUES must have at least caseproto_get_n_widths(PROTO)
+   elements; only that many elements of VALUES are initialized.
+
+   The caller retains ownership of PROTO. */
+void
+caseproto_init_values (const struct caseproto *proto, union value values[])
+{
+  init_long_strings (proto, 0, proto->n_long_strings, values);
+}
+
+/* Like caseproto_init_values, but returns false instead of
+   terminating if memory cannot be obtained. */
+bool
+caseproto_try_init_values (const struct caseproto *proto, union value values[])
+{
+  return try_init_long_strings (proto, 0, proto->n_long_strings, values);
+}
+
+/* Initializes the data in VALUES that are in NEW but not in OLD,
+   destroys the data in VALUES that are in OLD but not NEW, and
+   does not modify the data in VALUES that are in both OLD and
+   NEW.  VALUES must previously have been initialized as required
+   by OLD using e.g. caseproto_init_values.  The data in VALUES
+   that are in NEW but not in OLD will have indeterminate
+   contents until explicitly written.
+
+   OLD and NEW must be conformable for this operation, as
+   reported by caseproto_is_conformable.
+
+   The caller retains ownership of OLD and NEW. */
+void
+caseproto_reinit_values (const struct caseproto *old,
+                         const struct caseproto *new, union value values[])
+{
+  size_t old_n_long = old->n_long_strings;
+  size_t new_n_long = new->n_long_strings;
+
+  expensive_assert (caseproto_is_conformable (old, new));
+
+  if (new_n_long > old_n_long)
+    init_long_strings (new, old_n_long, new_n_long, values);
+  else if (new_n_long < old_n_long)
+    destroy_long_strings (old, new_n_long, old_n_long, values);
+}
+
+/* Frees the values in VALUES as required by PROTO, by calling
+   value_destroy() on each value for which this is required.  The
+   values must previously have been initialized using
+   e.g. caseproto_init_values.
+
+   The caller retains ownership of PROTO. */
+void
+caseproto_destroy_values (const struct caseproto *proto, union value values[])
+{
+  destroy_long_strings (proto, 0, proto->n_long_strings, values);
+}
+
+/* Copies COUNT values, whose widths are given by widths in PROTO
+   starting with index IDX, from SRC to DST.  The caller must
+   ensure that the values in SRC and DST were appropriately
+   initialized using e.g. caseproto_init_values. */
+void
+caseproto_copy (const struct caseproto *proto, size_t idx, size_t count,
+                union value *dst, const union value *src)
+{
+  size_t i;
+
+  assert (caseproto_range_is_valid (proto, idx, count));
+  for (i = 0; i < count; i++)
+    value_copy (&dst[idx + i], &src[idx + i], proto->widths[idx + i]);
+}
+\f
+void
+caseproto_free__ (struct caseproto *proto)
+{
+  free (proto->long_strings);
+  free (proto);
+}
+
+void
+caseproto_refresh_long_string_cache__ (const struct caseproto *proto_)
+{
+  struct caseproto *proto = (struct caseproto *) proto_;
+  size_t n, i;
+
+  assert (proto->long_strings == NULL);
+  assert (proto->n_long_strings > 0);
+
+  proto->long_strings = xmalloc (proto->n_long_strings
+                                 * sizeof *proto->long_strings);
+  n = 0;
+  for (i = 0; i < proto->n_widths; i++)
+    if (proto->widths[i] > MAX_SHORT_STRING)
+      proto->long_strings[n++] = i;
+  assert (n == proto->n_long_strings);
+}
+
+static struct caseproto *
+caseproto_unshare (struct caseproto *old)
+{
+  struct caseproto *new;
+  if (old->ref_cnt > 1)
+    {
+      new = xmemdup (old, caseproto_size (old->allocated_widths));
+      new->ref_cnt = 1;
+      --old->ref_cnt;
+    }
+  else
+    {
+      new = old;
+      free (new->long_strings);
+    }
+  new->long_strings = NULL;
+  return new;
+}
+
+static bool
+try_init_long_strings (const struct caseproto *proto,
+                       size_t first, size_t last, union value values[])
+{
+  size_t i;
+
+  if (last > 0 && proto->long_strings == NULL)
+    caseproto_refresh_long_string_cache__ (proto);
+
+  for (i = first; i < last; i++)
+    {
+      size_t idx = proto->long_strings[i];
+      if (!value_try_init (&values[idx], proto->widths[idx]))
+        {
+          destroy_long_strings (proto, first, i, values);
+          return false;
+        }
+    }
+  return true;
+}
+
+static void
+init_long_strings (const struct caseproto *proto,
+                   size_t first, size_t last, union value values[])
+{
+  if (!try_init_long_strings (proto, first, last, values))
+    xalloc_die ();
+}
+
+static void
+destroy_long_strings (const struct caseproto *proto, size_t first, size_t last,
+                      union value values[])
+{
+  size_t i;
+
+  if (last > 0 && proto->long_strings == NULL)
+    caseproto_refresh_long_string_cache__ (proto);
+
+  for (i = first; i < last; i++)
+    {
+      size_t idx = proto->long_strings[i];
+      value_destroy (&values[idx], proto->widths[idx]);
+    }
+}
+
+static size_t
+count_long_strings (const struct caseproto *proto, size_t idx, size_t count)
+{
+  size_t n, i;
+
+  n = 0;
+  for (i = 0; i < count; i++)
+    n += proto->widths[idx + i] > MAX_SHORT_STRING;
+  return n;
+}
diff --git a/src/data/caseproto.h b/src/data/caseproto.h
new file mode 100644 (file)
index 0000000..b85a9f3
--- /dev/null
@@ -0,0 +1,209 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef DATA_CASEPROTO_H
+#define DATA_CASEPROTO_H 1
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <data/value.h>
+#include <libpspp/compiler.h>
+
+/* Case prototype.
+
+   A case prototype specifies the number and type of the values
+   in a case.  It is essentially an array of integers, where the
+   array index is an index into a case and each element
+   represents the width of a value in a case.  Valid widths are:
+
+       * 0, indicating a numeric value.
+
+       * A positive integer between 1 and 32767, indicating the
+         size in bytes of a string value.
+
+       * -1, indicating that the value at this index in the case
+         is not used at all.  (This is rarely useful.)
+
+   Case prototypes are reference counted.  A newly created case
+   prototype has a single owner (the code that created it),
+   represented by an initial reference count of 1.  Other code
+   that receives the case prototype may keep a virtual copy of it
+   by calling caseproto_ref, which increments the case
+   prototype's reference count.  When this is done, the case
+   prototype becomes shared between its original owner and each
+   piece of code that incremented the reference count.
+
+   Functions that modifying case prototypes automatically unshare
+   them as necessary.  All of these functions potentially move
+   the caseproto around in memory even when the case prototype is
+   not shared.  Thus it is very important that every caller of a
+   function that modifies a case prototype thereafter uses the returned
+   caseproto instead of the one passed in as an argument.
+
+   Only the case prototype code should refer to caseproto members
+   directly.  Other code should use the provided helper
+   functions. */
+struct caseproto
+  {
+    size_t ref_cnt;             /* Reference count. */
+
+    /* Tracking of long string widths.  Lazily maintained: when
+       'long_strings' is null and 'n_long_strings' is nonzero,
+       the former must be regenerated. */
+    size_t *long_strings;       /* Array of indexes of long string widths. */
+    size_t n_long_strings;      /* Number of long string widths. */
+
+    /* Widths. */
+    size_t n_widths;            /* Number of widths. */
+    size_t allocated_widths;    /* Space allocated for 'widths' array. */
+    short int widths[1];        /* Width of each case value. */
+  };
+
+struct pool;
+
+/* Creation and destruction. */
+struct caseproto *caseproto_create (void) MALLOC_LIKE;
+static inline struct caseproto *caseproto_ref (const struct caseproto *);
+struct caseproto *caseproto_ref_pool (const struct caseproto *, struct pool *);
+static inline void caseproto_unref (struct caseproto *);
+
+/* Inspecting stored widths.  */
+static inline int caseproto_get_width (const struct caseproto *, size_t idx);
+static inline size_t caseproto_get_n_widths (const struct caseproto *);
+
+/* Adding and removing widths. */
+struct caseproto *caseproto_reserve (struct caseproto *, size_t n_widths)
+  WARN_UNUSED_RESULT;
+struct caseproto *caseproto_add_width (struct caseproto *, int width)
+  WARN_UNUSED_RESULT;
+struct caseproto *caseproto_set_width (struct caseproto *,
+                                       size_t idx, int width)
+  WARN_UNUSED_RESULT;
+struct caseproto *caseproto_insert_width (struct caseproto *,
+                                          size_t before, int width)
+  WARN_UNUSED_RESULT;
+struct caseproto *caseproto_remove_widths (struct caseproto *,
+                                           size_t idx, size_t cnt)
+  WARN_UNUSED_RESULT;
+struct caseproto *caseproto_move_widths (struct caseproto *,
+                                         size_t old_start, size_t new_start,
+                                         size_t cnt)
+  WARN_UNUSED_RESULT;
+
+/* Working with "union value" arrays. */
+bool caseproto_needs_init_values (const struct caseproto *);
+void caseproto_init_values (const struct caseproto *, union value[]);
+bool caseproto_try_init_values (const struct caseproto *, union value[]);
+void caseproto_reinit_values (const struct caseproto *old,
+                              const struct caseproto *new, union value[]);
+void caseproto_destroy_values (const struct caseproto *, union value[]);
+
+void caseproto_copy (const struct caseproto *, size_t idx, size_t count,
+                     union value *dst, const union value *src);
+
+/* Inspecting the cache of long string widths.
+
+   (These functions are useful for allocating cases, which
+   requires allocating a block memory for each long string value
+   in the case.) */
+static inline size_t caseproto_get_n_long_strings (const struct caseproto *);
+static inline size_t caseproto_get_long_string_idx (const struct caseproto *,
+                                                    size_t idx1);
+
+/* For use in assertions. */
+bool caseproto_range_is_valid (const struct caseproto *,
+                               size_t ofs, size_t count);
+bool caseproto_is_conformable (const struct caseproto *a,
+                               const struct caseproto *b);
+bool caseproto_equal (const struct caseproto *a, size_t a_start,
+                      const struct caseproto *b, size_t b_start,
+                      size_t n);
+\f
+/* Creation and destruction. */
+
+void caseproto_free__ (struct caseproto *);
+
+/* Increments case prototype PROTO's reference count and returns
+   PROTO.  Afterward, PROTO is shared among its reference count
+   holders. */
+static inline struct caseproto *
+caseproto_ref (const struct caseproto *proto_)
+{
+  struct caseproto *proto = (struct caseproto *) proto_;
+  proto->ref_cnt++;
+  return proto;
+}
+
+/* Decrements case prototype PROTO's reference count.  Frees
+   PROTO if its reference count drops to 0.
+
+   If PROTO is a null pointer, this function has no effect. */
+static inline void
+caseproto_unref (struct caseproto *proto)
+{
+  if (proto != NULL && !--proto->ref_cnt)
+    caseproto_free__ (proto);
+}
+\f
+/* Inspecting stored widths.  */
+
+/* Returns case prototype PROTO's width with the given IDX.  IDX
+   must be less than caseproto_get_n_widths(PROTO). */
+static inline int
+caseproto_get_width (const struct caseproto *proto, size_t idx)
+{
+  assert (idx < proto->n_widths);
+  return proto->widths[idx];
+}
+
+/* Returns the number of widths in case prototype PROTO. */
+static inline size_t
+caseproto_get_n_widths (const struct caseproto *proto)
+{
+  return proto->n_widths;
+}
+\f
+/* Inspecting the cache of long string widths. */
+
+void caseproto_refresh_long_string_cache__ (const struct caseproto *);
+
+/* Returns the number of long string widths in PROTO; that is,
+   the number of widths in PROTO that are greater than to
+   MAX_SHORT_STRING. */
+static inline size_t
+caseproto_get_n_long_strings (const struct caseproto *proto)
+{
+  return proto->n_long_strings;
+}
+
+/* Given long string width IDX1, returns a value IDX2 for which
+   caseproto_get_width(PROTO, IDX2) will return a value greater
+   than MAX_SHORT_STRING.  IDX1 must be less than
+   caseproto_get_n_long_strings(PROTO), and IDX2 will be less
+   than caseproto_get_n_widths(PROTO). */
+static inline size_t
+caseproto_get_long_string_idx (const struct caseproto *proto, size_t idx1)
+{
+  if (proto->long_strings == NULL)
+    caseproto_refresh_long_string_cache__ (proto);
+
+  assert (idx1 < proto->n_long_strings);
+  return proto->long_strings[idx1];
+}
+
+#endif /* data/caseproto.h */
index 37e1dc85c828f1908fef2454f3ecad872b9a6d68..5244202e4fde4c02f3124a1f08ea2e910db15f3f 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
@@ -78,7 +78,7 @@ casereader_create_filter_func (struct casereader *subreader,
   filter->aux = aux;
   filter->exclude = exclude;
   reader = casereader_create_sequential (
-    NULL, casereader_get_value_cnt (filter->subreader), CASENUMBER_MAX,
+    NULL, casereader_get_proto (filter->subreader), CASENUMBER_MAX,
     &casereader_filter_class, filter);
   taint_propagate (casereader_get_taint (filter->subreader),
                    casereader_get_taint (reader));
@@ -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..b8d53500504fb2c2595e894b9baf944a8651d0b7 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,27 +91,28 @@ 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 *
 casereader_create_sequential (const struct taint *,
-                              size_t value_cnt, casenumber case_cnt,
+                              const struct caseproto *, casenumber case_cnt,
                               const struct casereader_class *, void *);
 
 void *casereader_dynamic_cast (struct casereader *, const struct casereader_class *);
@@ -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.
 
@@ -155,7 +160,7 @@ struct casereader_random_class
   };
 
 struct casereader *
-casereader_create_random (size_t value_cnt, casenumber case_cnt,
+casereader_create_random (const struct caseproto *, casenumber case_cnt,
                           const struct casereader_random_class *, void *aux);
 
 #endif /* data/casereader-provider.h */
index 229dac2e54944c8df81088080c1032330c0cd84e..feffa15c4510ba97e57cd95705cd9afd290c1e04 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
    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/variable.h>
 #include <data/casereader-provider.h>
 #include <libpspp/taint.h>
 
@@ -33,7 +33,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 +42,11 @@ 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, and populate it based on
+   INPUT and auxiliary data AUX.  TRANSLATE must destroy its
+   input case.
+
+   The cases returned by TRANSLATE must match OUTPUT_PROTO.
 
    When the translating casereader is destroyed, DESTROY will be
    called to allow any state maintained by TRANSLATE to be freed.
@@ -54,10 +56,9 @@ static const struct casereader_class casereader_translator_class;
    when the translating casereader is destroyed. */
 struct casereader *
 casereader_create_translator (struct casereader *subreader,
-                              size_t output_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+                              const struct caseproto *output_proto,
+                              struct ccase *(*translate) (struct ccase *input,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux)
 {
@@ -68,7 +69,7 @@ casereader_create_translator (struct casereader *subreader,
   ct->destroy = destroy;
   ct->aux = aux;
   reader = casereader_create_sequential (
-    NULL, output_value_cnt, casereader_get_case_cnt (ct->subreader),
+    NULL, output_proto, casereader_get_case_cnt (ct->subreader),
     &casereader_translator_class, ct);
   taint_propagate (casereader_get_taint (ct->subreader),
                    casereader_get_taint (reader));
@@ -76,20 +77,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 +106,86 @@ 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
+{
+  struct caseproto *proto;
+  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->proto = caseproto_ref (casereader_get_proto (subreader));
+  can->proto = caseproto_add_width (can->proto, 0);
+  can->n = 0;
+  can->aux = aux;
+  can->func = func;
+  can->destroy = destroy;
+  return casereader_create_translator (subreader, can->proto,
+                                       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->proto);
+  case_data_rw_idx (c, caseproto_get_n_widths (can->proto) - 1)->f = new_value;
+  return c;
+}
+
+static bool
+can_destroy (void *can_)
+{
+  struct casereader_append_numeric *can = can_;
+  if (can->destroy)
+    can->destroy (can->aux);
+  caseproto_unref (can->proto);
+  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 +200,292 @@ 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_)
-{
-  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++;
+
+\f
+
+struct casereader_append_rank
+{
+  struct casereader *clone;
+  casenumber n;
+  const struct variable *var;
+  const struct variable *weight;
+  struct caseproto *proto;
+  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_append_rank *car = xmalloc (sizeof *car);
+  car->proto = caseproto_ref (casereader_get_proto (subreader));
+  car->proto = caseproto_add_width (car->proto, 0);
+  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->proto,
+                                       car_translate, car_destroy, car);
+}
+
+
+static bool
+car_destroy (void *car_)
+{
+  struct casereader_append_rank *car = car_;
+  casereader_destroy (car->clone);
+  caseproto_unref (car->proto);
+  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->proto);
+  case_data_rw_idx (input, caseproto_get_n_widths (car->proto) - 1)->f
+    = car->mean_rank;
+  car->prev_value = value;
+  return input;
+}
+
+
+\f
+
+struct consolidator
+{
+  const struct variable *key;
+  const struct variable *weight;
+  double cc;
+  double prev_cc;
+
+  casenumber n;
+  struct casereader *clone;
+  struct caseproto *proto;
+  int direction;
+};
+
 static bool
-cas_destroy (void *cas_) 
+uniquify (const struct ccase *c, void *aux)
 {
-  struct casereader_arithmetic_sequence *cas = cas_;
-  free (cas);
+  struct consolidator *cdr = aux;
+  const union value *current_value = case_data (c, cdr->key);
+  const int key_width = var_get_width (cdr->key);
+  const double weight = cdr->weight ? case_data (c, cdr->weight)->f : 1.0;
+  const struct ccase *next_case = casereader_peek (cdr->clone, cdr->n + 1);
+  int dir = 0;
+
+  cdr->n ++;
+  cdr->cc += weight;
+
+  if ( NULL == next_case)
+      goto end;
+  
+  dir = value_compare_3way (case_data (next_case, cdr->key),
+                           current_value, key_width);
+  if ( dir != 0 )
+    {
+      /* Insist that the data are sorted */
+      assert (cdr->direction == 0 || dir == cdr->direction);
+      cdr->direction = dir;
+      goto end;
+    }
+  
+  return false;
+
+ end:
+  cdr->prev_cc = cdr->cc;
+  cdr->cc = 0;
   return true;
 }
+
+
+
+static struct ccase *
+consolodate_weight (struct ccase *input, void *aux)
+{
+  struct consolidator *cdr = aux;
+  struct ccase *c;
+
+  if (cdr->weight)
+    {
+      c = case_unshare (input);
+      case_data_rw (c, cdr->weight)->f = cdr->prev_cc;
+    }
+  else
+    {
+      c = case_unshare_and_resize (input, cdr->proto);
+      case_data_rw_idx (c, caseproto_get_n_widths (cdr->proto) - 1)->f = cdr->prev_cc;    
+    }
+
+  return c;
+}
+
+
+static bool
+uniquify_destroy (void *aux)
+{
+  struct consolidator *cdr = aux;
+
+  casereader_destroy (cdr->clone);
+  caseproto_unref (cdr->proto);
+  free (cdr);
+
+  return true;
+}
+
+
+
+/* Returns a new casereader which is based upon INPUT, but which contains a maximum 
+   of one case for each distinct value of KEY.
+   If WEIGHT is non-null, then the new casereader's values for this variable
+   will be the sum of all values matching KEY.
+   IF WEIGHT is null, then the new casereader will have an additional numeric
+   value appended, which will contain the total number of cases containing
+   KEY.
+   INPUT must be sorted on KEY
+*/
+struct casereader *
+casereader_create_distinct (struct casereader *input,
+                                              const struct variable *key,
+                                              const struct variable *weight)
+{
+  struct casereader *u ;
+  struct casereader *ud ;
+  struct caseproto *output_proto = caseproto_ref (casereader_get_proto (input));
+
+  struct consolidator *cdr = xmalloc (sizeof (*cdr));
+  cdr->n = 0;
+  cdr->key = key;
+  cdr->weight = weight;
+  cdr->cc = 0;
+  cdr->clone = casereader_clone (input);
+  cdr->direction = 0;
+
+  if ( NULL == cdr->weight )
+    output_proto = caseproto_add_width (output_proto, 0);
+
+  cdr->proto = output_proto;
+
+  u = casereader_create_filter_func (input, uniquify,
+                                    NULL, cdr, NULL);
+
+  ud = casereader_create_translator (u,
+                                    output_proto,
+                                    consolodate_weight,
+                                    uniquify_destroy,
+                                    cdr);
+
+  return ud;
+}
+
index ee7facb769dbf9a26dc96d717f1249a7fe44879b..a1550ac57560e5fb623d12ede14b7e1a65ca661b 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
@@ -34,7 +34,7 @@
 struct casereader
   {
     struct taint *taint;                  /* Corrupted? */
-    size_t value_cnt;                     /* Values per case. */
+    struct caseproto *proto;              /* Format of contained cases. */
     casenumber case_cnt;                  /* Number of cases,
                                              CASENUMBER_MAX if unknown. */
     const struct casereader_class *class; /* Class. */
@@ -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,21 @@ 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;
+          size_t n_widths UNUSED = caseproto_get_n_widths (reader->proto);
+          assert (case_get_value_cnt (c) >= n_widths);
+          expensive_assert (caseproto_equal (case_get_proto (c), 0,
+                                             reader->proto, 0, n_widths));
+          return c;
         }
     }
   reader->case_cnt = 0;
-  case_nullify (c);
-  return false;
+  return NULL;
 }
 
 /* Destroys READER.
@@ -93,6 +96,7 @@ casereader_destroy (struct casereader *reader)
     {
       reader->class->destroy (reader, reader->aux);
       ok = taint_destroy (reader->taint);
+      caseproto_unref (reader->proto);
       free (reader);
     }
   return ok;
@@ -160,27 +164,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 +195,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 +272,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);
@@ -277,11 +286,12 @@ casereader_count_cases (struct casereader *reader)
   return reader->case_cnt;
 }
 
-/* Returns the number of struct values in each case in READER. */
-size_t
-casereader_get_value_cnt (struct casereader *reader)
+/* Returns the prototype for the cases in READER.  The caller
+   must not unref the returned prototype. */
+const struct caseproto *
+casereader_get_proto (const struct casereader *reader)
 {
-  return reader->value_cnt;
+  return reader->proto;
 }
 
 /* Copies all the cases in READER to WRITER, propagating errors
@@ -289,12 +299,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);
 }
 
@@ -315,8 +325,9 @@ casereader_transfer (struct casereader *reader, struct casewriter *writer)
    function, in which case the cloned casereader should have the
    same taint object as the original casereader.)
 
-   VALUE_CNT must be the number of struct values per case read
-   from the casereader.
+   PROTO must be the prototype for the cases that may be read
+   from the casereader.  The caller retains its reference to
+   PROTO.
 
    CASE_CNT is an upper limit on the number of cases that
    casereader_read will return from the casereader in successive
@@ -329,12 +340,13 @@ casereader_transfer (struct casereader *reader, struct casewriter *writer)
    functions, respectively. */
 struct casereader *
 casereader_create_sequential (const struct taint *taint,
-                              size_t value_cnt, casenumber case_cnt,
+                              const struct caseproto *proto,
+                              casenumber case_cnt,
                               const struct casereader_class *class, void *aux)
 {
   struct casereader *reader = xmalloc (sizeof *reader);
   reader->taint = taint != NULL ? taint_clone (taint) : taint_create ();
-  reader->value_cnt = value_cnt;
+  reader->proto = caseproto_ref (proto);
   reader->case_cnt = case_cnt;
   reader->class = class;
   reader->aux = aux;
@@ -429,8 +441,9 @@ compare_random_readers_by_offset (const struct heap_node *a_,
    casereader_create_sequential is more appropriate for a data
    source that is naturally sequential.
 
-   VALUE_CNT must be the number of struct values per case read
-   from the casereader.
+   PROTO must be the prototype for the cases that may be read
+   from the casereader.  The caller retains its reference to
+   PROTO.
 
    CASE_CNT is an upper limit on the number of cases that
    casereader_read will return from the casereader in successive
@@ -442,7 +455,7 @@ compare_random_readers_by_offset (const struct heap_node *a_,
    member functions and auxiliary data to pass to those member
    functions, respectively. */
 struct casereader *
-casereader_create_random (size_t value_cnt, casenumber case_cnt,
+casereader_create_random (const struct caseproto *proto, casenumber case_cnt,
                           const struct casereader_random_class *class,
                           void *aux)
 {
@@ -451,7 +464,7 @@ casereader_create_random (size_t value_cnt, casenumber case_cnt,
   shared->class = class;
   shared->aux = aux;
   shared->min_offset = 0;
-  return casereader_create_sequential (NULL, value_cnt, case_cnt,
+  return casereader_create_sequential (NULL, proto, case_cnt,
                                        &random_reader_casereader_class,
                                        make_random_reader (shared, 0));
 }
@@ -475,22 +488,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
@@ -521,7 +532,7 @@ random_reader_clone (struct casereader *reader, void *br_)
   struct random_reader *br = br_;
   struct random_reader_shared *shared = br->shared;
   return casereader_create_sequential (casereader_get_taint (reader),
-                                       casereader_get_value_cnt (reader),
+                                       reader->proto,
                                        casereader_get_case_cnt (reader),
                                        &random_reader_casereader_class,
                                        make_random_reader (shared,
@@ -529,15 +540,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. */
@@ -586,12 +596,11 @@ static const struct casereader_random_class shim_class;
 static void
 insert_shim (struct casereader *reader)
 {
-  size_t value_cnt = casereader_get_value_cnt (reader);
+  const struct caseproto *proto = casereader_get_proto (reader);
   casenumber case_cnt = casereader_get_case_cnt (reader);
   struct shim *b = xmalloc (sizeof *b);
-  b->window = casewindow_create (value_cnt, settings_get_workspace_cases (value_cnt));
-  b->subreader = casereader_create_random (value_cnt, case_cnt,
-                                           &shim_class, b);
+  b->window = casewindow_create (proto, settings_get_workspace_cases (proto));
+  b->subreader = casereader_create_random (proto, case_cnt, &shim_class, b);
   casereader_swap (reader, b->subreader);
   taint_propagate (casewindow_get_taint (b->window),
                    casereader_get_taint (reader));
@@ -607,24 +616,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..3b903bbfd52d66cdba8ffe0e960fa7f0d1ef2474 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 *);
@@ -78,7 +77,7 @@ const struct taint *casereader_get_taint (const struct casereader *);
 
 casenumber casereader_get_case_cnt (struct casereader *);
 casenumber casereader_count_cases (struct casereader *);
-size_t casereader_get_value_cnt (struct casereader *);
+const struct caseproto *casereader_get_proto (const struct casereader *);
 
 void casereader_transfer (struct casereader *, struct casewriter *);
 \f
@@ -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 *
@@ -105,15 +105,45 @@ casereader_create_counter (struct casereader *, casenumber *counter,
                            casenumber initial_value);
 
 struct casereader *
-casereader_create_translator (struct casereader *, size_t output_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+casereader_create_translator (struct casereader *,
+                              const struct caseproto *output_proto,
+                              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);
+
+struct casereader *
+casereader_create_distinct (struct casereader *input,
+                           const struct variable *key,
+                           const struct variable *weight);
+
+
 #endif /* data/casereader.h */
index 0d735c4dc3ff909c41278df40b5179bdb3dbd2cd..9b04b9413c61bbace1e5b189d68e65eebe152482 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
@@ -36,7 +36,7 @@
 struct casewindow
   {
     /* Common data. */
-    size_t value_cnt;             /* Number of values per case. */
+    struct caseproto *proto;      /* Prototype of cases in window. */
     casenumber max_in_core_cases; /* Max cases before dumping to disk. */
     struct taint *taint;          /* Taint status. */
 
@@ -48,11 +48,11 @@ struct casewindow
 /* Implementation of a casewindow. */
 struct casewindow_class
   {
-    void *(*create) (struct taint *, size_t value_cnt);
+    void *(*create) (struct taint *, const struct caseproto *);
     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);
   };
 
@@ -63,28 +63,30 @@ static const struct casewindow_class casewindow_file_class;
 /* Creates and returns a new casewindow using the given
    parameters. */
 static struct casewindow *
-do_casewindow_create (struct taint *taint,
-                      size_t value_cnt, casenumber max_in_core_cases)
+do_casewindow_create (struct taint *taint, const struct caseproto *proto,
+                      casenumber max_in_core_cases)
 {
   struct casewindow *cw = xmalloc (sizeof *cw);
   cw->class = (max_in_core_cases
               ? &casewindow_memory_class
               : &casewindow_file_class);
-  cw->aux = cw->class->create (taint, value_cnt);
-  cw->value_cnt = value_cnt;
+  cw->aux = cw->class->create (taint, proto);
+  cw->proto = caseproto_ref (proto);
   cw->max_in_core_cases = max_in_core_cases;
   cw->taint = taint;
   return cw;
 }
 
-/* Creates and returns a new casewindow for cases with VALUE_CNT
-   values each.  If the casewindow holds more than
+/* Creates and returns a new casewindow for cases that take the
+   form specified by PROTO.  If the casewindow holds more than
    MAX_IN_CORE_CASES cases at any time, its cases will be dumped
-   to disk; otherwise, its cases will be held in memory. */
+   to disk; otherwise, its cases will be held in memory.
+
+   The caller retains its reference to PROTO. */
 struct casewindow *
-casewindow_create (size_t value_cnt, casenumber max_in_core_cases)
+casewindow_create (const struct caseproto *proto, casenumber max_in_core_cases)
 {
-  return do_casewindow_create (taint_create (), value_cnt, max_in_core_cases);
+  return do_casewindow_create (taint_create (), proto, max_in_core_cases);
 }
 
 /* Destroys casewindow CW.
@@ -98,6 +100,7 @@ casewindow_destroy (struct casewindow *cw)
     {
       cw->class->destroy (cw->aux);
       ok = taint_destroy (cw->taint);
+      caseproto_unref (cw->proto);
       free (cw);
     }
   return ok;
@@ -117,14 +120,14 @@ static void
 casewindow_to_disk (struct casewindow *old)
 {
   struct casewindow *new;
-  new = do_casewindow_create (taint_clone (old->taint), old->value_cnt, 0);
+  new = do_casewindow_create (taint_clone (old->taint), old->proto, 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 +150,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 +161,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. */
@@ -184,11 +183,12 @@ casewindow_get_case_cnt (const struct casewindow *cw)
   return cw->class->get_case_cnt (cw->aux);
 }
 
-/* Returns the number of values per case in casewindow CW. */
-size_t
-casewindow_get_value_cnt (const struct casewindow *cw)
+/* Returns the case prototype for the cases in casewindow CW.
+   The caller must not unref the returned prototype. */
+const struct caseproto *
+casewindow_get_proto (const struct casewindow *cw)
 {
-  return cw->value_cnt;
+  return cw->proto;
 }
 
 /* Returns true if casewindow CW is tainted.
@@ -218,11 +218,12 @@ casewindow_get_taint (const struct casewindow *cw)
 struct casewindow_memory
   {
     struct deque deque;
-    struct ccase *cases;
+    struct ccase **cases;
   };
 
 static void *
-casewindow_memory_create (struct taint *taint UNUSED, size_t value_cnt UNUSED)
+casewindow_memory_create (struct taint *taint UNUSED,
+                          const struct caseproto *proto UNUSED)
 {
   struct casewindow_memory *cwm = xmalloc (sizeof *cwm);
   cwm->cases = deque_init (&cwm->deque, 4, sizeof *cwm->cases);
@@ -234,7 +235,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 +246,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 +255,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
@@ -290,10 +290,10 @@ struct casewindow_file
   };
 
 static void *
-casewindow_file_create (struct taint *taint, size_t value_cnt)
+casewindow_file_create (struct taint *taint, const struct caseproto *proto)
 {
   struct casewindow_file *cwf = xmalloc (sizeof *cwf);
-  cwf->file = case_tmpfile_create (value_cnt);
+  cwf->file = case_tmpfile_create (proto);
   cwf->head = cwf->tail = 0;
   taint_propagate (case_tmpfile_get_taint (cwf->file), taint);
   return cwf;
@@ -325,11 +325,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..b303c85dab5b53d99391a2698901a83065ea0a54 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
 #ifndef DATA_CASEWINDOW_H
 #define DATA_CASEWINDOW_H 1
 
-#include <stddef.h>
 #include <data/case.h>
 
-struct casewindow *casewindow_create (size_t value_cnt,
+struct caseproto;
+
+struct casewindow *casewindow_create (const struct caseproto *,
                                       casenumber max_in_core_cases);
 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 *);
-size_t casewindow_get_value_cnt (const struct casewindow *);
+struct ccase *casewindow_get_case (const struct casewindow *,
+                                   casenumber case_idx);
+const struct caseproto *casewindow_get_proto (const struct casewindow *);
 casenumber casewindow_get_case_cnt (const struct casewindow *);
 
 bool casewindow_error (const struct casewindow *);
index 8298af465da9d88640fe813f6a12309c6effd036..7231a1f3bbfc1af5580547ae723090f03035f098 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
@@ -56,7 +57,7 @@ struct casewriter_class
     struct casereader *(*convert_to_reader) (struct casewriter *, void *aux);
   };
 
-struct casewriter *casewriter_create (size_t value_cnt,
+struct casewriter *casewriter_create (const struct caseproto *,
                                       const struct casewriter_class *, void *);
 
 #endif /* data/casewriter-provider.h */
index c630b44cda397dfffccd980c9d3ecb65255e91dc..e80b9d68187b3f65e33b35051b0b6d891ca5f312 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,16 @@ 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, 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.
+
+   The cases returned by TRANSLATE must match OUTPUT_PROTO.
+
+   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.
@@ -50,10 +56,9 @@ static const struct casewriter_class casewriter_translator_class;
    when the translating casewriter is destroyed. */
 struct casewriter *
 casewriter_create_translator (struct casewriter *subwriter,
-                              size_t translated_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+                              const struct caseproto *translated_proto,
+                              struct ccase *(*translate) (struct ccase *,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux)
 {
@@ -63,7 +68,7 @@ casewriter_create_translator (struct casewriter *subwriter,
   ct->translate = translate;
   ct->destroy = destroy;
   ct->aux = aux;
-  writer = casewriter_create (translated_value_cnt,
+  writer = casewriter_create (translated_proto,
                               &casewriter_translator_class, ct);
   taint_propagate (casewriter_get_taint (ct->subwriter),
                    casewriter_get_taint (writer));
@@ -75,10 +80,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..f7760eca04a5ae5d1b78bf45c46c92676b63a0c7 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
@@ -26,6 +26,7 @@
 #include <data/casereader-provider.h>
 #include <data/casewindow.h>
 #include <data/settings.h>
+#include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/taint.h>
 
 struct casewriter
   {
     struct taint *taint;
-    size_t value_cnt;
+    struct caseproto *proto;
     casenumber case_cnt;
     const struct casewriter_class *class;
     void *aux;
   };
 
-static struct casewriter *create_casewriter_window (size_t value_cnt,
+static struct casewriter *create_casewriter_window (const struct caseproto *,
                                                     casenumber max_in_core);
 
-/* Writes case C to WRITER. */
+/* Writes case C to WRITER.  Ownership of C is transferred to
+   WRITER. */
 void
 casewriter_write (struct casewriter *writer, struct ccase *c)
 {
-  assert (case_get_value_cnt (c) >= writer->value_cnt);
+  size_t n_widths UNUSED = caseproto_get_n_widths (writer->proto);
+  assert (case_get_value_cnt (c) >= n_widths);
+  expensive_assert (caseproto_equal (case_get_proto (c), 0,
+                                     writer->proto, 0, n_widths));
   writer->class->write (writer, writer->aux, c);
 }
 
@@ -64,17 +69,18 @@ casewriter_destroy (struct casewriter *writer)
     {
       writer->class->destroy (writer, writer->aux);
       ok = taint_destroy (writer->taint);
+      caseproto_unref (writer->proto);
       free (writer);
     }
   return ok;
 }
 
-/* Returns the number of `union value's in each case written to
-   WRITER. */
-size_t
-casewriter_get_value_cnt (const struct casewriter *writer)
+/* Returns the prototype for that cases written to WRITER must
+   follow. */
+const struct caseproto *
+casewriter_get_proto (const struct casewriter *writer)
 {
-  return writer->value_cnt;
+  return writer->proto;
 }
 
 /* Destroys WRITER and in its place returns a casereader that can
@@ -91,8 +97,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);
@@ -143,23 +148,24 @@ casewriter_get_taint (const struct casewriter *writer)
 }
 
 /* Creates and returns a new casewriter with the given CLASS and
-   auxiliary data AUX.  The casewriter accepts cases with
-   VALUE_CNT `union value's. */
+   auxiliary data AUX.  The casewriter accepts cases that match
+   case prototype PROTO, of which the caller retains
+   ownership. */
 struct casewriter *
-casewriter_create (size_t value_cnt,
+casewriter_create (const struct caseproto *proto,
                    const struct casewriter_class *class, void *aux)
 {
   struct casewriter *writer = xmalloc (sizeof *writer);
   writer->taint = taint_create ();
-  writer->value_cnt = value_cnt;
+  writer->proto = caseproto_ref (proto);
   writer->case_cnt = 0;
   writer->class = class;
   writer->aux = aux;
   return writer;
 }
 
-/* Returns a casewriter for cases with VALUE_CNT struct values
-   per case.  The cases written to the casewriter will be kept in
+/* Returns a casewriter for cases that match case prototype
+   PROTO.  The cases written to the casewriter will be kept in
    memory, unless the amount of memory used grows too large, in
    which case they will be written to disk.
 
@@ -168,33 +174,34 @@ casewriter_create (size_t value_cnt,
 
    This is usually the right kind of casewriter to use. */
 struct casewriter *
-autopaging_writer_create (size_t value_cnt)
+autopaging_writer_create (const struct caseproto *proto)
 {
-  return create_casewriter_window (value_cnt, settings_get_workspace_cases (value_cnt));
+  return create_casewriter_window (proto,
+                                   settings_get_workspace_cases (proto));
 }
 
-/* Returns a casewriter for cases with VALUE_CNT struct values
-   per case.  The cases written to the casewriter will be kept in
+/* Returns a casewriter for cases that match case prototype
+   PROTO.  The cases written to the casewriter will be kept in
    memory.
 
    A casewriter created with this function may be passed to
    casewriter_make_reader. */
 struct casewriter *
-mem_writer_create (size_t value_cnt)
+mem_writer_create (const struct caseproto *proto)
 {
-  return create_casewriter_window (value_cnt, CASENUMBER_MAX);
+  return create_casewriter_window (proto, CASENUMBER_MAX);
 }
 
-/* Returns a casewriter for cases with VALUE_CNT struct values
-   per case.  The cases written to the casewriter will be written
+/* Returns a casewriter for cases that match case prototype
+   PROTO.  The cases written to the casewriter will be written
    to disk.
 
    A casewriter created with this function may be passed to
    casewriter_make_reader. */
 struct casewriter *
-tmpfile_writer_create (size_t value_cnt)
+tmpfile_writer_create (const struct caseproto *proto)
 {
-  return create_casewriter_window (value_cnt, 0);
+  return create_casewriter_window (proto, 0);
 }
 \f
 static const struct casewriter_class casewriter_window_class;
@@ -206,10 +213,11 @@ static const struct casereader_random_class casereader_window_class;
    memory until MAX_IN_CORE_CASES have been written, at which
    point they will be written to disk. */
 static struct casewriter *
-create_casewriter_window (size_t value_cnt, casenumber max_in_core_cases)
+create_casewriter_window (const struct caseproto *proto,
+                          casenumber max_in_core_cases)
 {
-  struct casewindow *window = casewindow_create (value_cnt, max_in_core_cases);
-  struct casewriter *writer = casewriter_create (value_cnt,
+  struct casewindow *window = casewindow_create (proto, max_in_core_cases);
+  struct casewriter *writer = casewriter_create (proto,
                                                  &casewriter_window_class,
                                                  window);
   taint_propagate (casewindow_get_taint (window),
@@ -241,27 +249,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_proto (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..146cc654608f754e2f7178c9738c477c7644627b 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
@@ -18,7 +18,6 @@
 #define DATA_CASEWRITER_H 1
 
 #include <stdbool.h>
-#include <stddef.h>
 #include <data/transformations.h>
 #include <libpspp/compiler.h>
 
@@ -27,7 +26,7 @@ struct casewriter;
 void casewriter_write (struct casewriter *, struct ccase *);
 bool casewriter_destroy (struct casewriter *);
 
-size_t casewriter_get_value_cnt (const struct casewriter *);
+const struct caseproto *casewriter_get_proto (const struct casewriter *);
 
 struct casereader *casewriter_make_reader (struct casewriter *);
 
@@ -37,15 +36,15 @@ bool casewriter_error (const struct casewriter *);
 void casewriter_force_error (struct casewriter *);
 const struct taint *casewriter_get_taint (const struct casewriter *);
 
-struct casewriter *mem_writer_create (size_t value_cnt);
-struct casewriter *tmpfile_writer_create (size_t value_cnt);
-struct casewriter *autopaging_writer_create (size_t value_cnt);
+struct casewriter *mem_writer_create (const struct caseproto *);
+struct casewriter *tmpfile_writer_create (const struct caseproto *);
+struct casewriter *autopaging_writer_create (const struct caseproto *);
 \f
 struct casewriter *
-casewriter_create_translator (struct casewriter *, size_t translated_value_cnt,
-                              void (*translate) (struct ccase *input,
-                                                 struct ccase *output,
-                                                 void *aux),
+casewriter_create_translator (struct casewriter *,
+                              const struct caseproto *translated_proto,
+                              struct ccase *(*translate) (struct ccase *input,
+                                                          void *aux),
                               bool (*destroy) (void *aux),
                               void *aux);
 
index 1620bc7f7f9b3c27bbde71737eeb860d5c4f7cf0..968dd4c52da8369e629fb2797faa056db4b67227 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
 #include <config.h>
 
 #include <assert.h>
+#include <data/category.h>
+#include <data/value.h>
+#include <data/variable.h>
+#include <gl/xalloc.h>
+#include <libpspp/message.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include <libpspp/message.h>
-#include "category.h"
-#include "value.h"
-#include "variable.h"
-
-#include "xalloc.h"
-
 #define CAT_VALUE_NOT_FOUND -1
 
 #define N_INITIAL_CATEGORIES 1
@@ -108,7 +106,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 (value_equal (candidate, val, var_get_width (v)))
        {
          return i;
        }
@@ -182,3 +180,20 @@ cat_get_n_categories (const struct variable *v)
   return var_get_obs_vals (v)->n_categories;
 }
 
+/*
+  If VAR is categorical with d categories, its first category should
+  correspond to the origin in d-dimensional Euclidean space.
+ */
+bool
+cat_is_origin (const struct variable *var, const union value *val)
+{
+  if (var_is_numeric (var))
+    {
+      return false;
+    }
+  if (cat_value_find (var, val) == 0)
+    {
+      return true;
+    }
+  return false;
+}
index db4bb339bcb5e6ecab1bd0de98a76632dfe20f95..f90ae7cd2809e9b3b96d8968de1c1f1ae71e0610 100644 (file)
@@ -32,7 +32,7 @@
 
 #ifndef CATEGORY_H
 #define CATEGORY_H
-
+#include <stdbool.h>
 #include <stddef.h>
 
 struct cat_vals;
@@ -61,5 +61,9 @@ cat_get_category_count (const size_t, const struct variable *);
  */
 size_t  cat_get_n_categories (const struct variable *v);
 
-
+/*
+  If VAR is categorical with d categories, its first category should
+  correspond to the origin in d-dimensional Euclidean space.
+ */
+bool cat_is_origin (const struct variable *, const union value *);
 #endif
index a6544afbbeb7c338b10f4fe9a978c649fb910e37..33e369f971da8751f78e29e76f23d230e6e0227d 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 "settings.h"
 #include "value.h"
 #include "format.h"
+#include "dictionary.h"
 
 #include <libpspp/assertion.h>
 #include <libpspp/legacy-encoding.h>
+#include <libpspp/i18n.h>
 #include <libpspp/compiler.h>
 #include <libpspp/integer-format.h>
 #include <libpspp/message.h>
@@ -53,7 +55,7 @@
 /* Information about parsing one data field. */
 struct data_in
   {
-    enum legacy_encoding encoding;/* Encoding of source. */
+    const char *src_enc;        /* Encoding of source. */
     struct substring input;     /* Source. */
     enum fmt_type format;       /* Input format. */
     int implied_decimals;       /* Number of implied decimal places. */
@@ -85,8 +87,12 @@ static int hexit_value (int c);
 \f
 /* Parses the characters in INPUT, which are encoded in the given
    ENCODING, according to FORMAT.  Stores the parsed
-   representation in OUTPUT, which has the given WIDTH (0 for
-   a numeric field, otherwise the string width).
+   representation in OUTPUT, which the caller must have
+   initialized with the given WIDTH (0 for a numeric field,
+   otherwise the string width).
+   Iff FORMAT is a string format, then DICT must be a pointer
+   to the dictionary associated with OUTPUT.  Otherwise, DICT
+   may be null.
 
    If no decimal point is included in a numeric format, then
    IMPLIED_DECIMALS decimal places are implied.  Specify 0 if no
@@ -99,9 +105,11 @@ static int hexit_value (int c);
    FIRST_COLUMN plus the length of the input because of the
    possibility of escaped quotes in strings, etc.) */
 bool
-data_in (struct substring input, enum legacy_encoding encoding,
+data_in (struct substring input, const char *encoding,
          enum fmt_type format, int implied_decimals,
-         int first_column, int last_column, union value *output, int width)
+         int first_column, int last_column,
+        const struct dictionary *dict,
+        union value *output, int width)
 {
   static data_in_parser_func *const handlers[FMT_NUMBER_OF_FORMATS] =
     {
@@ -110,25 +118,11 @@ data_in (struct substring input, enum legacy_encoding encoding,
     };
 
   struct data_in i;
-  void *copy = NULL;
+
   bool ok;
 
   assert ((width != 0) == fmt_is_string (format));
 
-  if (encoding == LEGACY_NATIVE
-      || fmt_get_category (format) & (FMT_CAT_BINARY | FMT_CAT_STRING))
-    {
-      i.input = input;
-      i.encoding = encoding;
-    }
-  else
-    {
-      ss_alloc_uninit (&i.input, ss_length (input));
-      legacy_recode (encoding, ss_data (input), LEGACY_NATIVE,
-                     ss_data (i.input), ss_length (input));
-      i.encoding = LEGACY_NATIVE;
-      copy = ss_data (i.input);
-    }
   i.format = format;
   i.implied_decimals = implied_decimals;
 
@@ -137,21 +131,39 @@ data_in (struct substring input, enum legacy_encoding encoding,
 
   i.first_column = first_column;
   i.last_column = last_column;
+  i.src_enc = encoding;
 
-  if (!ss_is_empty (i.input))
+  if (ss_is_empty (input))
     {
-      ok = handlers[i.format] (&i);
-      if (!ok)
-        default_result (&i);
+      default_result (&i);
+      return true;
+    }
+
+  if (fmt_get_category (format) & ( FMT_CAT_BINARY | FMT_CAT_HEXADECIMAL | FMT_CAT_LEGACY))
+    {
+      i.input = input;
     }
   else
     {
-      default_result (&i);
-      ok = true;
+      const char *dest_encoding;
+      char *s = NULL;
+      if ( dict == NULL)
+       {
+         assert (0 == (fmt_get_category (format) & (FMT_CAT_BINARY | FMT_CAT_STRING)));
+         dest_encoding = LEGACY_NATIVE;
+       }
+      else
+       dest_encoding = dict_get_encoding (dict);
+
+      s = recode_string (dest_encoding, i.src_enc, ss_data (input), ss_length (input));
+      ss_alloc_uninit (&i.input, strlen (s));
+      memcpy (ss_data (i.input), s, ss_length (input));
+      free (s);
     }
 
-  if (copy)
-    free (copy);
+  ok = handlers[i.format] (&i);
+  if (!ok)
+    default_result (&i);
 
   return ok;
 }
@@ -607,12 +619,13 @@ parse_A (struct data_in *i)
 {
   /* This is equivalent to buf_copy_rpad, except that we posibly
      do a character set recoding in the middle. */
-  char *dst = i->output->s;
+  uint8_t *dst = value_str_rw (i->output, i->width);
   size_t dst_size = i->width;
   const char *src = ss_data (i->input);
   size_t src_size = ss_length (i->input);
 
-  legacy_recode (i->encoding, src, LEGACY_NATIVE, dst, MIN (src_size, dst_size));
+  memcpy (dst, src, MIN (src_size, dst_size));
+
   if (dst_size > src_size)
     memset (&dst[src_size], ' ', dst_size - src_size);
 
@@ -623,6 +636,7 @@ parse_A (struct data_in *i)
 static bool
 parse_AHEX (struct data_in *i)
 {
+  uint8_t *s = value_str_rw (i->output, i->width);
   size_t j;
 
   for (j = 0; ; j++)
@@ -637,10 +651,10 @@ parse_AHEX (struct data_in *i)
           return false;
         }
 
-      if (i->encoding != LEGACY_NATIVE)
+      if (0 != strcmp (i->src_enc, LEGACY_NATIVE))
         {
-          hi = legacy_to_native (i->encoding, hi);
-          lo = legacy_to_native (i->encoding, lo);
+          hi = legacy_to_native (i->src_enc, hi);
+          lo = legacy_to_native (i->src_enc, lo);
         }
       if (!c_isxdigit (hi) || !c_isxdigit (lo))
        {
@@ -649,10 +663,10 @@ parse_AHEX (struct data_in *i)
        }
 
       if (j < i->width)
-        i->output->s[j] = hexit_value (hi) * 16 + hexit_value (lo);
+        s[j] = hexit_value (hi) * 16 + hexit_value (lo);
     }
 
-  memset (i->output->s + j, ' ', i->width - j);
+  memset (&s[j], ' ', i->width - j);
 
   return true;
 }
@@ -1220,7 +1234,7 @@ static void
 default_result (struct data_in *i)
 {
   if (fmt_is_string (i->format))
-    memset (i->output->s, ' ', i->width);
+    memset (value_str_rw (i->output, i->width), ' ', i->width);
   else
     i->output->f = settings_get_blanks ();
 }
index 7d4df5adccb9310d6964e5a544a3880ef5238968..3ebd5933c43b0370e29cfd279cd3b22fcdbb0b4d 100644 (file)
 #include <libpspp/float-format.h>
 #include <libpspp/integer-format.h>
 #include <libpspp/str.h>
-
+#include <data/format.h>
 
 enum fmt_type;
 union value;
-bool data_in (struct substring input, enum legacy_encoding,
+struct dictionary;
+bool data_in (struct substring input, const char *encoding,
               enum fmt_type, int implied_decimals,
               int first_column, int last_column,
+             const struct dictionary *dict,
               union value *output, int width);
 
 #endif /* data/data-in.h */
index 86688a6774cd939facdb8453dbd7e9e2832934ec..94a6130adb154bd06b61901414e4209498cbf106 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
@@ -36,6 +36,8 @@
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
+#include <libpspp/pool.h>
+#include <libpspp/i18n.h>
 
 #include "minmax.h"
 
@@ -83,35 +85,68 @@ static void output_binary_integer (uint64_t, int bytes, enum integer_format,
                                    char *);
 static void output_hex (const void *, size_t bytes, char *);
 \f
-/* Converts the INPUT value into printable form in the exactly
-   FORMAT->W characters in OUTPUT according to format
-   specification FORMAT.  The output is recoded from native form
-   into the given legacy character ENCODING.  No null terminator
-   is appended to the buffer.  */
-void
-data_out_legacy (const union value *input, enum legacy_encoding encoding,
-                 const struct fmt_spec *format, char *output)
-{
-  static data_out_converter_func *const converters[FMT_NUMBER_OF_FORMATS] =
+
+static data_out_converter_func *const converters[FMT_NUMBER_OF_FORMATS] =
     {
 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) output_##METHOD,
 #include "format.def"
     };
 
+/* Similar to data_out. Additionally recodes the output from
+   native form into the given legacy character ENCODING.
+   OUTPUT must be provided by the caller and must be at least
+   FORMAT->w long. No null terminator is appended to OUTPUT.
+*/
+void
+data_out_legacy (const union value *input, const char *encoding,
+                 const struct fmt_spec *format, char *output)
+{
   assert (fmt_check_output (format));
 
   converters[format->type] (input, format, output);
-  if (encoding != LEGACY_NATIVE
+  if (0 != strcmp (encoding, LEGACY_NATIVE)
       && fmt_get_category (format->type) != FMT_CAT_BINARY)
-    legacy_recode (LEGACY_NATIVE, output, encoding, output, format->w);
+    {
+      char *s  = recode_string (encoding, LEGACY_NATIVE, output, format->w );
+      memcpy (output, s, format->w);
+      free (s);
+    }
 }
 
-/* Same as data_out_legacy with ENCODING set to LEGACY_NATIVE.  */
-void
-data_out (const union value *value, const struct fmt_spec *format,
-          char *output)
+/* Converts the INPUT value into a UTF8 encoded string, according
+   to format specification FORMAT. 
+
+   VALUE must be the correct width for FORMAT, that is, its
+   width must be fmt_var_width(FORMAT).
+
+   ENCODING must be the encoding of INPUT.  Normally this can
+   be obtained by calling dict_get_encoding on the dictionary
+   with which INPUT is associated.
+
+   The return value is dynamically allocated, and must be freed
+   by the caller.  If POOL is non-null, then the return value is
+   allocated on that pool.
+*/
+char *
+data_out_pool (const union value *input, const char *encoding,
+              const struct fmt_spec *format, struct pool *pool)
+{
+  char *output = xmalloc (format->w + 1);
+  char *t ;
+  assert (fmt_check_output (format));
+
+  converters[format->type] (input, format, output);
+  output[format->w] = '\0';
+
+  t =  recode_string_pool (UTF8, encoding, output, format->w, pool);
+  free (output);
+  return t;
+}
+
+char *
+data_out (const union value *input, const char *encoding, const struct fmt_spec *format)
 {
-  return data_out_legacy (value, LEGACY_NATIVE, format, output);
+  return data_out_pool (input, encoding, format, NULL);
 }
 
 \f
@@ -415,7 +450,7 @@ output_date (const union value *input, const struct fmt_spec *format,
         }
     }
 
-  buf_copy_lpad (output, format->w, tmp, p - tmp);
+  buf_copy_lpad (output, format->w, tmp, p - tmp, ' ');
   return;
 
  overflow:
@@ -439,7 +474,7 @@ output_WKDAY (const union value *input, const struct fmt_spec *format,
     };
 
   if (input->f >= 1 && input->f < 8)
-    buf_copy_str_rpad (output, format->w, weekdays[(int) input->f - 1]);
+    buf_copy_str_rpad (output, format->w, weekdays[(int) input->f - 1], ' ');
   else
     {
       if (input->f != SYSMIS)
@@ -460,7 +495,7 @@ output_MONTH (const union value *input, const struct fmt_spec *format,
     };
 
   if (input->f >= 1 && input->f < 13)
-    buf_copy_str_rpad (output, format->w, months[(int) input->f - 1]);
+    buf_copy_str_rpad (output, format->w, months[(int) input->f - 1], ' ');
   else
     {
       if (input->f != SYSMIS)
@@ -474,7 +509,7 @@ static void
 output_A (const union value *input, const struct fmt_spec *format,
           char *output)
 {
-  memcpy (output, input->s, format->w);
+  memcpy (output, value_str (input, format->w), format->w);
 }
 
 /* Outputs AHEX format. */
@@ -482,7 +517,7 @@ static void
 output_AHEX (const union value *input, const struct fmt_spec *format,
              char *output)
 {
-  output_hex (input->s, format->w / 2, output);
+  output_hex (value_str (input, format->w), format->w / 2, output);
 }
 \f
 /* Decimal and scientific formatting. */
@@ -934,7 +969,7 @@ output_infinite (double number, const struct fmt_spec *format, char *output)
       else
         s = "Unknown";
 
-      buf_copy_str_lpad (output, format->w, s);
+      buf_copy_str_lpad (output, format->w, s, ' ');
     }
   else
     output_overflow (format, output);
index f9f70da90d37843d546752b3ad97baf041fdfc51..735679b41257b78d0918ae4c83e29c90eeb62546 100644 (file)
 struct fmt_spec;
 union value;
 
-void data_out (const union value *, const struct fmt_spec *, char *);
+char * data_out (const union value *, const char *encoding, const struct fmt_spec *);
 
-void data_out_legacy (const union value *, enum legacy_encoding,
-                      const struct fmt_spec *, char *);
+char * data_out_pool (const union value *, const char *encoding, const struct fmt_spec *, struct pool *pool);
+
+void data_out_legacy (const union value *input, const char *encoding,
+                     const struct fmt_spec *format, char *output);
 
 #endif /* data-out.h */
index da85d963c17dfb166ff93ab1e7882f1ee8105d42..fa24d8ce77014e980cb79e4700ffc63832c5d061 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 <data/casereader.h>
 #include <data/casewriter.h>
 #include <data/lazy-casereader.h>
-#include <data/sparse-cases.h>
+#include <data/settings.h>
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
-#include <libpspp/model-checker.h>
+#include <libpspp/misc.h>
 #include <libpspp/range-map.h>
 #include <libpspp/range-set.h>
+#include <libpspp/sparse-xarray.h>
 #include <libpspp/taint.h>
 #include <libpspp/tower.h>
 
@@ -38,6 +39,8 @@
 #include "md4.h"
 #include "xalloc.h"
 
+struct column;
+
 static struct axis *axis_create (void);
 static struct axis *axis_clone (const struct axis *);
 static void axis_destroy (struct axis *);
@@ -48,8 +51,8 @@ static bool axis_allocate (struct axis *, unsigned long int request,
                            unsigned long int *start,
                            unsigned long int *width);
 static void axis_make_available (struct axis *,
-                                unsigned long int start,
-                                unsigned long int width);
+                                unsigned long int start,
+                                unsigned long int width);
 static unsigned long int axis_extend (struct axis *, unsigned long int width);
 
 static unsigned long int axis_map (const struct axis *, unsigned long log_pos);
@@ -66,27 +69,25 @@ static void axis_move (struct axis *,
                        unsigned long int new_start,
                        unsigned long int cnt);
 
-static struct source *source_create_empty (size_t column_cnt);
+static struct source *source_create_empty (size_t n_bytes);
 static struct source *source_create_casereader (struct casereader *);
 static struct source *source_clone (const struct source *);
 static void source_destroy (struct source *);
 
-static casenumber source_get_backing_row_cnt (const struct source *);
-static size_t source_get_column_cnt (const struct source *);
-
-static bool source_read (const struct source *,
-                         casenumber row, size_t column,
-                         union value[], size_t value_cnt);
-static bool source_write (struct source *,
-                          casenumber row, size_t column,
-                          const union value[], size_t value_cnt);
-static bool source_write_columns (struct source *, size_t start_column,
-                                  const union value[], size_t value_cnt);
-static bool source_has_backing (const struct source *);
-static void source_increase_use (struct source *, size_t delta);
-static void source_decrease_use (struct source *, size_t delta);
+static casenumber source_get_backing_n_rows (const struct source *);
+
+static int source_allocate_column (struct source *, int width);
+static void source_release_column (struct source *, int ofs, int width);
 static bool source_in_use (const struct source *);
 
+static bool source_read (const struct column *, casenumber row, union value *);
+static bool source_write (const struct column *, casenumber row,
+                          const union value *);
+static bool source_write_column (struct column *, const union value *);
+static bool source_has_backing (const struct source *);
+
+static int get_source_index (const struct datasheet *ds, const struct source *source);
+
 /* A datasheet is internally composed from a set of data files,
    called "sources".  The sources that make up a datasheet must
    have the same number of rows (cases), but their numbers of
@@ -100,36 +101,46 @@ static bool source_in_use (const struct source *);
    to the column mapping.
 
    Each source in a datasheet can be a casereader or a
-   sparse_cases.  Casereaders are read-only, so when sources made
-   from casereaders need to be modified, it is done "virtually"
-   through being overlaid by a sparse_cases. */
+   sparse_xarray.  Casereaders are read-only, so when sources
+   made from casereaders need to be modified, it is done
+   "virtually" through being overlaid by a sparse_xarray. */
+struct source
+  {
+    struct range_set *avail;    /* Free bytes are set to 1s. */
+    struct sparse_xarray *data; /* Data at top level, atop the backing. */
+    struct casereader *backing; /* Backing casereader (or null). */
+    casenumber backing_rows;    /* Number of rows in backing (if backed). */
+    size_t n_used;              /* Number of column in use (if backed). */
+  };
 
-/* A datasheet. */
-struct datasheet
+/* A logical column. */
+struct column
   {
-    /* Mappings from logical to physical columns/rows. */
-    struct axis *columns;
-    struct axis *rows;
-
-    /* Mapping from physical columns to "source_info"s. */
-    struct range_map sources;
-
-    /* Minimum number of columns to put in a new source when we
-       need new columns and none are free.  We double it whenever
-       we add a new source to keep the number of file descriptors
-       needed by the datasheet to a minimum, reducing the
-       likelihood of running out. */
-    unsigned column_min_alloc;
-
-    /* Indicates corrupted data in the datasheet. */
-    struct taint *taint;
+    struct source *source;      /* Source of the underlying physical column. */
+    int value_ofs;              /* If 'source' has a backing casereader,
+                                   column's value offset in its cases. */
+    int byte_ofs;               /* Byte offset in source's sparse_xarray. */
+    int width;                  /* 0=numeric, otherwise string width. */
   };
 
-/* Maps from a range of physical columns to a source. */
-struct source_info
+/* A datasheet. */
+struct datasheet
   {
-    struct range_map_node column_range;
-    struct source *source;
+    /* Data sources. */
+    struct source **sources;    /* Sources, in no particular order. */
+    size_t n_sources;           /* Number of sources. */
+
+    /* Columns. */
+    struct caseproto *proto;    /* Prototype for rows (initialized lazily). */
+    struct column *columns;     /* Logical to physical column mapping. */
+    size_t n_columns;           /* Number of logical columns. */
+    unsigned column_min_alloc;  /* Min. # of columns to put in a new source. */
+
+    /* Rows. */
+    struct axis *rows;          /* Logical to physical row mapping. */
+
+    /* Tainting. */
+    struct taint *taint;        /* Indicates corrupted data. */
   };
 
 /* Is this operation a read or a write? */
@@ -139,13 +150,51 @@ enum rw_op
     OP_WRITE
   };
 
-static void free_source_info (struct datasheet *, struct source_info *);
-static struct source_info *source_info_from_range_map (
-  struct range_map_node *);
+static void allocate_column (struct datasheet *, int width, struct column *);
+static void release_source (struct datasheet *, struct source *);
 static bool rw_case (struct datasheet *ds, enum rw_op op,
-                     casenumber lrow, size_t start_column, size_t column_cnt,
+                     casenumber lrow, size_t start_column, size_t n_columns,
                      union value data[]);
 
+/* Returns the number of bytes needed to store a value with the
+   given WIDTH on disk. */
+static size_t
+width_to_n_bytes (int width)
+{
+  return width == 0 ? sizeof (double) : width;
+}
+
+/* Returns the address of the data in VALUE (for reading or
+   writing to/from disk).  VALUE must have the given WIDTH. */
+static void *
+value_to_data (const union value *value_, int width)
+{
+  union value *value = (union value *) value_;
+  assert (sizeof value->f == sizeof (double));
+  if (width == 0)
+    return &value->f;
+  else
+    return value_str_rw (value, width);
+}
+
+/* Returns the number of bytes needed to store all the values in
+   PROTO on disk. */
+static size_t
+caseproto_to_n_bytes (const struct caseproto *proto)
+{
+  size_t n_bytes;
+  size_t i;
+
+  n_bytes = 0;
+  for (i = 0; i < caseproto_get_n_widths (proto); i++)
+    {
+      int width = caseproto_get_width (proto, i);
+      if (width >= 0)
+        n_bytes += width_to_n_bytes (width);
+    }
+  return n_bytes;
+}
+
 /* Creates and returns a new datasheet.
 
    If READER is nonnull, then the datasheet initially contains
@@ -153,42 +202,50 @@ static bool rw_case (struct datasheet *ds, enum rw_op op,
 struct datasheet *
 datasheet_create (struct casereader *reader)
 {
-  /* Create datasheet. */
   struct datasheet *ds = xmalloc (sizeof *ds);
-  ds->columns = axis_create ();
+  ds->sources = NULL;
+  ds->n_sources = 0;
+  ds->proto = NULL;
+  ds->columns = NULL;
+  ds->n_columns = 0;
+  ds->column_min_alloc = 8;
   ds->rows = axis_create ();
-  range_map_init (&ds->sources);
-  ds->column_min_alloc = 1;
   ds->taint = taint_create ();
 
-  /* Add backing. */
   if (reader != NULL)
     {
-      size_t column_cnt;
-      casenumber row_cnt;
-      struct source_info *si;
-
-      si = xmalloc (sizeof *si);
-      si->source = source_create_casereader (reader);
-      column_cnt = source_get_column_cnt (si->source);
-      row_cnt = source_get_backing_row_cnt (si->source);
-      source_increase_use (si->source, column_cnt);
-
-      if ( column_cnt > 0 )
-       {
-         unsigned long int column_start;
-      column_start = axis_extend (ds->columns, column_cnt);
-      axis_insert (ds->columns, 0, column_start, column_cnt);
-      range_map_insert (&ds->sources, column_start, column_cnt,
-                        &si->column_range);
-       }
-
-      if ( row_cnt > 0 )
-       {
-         unsigned long int row_start;
-      row_start = axis_extend (ds->rows, row_cnt);
-      axis_insert (ds->rows, 0, row_start, row_cnt);
-       }
+      casenumber n_rows;
+      size_t byte_ofs;
+      size_t i;
+
+      taint_propagate (casereader_get_taint (reader), ds->taint);
+
+      ds->proto = caseproto_ref (casereader_get_proto (reader));
+
+      ds->sources = xmalloc (sizeof *ds->sources);
+      ds->sources[0] = source_create_casereader (reader);
+      ds->n_sources = 1;
+
+      ds->n_columns = caseproto_get_n_widths (ds->proto);
+      ds->columns = xnmalloc (ds->n_columns, sizeof *ds->columns);
+      byte_ofs = 0;
+      for (i = 0; i < ds->n_columns; i++)
+        {
+          struct column *column = &ds->columns[i];
+          int width = caseproto_get_width (ds->proto, i);
+          column->source = ds->sources[0];
+          column->width = width;
+          if (width >= 0)
+            {
+              column->value_ofs = i;
+              column->byte_ofs = byte_ofs;
+              byte_ofs += width_to_n_bytes (column->width);
+            }
+        }
+
+      n_rows = source_get_backing_n_rows (ds->sources[0]);
+      if (n_rows > 0)
+        axis_insert (ds->rows, 0, axis_extend (ds->rows, n_rows), n_rows);
     }
 
   return ds;
@@ -198,21 +255,47 @@ datasheet_create (struct casereader *reader)
 void
 datasheet_destroy (struct datasheet *ds)
 {
+  size_t i;
+
   if (ds == NULL)
     return;
 
-  axis_destroy (ds->columns);
+  for (i = 0; i < ds->n_sources; i++)
+    source_destroy (ds->sources[i]);
+  free (ds->sources);
+  caseproto_unref (ds->proto);
+  free (ds->columns);
   axis_destroy (ds->rows);
-  while (!range_map_is_empty (&ds->sources))
-    {
-      struct range_map_node *r = range_map_first (&ds->sources);
-      struct source_info *si = source_info_from_range_map (r);
-      free_source_info (ds, si);
-    }
   taint_destroy (ds->taint);
   free (ds);
 }
 
+/* Returns the prototype for the cases in DS.  The caller must
+   not unref the returned prototype. */
+const struct caseproto *
+datasheet_get_proto (const struct datasheet *ds_)
+{
+  struct datasheet *ds = (struct datasheet *) ds_;
+  if (ds->proto == NULL)
+    {
+      size_t i;
+
+      ds->proto = caseproto_create ();
+      for (i = 0; i < ds->n_columns; i++)
+        ds->proto = caseproto_add_width (ds->proto, ds->columns[i].width);
+    }
+  return ds->proto;
+}
+
+/* Returns the width of the given COLUMN within DS.
+   COLUMN must be less than the number of columns in DS. */
+int
+datasheet_get_column_width (const struct datasheet *ds, size_t column)
+{
+  assert (column < datasheet_get_n_columns (ds));
+  return ds->columns[column].width;
+}
+
 /* Moves datasheet DS to a new location in memory, and returns
    the new location.  Afterward, the datasheet must not be
    accessed at its former location.
@@ -253,158 +336,225 @@ datasheet_get_taint (const struct datasheet *ds)
 
 /* Returns the number of rows in DS. */
 casenumber
-datasheet_get_row_cnt (const struct datasheet *ds)
+datasheet_get_n_rows (const struct datasheet *ds)
 {
   return axis_get_size (ds->rows);
 }
 
 /* Returns the number of columns in DS. */
 size_t
-datasheet_get_column_cnt (const struct datasheet *ds)
+datasheet_get_n_columns (const struct datasheet *ds)
 {
-  return axis_get_size (ds->columns);
+  return ds->n_columns;
 }
 
-/* Inserts CNT columns into datasheet DS just before column
-   BEFORE.  Initializes the contents of each row in the inserted
-   columns to the CNT values in INIT_VALUES.
+/* Inserts a column of the given WIDTH into datasheet DS just
+   before column BEFORE.  Initializes the contents of each row in
+   the inserted column to VALUE (which must have width WIDTH).
 
    Returns true if successful, false on failure.  In case of
    failure, the datasheet is unchanged. */
 bool
-datasheet_insert_columns (struct datasheet *ds,
-                          const union value init_values[], size_t cnt,
-                          size_t before)
+datasheet_insert_column (struct datasheet *ds,
+                         const union value *value, int width, size_t before)
 {
-  size_t added = 0;
-  while (cnt > 0)
-    {
-      unsigned long first_phy; /* First allocated physical column. */
-      unsigned long phy_cnt;   /* Number of allocated physical columns. */
+  struct column *col;
 
-      /* Allocate physical columns from the pool of available
-         columns. */
-      if (!axis_allocate (ds->columns, cnt, &first_phy, &phy_cnt))
-        {
-          /* No columns were available.  Create a new source and
-             extend the axis to make some new ones available. */
-          struct source_info *si;
-
-          phy_cnt = MAX (cnt, ds->column_min_alloc);
-          first_phy = axis_extend (ds->columns, phy_cnt);
-          ds->column_min_alloc = MIN (65536, ds->column_min_alloc * 2);
-
-          si = xmalloc (sizeof *si);
-          si->source = source_create_empty (phy_cnt);
-          range_map_insert (&ds->sources, first_phy, phy_cnt,
-                            &si->column_range);
-          if (phy_cnt > cnt)
-            {
-              axis_make_available (ds->columns, first_phy + cnt,
-                                   phy_cnt - cnt);
-              phy_cnt = cnt;
-            }
-        }
+  ds->columns = xnrealloc (ds->columns,
+                           ds->n_columns + 1, sizeof *ds->columns);
+  insert_element (ds->columns, ds->n_columns, sizeof *ds->columns, before);
+  col = &ds->columns[before];
+  ds->n_columns++;
 
-      /* Initialize the columns and insert them into the columns
-         axis. */
-      while (phy_cnt > 0)
-        {
-          struct range_map_node *r; /* Range map holding FIRST_PHY column. */
-          struct source_info *s;    /* Source containing FIRST_PHY column. */
-          size_t source_avail;      /* Number of phys columns available. */
-          size_t source_cnt;        /* Number of phys columns to use. */
-
-          /* Figure out how many columns we can and want to take
-             starting at FIRST_PHY, and then insert them into the
-             columns axis. */
-          r = range_map_lookup (&ds->sources, first_phy);
-          s = source_info_from_range_map (r);
-          source_avail = range_map_node_get_end (r) - first_phy;
-          source_cnt = MIN (phy_cnt, source_avail);
-          axis_insert (ds->columns, before, first_phy, source_cnt);
-
-          /* Initialize the data for those columns in the
-             source. */
-          if (!source_write_columns (s->source,
-                                     first_phy - range_map_node_get_start (r),
-                                     init_values, source_cnt))
-            {
-              datasheet_delete_columns (ds, before - added,
-                                        source_cnt + added);
-              taint_set_taint (ds->taint);
-              return false;
-            }
-          source_increase_use (s->source, source_cnt);
-
-          /* Advance. */
-          phy_cnt -= source_cnt;
-          first_phy += source_cnt;
-          init_values += source_cnt;
-          cnt -= source_cnt;
-          before += source_cnt;
-          added += source_cnt;
-        }
+  allocate_column (ds, width, col);
+
+  if (width >= 0 && !source_write_column (col, value))
+    {
+      datasheet_delete_columns (ds, before, 1);
+      taint_set_taint (ds->taint);
+      return false;
     }
+
   return true;
 }
 
-/* Deletes the CNT columns in DS starting from column START. */
+/* Deletes the N columns in DS starting from column START. */
 void
-datasheet_delete_columns (struct datasheet *ds, size_t start, size_t cnt)
+datasheet_delete_columns (struct datasheet *ds, size_t start, size_t n)
 {
-  size_t lcol;
-
-  assert ( start + cnt <= axis_get_size (ds->columns) );
-
-  /* Free up columns for reuse. */
-  for (lcol = start; lcol < start + cnt; lcol++)
+  if (n > 0)
     {
-      size_t pcol = axis_map (ds->columns, lcol);
-      struct range_map_node *r = range_map_lookup (&ds->sources, pcol);
-      struct source_info *si = source_info_from_range_map (r);
+      size_t i;
 
-      source_decrease_use (si->source, 1);
-      if (source_has_backing (si->source))
+      for (i = start; i < start + n; i++)
         {
-          if (!source_in_use (si->source))
-            free_source_info (ds, si);
+          struct column *column = &ds->columns[i];
+          struct source *source = column->source;
+          source_release_column (source, column->byte_ofs, column->width);
+          release_source (ds, source);
         }
-      else
-        axis_make_available (ds->columns, pcol, 1);
-    }
 
-  /* Remove columns from logical-to-physical mapping. */
-  axis_remove (ds->columns, start, cnt);
+      remove_range (ds->columns, ds->n_columns, sizeof *ds->columns, start, n);
+      ds->n_columns -= n;
+
+      caseproto_unref (ds->proto);
+      ds->proto = NULL;
+    }
 }
 
-/* Moves the CNT columns in DS starting at position OLD_START so
+/* Moves the N columns in DS starting at position OLD_START so
    that they then start at position NEW_START.  Equivalent to
    deleting the column rows, then inserting them at what becomes
-   position NEW_START after the deletion.*/
+   position NEW_START after the deletion. */
 void
 datasheet_move_columns (struct datasheet *ds,
                         size_t old_start, size_t new_start,
-                        size_t cnt)
+                        size_t n)
+{
+  move_range (ds->columns, ds->n_columns, sizeof *ds->columns,
+              old_start, new_start, n);
+
+  caseproto_unref (ds->proto);
+  ds->proto = NULL;
+}
+
+struct resize_datasheet_value_aux
+  {
+    union value src_value;
+    size_t src_ofs;
+    int src_width;
+
+    void (*resize_cb) (const union value *, union value *, void *aux);
+    void *resize_cb_aux;
+
+    union value dst_value;
+    size_t dst_ofs;
+    int dst_width;
+  };
+
+static bool
+resize_datasheet_value (const void *src, void *dst, void *aux_)
 {
-  axis_move (ds->columns, old_start, new_start, cnt);
+  struct resize_datasheet_value_aux *aux = aux_;
+
+  memcpy (value_to_data (&aux->src_value, aux->src_width),
+          (uint8_t *) src + aux->src_ofs,
+          width_to_n_bytes (aux->src_width));
+
+  aux->resize_cb (&aux->src_value, &aux->dst_value, aux->resize_cb_aux);
+
+  memcpy ((uint8_t *) dst + aux->dst_ofs,
+          value_to_data (&aux->dst_value, aux->dst_width),
+          width_to_n_bytes (aux->dst_width));
+
+  return true;
 }
 
-/* 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)
+datasheet_resize_column (struct datasheet *ds, size_t column, int new_width,
+                         void (*resize_cb) (const union value *,
+                                            union value *, void *aux),
+                         void *resize_cb_aux)
 {
-  size_t column_cnt = datasheet_get_column_cnt (ds);
-  case_create (c, column_cnt);
+  struct column old_col;
+  struct column *col;
+  int old_width;
+
+  assert (column < datasheet_get_n_columns (ds));
+
+  col = &ds->columns[column];
+  old_col = *col;
+  old_width = old_col.width;
+
+  if (new_width == -1)
+    {
+      if (old_width != -1)
+        {
+          datasheet_delete_columns (ds, column, 1);
+          datasheet_insert_column (ds, NULL, -1, column);
+        }
+    }
+  else if (old_width == -1)
+    {
+      union value value;
+      value_init (&value, new_width);
+      value_set_missing (&value, new_width);
+      if (resize_cb != NULL)
+        resize_cb (NULL, &value, resize_cb_aux);
+      datasheet_delete_columns (ds, column, 1);
+      datasheet_insert_column (ds, &value, new_width, column);
+      value_destroy (&value, new_width);
+    }
+  else if (source_has_backing (col->source))
+    {
+      unsigned long int n_rows = axis_get_size (ds->rows);
+      unsigned long int lrow;
+      union value src, dst;
+
+      source_release_column (col->source, col->byte_ofs, col->width);
+      allocate_column (ds, new_width, col);
+
+      value_init (&src, old_width);
+      value_init (&dst, new_width);
+      for (lrow = 0; lrow < n_rows; lrow++)
+        {
+          unsigned long int prow = axis_map (ds->rows, lrow);
+          if (!source_read (&old_col, prow, &src))
+            {
+              /* FIXME: back out col changes. */
+              return false;
+            }
+          resize_cb (&src, &dst, resize_cb_aux);
+          if (!source_write (col, prow, &dst))
+            {
+              /* FIXME: back out col changes. */
+              return false;
+            }
+        }
+
+      release_source (ds, old_col.source);
+    }
+  else
+    {
+      struct resize_datasheet_value_aux aux;
+
+      source_release_column (col->source, col->byte_ofs, col->width);
+      allocate_column (ds, new_width, col);
+
+      value_init (&aux.src_value, old_col.width);
+      aux.src_ofs = old_col.byte_ofs;
+      aux.src_width = old_col.width;
+      aux.resize_cb = resize_cb;
+      aux.resize_cb_aux = resize_cb_aux;
+      value_init (&aux.dst_value, new_width);
+      aux.dst_ofs = col->byte_ofs;
+      aux.dst_width = new_width;
+      sparse_xarray_copy (old_col.source->data, col->source->data,
+                          resize_datasheet_value, &aux);
+      value_destroy (&aux.src_value, old_width);
+      value_destroy (&aux.dst_value, new_width);
+
+      release_source (ds, old_col.source);
+    }
+  return true;
+}
+
+/* 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 n_columns = datasheet_get_n_columns (ds);
+  struct ccase *c = case_create (datasheet_get_proto (ds));
   if (rw_case ((struct datasheet *) ds, OP_READ,
-               row, 0, column_cnt, case_data_all_rw (c)))
-    return true;
+               row, 0, n_columns, case_data_all_rw (c)))
+    return c;
   else
     {
-      case_destroy (c);
-      return false;
+      case_unref (c);
+      return NULL;
     }
 }
 
@@ -415,44 +565,47 @@ datasheet_get_row (const struct datasheet *ds, casenumber row, struct ccase *c)
 bool
 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,
+  size_t n_columns = datasheet_get_n_columns (ds);
+  bool ok = rw_case (ds, OP_WRITE, row, 0, n_columns,
                      (union value *) case_data_all (c));
-  case_destroy (c);
+  case_unref (c);
   return ok;
 }
 
-/* Stores the values of the WIDTH columns in DS in the given ROW
-   starting at COLUMN in DS into VALUES.  Returns true if
+/* Stores the values of COLUMN in DS in the given ROW in DS into
+   VALUE.  The caller must have already initialized VALUE as a
+   value of the appropriate width (as returned by
+   datasheet_get_column_width (DS, COLUMN)).  Returns true if
    successful, false on I/O error. */
 bool
-datasheet_get_value (const struct datasheet *ds, casenumber row, size_t column,
-                     union value *value, int width)
+datasheet_get_value (const struct datasheet *ds, casenumber row,
+                     size_t column, union value *value)
 {
-  assert ( row >= 0 );
-  return rw_case ((struct datasheet *) ds,
-                  OP_READ, row, column, value_cnt_from_width (width), value);
+  assert (row >= 0);
+  return rw_case ((struct datasheet *) ds, OP_READ, row, column, 1, value);
 }
 
-/* Stores the WIDTH given VALUES into the given ROW in DS
-   starting at COLUMN.  Returns true if successful, false on I/O
-   error.  On failure, the given ROW might be partially modified
-   or corrupted. */
+/* Stores VALUE into DS in the given ROW and COLUMN.  VALUE must
+   have the correct width for COLUMN (as returned by
+   datasheet_get_column_width (DS, COLUMN)).  Returns true if
+   successful, false on I/O error.  On failure, ROW might be
+   partially modified or corrupted. */
 bool
-datasheet_put_value (struct datasheet *ds, casenumber row, size_t column,
-                     const union value *value, int width)
+datasheet_put_value (struct datasheet *ds UNUSED, casenumber row UNUSED,
+                     size_t column UNUSED, const union value *value UNUSED)
 {
-  return rw_case (ds, OP_WRITE, row, column, value_cnt_from_width (width),
-                  (union value *) value);
+  return rw_case (ds, OP_WRITE, row, column, 1, (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 +630,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;
           }
@@ -533,28 +686,28 @@ datasheet_make_reader (struct datasheet *ds)
 {
   struct casereader *reader;
   ds = datasheet_rename (ds);
-  reader = casereader_create_random (datasheet_get_column_cnt (ds),
-                                     datasheet_get_row_cnt (ds),
+  reader = casereader_create_random (datasheet_get_proto (ds),
+                                     datasheet_get_n_rows (ds),
                                      &datasheet_reader_class, ds);
   taint_propagate (datasheet_get_taint (ds), casereader_get_taint (reader));
   return reader;
 }
 
 /* "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_n_rows (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. */
@@ -582,50 +735,93 @@ static const struct casereader_random_class datasheet_reader_class =
     datasheet_reader_advance,
   };
 \f
-/* Removes SI from DS's set of sources and destroys its
-   source. */
 static void
-free_source_info (struct datasheet *ds, struct source_info *si)
+allocate_column (struct datasheet *ds, int width, struct column *column)
 {
-  range_map_delete (&ds->sources, &si->column_range);
-  source_destroy (si->source);
-  free (si);
+  caseproto_unref (ds->proto);
+  ds->proto = NULL;
+
+  column->value_ofs = -1;
+  column->width = width;
+  if (width >= 0)
+    {
+      int n_bytes;
+      size_t i;
+
+      n_bytes = width_to_n_bytes (width);
+      for (i = 0; i < ds->n_sources; i++)
+        {
+          column->source = ds->sources[i];
+          column->byte_ofs = source_allocate_column (column->source, n_bytes);
+          if (column->byte_ofs >= 0)
+            return;
+        }
+
+      column->source = source_create_empty (MAX (n_bytes,
+                                                 ds->column_min_alloc));
+      ds->sources = xnrealloc (ds->sources,
+                               ds->n_sources + 1, sizeof *ds->sources);
+      ds->sources[ds->n_sources++] = column->source;
+
+      ds->column_min_alloc = MIN (65536, ds->column_min_alloc * 2);
+
+      column->byte_ofs = source_allocate_column (column->source, n_bytes);
+      assert (column->byte_ofs >= 0);
+    }
+  else
+    {
+      column->source = NULL;
+      column->byte_ofs = -1;
+    }
 }
 
-static struct source_info *
-source_info_from_range_map (struct range_map_node *node)
+static void
+release_source (struct datasheet *ds, struct source *source)
 {
-  return range_map_data (node, struct source_info, column_range);
+  if (source_has_backing (source) && !source_in_use (source))
+    {
+      /* Since only the first source to be added ever
+         has a backing, this source must have index
+         0.  */
+      assert (source == ds->sources[0]);
+      ds->sources[0] = ds->sources[--ds->n_sources];
+      source_destroy (source);
+    }
 }
 
 /* Reads (if OP is OP_READ) or writes (if op is OP_WRITE) the
-   COLUMN_CNT columns starting from column START_COLUMN in row
-   LROW to/from the COLUMN_CNT values in DATA. */
+   N_COLUMNS columns starting from column START_COLUMN in row
+   LROW to/from the N_COLUMNS values in DATA. */
 static bool
 rw_case (struct datasheet *ds, enum rw_op op,
-         casenumber lrow, size_t start_column, size_t column_cnt,
+         casenumber lrow, size_t start_column, size_t n_columns,
          union value data[])
 {
   casenumber prow;
-  size_t lcol;
+  size_t i;
 
-  assert (lrow < datasheet_get_row_cnt (ds));
-  assert (column_cnt <= datasheet_get_column_cnt (ds));
-  assert (start_column + column_cnt <= datasheet_get_column_cnt (ds));
+  assert (lrow < datasheet_get_n_rows (ds));
+  assert (n_columns <= datasheet_get_n_columns (ds));
+  assert (start_column + n_columns <= datasheet_get_n_columns (ds));
 
   prow = axis_map (ds->rows, lrow);
-  for (lcol = start_column; lcol < start_column + column_cnt; lcol++, data++)
+  for (i = 0; i < n_columns; i++)
     {
-      size_t pcol = axis_map (ds->columns, lcol);
-      struct range_map_node *r = range_map_lookup (&ds->sources, pcol);
-      struct source_info *s = source_info_from_range_map (r);
-      size_t pcol_ofs = pcol - range_map_node_get_start (r);
-      if (!(op == OP_READ
-            ? source_read (s->source, prow, pcol_ofs, data, 1)
-            : source_write (s->source, prow, pcol_ofs, data, 1)))
+      struct column *c = &ds->columns[start_column + i];
+      if (c->width >= 0)
         {
-          taint_set_taint (ds->taint);
-          return false;
+          bool ok;
+
+          if (op == OP_READ)
+            ok = source_read (c, prow, &data[i]);
+          else
+            ok = source_write (c, prow, &data[i]);
+
+          if (!ok)
+            {
+              taint_set_taint (ds->taint);
+              return false;
+            }
         }
     }
   return true;
@@ -638,25 +834,23 @@ rw_case (struct datasheet *ds, enum rw_op op,
    axis_map and axis_get_size functions inspect this mapping, and
    the axis_insert, axis_remove, and axis_move functions modify
    it.  Second, it tracks the set of ordinates that are unused
-   and available for reuse.  (Not all unused ordinates are
-   available for reuse: in particular, unused columns that are
-   backed by a casereader are never reused.)  The axis_allocate,
+   and available for reuse.  The axis_allocate,
    axis_make_available, and axis_extend functions affect the set
    of available ordinates. */
 struct axis
-  {
-    struct tower log_to_phy;     /* Map from logical to physical ordinates;
-                                    contains "struct axis_group"s. */
-    struct range_set *available; /* Set of unused, available ordinates. */
-    unsigned long int phy_size;  /* Current physical length of axis. */
-  };
+{
+  struct tower log_to_phy;     /* Map from logical to physical ordinates;
+                                 contains "struct axis_group"s. */
+  struct range_set *available; /* Set of unused, available ordinates. */
+  unsigned long int phy_size;  /* Current physical length of axis. */
+};
 
 /* A mapping from logical to physical ordinates. */
 struct axis_group
-  {
-    struct tower_node logical;  /* Range of logical ordinates. */
-    unsigned long phy_start;    /* First corresponding physical ordinate. */
-  };
+{
+  struct tower_node logical;  /* Range of logical ordinates. */
+  unsigned long phy_start;    /* First corresponding physical ordinate. */
+};
 
 static struct axis_group *axis_group_from_tower_node (struct tower_node *);
 static struct tower_node *make_axis_group (unsigned long int phy_start);
@@ -694,7 +888,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 +912,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 +1116,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 +1156,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 +1174,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 +1202,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);
       }
@@ -1016,24 +1210,21 @@ check_axis_merged (const struct axis *axis UNUSED)
 }
 \f
 /* A source. */
-struct source
-  {
-    size_t columns_used;        /* Number of columns in use by client. */
-    struct sparse_cases *data;  /* Data at top level, atop the backing. */
-    struct casereader *backing; /* Backing casereader (or null). */
-    casenumber backing_rows;    /* Number of rows in backing (if nonnull). */
-  };
 
-/* Creates and returns an empty, unbacked source with COLUMN_CNT
-   columns and an initial "columns_used" of 0. */
+/* Creates and returns an empty, unbacked source with N_BYTES
+   bytes per case, none of which are initially in use. */
 static struct source *
-source_create_empty (size_t column_cnt)
+source_create_empty (size_t n_bytes)
 {
   struct source *source = xmalloc (sizeof *source);
-  source->columns_used = 0;
-  source->data = sparse_cases_create (column_cnt);
+  size_t row_size = n_bytes + 4 * sizeof (void *);
+  size_t max_memory_rows = settings_get_workspace () / row_size;
+  source->avail = range_set_create ();
+  range_set_insert (source->avail, 0, n_bytes);
+  source->data = sparse_xarray_create (n_bytes, MAX (max_memory_rows, 4));
   source->backing = NULL;
   source->backing_rows = 0;
+  source->n_used = 0;
   return source;
 }
 
@@ -1042,10 +1233,22 @@ source_create_empty (size_t column_cnt)
 static struct source *
 source_create_casereader (struct casereader *reader)
 {
-  struct source *source
-    = source_create_empty (casereader_get_value_cnt (reader));
+  const struct caseproto *proto = casereader_get_proto (reader);
+  size_t n_bytes = caseproto_to_n_bytes (proto);
+  struct source *source = source_create_empty (n_bytes);
+  size_t n_columns;
+  size_t i;
+
+  range_set_delete (source->avail, 0, n_bytes);
   source->backing = reader;
   source->backing_rows = casereader_count_cases (reader);
+
+  source->n_used = 0;
+  n_columns = caseproto_get_n_widths (proto);
+  for (i = 0; i < n_columns; i++)
+    if (caseproto_get_width (proto, i) >= 0)
+      source->n_used++;
+
   return source;
 }
 
@@ -1058,10 +1261,11 @@ static struct source *
 source_clone (const struct source *old)
 {
   struct source *new = xmalloc (sizeof *new);
-  new->columns_used = old->columns_used;
-  new->data = sparse_cases_clone (old->data);
+  new->avail = range_set_clone (old->avail, NULL);
+  new->data = sparse_xarray_clone (old->data);
   new->backing = old->backing != NULL ? casereader_clone (old->backing) : NULL;
   new->backing_rows = old->backing_rows;
+  new->n_used = old->n_used;
   if (new->data == NULL)
     {
       source_destroy (new);
@@ -1070,23 +1274,28 @@ source_clone (const struct source *old)
   return new;
 }
 
-/* Increases the columns_used count of SOURCE by DELTA.
-   The new value must not exceed SOURCE's number of columns. */
-static void
-source_increase_use (struct source *source, size_t delta)
+static int
+source_allocate_column (struct source *source, int width)
 {
-  source->columns_used += delta;
-  assert (source->columns_used <= sparse_cases_get_value_cnt (source->data));
+  unsigned long int start;
+  int n_bytes;
+
+  assert (width >= 0);
+  n_bytes = width_to_n_bytes (width);
+  if (source->backing == NULL
+      && range_set_allocate_fully (source->avail, n_bytes, &start))
+    return start;
+  else
+    return -1;
 }
 
-/* Decreases the columns_used count of SOURCE by DELTA.
-   This must not attempt to decrease the columns_used count below
-   zero. */
 static void
-source_decrease_use (struct source *source, size_t delta)
+source_release_column (struct source *source, int ofs, int width)
 {
-  assert (delta <= source->columns_used);
-  source->columns_used -= delta;
+  assert (width >= 0);
+  range_set_insert (source->avail, ofs, width_to_n_bytes (width));
+  if (source->backing != NULL)
+    source->n_used--;
 }
 
 /* Returns true if SOURCE has any columns in use,
@@ -1094,7 +1303,7 @@ source_decrease_use (struct source *source, size_t delta)
 static bool
 source_in_use (const struct source *source)
 {
-  return source->columns_used > 0;
+  return source->n_used > 0;
 }
 
 /* Destroys SOURCE and its data and backing, if any. */
@@ -1103,7 +1312,8 @@ source_destroy (struct source *source)
 {
   if (source != NULL)
     {
-      sparse_cases_destroy (source->data);
+      range_set_destroy (source->avail);
+      sparse_xarray_destroy (source->data);
       casereader_destroy (source->backing);
       free (source);
     }
@@ -1112,89 +1322,99 @@ source_destroy (struct source *source)
 /* Returns the number of rows in SOURCE's backing casereader
    (SOURCE must have a backing casereader). */
 static casenumber
-source_get_backing_row_cnt (const struct source *source)
+source_get_backing_n_rows (const struct source *source)
 {
   assert (source_has_backing (source));
   return source->backing_rows;
 }
 
-/* Returns the number of columns in SOURCE. */
-static size_t
-source_get_column_cnt (const struct source *source)
-{
-  return sparse_cases_get_value_cnt (source->data);
-}
+/* Reads the given COLUMN from SOURCE in the given ROW, into
+   VALUE.  Returns true if successful, false on I/O error.
 
-/* Reads VALUE_CNT columns from SOURCE in the given ROW, starting
-   from COLUMN, into VALUES.  Returns true if successful, false
-   on I/O error. */
+   The caller must have initialized VALUE with the proper
+   width. */
 static bool
-source_read (const struct source *source,
-             casenumber row, size_t column,
-             union value values[], size_t value_cnt)
+source_read (const struct column *column, casenumber row, union value *value)
 {
-  if (source->backing == NULL || sparse_cases_contains_row (source->data, row))
-    return sparse_cases_read (source->data, row, column, values, value_cnt);
+  struct source *source = column->source;
+
+  assert (column->width >= 0);
+  if (source->backing == NULL
+      || sparse_xarray_contains_row (source->data, row))
+    return sparse_xarray_read (source->data, row, column->byte_ofs,
+                               width_to_n_bytes (column->width),
+                               value_to_data (value, column->width));
   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);
+          value_copy (value, case_data_idx (c, column->value_ofs),
+                      column->width);
+          case_unref (c);
         }
       return ok;
     }
 }
 
-/* Writes the VALUE_CNT values in VALUES to SOURCE in the given
-   ROW, starting at ROW.  Returns true if successful, false on
-   I/O error.  On error, the row's data may be completely or
-   partially corrupted, both inside and outside the region to be
-   written.  */
 static bool
-source_write (struct source *source,
-              casenumber row, size_t column,
-              const union value values[], size_t value_cnt)
+copy_case_into_source (struct source *source, struct ccase *c, casenumber row)
 {
-  size_t column_cnt = sparse_cases_get_value_cnt (source->data);
-  bool ok;
+  const struct caseproto *proto = casereader_get_proto (source->backing);
+  size_t n_widths = caseproto_get_n_widths (proto);
+  size_t ofs;
+  size_t i;
 
-  if (source->backing == NULL
-      || (column == 0 && value_cnt == column_cnt)
-      || sparse_cases_contains_row (source->data, row))
-    ok = sparse_cases_write (source->data, row, column, values, value_cnt);
-  else
+  ofs = 0;
+  for (i = 0; i < n_widths; i++)
     {
-      struct ccase c;
-      if (row < source->backing_rows)
-        ok = casereader_peek (source->backing, row, &c);
-      else
-        {
-          /* It's not one of the backed rows.  Ideally, this
-             should never happen: we'd always be writing the full
-             contents of new, unbacked rows in a single call to
-             this function, so that the first case above would
-             trigger.  But that's a little difficult at higher
-             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;
-        }
-      if (ok)
+      int width = caseproto_get_width (proto, i);
+      if (width >= 0)
         {
-          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);
+          int n_bytes = width_to_n_bytes (width);
+          if (!sparse_xarray_write (source->data, row, ofs, n_bytes,
+                                    value_to_data (case_data_idx (c, i),
+                                                   width)))
+            return false;
+          ofs += n_bytes;
         }
     }
-  return ok;
+  return true;
+}
+
+/* Writes VALUE to SOURCE in the given ROW and COLUMN.  Returns
+   true if successful, false on I/O error.  On error, the row's
+   data may be completely or partially corrupted, both inside and
+   outside the region to be written.  */
+static bool
+source_write (const struct column *column, casenumber row,
+              const union value *value)
+{
+  struct source *source = column->source;
+  struct casereader *backing = source->backing;
+
+  assert (column->width >= 0);
+  if (backing != NULL
+      && !sparse_xarray_contains_row (source->data, row)
+      && row < source->backing_rows)
+    {
+      struct ccase *c;
+      bool ok;
+
+      c = casereader_peek (backing, row);
+      if (c == NULL)
+        return false;
+
+      ok = copy_case_into_source (source, c, row);
+      case_unref (c);
+      if (!ok)
+        return false;
+    }
+
+  return sparse_xarray_write (source->data, row, column->byte_ofs,
+                              width_to_n_bytes (column->width),
+                              value_to_data (value, column->width));
 }
 
 /* Within SOURCE, which must not have a backing casereader,
@@ -1204,17 +1424,20 @@ source_write (struct source *source,
    false if an I/O error occurs.
 
    We don't support backing != NULL because (1) it's harder and
-   (2) source_write_columns is only called by
-   datasheet_insert_columns, which doesn't reuse columns from
+   (2) this function is only called by
+   datasheet_insert_column, which doesn't reuse columns from
    sources that are backed by casereaders. */
 static bool
-source_write_columns (struct source *source, size_t start_column,
-                      const union value values[], size_t value_cnt)
+source_write_column (struct column *column, const union value *value)
 {
-  assert (source->backing == NULL);
+  int width = column->width;
+
+  assert (column->source->backing == NULL);
+  assert (width >= 0);
 
-  return sparse_cases_write_columns (source->data, start_column,
-                                     values, value_cnt);
+  return sparse_xarray_write_columns (column->source->data, column->byte_ofs,
+                                      width_to_n_bytes (width),
+                                      value_to_data (value, width));
 }
 
 /* Returns true if SOURCE has a backing casereader, false
@@ -1227,485 +1450,69 @@ source_has_backing (const struct source *source)
 \f
 /* Datasheet model checker test driver. */
 
-/* Maximum size of datasheet supported for model checking
-   purposes. */
-#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)
+static int
+get_source_index (const struct datasheet *ds, const struct source *source)
 {
-  unsigned int hash[DIV_RND_UP (20, sizeof (unsigned int))];
-  struct md4_ctx ctx;
-  struct range_map_node *r;
+  size_t i;
 
-  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];
+  for (i = 0; i < ds->n_sources; i++)
+    if (ds->sources[i] == source)
+      return i;
+  NOT_REACHED ();
 }
 
 /* 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;
-  struct range_map_node *r;
+  size_t i;
 
   ds = xmalloc (sizeof *ds);
-  ds->columns = axis_clone (ods->columns);
-  ds->rows = axis_clone (ods->rows);
-  range_map_init (&ds->sources);
-  for (r = range_map_first (&ods->sources); r != NULL;
-       r = range_map_next (&ods->sources, r))
-    {
-      const struct source_info *osi = source_info_from_range_map (r);
-      struct source_info *si = xmalloc (sizeof *si);
-      si->source = source_clone (osi->source);
-      range_map_insert (&ds->sources, range_map_node_get_start (r),
-                        range_map_node_get_width (r), &si->column_range);
-    }
-  ds->column_min_alloc = ods->column_min_alloc;
-  ds->taint = taint_create ();
-
-  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);
-}
+  ds->sources = xmalloc (ods->n_sources * sizeof *ds->sources);
+  for (i = 0; i < ods->n_sources; i++)
+    ds->sources[i] = source_clone (ods->sources[i]);
+  ds->n_sources = ods->n_sources;
+
+  ds->proto = ods->proto != NULL ? caseproto_ref (ods->proto) : NULL;
+  ds->columns = xmemdup (ods->columns, ods->n_columns * sizeof *ods->columns);
+  for (i = 0; i < ods->n_columns; i++)
+    ds->columns[i].source
+      = ds->sources[get_source_index (ods, ods->columns[i].source)];
+  ds->n_columns = ods->n_columns;
+  ds->column_min_alloc = ods->column_min_alloc;
 
-/* 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;
+  ds->rows = axis_clone (ods->rows);
 
-  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;
-      }
-}
+  ds->taint = taint_create ();
 
-/* 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);
+  return ds;
 }
 
-/* "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;
+  size_t i;
 
-  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);
+  for (i = 0; i < ds->n_columns; i++)
     {
-      /* 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);
+      const struct column *column = &ds->columns[i];
+      int source_n_bytes = sparse_xarray_get_n_columns (column->source->data);
+      md4_process_bytes (&source_n_bytes, sizeof source_n_bytes, &ctx);
+      /*md4_process_bytes (&column->byte_ofs, sizeof column->byte_ofs, &ctx);*/
+      md4_process_bytes (&column->value_ofs, sizeof column->value_ofs, &ctx);
+      md4_process_bytes (&column->width, sizeof column->width, &ctx);
     }
-}
-
-/* "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);
+  axis_hash (ds->rows, &ctx);
+  md4_process_bytes (&ds->column_min_alloc, sizeof ds->column_min_alloc, &ctx);
+  md4_finish_ctx (&ctx, hash);
+  return hash[0];
 }
index 3d106193d1cd393bec14297a392bb68bac983604..0508896ae9e30876be39c4e749aabe50d4e572ee 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 <data/case.h>
 #include <data/value.h>
 
+struct caseproto;
 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.  */
+/* A datasheet is a 2-d array of "union value"s 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 *);
 
+const struct caseproto *datasheet_get_proto (const struct datasheet *);
+int datasheet_get_column_width (const struct datasheet *, size_t column);
+
 bool datasheet_error (const struct datasheet *);
 void datasheet_force_error (struct datasheet *);
 const struct taint *datasheet_get_taint (const struct datasheet *);
@@ -38,19 +42,22 @@ 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);
+size_t datasheet_get_n_columns (const struct datasheet *);
+bool datasheet_insert_column (struct datasheet *,
+                              const union value *, int width, 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);
+bool datasheet_resize_column (struct datasheet *, size_t column, int new_width,
+                              void (*resize_cb) (const union value *,
+                                                 union value *, void *aux),
+                              void *aux);
 
 /* Rows. */
-casenumber datasheet_get_row_cnt (const struct datasheet *);
+casenumber datasheet_get_n_rows (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 +66,15 @@ 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);
+                          union value *);
 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;
+                          const union value *);
 
-    /* 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..67af049b24fee7a72e3dd6b6561a6784f87a70b7 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 <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>
@@ -50,6 +51,8 @@ struct dictionary
   {
     struct variable **var;     /* Variables. */
     size_t var_cnt, var_cap;    /* Number of variables, capacity. */
+    struct caseproto *proto;    /* Prototype for dictionary cases
+                                   (updated lazily). */
     struct hsh_table *name_tab;        /* Variable index by name. */
     int next_value_idx;         /* Index of next `union value' to allocate. */
     const struct variable **split;    /* SPLIT FILE vars. */
@@ -61,11 +64,51 @@ 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. */
+
+    char *encoding;             /* Character encoding of string data */
+
     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_encoding (struct dictionary *d, const char *enc)
+{
+  if (enc)
+    d->encoding = xstrdup (enc);
+}
+
+const char *
+dict_get_encoding (const struct dictionary *d)
+{
+  return d->encoding ;
+}
+
+
+void
+dict_set_change_callback (struct dictionary *d,
+                         void (*changed) (struct dictionary *, void*),
+                         void *data)
+{
+  d->changed = changed;
+  d->changed_data = data;
+}
+
+/* Discards dictionary D's caseproto.  (It will be regenerated
+   lazily, on demand.) */
+static void
+invalidate_proto (struct dictionary *d)
+{
+  caseproto_unref (d->proto);
+  d->proto = NULL;
+}
+
 /* Print a representation of dictionary D to stdout, for
    debugging purposes. */
 void
@@ -115,6 +158,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 +222,11 @@ dict_clone (const struct dictionary *s)
   for (i = 0; i < s->vector_cnt; i++)
     d->vector[i] = vector_clone (s->vector[i], s, d);
 
+  if ( s->encoding)
+    d->encoding = xstrdup (s->encoding);
+
+  dict_set_attributes (d, dict_get_attributes (s));
+
   return d;
 }
 
@@ -198,6 +247,7 @@ dict_clear (struct dictionary *d)
   free (d->var);
   d->var = NULL;
   d->var_cnt = d->var_cap = 0;
+  invalidate_proto (d);
   hsh_clear (d->name_tab);
   d->next_value_idx = 0;
   dict_set_split_vars (d, NULL, 0);
@@ -208,6 +258,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 +286,7 @@ dict_destroy (struct dictionary *d)
 
       dict_clear (d);
       hsh_destroy (d->name_tab);
+      attrset_destroy (&d->attributes);
       free (d);
     }
 }
@@ -327,10 +379,12 @@ 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);
 
-  d->next_value_idx += var_get_value_cnt (v);
+  d->next_value_idx++;
+  invalidate_proto (d);
 
   return v;
 }
@@ -454,6 +508,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);
 }
@@ -496,7 +551,7 @@ dict_delete_var (struct dictionary *d, struct variable *v)
 {
   int dict_index = var_get_dict_index (v);
   const int case_index = var_get_case_index (v);
-  const int val_cnt = var_get_value_cnt (v);
+  const int width = var_get_width (v);
 
   assert (dict_contains_var (d, v));
 
@@ -528,9 +583,11 @@ 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);
 
+  invalidate_proto (d);
   if (d->callbacks &&  d->callbacks->var_deleted )
-    d->callbacks->var_deleted (d, dict_index, case_index, val_cnt, d->cb_data);
+    d->callbacks->var_deleted (d, dict_index, case_index, width, d->cb_data);
 }
 
 /* Deletes the COUNT variables listed in VARS from D.  This is
@@ -659,6 +716,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 +956,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 +985,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,
@@ -952,6 +1012,25 @@ dict_set_case_limit (struct dictionary *d, casenumber case_limit)
   d->case_limit = case_limit;
 }
 
+/* Returns the prototype used for cases created by dictionary D. */
+const struct caseproto *
+dict_get_proto (const struct dictionary *d_)
+{
+  struct dictionary *d = (struct dictionary *) d_;
+  if (d->proto == NULL)
+    {
+      size_t i;
+
+      d->proto = caseproto_create ();
+      d->proto = caseproto_reserve (d->proto, d->var_cnt);
+      for (i = 0; i < d->var_cnt; i++)
+        d->proto = caseproto_set_width (d->proto,
+                                        var_get_case_index (d->var[i]),
+                                        var_get_width (d->var[i]));
+    }
+  return d->proto;
+}
+
 /* Returns the case index of the next value to be added to D.
    This value is the number of `union value's that need to be
    allocated to store a case for dictionary D. */
@@ -984,37 +1063,11 @@ dict_compact_values (struct dictionary *d)
   for (i = 0; i < d->var_cnt; i++)
     {
       struct variable *v = d->var[i];
-      set_var_case_index (v, d->next_value_idx);
-      d->next_value_idx += var_get_value_cnt (v);
+      set_var_case_index (v, d->next_value_idx++);
     }
+  invalidate_proto (d);
 }
 
-/*
-   Reassigns case indices for D, increasing each index above START by
-   the value PADDING.
-*/
-static void
-dict_pad_values (struct dictionary *d, int start, int padding)
-{
-  size_t i;
-
-  if ( padding <= 0 ) 
-       return;
-
-  for (i = 0; i < d->var_cnt; ++i)
-    {
-      struct variable *v = d->var[i];
-
-      int index = var_get_case_index (v);
-
-      if ( index >= start)
-       set_var_case_index (v, index + padding);
-    }
-
-  d->next_value_idx += padding;
-}
-
-
 /* Returns the number of values occupied by the variables in
    dictionary D.  All variables are considered if EXCLUDE_CLASSES
    is 0, or it may contain one or more of (1u << DC_ORDINARY),
@@ -1040,10 +1093,38 @@ dict_count_values (const struct dictionary *d, unsigned int exclude_classes)
     {
       enum dict_class class = var_get_dict_class (d->var[i]);
       if (!(exclude_classes & (1u << class)))
-        cnt += var_get_value_cnt (d->var[i]);
+        cnt++;
     }
   return cnt;
 }
+
+/* Returns the case prototype that would result after deleting
+   all variables from D that are not in one of the
+   EXCLUDE_CLASSES and compacting the dictionary with
+   dict_compact().
+
+   The caller must unref the returned caseproto when it is no
+   longer needed. */
+struct caseproto *
+dict_get_compacted_proto (const struct dictionary *d,
+                          unsigned int exclude_classes)
+{
+  struct caseproto *proto;
+  size_t i;
+
+  assert ((exclude_classes & ~((1u << DC_ORDINARY)
+                               | (1u << DC_SYSTEM)
+                               | (1u << DC_SCRATCH))) == 0);
+
+  proto = caseproto_create ();
+  for (i = 0; i < d->var_cnt; i++)
+    {
+      struct variable *v = d->var[i];
+      if (!(exclude_classes & (1u << var_get_dict_class (v))))
+        proto = caseproto_add_width (proto, var_get_width (v));
+    }
+  return proto;
+}
 \f
 /* Returns the SPLIT FILE vars (see cmd_split_file()).  Call
    dict_get_split_cnt() to determine how many SPLIT FILE vars
@@ -1080,6 +1161,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 +1189,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);
 }
@@ -1180,7 +1263,7 @@ dict_add_document_line (struct dictionary *d, const char *line)
       msg (SW, _("Truncating document line to %d bytes."), DOC_LINE_LENGTH);
     }
   buf_copy_str_rpad (ds_put_uninit (&d->documents, DOC_LINE_LENGTH),
-                     DOC_LINE_LENGTH, line);
+                     DOC_LINE_LENGTH, line, ' ');
 }
 
 /* Returns the number of document lines in dictionary D. */
@@ -1285,6 +1368,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 +1404,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);
     }
@@ -1304,7 +1417,7 @@ dict_var_changed (const struct variable *v)
 /* Called from variable.c to notify the dictionary that the variable's width
    has changed */
 void
-dict_var_resized (const struct variable *v, int delta)
+dict_var_resized (const struct variable *v, int old_width)
 {
   if ( var_has_vardict (v))
     {
@@ -1313,9 +1426,30 @@ dict_var_resized (const struct variable *v, int delta)
 
       d = vdi->dict;
 
-      dict_pad_values (d, var_get_case_index(v) + 1, delta);
+      if (d->changed) d->changed (d, d->changed_data);
 
+      invalidate_proto (d);
       if ( d->callbacks && d->callbacks->var_resized )
-       d->callbacks->var_resized (d, var_get_dict_index (v), delta, d->cb_data);
+       d->callbacks->var_resized (d, var_get_dict_index (v), old_width,
+                                   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..02fd5cd4c0195f3ed73c994d04571b4988a796d3 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
@@ -99,6 +99,7 @@ casenumber dict_get_case_limit (const struct dictionary *);
 void dict_set_case_limit (struct dictionary *, casenumber);
 
 /* Size of cases for this dictionary. */
+const struct caseproto *dict_get_proto (const struct dictionary *);
 int dict_get_next_value_idx (const struct dictionary *);
 size_t dict_get_case_size (const struct dictionary *);
 
@@ -107,6 +108,8 @@ size_t dict_get_case_size (const struct dictionary *);
 size_t dict_count_values (const struct dictionary *,
                           unsigned int exclude_classes);
 void dict_compact_values (struct dictionary *);
+struct caseproto *dict_get_compacted_proto (const struct dictionary *,
+                                            unsigned int exclude_classes);
 
 /* SPLIT FILE variables. */
 const struct variable *const *dict_get_split_vars (const struct dictionary *);
@@ -142,6 +145,16 @@ 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 *);
+
+
+void dict_set_encoding (struct dictionary *d, const char *enc);
+const char *dict_get_encoding (const struct dictionary *d);
+
+
 /* Functions to be called upon dictionary changes. */
 struct dict_callbacks
  {
@@ -152,12 +165,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 5d807ab9a0a5a5b9867836bd62e9daf9ae494281..6ed3f8f9f95205158cbf2a33227e5dbec6a81529 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
@@ -49,7 +49,7 @@ struct file_handle
     /* FH_REF_FILE only. */
     char *file_name;           /* File name as provided by user. */
     enum fh_mode mode;         /* File mode. */
-    enum legacy_encoding encoding;/* File encoding. */
+    const char *encoding;       /* File encoding. */
 
     /* FH_REF_FILE and FH_REF_INLINE only. */
     size_t record_width;        /* Length of fixed-format records. */
@@ -325,7 +325,7 @@ fh_get_tab_width (const struct file_handle *handle)
 }
 
 /* Returns the encoding of characters read from HANDLE. */
-enum legacy_encoding
+const char *
 fh_get_legacy_encoding (const struct file_handle *handle)
 {
   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
@@ -592,10 +592,12 @@ static unsigned int
 hash_fh_lock (const void *lock_, const void *aux UNUSED)
 {
   const struct fh_lock *lock = lock_;
-  unsigned int hash = hsh_hash_int ((lock->referent << 3) | lock->access);
+  unsigned int basis;
   if (lock->referent == FH_REF_FILE)
-    hash ^= fn_hash_identity (lock->u.file);
+    basis = fn_hash_identity (lock->u.file);
   else if (lock->referent == FH_REF_SCRATCH)
-    hash ^= hsh_hash_int (lock->u.unique_id);
-  return hash;
+    basis = lock->u.unique_id;
+  else
+    basis = 0;
+  return hash_int ((lock->referent << 3) | lock->access, basis);
 }
index 73e118cdf848f81e1e6405983fec02b45f0a6a9c..b4a6d6100e8ed291ed8705a659d381beb8ec26a6 100644 (file)
@@ -54,7 +54,7 @@ struct fh_properties
     enum fh_mode mode;          /* File mode. */
     size_t record_width;        /* Length of fixed-format records. */
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
-    enum legacy_encoding encoding;/* ASCII or EBCDIC? */
+    const char *encoding;       /* ASCII or EBCDIC? */
   };
 
 void fh_init (void);
@@ -89,7 +89,7 @@ enum fh_mode fh_get_mode (const struct file_handle *) ;
 /* Properties of FH_REF_FILE and FH_REF_INLINE file handles. */
 size_t fh_get_record_width (const struct file_handle *);
 size_t fh_get_tab_width (const struct file_handle *);
-enum legacy_encoding fh_get_legacy_encoding (const struct file_handle *);
+const char *fh_get_legacy_encoding (const struct file_handle *);
 
 /* Properties of FH_REF_SCRATCH file handles. */
 struct scratch_handle *fh_get_scratch_handle (const struct file_handle *);
index e9411b65d950d444ee07dbfd155bdaeb982d1351..601afd5579e04a2ffff00193166989aedcd8e3b5 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
@@ -448,37 +448,51 @@ fn_compare_file_identities (const struct file_identity *a,
 unsigned int
 fn_hash_identity (const struct file_identity *identity)
 {
-  unsigned int hash = identity->device ^ identity->inode;
+  unsigned int hash = hash_int (identity->device, identity->inode);
   if (identity->name != NULL)
-    hash ^= hsh_hash_string (identity->name);
+    hash = hash_string (identity->name, hash);
   return hash;
 }
 
-
-
-#ifdef WINDOWS32
+#ifdef WIN32
 
 /* Apparently windoze users like to see output dumped into their home directory,
    not the current directory (!) */
 const char *
 default_output_path (void)
 {
-  static const char *home_dir = NULL;
+  static char *path = NULL;
 
-  /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
-     to HOME, because the user can change HOME.  */
-  if (home_dir == NULL)
+  if ( path == NULL)
     {
-      const char *home_drive = getenv ("HOMEDRIVE");
-      const char *home_path = getenv ("HOMEPATH");
+      /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give preference
+        to HOME, because the user can change HOME.  */
 
-      if (home_drive != NULL && home_path != NULL)
-       home_dir = xasprintf ("%s%s%c",
-                             home_drive, home_path, DIRECTORY_SEPARATOR);
-      else
-       home_dir = "c:/users/default/"; /* poor default */
+      const char *home_dir = getenv ("HOME");
+      int i;
+
+      if (home_dir == NULL)
+       {
+         const char *home_drive = getenv ("HOMEDRIVE");
+         const char *home_path = getenv ("HOMEPATH");
+
+
+         if (home_drive != NULL && home_path != NULL)
+           home_dir = xasprintf ("%s%s",
+                                 home_drive, home_path);
+       }
+
+      if (home_dir == NULL)
+       home_dir = "c:/users/default"; /* poor default */
+
+      path = xasprintf ("%s%c", home_dir, '/');
+
+
+      for(i = 0; i < strlen (path); i++)
+       if (path[i] == '\\') path[i] = '/';
     }
-  return home_dir;
+
+  return path;
 }
 
 #else
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..f166ee6aba86f1a00acefb409c466e253635a574 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
@@ -19,6 +19,9 @@
 #include <config.h>
 
 #include <libpspp/message.h>
+#include <libpspp/misc.h>
+
+#include "minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -57,11 +60,12 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 #include <data/identifier.h>
 #include <assert.h>
 
+/* Default width of string variables. */
+#define GNUMERIC_DEFAULT_WIDTH 8
 
 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 =
   {
@@ -169,10 +173,9 @@ struct gnumeric_reader
   int stop_row;
   int stop_col;
 
-
-  size_t value_cnt;
+  struct caseproto *proto;
   struct dictionary *dict;
-  struct ccase first_case;
+  struct ccase *first_case;
   bool used_first_case;
 };
 
@@ -193,7 +196,9 @@ 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);
+
+  caseproto_unref (r->proto);
 
   free (r);
 }
@@ -315,18 +320,17 @@ static void
 convert_xml_string_to_value (struct ccase *c, const struct variable *var,
                             const xmlChar *xv)
 {
-  char *text;
   int n_bytes = 0;
   union value *v = case_data_rw (c, var);
 
-  text = recode_string (CONV_UTF8_TO_PSPP, (const char *) xv, -1);
+  const char *text = (const char *) xv;
 
   if ( text)
     n_bytes = MIN (var_get_width (var), strlen (text));
 
   if ( var_is_alpha (var))
     {
-      memcpy (v->s, text, n_bytes);
+      memcpy (value_str_rw (v, var_get_width (var)), text, n_bytes);
     }
   else
     {
@@ -336,8 +340,6 @@ convert_xml_string_to_value (struct ccase *c, const struct variable *var,
       if ( errno != 0 || endptr == text)
        v->f = SYSMIS;
     }
-
-  free (text);
 }
 
 struct var_spec
@@ -363,7 +365,7 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 
   if ( NULL == gz)
     {
-      msg (ME, _("Error opening \"%s\" for reading as a gnumeric file: %s."),
+      msg (ME, _("Error opening \"%s\" for reading as a Gnumeric file: %s."),
            gri->file_name, strerror (errno));
 
       goto error;
@@ -452,7 +454,7 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
       if ( idx  >= n_var_specs )
        {
          n_var_specs =  idx + 1 ;
-         var_spec = realloc (var_spec, sizeof (*var_spec) * n_var_specs);
+         var_spec = xrealloc (var_spec, sizeof (*var_spec) * n_var_specs);
          var_spec [idx].name = NULL;
          var_spec [idx].width = -1;
          var_spec [idx].first_value = NULL;
@@ -460,16 +462,14 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 
       if ( r->node_type == XML_READER_TYPE_TEXT )
        {
-         char *text ;
          xmlChar *value = xmlTextReaderValue (r->xtr);
-
-         text = recode_string (CONV_UTF8_TO_PSPP, (const char *) value, -1);
+         const char *text  = (const char *) value;
 
          if ( r->row < r->start_row)
            {
              if ( gri->read_names )
                {
-                 var_spec [idx].name = strdup (text);
+                 var_spec [idx].name = xstrdup (text);
                }
            }
          else
@@ -478,11 +478,10 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 
              if (-1 ==  var_spec [idx].width )
                var_spec [idx].width = (gri->asw == -1) ?
-                 ROUND_UP (strlen(text), MAX_SHORT_STRING) : gri->asw;
+                 ROUND_UP (strlen(text), GNUMERIC_DEFAULT_WIDTH) : gri->asw;
            }
 
          free (value);
-         free (text);
        }
       else if ( r->node_type == XML_READER_TYPE_ELEMENT
                && r->state == STATE_CELL)
@@ -504,7 +503,7 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
   /* Create the dictionary and populate it */
   *dict = r->dict = dict_create ();
 
-  r->value_cnt = 0;
+  dict_set_encoding (r->dict, (const char *) xmlTextReaderConstEncoding (r->xtr));
 
   for (i = 0 ; i < n_var_specs ; ++i )
     {
@@ -513,9 +512,7 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
       /* Probably no data exists for this variable, so allocate a
         default width */
       if ( var_spec[i].width == -1 )
-       var_spec[i].width = MAX_SHORT_STRING;
-
-      r->value_cnt += value_cnt_from_width (var_spec[i].width);
+       var_spec[i].width = GNUMERIC_DEFAULT_WIDTH;
 
       if  ( ! dict_make_unique_var_name (r->dict, var_spec[i].name,
                                         &vstart, name))
@@ -537,15 +534,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,
-         ' ', MAX_SHORT_STRING * r->value_cnt);
+  r->proto = caseproto_ref (dict_get_proto (r->dict));
+  r->first_case = case_create (r->proto);
+  case_set_missing (r->first_case);
 
   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);
     }
 
@@ -559,7 +556,7 @@ gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
 
   return casereader_create_sequential
     (NULL,
-     r->value_cnt,
+     r->proto,
      n_cases,
      &gnm_file_casereader_class, r);
 
@@ -580,12 +577,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,14 +590,12 @@ 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);
-
-  memset (case_data_rw_idx (c, 0)->s, ' ', MAX_SHORT_STRING * r->value_cnt);
+  c = case_create (r->proto);
+  case_set_missing (c);
 
   while ((r->state == STATE_CELL || r->state == STATE_CELLS_START )
         && r->row == current_row && (ret = xmlTextReaderRead (r->xtr)))
@@ -611,7 +606,7 @@ gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_,
                                     r->col > r->stop_col))
        continue;
 
-      if ( r->col - r->start_col >= r->value_cnt)
+      if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
        continue;
 
       if ( r->stop_row != -1 && r->row > r->stop_row)
@@ -632,7 +627,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 a52944e2757a11db7ace38273283d081581d0375..7bd2261e60f15ac8f162996b7ee346c1b0eb5468 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2005 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -35,7 +35,7 @@ bool
 lex_is_id1 (char c_)
 {
   unsigned char c = c_;
-  return isalpha (c) || c == '@' || c == '#' || c == '$';
+  return isalpha (c) || c == '@' || c == '#' || c == '$' || c >= 128;
 }
 
 
index 2cb8911fe29770559eee3c7c2ea702e88a38b9f5..44897d165a3537fa69d9cdaf7c3418e24031bfab 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,8 +43,8 @@ static const struct casereader_class lazy_casereader_class;
    to a "serial number" that uniquely identifies the new lazy
    casereader, for use with lazy_casereader_destroy.
 
-   VALUE_CNT must be the number of struct values per case read
-   from the casereader.
+   PROTO must be the format of the cases to be read from the
+   casereader.
 
    CASE_CNT is an upper limit on the number of cases that
    casereader_read will return from the casereader in successive
@@ -52,7 +52,7 @@ static const struct casereader_class lazy_casereader_class;
    data source or CASENUMBER_MAX if the number of cases cannot be
    predicted in advance. */
 struct casereader *
-lazy_casereader_create (size_t value_cnt, casenumber case_cnt,
+lazy_casereader_create (const struct caseproto *proto, casenumber case_cnt,
                         struct casereader *(*callback) (void *aux), void *aux,
                         unsigned long int *serial)
 {
@@ -63,7 +63,7 @@ lazy_casereader_create (size_t value_cnt, casenumber case_cnt,
   *serial = lc->serial = next_serial++;
   lc->callback = callback;
   lc->aux = aux;
-  return casereader_create_sequential (NULL, value_cnt, case_cnt,
+  return casereader_create_sequential (NULL, proto, case_cnt,
                                        &lazy_casereader_class, lc);
 }
 
@@ -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 c83f39db82637430eabca7b4931ff0b2b8ca93d5..561d98eb943e0ff50fca1600957eb2a646bc5045 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 @@
 #include <stdbool.h>
 #include <data/case.h>
 
-struct casereader *lazy_casereader_create (size_t value_cnt,
+struct casereader *lazy_casereader_create (const struct caseproto *,
                                            casenumber case_cnt,
                                            struct casereader *(*) (void *aux),
                                            void *aux,
index 07ebb77a17312a388ced25b3f04e2ae4f4f0fea5..61bb9bcb13c4968a4500fc4fc98ee13e2f631c1b 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
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "missing-values.h"
+#include <data/missing-values.h>
 #include <assert.h>
 #include <stdlib.h>
-#include <libpspp/array.h>
+#include <data/variable.h>
 #include <libpspp/assertion.h>
-#include "variable.h"
 #include <libpspp/str.h>
 
 /* Types of user-missing values.
@@ -36,15 +35,47 @@ enum mv_type
   };
 
 /* Initializes MV as a set of missing values for a variable of
-   the given WIDTH.  Although only numeric variables and short
-   string variables may have missing values, WIDTH may be any
-   valid variable width. */
+   the given WIDTH.  MV should be destroyed with mv_destroy when
+   it is no longer needed. */
 void
 mv_init (struct missing_values *mv, int width)
 {
+  int i;
+
+  assert (width >= 0 && width <= MAX_STRING);
+  mv->type = MVT_NONE;
+  mv->width = width;
+  for (i = 0; i < 3; i++)
+    value_init (&mv->values[i], width);
+}
+
+/* Initializes MV as a set of missing values for a variable of
+   the given WIDTH.  MV will be automatically destroyed along
+   with POOL; it must not be passed to mv_destroy for explicit
+   destruction. */
+void
+mv_init_pool (struct pool *pool, struct missing_values *mv, int width)
+{
+  int i;
+
   assert (width >= 0 && width <= MAX_STRING);
   mv->type = MVT_NONE;
   mv->width = width;
+  for (i = 0; i < 3; i++)
+    value_init_pool (pool, &mv->values[i], width);
+}
+
+/* Frees any storage allocated by mv_init for MV. */
+void
+mv_destroy (struct missing_values *mv)
+{
+  if (mv != NULL)
+    {
+      int i;
+
+      for (i = 0; i < 3; i++)
+        value_destroy (&mv->values[i], mv->width);
+    }
 }
 
 /* Removes any missing values from MV. */
@@ -54,13 +85,32 @@ mv_clear (struct missing_values *mv)
   mv->type = MVT_NONE;
 }
 
-/* Copies SRC to MV. */
+/* Initializes MV as a copy of SRC. */
 void
 mv_copy (struct missing_values *mv, const struct missing_values *src)
 {
-  assert(src);
+  int i;
+
+  mv_init (mv, src->width);
+  mv->type = src->type;
+  for (i = 0; i < 3; i++)
+    value_copy (&mv->values[i], &src->values[i], mv->width);
+}
+
+/* Returns true if VALUE, of the given WIDTH, may be added to a
+   missing value set also of the given WIDTH.  This is normally
+   the case, but string missing values over MV_MAX_STRING bytes
+   long must consist solely of spaces after the first
+   MV_MAX_STRING bytes.  */
+bool
+mv_is_acceptable (const union value *value, int width)
+{
+  int i;
 
-  *mv = *src;
+  for (i = MV_MAX_STRING; i < width; i++)
+    if (value_str (value, width)[i] != ' ')
+      return false;
+  return true;
 }
 
 /* Returns true if MV is an empty set of missing values. */
@@ -80,20 +130,21 @@ mv_get_width (const struct missing_values *mv)
 
 /* Attempts to add individual value V to the set of missing
    values MV.  Returns true if successful, false if MV has no
-   more room for missing values.  (Long string variables never
-   accept missing values.) */
+   more room for missing values or if V is not an acceptable
+   missing value. */
 bool
 mv_add_value (struct missing_values *mv, const union value *v)
 {
-  if (mv->width > MAX_SHORT_STRING)
+  if (!mv_is_acceptable (v, mv->width))
     return false;
+
   switch (mv->type)
     {
     case MVT_NONE:
     case MVT_1:
     case MVT_2:
     case MVT_RANGE:
-      mv->values[mv->type & 3] = *v;
+      value_copy (&mv->values[mv->type & 3], v, mv->width);
       mv->type++;
       return true;
 
@@ -107,13 +158,20 @@ mv_add_value (struct missing_values *mv, const union value *v)
 /* Attempts to add S to the set of string missing values MV.  S
    must contain exactly as many characters as MV's width.
    Returns true if successful, false if MV has no more room for
-   missing values.  (Long string variables never accept missing
-   values.) */
+   missing values or if S is not an acceptable missing value. */
 bool
-mv_add_str (struct missing_values *mv, const char s[])
+mv_add_str (struct missing_values *mv, const uint8_t s[])
 {
+  union value v;
+  bool ok;
+
   assert (mv->width > 0);
-  return mv_add_value (mv, (union value *) s);
+  value_init (&v, mv->width);
+  memcpy (value_str_rw (&v, mv->width), s, mv->width);
+  ok = mv_add_value (mv, &v);
+  value_destroy (&v, mv->width);
+
+  return ok;
 }
 
 /* Attempts to add D to the set of numeric missing values MV.
@@ -122,8 +180,16 @@ mv_add_str (struct missing_values *mv, const char s[])
 bool
 mv_add_num (struct missing_values *mv, double d)
 {
+  union value v;
+  bool ok;
+
   assert (mv->width == 0);
-  return mv_add_value (mv, (union value *) &d);
+  value_init (&v, 0);
+  v.f = d;
+  ok = mv_add_value (mv, &v);
+  value_destroy (&v, 0);
+
+  return ok;
 }
 
 /* Attempts to add range [LOW, HIGH] to the set of numeric
@@ -152,7 +218,8 @@ mv_has_value (const struct missing_values *mv)
   return mv_n_values (mv) > 0;
 }
 
-/* Removes one individual value from MV and stores it in *V.
+/* Removes one individual value from MV and stores it in V, which
+   must have been initialized as a value with the same width as MV.
    MV must contain an individual value (as determined by
    mv_has_value()).
 
@@ -165,32 +232,46 @@ mv_has_value (const struct missing_values *mv)
 void
 mv_pop_value (struct missing_values *mv, union value *v)
 {
+  union value tmp;
+
   assert (mv_has_value (mv));
 
-  *v = mv->values[0];
-  remove_element (mv->values, mv->type & 3, sizeof *mv->values, 0);
+  value_copy (v, &mv->values[0], mv->width);
+  tmp = mv->values[0];
+  mv->values[0] = mv->values[1];
+  mv->values[1] = mv->values[2];
+  mv->values[2] = tmp;
   mv->type--;
 }
 
-/* Stores MV's value with index IDX in *V.
+/* Returns MV's discrete value with index IDX.  The caller must
+   not modify or free this value, or access it after MV is
+   modified or freed.
    IDX must be less than the number of discrete values in MV, as
-   reported by mv_n_values(MV). */
-void
-mv_get_value (const struct missing_values *mv, union value *v, int idx)
+   reported by mv_n_values. */
+const union value *
+mv_get_value (const struct missing_values *mv, int idx)
 {
   assert (idx >= 0 && idx < mv_n_values (mv));
-  *v = mv->values[idx];
+  return &mv->values[idx];
 }
 
-void
+/* Replaces MV's discrete value with index IDX by a copy of V,
+   which must have the same width as MV.
+   IDX must be less than the number of discrete values in MV, as
+   reported by mv_n_values. */
+bool
 mv_replace_value (struct missing_values *mv, const union value *v, int idx)
 {
   assert (idx >= 0) ;
   assert (idx < mv_n_values(mv));
 
-  mv->values[idx] = *v;
-}
+  if (!mv_is_acceptable (v, mv->width))
+    return false;
 
+  value_copy (&mv->values[idx], v, mv->width);
+  return true;
+}
 
 /* Returns the number of individual (not part of a range) missing
    values in MV. */
@@ -232,7 +313,6 @@ mv_get_range (const struct missing_values *mv, double *low, double *high)
   *high = mv->values[2].f;
 }
 
-
 /* Returns true if values[IDX] is in use when the `type' member
    is set to TYPE (in struct missing_values),
    false otherwise. */
@@ -262,17 +342,12 @@ using_element (unsigned type, int idx)
 /* Returns true if MV can be resized to the given WIDTH with
    mv_resize(), false otherwise.  Resizing is possible only when
    each value in MV (if any) is resizable from MV's current width
-   to WIDTH, as determined by value_is_resizable.  In addition,
-   resizing must not produce a non-empty set of long string
-   missing values. */
+   to WIDTH, as determined by value_is_resizable. */
 bool
 mv_is_resizable (const struct missing_values *mv, int width)
 {
   int i;
 
-  if (width > MAX_SHORT_STRING && mv->type != MVT_NONE)
-    return false;
-
   for (i = 0; i < 3; i++)
     if (using_element (mv->type, i)
         && !value_is_resizable (&mv->values[i], mv->width, width))
@@ -292,6 +367,11 @@ mv_resize (struct missing_values *mv, int width)
   for (i = 0; i < 3; i++)
     if (using_element (mv->type, i))
       value_resize (&mv->values[i], mv->width, width);
+    else
+      {
+        value_destroy (&mv->values[i], mv->width);
+        value_init (&mv->values[i], width);
+      }
   mv->width = width;
 }
 
@@ -324,8 +404,7 @@ is_num_user_missing (const struct missing_values *mv, double d)
    MV must be a set of string missing values.
    S[] must contain exactly as many characters as MV's width. */
 static bool
-is_str_user_missing (const struct missing_values *mv,
-                        const char s[])
+is_str_user_missing (const struct missing_values *mv, const uint8_t s[])
 {
   const union value *v = mv->values;
   assert (mv->width > 0);
@@ -334,14 +413,14 @@ is_str_user_missing (const struct missing_values *mv,
     case MVT_NONE:
       return false;
     case MVT_1:
-      return !memcmp (v[0].s, s, mv->width);
+      return !memcmp (value_str (&v[0], mv->width), s, mv->width);
     case MVT_2:
-      return (!memcmp (v[0].s, s, mv->width)
-              || !memcmp (v[1].s, s, mv->width));
+      return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
+              || !memcmp (value_str (&v[1], mv->width), s, mv->width));
     case MVT_3:
-      return (!memcmp (v[0].s, s, mv->width)
-              || !memcmp (v[1].s, s, mv->width)
-              || !memcmp (v[2].s, s, mv->width));
+      return (!memcmp (value_str (&v[0], mv->width), s, mv->width)
+              || !memcmp (value_str (&v[1], mv->width), s, mv->width)
+              || !memcmp (value_str (&v[2], mv->width), s, mv->width));
     case MVT_RANGE:
     case MVT_RANGE_1:
       NOT_REACHED ();
@@ -357,7 +436,7 @@ mv_is_value_missing (const struct missing_values *mv, const union value *v,
 {
   return (mv->width == 0
           ? mv_is_num_missing (mv, v->f, class)
-          : mv_is_str_missing (mv, v->s, class));
+          : mv_is_str_missing (mv, value_str (v, mv->width), class));
 }
 
 /* Returns true if D is a missing value in the given CLASS in MV,
@@ -377,7 +456,7 @@ mv_is_num_missing (const struct missing_values *mv, double d,
    MV must be a set of string missing values.
    S[] must contain exactly as many characters as MV's width. */
 bool
-mv_is_str_missing (const struct missing_values *mv, const char s[],
+mv_is_str_missing (const struct missing_values *mv, const uint8_t s[],
                    enum mv_class class)
 {
   assert (mv->width > 0);
index b504f8c769a928be238ce8d161a8cf577dbee051..4d046faec3a1be411c4b5be5b2f0e93881b0e182 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
           user-missing values, or a range of numeric values, or a
           range plus one discrete value.
 
-        - Short string variables may have up to 3 discrete string
-          user-missing values.
-
-        - Long string variables may not have user-missing
-          values. */
+        - String variables may have up to 3 discrete string
+          user-missing values.  (However, for long string
+          variables all bytes after the first MV_MAX_STRING must
+          be spaces.)
+*/
 
 #ifndef DATA_MISSING_VALUES_H
 #define DATA_MISSING_VALUES_H 1
 #include <stdbool.h>
 #include "data/value.h"
 
+struct pool;
+
+/* Missing values for long string variables after the first
+   MV_MAX_STRING bytes must be all spaces. */
+#define MV_MAX_STRING 8
+
 /* Missing values.
    Opaque--use access functions defined below. */
 struct missing_values
@@ -58,11 +64,13 @@ enum mv_class
 bool mv_is_value_missing (const struct missing_values *, const union value *,
                           enum mv_class);
 bool mv_is_num_missing (const struct missing_values *, double, enum mv_class);
-bool mv_is_str_missing (const struct missing_values *, const char[],
+bool mv_is_str_missing (const struct missing_values *, const uint8_t[],
                         enum mv_class);
 
 /* Initializing missing value sets. */
 void mv_init (struct missing_values *, int width);
+void mv_init_pool (struct pool *pool, struct missing_values *, int width);
+void mv_destroy (struct missing_values *);
 void mv_copy (struct missing_values *, const struct missing_values *);
 void mv_clear (struct missing_values *);
 
@@ -71,13 +79,14 @@ bool mv_is_resizable (const struct missing_values *, int width);
 void mv_resize (struct missing_values *, int width);
 
 /* Basic property inspection. */
+bool mv_is_acceptable (const union value *, int width);
 bool mv_is_empty (const struct missing_values *);
 int mv_get_width (const struct missing_values *);
 
 /* Inspecting discrete values. */
 int mv_n_values (const struct missing_values *);
 bool mv_has_value (const struct missing_values *);
-void mv_get_value (const struct missing_values *, union value *, int idx);
+const union value *mv_get_value (const struct missing_values *, int idx);
 
 /* Inspecting ranges. */
 bool mv_has_range (const struct missing_values *);
@@ -85,10 +94,10 @@ void mv_get_range (const struct missing_values *, double *low, double *high);
 
 /* Adding and modifying discrete values. */
 bool mv_add_value (struct missing_values *, const union value *);
-bool mv_add_str (struct missing_values *, const char[]);
+bool mv_add_str (struct missing_values *, const uint8_t[]);
 bool mv_add_num (struct missing_values *, double);
 void mv_pop_value (struct missing_values *, union value *);
-void mv_replace_value (struct missing_values *, const union value *, int idx);
+bool mv_replace_value (struct missing_values *, const union value *, int idx);
 
 /* Adding and modifying ranges. */
 bool mv_add_range (struct missing_values *, double low, double high);
index 823361b09593aacc7eb33dae402f1fb31e798bdf..a463124274a20c6cd62e1071f15c51cd95edcb3a 100644 (file)
@@ -43,6 +43,7 @@
 #include <libpspp/pool.h>
 #include <libpspp/str.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -74,8 +75,7 @@ struct pfm_reader
     char *trans;                /* 256-byte character set translation table. */
     int var_cnt;                /* Number of variables. */
     int weight_index;          /* 0-based index of weight variable, or -1. */
-    int *widths;                /* Variable widths, 0 for numeric. */
-    size_t value_cnt;          /* Number of `value's per case. */
+    struct caseproto *proto;    /* Format of output cases. */
     bool ok;                    /* Set false on I/O error. */
   };
 
@@ -256,8 +256,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   r->weight_index = -1;
   r->trans = NULL;
   r->var_cnt = 0;
-  r->widths = NULL;
-  r->value_cnt = 0;
+  r->proto = NULL;
   r->ok = true;
   if (setjmp (r->bail_out))
     goto error;
@@ -296,8 +295,8 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   if (!match (r, 'F'))
     error (r, _("Data record expected."));
 
-  r->value_cnt = dict_get_next_value_idx (*dict);
-  return casereader_create_sequential (NULL, r->value_cnt, CASENUMBER_MAX,
+  r->proto = caseproto_ref_pool (dict_get_proto (*dict), r->pool);
+  return casereader_create_sequential (NULL, r->proto, CASENUMBER_MAX,
                                        &por_file_casereader_class, r);
 
  error:
@@ -448,6 +447,28 @@ read_string (struct pfm_reader *r, char *buf)
   *buf = '\0';
 }
 
+
+/* Reads a string into BUF, which must have room for 256
+   characters.
+   Returns the number of bytes read.
+*/
+static size_t
+read_bytes (struct pfm_reader *r, uint8_t *buf)
+{
+  int n = read_int (r);
+  if (n < 0 || n > 255)
+    error (r, _("Bad string length %d."), n);
+
+  while (n-- > 0)
+    {
+      *buf++ = r->cc;
+      advance (r);
+    }
+  return n;
+}
+
+
+
 /* Reads a string and returns a copy of it allocated from R's
    pool. */
 static char *
@@ -608,7 +629,7 @@ assign_default:
   return fmt_default_for_width (var_get_width (v));
 }
 
-static union value parse_value (struct pfm_reader *, struct variable *);
+static void parse_value (struct pfm_reader *, int width, union value *);
 
 /* Read information on all the variables.  */
 static void
@@ -623,7 +644,6 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
   r->var_cnt = read_int (r);
   if (r->var_cnt <= 0)
     error (r, _("Invalid number of variables %d."), r->var_cnt);
-  r->widths = pool_nalloc (r->pool, r->var_cnt, sizeof *r->widths);
 
   /* Purpose of this value is unknown.  It is typically 161. */
   read_int (r);
@@ -652,7 +672,6 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
       width = read_int (r);
       if (width < 0)
        error (r, _("Invalid variable width %d."), width);
-      r->widths[i] = width;
 
       read_string (r, name);
       for (j = 0; j < 6; j++)
@@ -689,7 +708,7 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
       var_set_write_format (v, &write);
 
       /* Range missing values. */
-      mv_init (&miss, var_get_width (v));
+      mv_init (&miss, width);
       if (match (r, 'B'))
         {
           double x = read_float (r);
@@ -704,11 +723,17 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
       /* Single missing values. */
       while (match (r, '8'))
         {
-          union value value = parse_value (r, v);
+          int mv_width = MIN (width, 8);
+          union value value;
+
+          parse_value (r, mv_width, &value);
+          value_resize (&value, mv_width, width);
           mv_add_value (&miss, &value);
+          value_destroy (&value, width);
         }
 
       var_set_missing_values (v, &miss);
+      mv_destroy (&miss);
 
       if (match (r, 'C'))
         {
@@ -729,22 +754,19 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
     }
 }
 
-/* Parse a value for variable VV into value V. */
-static union value
-parse_value (struct pfm_reader *r, struct variable *vv)
+/* Parse a value of with WIDTH into value V. */
+static void
+parse_value (struct pfm_reader *r, int width, union value *v)
 {
-  union value v;
-
-  if (var_is_alpha (vv))
+  value_init (v, width);
+  if (width > 0)
     {
-      char string[256];
-      read_string (r, string);
-      buf_copy_str_rpad (v.s, 8, string);
+      uint8_t buf[256];
+      size_t n_bytes = read_bytes (r, buf);
+      value_copy_buf_rpad (v, width, buf, n_bytes, ' ');
     }
   else
-    v.f = read_float (r);
-
-  return v;
+    v->f = read_float (r);
 }
 
 /* Parse a value label record and return success. */
@@ -784,17 +806,14 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict)
       char label[256];
       int j;
 
-      val = parse_value (r, v[0]);
+      parse_value (r, var_get_width (v[0]), &val);
       read_string (r, label);
 
       /* Assign the value label to each variable. */
       for (j = 0; j < nv; j++)
-       {
-         struct variable *var = v[j];
+        var_replace_value_label (v[j], &val, label);
 
-         if (!var_is_long_string (var))
-            var_replace_value_label (var, &val, label);
-       }
+      value_destroy (&val, var_get_width (v[0]));
     }
 }
 
@@ -814,50 +833,46 @@ 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 (r->proto);
   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;
   for (i = 0; i < r->var_cnt; i++)
     {
-      int width = r->widths[i];
+      int width = caseproto_get_width (r->proto, i);
 
       if (width == 0)
-        {
-          case_data_rw_idx (c, idx)->f = read_float (r);
-          idx++;
-        }
+        case_data_rw_idx (c, i)->f = read_float (r);
       else
         {
-          char string[256];
-          read_string (r, string);
-          buf_copy_str_rpad (case_data_rw_idx (c, idx)->s, width, string);
-          idx += DIV_RND_UP (width, MAX_SHORT_STRING);
+          uint8_t buf[256];
+          size_t n_bytes = read_bytes (r, buf);
+          u8_buf_copy_rpad (case_str_rw_idx (c, i), width, buf, n_bytes, ' ');
         }
     }
 
-  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..7b1b25512a89358b966d44416467dfd5e4bf2b10 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
@@ -45,6 +45,7 @@
 #include <libpspp/str.h>
 #include <libpspp/version.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -172,7 +173,7 @@ pfm_open_writer (struct file_handle *fh, struct dictionary *dict,
   buf_write (w, "F", 1);
   if (ferror (w->file))
     goto error;
-  return casewriter_create (dict_get_next_value_idx (dict),
+  return casewriter_create (dict_get_proto (dict),
                             &por_file_casewriter_class, w);
 
 error:
@@ -306,17 +307,17 @@ write_format (struct pfm_writer *w, struct fmt_spec f, int width)
   write_int (w, f.d);
 }
 
-/* Write value V for variable VV to file H. */
+/* Write value V with width WIDTH to file H. */
 static void
-write_value (struct pfm_writer *w, union value *v, struct variable *vv)
+write_value (struct pfm_writer *w, const union value *v, int width)
 {
-  if (var_is_numeric (vv))
+  if (width == 0)
     write_float (w, v->f);
   else
     {
-      int width = MIN (var_get_width (vv), MAX_POR_WIDTH);
+      width = MIN (width, MAX_POR_WIDTH);
       write_int (w, width);
-      buf_write (w, v->s, width);
+      buf_write (w, value_str (v, width), width);
     }
 }
 
@@ -343,6 +344,7 @@ write_variables (struct pfm_writer *w, struct dictionary *dict)
       struct variable *v = dict_get_var (dict, i);
       struct missing_values mv;
       int width = MIN (var_get_width (v), MAX_POR_WIDTH);
+      int j;
 
       buf_write (w, "7", 1);
       write_int (w, width);
@@ -352,10 +354,12 @@ write_variables (struct pfm_writer *w, struct dictionary *dict)
 
       /* Write missing values. */
       mv_copy (&mv, var_get_missing_values (v));
-      while (mv_has_range (&mv))
+      if (var_get_width (v) > 8)
+        mv_resize (&mv, 8);
+      if (mv_has_range (&mv))
         {
           double x, y;
-          mv_pop_range (&mv, &x, &y);
+          mv_get_range (&mv, &x, &y);
           if (x == LOWEST)
             {
               buf_write (w, "9", 1);
@@ -373,13 +377,12 @@ write_variables (struct pfm_writer *w, struct dictionary *dict)
               write_float (w, y);
             }
         }
-      while (mv_has_value (&mv))
+      for (j = 0; j < mv_n_values (&mv); j++)
         {
-          union value value;
-          mv_pop_value (&mv, &value);
           buf_write (w, "8", 1);
-          write_value (w, &value, v);
+          write_value (w, mv_get_value (&mv, j), mv_get_width (&mv));
         }
+      mv_destroy (&mv);
 
       /* Write variable label. */
       if (var_get_label (v) != NULL)
@@ -398,12 +401,12 @@ write_value_labels (struct pfm_writer *w, const struct dictionary *dict)
 
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     {
-      struct val_labs_iterator *j;
       struct variable *v = dict_get_var (dict, i);
       const struct val_labs *val_labs = var_get_value_labels (v);
-      struct val_lab *vl;
+      size_t n_labels = val_labs_count (val_labs);
+      const struct val_lab **labels;
 
-      if (val_labs == NULL)
+      if (n_labels == 0)
        continue;
 
       buf_write (w, "D", 1);
@@ -411,12 +414,15 @@ write_value_labels (struct pfm_writer *w, const struct dictionary *dict)
       write_string (w, var_get_short_name (v, 0));
       write_int (w, val_labs_count (val_labs));
 
-      for (vl = val_labs_first_sorted (val_labs, &j); vl != NULL;
-           vl = val_labs_next (val_labs, &j))
+      n_labels = val_labs_count (val_labs);
+      labels = val_labs_sorted (val_labs);
+      for (i = 0; i < n_labels; i++)
         {
-          write_value (w, &vl->value, v);
-          write_string (w, vl->label);
+          const struct val_lab *vl = labels[i];
+          write_value (w, val_lab_get_value (vl), var_get_width (v));
+          write_string (w, val_lab_get_label (vl));
         }
+      free (labels);
     }
 }
 
@@ -464,7 +470,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..b762214d10e20a33d6bbdf89ac52253a67589453 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
@@ -36,7 +36,9 @@
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
 #include <libpspp/taint.h>
+#include <libpspp/i18n.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
 struct dataset {
@@ -79,7 +81,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 +94,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 +105,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)
@@ -166,11 +187,19 @@ proc_open (struct dataset *ds)
     {
       struct dictionary *pd = ds->permanent_dict;
       size_t compacted_value_cnt = dict_count_values (pd, 1u << DC_SCRATCH);
-      bool should_compact = compacted_value_cnt < dict_get_next_value_idx (pd);
-      ds->compactor = (should_compact
-                       ? case_map_to_compact_dict (pd, 1u << DC_SCRATCH)
-                       : NULL);
-      ds->sink = autopaging_writer_create (compacted_value_cnt);
+      if (compacted_value_cnt < dict_get_next_value_idx (pd))
+        {
+          struct caseproto *compacted_proto;
+          compacted_proto = dict_get_compacted_proto (pd, 1u << DC_SCRATCH);
+          ds->compactor = case_map_to_compact_dict (pd, 1u << DC_SCRATCH);
+          ds->sink = autopaging_writer_create (compacted_proto);
+          caseproto_unref (compacted_proto);
+        }
+      else
+        {
+          ds->compactor = NULL;
+          ds->sink = autopaging_writer_create (dict_get_proto (pd));
+        }
     }
   else
     {
@@ -188,8 +217,7 @@ proc_open (struct dataset *ds)
   /* FIXME: use taint in dataset in place of `ok'? */
   /* FIXME: for trivial cases we can just return a clone of
      ds->source? */
-  return casereader_create_sequential (NULL,
-                                       dict_get_next_value_idx (ds->dict),
+  return casereader_create_sequential (NULL, dict_get_proto (ds->dict),
                                        CASENUMBER_MAX,
                                        &proc_casereader_class, ds);
 }
@@ -203,15 +231,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 +247,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_proto (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 +296,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 +321,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 +380,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 +540,25 @@ 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);
+
+  dict_set_encoding (ds->dict, get_default_encoding ());
+
   ds->caseinit = caseinit_create ();
   proc_cancel_all_transformations (ds);
   return ds;
@@ -596,6 +628,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 +712,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 +751,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..68ea7e5672cea1c483fb356e75b5834852642c1d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
 #include <libpspp/message.h>
 #include <gl/xalloc.h>
 #include <data/dictionary.h>
+#include <math.h>
 #include <stdlib.h>
 
 #include "psql-reader.h"
 #include "calendar.h"
 
 #include <inttypes.h>
+#include <libpspp/misc.h>
 #include <libpspp/str.h>
 
+#include "minmax.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) (msgid)
@@ -37,7 +41,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"));
 
@@ -50,6 +54,9 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
 #include <libpq-fe.h>
 
 
+/* Default width of string variables. */
+#define PSQL_DEFAULT_WIDTH 8
+
 /* These macros  must be the same as in catalog/pg_types.h from the postgres source */
 #define BOOLOID            16
 #define BYTEAOID           17
@@ -75,8 +82,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 =
   {
@@ -96,7 +102,7 @@ struct psql_reader
 
   double postgres_epoch;
 
-  size_t value_cnt;
+  struct caseproto *proto;
   struct dictionary *dict;
 
   /* An array of ints, which maps psql column numbers into
@@ -109,8 +115,7 @@ struct psql_reader
 };
 
 
-static bool set_value (struct psql_reader *r,
-                      struct ccase *cc);
+static struct ccase *set_value (struct psql_reader *r);
 
 
 
@@ -177,8 +182,6 @@ create_var (struct psql_reader *r, const struct fmt_spec *fmt,
   struct variable *var;
   char name[VAR_NAME_LEN + 1];
 
-  r->value_cnt += value_cnt_from_width (width);
-
   if ( ! dict_make_unique_var_name (r->dict, suggested_name, &vx, name))
     {
       msg (ME, _("Cannot create variable name from %s"), suggested_name);
@@ -290,10 +293,22 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
   /* Create the dictionary and populate it */
   *dict = r->dict = dict_create ();
 
+  {
+    const int enc = PQclientEncoding (r->conn);
+
+    /* According to section 22.2 of the Postgresql manual
+       a value of zero (SQL_ASCII) indicates
+       "a declaration of ignorance about the encoding".
+       Accordingly, we don't set the dictionary's encoding
+       if we find this value.
+    */
+    if ( enc != 0 )
+      dict_set_encoding (r->dict, pg_encoding_to_char (enc));
+  }
+
   /*
     select count (*) from (select * from medium) stupid_sql_standard;
   */
-
   ds_init_cstr (&query,
                "BEGIN READ ONLY ISOLATION LEVEL SERIALIZABLE; "
                "DECLARE  pspp BINARY CURSOR FOR ");
@@ -347,7 +362,7 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
   n_tuples = PQntuples (qres);
   n_fields = PQnfields (qres);
 
-  r->value_cnt = 0;
+  r->proto = NULL;
   r->vmap = NULL;
   r->vmapsize = 0;
 
@@ -364,7 +379,7 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
       if ( n_tuples > 0 )
        length = PQgetlength (qres, 0, i);
       else 
-       length = MAX_SHORT_STRING;
+       length = PSQL_DEFAULT_WIDTH;
 
       switch (type)
        {
@@ -391,13 +406,13 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
        case BPCHAROID:
          fmt.type = FMT_A;
          width = (info->str_width == -1) ?
-           ROUND_UP (length, MAX_SHORT_STRING) : info->str_width;
+           ROUND_UP (length, PSQL_DEFAULT_WIDTH) : info->str_width;
          fmt.w = width;
          fmt.d = 0;
           break;
        case BYTEAOID:
          fmt.type = FMT_AHEX;
-         width = length > 0 ? length : MAX_SHORT_STRING;
+         width = length > 0 ? length : PSQL_DEFAULT_WIDTH;
          fmt.w = width * 2;
          fmt.d = 0;
          break;
@@ -436,14 +451,14 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
        default:
           msg (MW, _("Unsupported OID %d.  SYSMIS values will be inserted."), type);
          fmt.type = FMT_A;
-         width = length > 0 ? length : MAX_SHORT_STRING;
+         width = length > 0 ? length : PSQL_DEFAULT_WIDTH;
          fmt.w = width ;
          fmt.d = 0;
          break;
        }
 
       if ( width == 0 && fmt_is_string (fmt.type))
-       fmt.w = width = MAX_SHORT_STRING;
+       fmt.w = width = PSQL_DEFAULT_WIDTH;
 
 
       var = create_var (r, &fmt, width, PQfname (qres, i), i);
@@ -518,10 +533,11 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
   ds_put_format (&r->fetch_cmd,  "FETCH FORWARD %d FROM pspp", r->cache_size);
 
   reload_cache (r);
+  r->proto = caseproto_ref (dict_get_proto (*dict));
 
   return casereader_create_sequential
     (NULL,
-     r->value_cnt,
+     r->proto,
      n_cases,
      &psql_casereader_class, r);
 
@@ -544,15 +560,15 @@ psql_casereader_destroy (struct casereader *reader UNUSED, void *r_)
   free (r->vmap);
   if (r->res) PQclear (r->res);
   PQfinish (r->conn);
+  caseproto_unref (r->proto);
 
   free (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,25 +578,25 @@ 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);
-  memset (case_data_rw_idx (c, 0)->s, ' ', MAX_SHORT_STRING * r->value_cnt);
+  c = case_create (r->proto);
+  case_set_missing (c);
 
 
   for (i = 0 ; i < n_vars ; ++i )
@@ -822,7 +838,8 @@ set_value (struct psql_reader *r,
            case VARCHAROID:
            case BPCHAROID:
            case BYTEAOID:
-             memcpy (val->s, (char *) vptr, MIN (length, var_width));
+             memcpy (value_str_rw (val, var_width), (char *) vptr,
+                      MIN (length, var_width));
              break;
 
            case NUMERICOID:
@@ -874,7 +891,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..631305fe9d96c6a668c33473780867930f88d3a4 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
@@ -59,7 +59,6 @@ scratch_writer_open (struct file_handle *fh,
   struct scratch_writer *writer;
   struct casewriter *casewriter;
   struct fh_lock *lock;
-  size_t dict_value_cnt;
 
   /* Get exclusive write access to handle. */
   /* TRANSLATORS: this fragment will be interpolated into
@@ -83,10 +82,9 @@ scratch_writer_open (struct file_handle *fh,
     }
   else
     writer->compactor = NULL;
-  dict_value_cnt = dict_get_next_value_idx (writer->dict);
-  writer->subwriter = autopaging_writer_create (dict_value_cnt);
+  writer->subwriter = autopaging_writer_create (dict_get_proto (writer->dict));
 
-  casewriter = casewriter_create (dict_value_cnt,
+  casewriter = casewriter_create (dict_get_proto (writer->dict),
                                   &scratch_writer_casewriter_class, writer);
   taint_propagate (casewriter_get_taint (writer->subwriter),
                    casewriter_get_taint (casewriter));
@@ -99,15 +97,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..c86ac9cc7e2c856ee685d271cf0995e5a9bbafd1 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 "format.h"
 #include "value.h"
 #include "xalloc.h"
+#include <data/case.h>
 #include <libpspp/i18n.h>
 #include <libpspp/integer-format.h>
 #include <libpspp/message.h>
 
 #include "error.h"
+#include "minmax.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -147,15 +149,15 @@ settings_init (int *width, int *length)
 {
   init_viewport (width, length);
   settings_set_epoch (-1);
-  i18n_init ();
   the_settings.styles = fmt_create ();
+
+  settings_set_decimal_char (get_system_decimal ());
 }
 
 void
 settings_done (void)
 {
   fmt_done (the_settings.styles);
-  i18n_done ();
 }
 
 /* Returns the floating-point format used for RB and RBHEX
@@ -515,13 +517,12 @@ settings_get_workspace (void)
 }
 
 /* Approximate maximum number of cases to allocate in-core, given
-   that each case contains VALUE_CNT values. */
+   that each case has the format given in PROTO. */
 size_t
-settings_get_workspace_cases (size_t value_cnt)
+settings_get_workspace_cases (const struct caseproto *proto)
 {
-  size_t case_size = sizeof (union value) * value_cnt + 4 * sizeof (void *);
-  size_t case_cnt = MAX (settings_get_workspace () / case_size, 4);
-  return case_cnt;
+  size_t n_cases = settings_get_workspace () / case_get_cost (proto);
+  return MAX (n_cases, 4);
 }
 
 /* Set approximate maximum amount of memory to use for cases, in
index e39a63a0fefc766546362d7e1bb412454539f10f..3de1715f0738595dc7fd53a4df82b34a96dc98f0 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 <stdbool.h>
 #include <stddef.h>
+#include <data/format.h>
+#include <libpspp/float-format.h>
+#include <libpspp/integer-format.h>
 
+struct caseproto;
 struct settings;
 
-
 void settings_init (int *, int *);
 void settings_done (void);
 
@@ -104,7 +107,7 @@ char settings_get_endcmd (void);
 void settings_set_endcmd (char);
 
 size_t settings_get_workspace (void);
-size_t settings_get_workspace_cases (size_t value_cnt);
+size_t settings_get_workspace_cases (const struct caseproto *);
 void settings_set_workspace (size_t);
 
 const struct fmt_spec *settings_get_format (void);
index 08bf7587876b4ac56d2b54bd8cc801c00bdd04f2..1b8be927a69c70f918f47ba1505d59787ae6fa03 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
@@ -39,9 +39,9 @@ compare_strings (const void *a, const void *b, const void *aux UNUSED)
 
 /* Hashes a string. */
 static unsigned
-hash_string (const void *s, const void *aux UNUSED)
+do_hash_string (const void *s, const void *aux UNUSED)
 {
-  return hsh_hash_string (s);
+  return hash_string (s, 0);
 }
 
 /* Sets V's short name to BASE, followed by a suffix of the form
@@ -128,7 +128,7 @@ short_names_assign (struct dictionary *d)
      the hash table point to strings owned by dictionary
      variables, not by us, so we don't need to provide a free
      function. */
-  short_names = hsh_create (var_cnt, compare_strings, hash_string,
+  short_names = hsh_create (var_cnt, compare_strings, do_hash_string,
                             NULL, NULL);
 
   /* Clear short names that conflict with a variable name. */
diff --git a/src/data/sparse-cases.c b/src/data/sparse-cases.c
deleted file mode 100644 (file)
index 1f3fb09..0000000
+++ /dev/null
@@ -1,332 +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/sparse-cases.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <data/settings.h>
-#include <data/case-tmpfile.h>
-#include <libpspp/assertion.h>
-#include <libpspp/range-set.h>
-#include <libpspp/sparse-array.h>
-
-#include "xalloc.h"
-
-/* A sparse array of cases. */
-struct sparse_cases
-  {
-    size_t column_cnt;                  /* Number of values per case. */
-    union value *default_columns;       /* Defaults for unwritten cases. */
-    casenumber max_memory_cases;        /* Max cases before dumping to disk. */
-    struct sparse_array *memory;        /* Backing, if stored in memory. */
-    struct case_tmpfile *disk;          /* Backing, if stored on disk. */
-    struct range_set *disk_cases;       /* Allocated cases, if on disk. */
-  };
-
-/* Creates and returns a new sparse array of cases with
-   COLUMN_CNT values per case. */
-struct sparse_cases *
-sparse_cases_create (size_t column_cnt)
-{
-  struct sparse_cases *sc = xmalloc (sizeof *sc);
-  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->disk = NULL;
-  sc->disk_cases = NULL;
-  return sc;
-}
-
-/* Creates and returns a new sparse array of cases that contains
-   the same data as OLD. */
-struct sparse_cases *
-sparse_cases_clone (const struct sparse_cases *old)
-{
-  struct sparse_cases *new = xmalloc (sizeof *new);
-
-  new->column_cnt = old->column_cnt;
-
-  if (old->default_columns != NULL)
-    new->default_columns
-      = xmemdup (old->default_columns,
-                 old->column_cnt * sizeof *old->default_columns);
-  else
-    new->default_columns = NULL;
-
-  new->max_memory_cases = old->max_memory_cases;
-
-  if (old->memory != NULL)
-    {
-      unsigned long int idx;
-      struct ccase *c;
-
-      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);
-    }
-  else
-    new->memory = NULL;
-
-  if (old->disk != NULL)
-    {
-      const struct range_set_node *node;
-
-      new->disk = case_tmpfile_create (old->column_cnt);
-      new->disk_cases = range_set_create ();
-      for (node = range_set_first (old->disk_cases); node != NULL;
-           node = range_set_next (old->disk_cases, node))
-        {
-          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;
-              }
-        }
-    }
-  else
-    {
-      new->disk = NULL;
-      new->disk_cases = NULL;
-    }
-
-  return new;
-}
-
-/* Destroys sparse array of cases SC. */
-void
-sparse_cases_destroy (struct sparse_cases *sc)
-{
-  if (sc != NULL)
-    {
-      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);
-          sparse_array_destroy (sc->memory);
-        }
-      free (sc->default_columns);
-      case_tmpfile_destroy (sc->disk);
-      range_set_destroy (sc->disk_cases);
-      free (sc);
-    }
-}
-
-/* Returns the number of `union value's in each case in SC. */
-size_t
-sparse_cases_get_value_cnt (const struct sparse_cases *sc)
-{
-  return sc->column_cnt;
-}
-
-/* Dumps the cases in SC, which must currently be stored in
-   memory, to disk.  Returns true if successful, false on I/O
-   error. */
-static bool
-dump_sparse_cases_to_disk (struct sparse_cases *sc)
-{
-  unsigned long int idx;
-  struct ccase *c;
-
-  assert (sc->memory != NULL);
-  assert (sc->disk == NULL);
-
-  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))
-    {
-      if (!case_tmpfile_put_case (sc->disk, idx, c))
-        {
-          case_tmpfile_destroy (sc->disk);
-          sc->disk = NULL;
-          range_set_destroy (sc->disk_cases);
-          sc->disk_cases = NULL;
-          return false;
-        }
-      range_set_insert (sc->disk_cases, idx, 1);
-    }
-  sparse_array_destroy (sc->memory);
-  sc->memory = NULL;
-  return true;
-}
-
-/* Returns true if any data has ever been written to ROW in SC,
-   false otherwise. */
-bool
-sparse_cases_contains_row (const struct sparse_cases *sc, casenumber row)
-{
-  return (sc->memory != NULL
-          ? sparse_array_get (sc->memory, row) != NULL
-          : range_set_contains (sc->disk_cases, row));
-}
-
-/* Reads columns COLUMNS...(COLUMNS + VALUE_CNT), exclusive, in
-   the given ROW in SC, into the VALUE_CNT values in VALUES.
-   Returns true if successful, false on I/O error. */
-bool
-sparse_cases_read (struct sparse_cases *sc, casenumber row, size_t column,
-                   union value values[], size_t value_cnt)
-{
-  assert (value_cnt <= sc->column_cnt);
-  assert (column + value_cnt <= sc->column_cnt);
-
-  if (sparse_cases_contains_row (sc, row))
-    {
-      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);
-    }
-  else
-    {
-      assert (sc->default_columns != NULL);
-      memcpy (values, sc->default_columns + column,
-              sizeof *values * value_cnt);
-    }
-
-  return true;
-}
-
-/* Implements sparse_cases_write for an on-disk sparse_cases. */
-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;
-  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;
-
-  /* Copy in new data. */
-  case_copy_in (&c, column, values, value_cnt);
-
-  /* Write new case. */
-  ok = case_tmpfile_put_case (sc->disk, row, &c);
-  if (ok)
-    range_set_insert (sc->disk_cases, row, 1);
-
-  return ok;
-}
-
-/* Writes the VALUE_CNT values in VALUES into columns
-   COLUMNS...(COLUMNS + VALUE_CNT), exclusive, in the given ROW
-   in SC.
-   Returns true if successful, false on I/O error. */
-bool
-sparse_cases_write (struct sparse_cases *sc, casenumber row, size_t column,
-                    const union value values[], size_t value_cnt)
-{
-  if (sc->memory != NULL)
-    {
-      struct ccase *c = sparse_array_get (sc->memory, row);
-      if (c == NULL)
-        {
-          if (sparse_array_count (sc->memory) >= sc->max_memory_cases)
-            {
-              if (!dump_sparse_cases_to_disk (sc))
-                return false;
-              return write_disk_case (sc, row, column, values, value_cnt);
-            }
-
-          c = sparse_array_insert (sc->memory, row);
-          case_create (c, 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);
-        }
-      case_copy_in (c, column, values, value_cnt);
-      return true;
-    }
-  else
-    return write_disk_case (sc, row, column, values, value_cnt);
-}
-
-/* Writes the VALUE_CNT values in VALUES to columns
-   START_COLUMN...(START_COLUMN + VALUE_CNT), exclusive, in every
-   row in SC, even those rows that have not yet been written.
-   Returns true if successful, false on I/O error.
-
-   The runtime of this function is linear in the number of rows
-   in SC that have already been written. */
-bool
-sparse_cases_write_columns (struct sparse_cases *sc, size_t start_column,
-                            const union value values[], size_t value_cnt)
-{
-  assert (value_cnt <= sc->column_cnt);
-  assert (start_column + value_cnt <= sc->column_cnt);
-
-  /* Set defaults. */
-  if (sc->default_columns == NULL)
-    sc->default_columns = xnmalloc (sc->column_cnt,
-                                    sizeof *sc->default_columns);
-  memcpy (sc->default_columns + start_column, values,
-          value_cnt * sizeof *sc->default_columns);
-
-  /* Set individual rows. */
-  if (sc->memory != NULL)
-    {
-      struct ccase *c;
-      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);
-    }
-  else
-    {
-      const struct range_set_node *node;
-
-      for (node = range_set_first (sc->disk_cases); node != NULL;
-           node = range_set_next (sc->disk_cases, node))
-        {
-          unsigned long int start = range_set_node_get_start (node);
-          unsigned long int end = range_set_node_get_end (node);
-          unsigned long int row;
-
-          for (row = start; row < end; row++)
-            case_tmpfile_put_values (sc->disk, row,
-                                     start_column, values, value_cnt);
-        }
-
-      if (case_tmpfile_error (sc->disk))
-        return false;
-    }
-  return true;
-}
diff --git a/src/data/sparse-cases.h b/src/data/sparse-cases.h
deleted file mode 100644 (file)
index 139863f..0000000
+++ /dev/null
@@ -1,66 +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/>. */
-
-/* Sparse array of cases.
-
-   Implements a 2-d sparse array in which each row represents a
-   case, each column represents a variable, and each intersection
-   contains a `union value'.  Data in the array may be accessed
-   randomly by column and row.  When the number of cases stored
-   in the array is small, the data is stored in memory in memory;
-   when it is large, the data is stored in a temporary file.
-
-   The sparse_cases_write_columns function provides a somewhat
-   unusual ability: to write a given value to every row in a
-   column or set of columns.  This overwrites any values
-   previously written into those columns.  For rows that have
-   never been written, this function sets "default" values that
-   later writes can override.
-
-   The array keeps track of which row have been written.  If
-   sparse_cases_write_columns has been used, reading from a row
-   that has never been written yields the default values;
-   otherwise, reading from such a row in an error.  It is
-   permissible to write to only some columns in a row and leave
-   the rest of the row's data undefined (or, if
-   sparse_cases_write_columns has been used, at the default
-   values).  The array does not keep track of which columns in a
-   row have never been written, but reading values that have
-   never been written or set as defaults yields undefined
-   behavior. */
-
-#ifndef DATA_SPARSE_CASES_H
-#define DATA_SPARSE_CASES_H 1
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <data/case.h>
-
-struct sparse_cases *sparse_cases_create (size_t value_cnt);
-struct sparse_cases *sparse_cases_clone (const struct sparse_cases *);
-void sparse_cases_destroy (struct sparse_cases *);
-
-size_t sparse_cases_get_value_cnt (const struct sparse_cases *);
-
-bool sparse_cases_contains_row (const struct sparse_cases *, casenumber row);
-bool sparse_cases_read (struct sparse_cases *, casenumber row, size_t column,
-                        union value[], size_t value_cnt);
-bool sparse_cases_write (struct sparse_cases *, casenumber row, size_t column,
-                         const union value[], size_t value_cnt);
-bool sparse_cases_write_columns (struct sparse_cases *, size_t start_column,
-                                 const union value[], size_t value_cnt);
-
-#endif /* data/sparse-cases.h */
diff --git a/src/data/subcase.c b/src/data/subcase.c
new file mode 100644 (file)
index 0000000..6ffaa4c
--- /dev/null
@@ -0,0 +1,362 @@
+/* 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"
+
+static void invalidate_proto (struct subcase *sc);
+
+/* Initializes SC as a subcase that contains no fields. */
+void
+subcase_init_empty (struct subcase *sc)
+{
+  sc->fields = NULL;
+  sc->n_fields = 0;
+  sc->proto = NULL;
+}
+
+/* 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->proto = NULL;
+  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;
+    }
+}
+
+/* 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);
+}
+
+
+void
+subcase_init (struct subcase *sc, int index, int width,
+                  enum subcase_direction direction)
+{
+  subcase_init_empty (sc);
+  subcase_add (sc, index, width, direction);
+}
+
+
+/* Removes all the fields from SC. */
+void
+subcase_clear (struct subcase *sc)
+{
+  sc->n_fields = 0;
+  invalidate_proto (sc);
+}
+
+/* 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->proto = orig->proto ? caseproto_ref (orig->proto) : NULL;
+}
+
+/* Frees the memory owned by SC (but not SC itself). */
+void
+subcase_destroy (struct subcase *sc)
+{
+  free (sc->fields);
+  caseproto_unref (sc->proto);
+}
+
+
+/* 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)
+{
+  return subcase_add (sc, var_get_case_index (var),
+                     var_get_width (var), direction);
+}
+
+/* Add a field for CASE_INDEX, WIDTH to SC, with DIRECTION as the sort order.
+   Returns true if successful, false if CASE_INDEX already has a field
+   in SC. */
+bool
+subcase_add (struct subcase *sc, int case_index, int width,
+                 enum subcase_direction direction)
+{
+  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 = width;
+  field->direction = direction;
+  invalidate_proto (sc);
+  return true;
+}
+
+/* Obtains a caseproto for a case described by SC.  The caller
+   must not modify or unref the returned case prototype. */
+const struct caseproto *
+subcase_get_proto (const struct subcase *sc_)
+{
+  struct subcase *sc = (struct subcase *) sc_;
+
+  if (sc->proto == NULL)
+    {
+      size_t i;
+
+      sc->proto = caseproto_create ();
+      for (i = 0; i < sc->n_fields; i++)
+        sc->proto = caseproto_add_width (sc->proto, sc->fields[i].width);
+    }
+  return sc->proto;
+}
+
+/* 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_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_fields(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];
+      union value *value = &values[i];
+      value_copy (value, case_data_idx (c, field->case_index), field->width);
+    }
+}
+
+/* Copies the data in VALUES into the fields in C represented by
+   SC.  VALUES must have at least subcase_get_n_fields(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];
+      const union value *value = &values[i];
+      value_copy (case_data_rw_idx (c, field->case_index), value,
+                  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[i],
+                                    case_data_idx (b, field->case_index),
+                                    field->width);
+      if (cmp != 0)
+        return field->direction == SC_ASCEND ? cmp : -cmp;
+    }
+  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];
+      int cmp = value_compare_3way (a++, b++, field->width);
+      if (cmp != 0)
+        return field->direction == SC_ASCEND ? cmp : -cmp;
+    }
+  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;
+}
+
+/* Discards SC's case prototype.  (It will be recreated if needed
+   again later.) */
+static void
+invalidate_proto (struct subcase *sc)
+{
+  caseproto_unref (sc->proto);
+  sc->proto = NULL;
+}
diff --git a/src/data/subcase.h b/src/data/subcase.h
new file mode 100644 (file)
index 0000000..6e59da1
--- /dev/null
@@ -0,0 +1,121 @@
+/* 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_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;
+
+    struct caseproto *proto;    /* Created lazily. */
+  };
+
+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_init (struct subcase *, int index, int width,
+                  enum subcase_direction);
+
+void subcase_clone (struct subcase *, const struct subcase *);
+void subcase_clear (struct subcase *);
+void subcase_destroy (struct subcase *);
+
+bool subcase_add (struct subcase *sc, int index, int width,
+                 enum subcase_direction direction);
+
+bool subcase_add_var (struct subcase *, const struct variable *,
+                      enum subcase_direction);
+
+const struct caseproto *subcase_get_proto (const struct subcase *);
+
+static inline bool subcase_is_empty (const struct subcase *);
+static inline size_t subcase_get_n_fields (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;
+}
+
+#endif /* data/subcase.h */
index 2a2979f92a8db7c10c477f150de952b57be9deff..dd50ea8babefb434edece1080504bcc0f1e22e13 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
@@ -22,6 +22,7 @@
 #include <data/value.h>
 #include <data/variable.h>
 #include <libpspp/assertion.h>
+#include <libpspp/misc.h>
 
 #include "minmax.h"
 #include "xalloc.h"
@@ -228,7 +229,8 @@ sfm_dictionary_to_sfm_vars (const struct dictionary *dict,
           if (used_bytes != 0)
             {
               sv = &(*sfm_vars)[(*sfm_var_cnt)++];
-              sv->width = width == 0 ? 0 : used_bytes;
+              sv->var_width = width;
+              sv->segment_width = width == 0 ? 0 : used_bytes;
               sv->case_index = var_get_case_index (dv);
               sv->offset = sfm_segment_offset (width, j);
               sv->padding = padding;
@@ -236,13 +238,11 @@ sfm_dictionary_to_sfm_vars (const struct dictionary *dict,
           else
             {
               /* Segment is all padding.  Just add it to the
-                 previous segment.  (Otherwise we'd have an
-                 ambiguity whether ->width of 0 indicates a
-                 numeric variable or an all-padding segment.) */
+                 previous segment. */
               sv = &(*sfm_vars)[*sfm_var_cnt - 1];
               sv->padding += padding;
             }
-          assert ((sv->width + sv->padding) % 8 == 0);
+          assert ((sv->segment_width + sv->padding) % 8 == 0);
         }
     }
 
index 9d5f52f0ebd304c3c87fd8b26d523b1e47a9f0d8..e839cd6ac02008ab5fec2ee08feffcfbdcad003f 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
@@ -38,7 +38,8 @@ struct dictionary;
 /* A variable in a system file. */
 struct sfm_var
   {
-    int width;                  /* Value width (0=numeric, else string). */
+    int var_width;              /* Variable width (0 to 32767). */
+    int segment_width;          /* Segment width (0 to 255). */
     int case_index;             /* Index into case. */
 
     /* The following members are interesting only for string
index fe7b53342128087687d39e74d46721b1cb5917ed..b0a41a83573b0e986fb35bfb3bce46379282662f 100644 (file)
@@ -25,6 +25,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 
+#include <libpspp/i18n.h>
 #include <libpspp/assertion.h>
 #include <libpspp/message.h>
 #include <libpspp/compiler.h>
@@ -34,6 +35,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>
@@ -70,7 +72,7 @@ struct sfm_reader
     struct fh_lock *lock;       /* Mutual exclusion for file handle. */
     FILE *file;                 /* File stream. */
     bool error;                 /* I/O or corruption error? */
-    size_t value_cnt;           /* Number of "union value"s in struct case. */
+    struct caseproto *proto;    /* Format of output cases. */
 
     /* File format. */
     enum integer_format integer_format; /* On-disk integer format. */
@@ -98,9 +100,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 +116,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
@@ -151,7 +163,9 @@ static void read_extension_record (struct sfm_reader *, struct dictionary *,
                                    struct sfm_read_info *);
 static void read_machine_integer_info (struct sfm_reader *,
                                        size_t size, size_t count,
-                                       struct sfm_read_info *);
+                                       struct sfm_read_info *,
+                                      struct dictionary *
+                                      );
 static void read_machine_float_info (struct sfm_reader *,
                                      size_t size, size_t count);
 static void read_display_parameters (struct sfm_reader *,
@@ -163,7 +177,71 @@ 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 *);
+static void read_long_string_value_labels (struct sfm_reader *,
+                                          size_t size, size_t count,
+                                          struct dictionary *);
+
+/* Convert all the strings in DICT from the dict encoding to UTF8 */
+static void
+recode_strings (struct dictionary *dict)
+{
+  int i;
+
+  const char *enc = dict_get_encoding (dict);
+
+  if ( NULL == enc)
+    enc = get_default_encoding ();
+
+  for (i = 0 ; i < dict_get_var_cnt (dict); ++i)
+    {
+      /* Convert the long variable name */
+      struct variable *var = dict_get_var (dict, i);
+      const char *native_name = var_get_name (var);
+      char *utf8_name = recode_string (UTF8, enc, native_name, -1);
+      if ( 0 != strcmp (utf8_name, native_name))
+       {
+         if ( NULL == dict_lookup_var (dict, utf8_name))
+           dict_rename_var (dict, var, utf8_name);
+         else
+           msg (MW,
+            _("Recoded variable name duplicates an existing `%s' within system file."), utf8_name);
+    }
+
+      free (utf8_name);
+
+      /* Convert the variable label */
+      if (var_has_label (var))
+       {
+         char *utf8_label = recode_string (UTF8, enc, var_get_label (var), -1);
+         var_set_label (var, utf8_label);
+         free (utf8_label);
+       }
+
+      if (var_has_value_labels (var))
+       {
+         const struct val_lab *vl = NULL;
+         const struct val_labs *vlabs = var_get_value_labels (var);
+
+         for (vl = val_labs_first (vlabs); vl != NULL; vl = val_labs_next (vlabs, vl))
+           {
+             const union value *val = val_lab_get_value (vl);
+             const char *label = val_lab_get_label (vl);
+             char *new_label = NULL;
 
+             new_label = recode_string (UTF8, enc, label, -1);
+
+             var_replace_value_label (var, val, new_label);
+             free (new_label);
+           }
+       }
+    }
+}
 
 /* Opens the system file designated by file handle FH for
    reading.  Reads the system file's dictionary into *DICT.
@@ -282,6 +360,8 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
       r->has_long_var_names = true;
     }
 
+  recode_strings (*dict);
+
   /* Read record 999 data, which is just filler. */
   read_int (r);
 
@@ -301,11 +381,11 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
      dictionary and may destroy or modify its variables. */
   sfm_dictionary_to_sfm_vars (*dict, &r->sfm_vars, &r->sfm_var_cnt);
   pool_register (r->pool, free, r->sfm_vars);
+  r->proto = caseproto_ref_pool (dict_get_proto (*dict), r->pool);
 
   pool_free (r->pool, var_by_value_idx);
-  r->value_cnt = dict_get_next_value_idx (*dict);
   return casereader_create_sequential
-    (NULL, r->value_cnt,
+    (NULL, r->proto,
      r->case_cnt == -1 ? CASENUMBER_MAX: r->case_cnt,
                                        &sys_file_casereader_class, r);
 
@@ -509,7 +589,7 @@ read_variable_record (struct sfm_reader *r, struct dictionary *dict,
 
   /* Create variable. */
   if (width < 0 || width > 255)
-    sys_error (r, _("Bad variable width %d."), width);
+    sys_error (r, _("Bad width %d for variable %s."), width, name);
   var = dict_create_var (dict, name, width);
   if (var == NULL)
     sys_error (r,
@@ -543,7 +623,7 @@ read_variable_record (struct sfm_reader *r, struct dictionary *dict,
       struct missing_values mv;
       int i;
 
-      mv_init (&mv, var_get_width (var));
+      mv_init_pool (r->pool, &mv, var_get_width (var));
       if (var_is_numeric (var))
         {
           if (missing_value_code < -3 || missing_value_code > 3
@@ -562,21 +642,24 @@ read_variable_record (struct sfm_reader *r, struct dictionary *dict,
         }
       else
         {
+          int mv_width = MAX (width, 8);
+          union value value;
+
           if (missing_value_code < 1 || missing_value_code > 3)
             sys_error (r, _("String missing value indicator field is not "
                             "0, 1, 2, or 3."));
-          if (var_is_long_string (var))
-            sys_warn (r, _("Ignoring missing values on long string variable "
-                           "%s, which PSPP does not yet support."), name);
+
+          value_init (&value, mv_width);
+          value_set_missing (&value, mv_width);
           for (i = 0; i < missing_value_code; i++)
             {
-              char string[9];
-              read_string (r, string, sizeof string);
-              mv_add_str (&mv, string);
+              uint8_t *s = value_str_rw (&value, mv_width);
+              read_bytes (r, s, 8);
+              mv_add_str (&mv, s);
             }
+          value_destroy (&value, mv_width);
         }
-      if (!var_is_long_string (var))
-        var_set_missing_values (var, &mv);
+      var_set_missing_values (var, &mv);
     }
 
   /* Set formats. */
@@ -721,7 +804,7 @@ read_extension_record (struct sfm_reader *r, struct dictionary *dict,
   switch (subtype)
     {
     case 3:
-      read_machine_integer_info (r, size, count, info);
+      read_machine_integer_info (r, size, count, info, dict);
       return;
 
     case 4:
@@ -764,21 +847,28 @@ 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
          the character encoding, e.g. "windows-1252". */
-      break;
+      {
+       char *encoding = pool_calloc (r->pool, size, count + 1);
+       read_string (r, encoding, count + 1);
+       dict_set_encoding (dict, encoding);
+       return;
+      }
 
     case 21:
       /* New in SPSS 16.  Encodes value labels for long string
          variables. */
-      sys_warn (r, _("Ignoring value labels for long string variables, "
-                     "which PSPP does not yet support."));
-      break;
+      read_long_string_value_labels (r, size, count, dict);
+      return;
 
     default:
       sys_warn (r, _("Unrecognized record type 7, subtype %d.  Please send a copy of this file, and the syntax which created it to %s"),
@@ -792,7 +882,8 @@ read_extension_record (struct sfm_reader *r, struct dictionary *dict,
 /* Read record type 7, subtype 3. */
 static void
 read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count,
-                           struct sfm_read_info *info)
+                           struct sfm_read_info *info,
+                          struct dictionary *dict)
 {
   int version_major = read_int (r);
   int version_minor = read_int (r);
@@ -801,7 +892,7 @@ read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count,
   int float_representation = read_int (r);
   int compression_code UNUSED = read_int (r);
   int integer_representation = read_int (r);
-  int character_code UNUSED = read_int (r);
+  int character_code = read_int (r);
 
   int expected_float_format;
   int expected_integer_format;
@@ -846,6 +937,47 @@ read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count,
                 gettext (endian[integer_representation == 1]),
                 gettext (endian[expected_integer_format == 1]));
     }
+
+
+  /*
+    Record 7 (20) provides a much more reliable way of
+    setting the encoding.
+    The character_code is used as a fallback only.
+  */
+  if ( NULL == dict_get_encoding (dict))
+    {
+      switch (character_code)
+       {
+       case 1:
+         dict_set_encoding (dict, "EBCDIC-US");
+         break;
+       case 2:
+       case 3:
+         /* These ostensibly mean "7-bit ASCII" and "8-bit ASCII"[sic]
+            respectively.   However, there are known to be many files
+            in the wild with character code 2, yet have data which are
+            clearly not ascii.
+            Therefore we ignore these values.
+         */
+         return;
+       case 4:
+         dict_set_encoding (dict, "MS_KANJI");
+         break;
+       case 65000:
+         dict_set_encoding (dict, "UTF-7");
+         break;
+       case 65001:
+         dict_set_encoding (dict, "UTF-8");
+         break;
+       default:
+         {
+           char enc[100];
+           snprintf (enc, 100, "CP%d", character_code);
+           dict_set_encoding (dict, enc);
+         }
+         break;
+       };
+    }
 }
 
 /* Read record type 7, subtype 4. */
@@ -861,11 +993,16 @@ read_machine_float_info (struct sfm_reader *r, size_t size, size_t count)
                size, count);
 
   if (sysmis != SYSMIS)
-    sys_warn (r, _("File specifies unexpected value %g as SYSMIS."), sysmis);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              sysmis, "SYSMIS");
+
   if (highest != HIGHEST)
-    sys_warn (r, _("File specifies unexpected value %g as HIGHEST."), highest);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              highest, "HIGHEST");
+
   if (lowest != LOWEST)
-    sys_warn (r, _("File specifies unexpected value %g as LOWEST."), lowest);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              lowest, "LOWEST");
 }
 
 /* Read record type 7, subtype 11, which specifies how variables
@@ -944,14 +1081,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;
@@ -997,7 +1132,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;
 }
 
@@ -1007,14 +1142,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;
@@ -1062,7 +1195,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);
 }
 
@@ -1076,7 +1209,7 @@ read_value_labels (struct sfm_reader *r,
 
   struct label
     {
-      char raw_value[8];        /* Value as uninterpreted bytes. */
+      uint8_t raw_value[8];        /* Value as uninterpreted bytes. */
       union value value;        /* Value. */
       char *label;              /* Null-terminated label string. */
     };
@@ -1086,6 +1219,7 @@ read_value_labels (struct sfm_reader *r,
 
   struct variable **var = NULL;        /* Associated variables. */
   int var_cnt;                 /* Number of associated variables. */
+  int max_width;                /* Maximum width of string variables. */
 
   int i;
 
@@ -1144,12 +1278,15 @@ read_value_labels (struct sfm_reader *r,
 
   /* Read the list of variables. */
   var = pool_nalloc (subpool, var_cnt, sizeof *var);
+  max_width = 0;
   for (i = 0; i < var_cnt; i++)
     {
       var[i] = lookup_var_by_value_idx (r, var_by_value_idx, read_int (r));
-      if (var_is_long_string (var[i]))
-        sys_error (r, _("Value labels are not allowed on long string "
-                        "variables (%s)."), var_get_name (var[i]));
+      if (var_get_width (var[i]) > 8)
+        sys_error (r, _("Value labels may not be added to long string "
+                        "variables (e.g. %s) using records types 3 and 4."),
+                   var_get_name (var[i]));
+      max_width = MAX (max_width, var_get_width (var[i]));
     }
 
   /* Type check the variables. */
@@ -1168,9 +1305,10 @@ read_value_labels (struct sfm_reader *r,
     {
       struct label *label = labels + i;
 
+      value_init_pool (subpool, &label->value, max_width);
       if (var_is_alpha (var[0]))
-        buf_copy_rpad (label->value.s, sizeof label->value.s,
-                       label->raw_value, sizeof label->raw_value);
+        u8_buf_copy_rpad (value_str_rw (&label->value, max_width), max_width,
+                       label->raw_value, sizeof label->raw_value, ' ');
       else
         label->value.f = float_get_double (r->float_format, label->raw_value);
     }
@@ -1192,7 +1330,7 @@ read_value_labels (struct sfm_reader *r,
                           label->value.f, var_get_name (v));
               else
                 sys_warn (r, _("Duplicate value label for \"%.*s\" on %s."),
-                          var_get_width (v), label->value.s,
+                          max_width, value_str (&label->value, max_width),
                           var_get_name (v));
             }
        }
@@ -1200,6 +1338,203 @@ 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);
+}
+
+static void
+skip_long_string_value_labels (struct sfm_reader *r, size_t n_labels)
+{
+  size_t i;
+
+  for (i = 0; i < n_labels; i++)
+    {
+      size_t value_length, label_length;
+
+      value_length = read_int (r);
+      skip_bytes (r, value_length);
+      label_length = read_int (r);
+      skip_bytes (r, label_length);
+    }
+}
+
+static void
+read_long_string_value_labels (struct sfm_reader *r,
+                              size_t size, size_t count,
+                              struct dictionary *d)
+{
+  const off_t start = ftello (r->file);
+  while (ftello (r->file) - start < size * count)
+    {
+      char var_name[VAR_NAME_LEN + 1];
+      size_t n_labels, i;
+      struct variable *v;
+      union value value;
+      int var_name_len;
+      int width;
+
+      /* Read header. */
+      var_name_len = read_int (r);
+      if (var_name_len > VAR_NAME_LEN)
+        sys_error (r, _("Variable name length in long string value label "
+                        "record (%d) exceeds %d-byte limit."),
+                   var_name_len, VAR_NAME_LEN);
+      read_string (r, var_name, var_name_len + 1);
+      width = read_int (r);
+      n_labels = read_int (r);
+
+      v = dict_lookup_var (d, var_name);
+      if (v == NULL)
+        {
+          sys_warn (r, _("Ignoring long string value record for "
+                         "unknown variable %s."), var_name);
+          skip_long_string_value_labels (r, n_labels);
+          continue;
+        }
+      if (var_is_numeric (v))
+        {
+          sys_warn (r, _("Ignoring long string value record for "
+                         "numeric variable %s."), var_name);
+          skip_long_string_value_labels (r, n_labels);
+          continue;
+        }
+      if (width != var_get_width (v))
+        {
+          sys_warn (r, _("Ignoring long string value record for variable %s "
+                         "because the record's width (%d) does not match the "
+                         "variable's width (%d)"),
+                    var_name, width, var_get_width (v));
+          skip_long_string_value_labels (r, n_labels);
+          continue;
+        }
+
+      /* Read values. */
+      value_init_pool (r->pool, &value, width);
+      for (i = 0; i < n_labels; i++)
+       {
+          size_t value_length, label_length;
+          char label[256];
+          bool skip = false;
+
+          /* Read value. */
+          value_length = read_int (r);
+          if (value_length == width)
+            read_bytes (r, value_str_rw (&value, width), width);
+          else
+            {
+              sys_warn (r, _("Ignoring long string value %zu for variable %s, "
+                             "with width %d, that has bad value width %zu."),
+                        i, var_get_name (v), width, value_length);
+              skip_bytes (r, value_length);
+              skip = true;
+            }
+
+          /* Read label. */
+          label_length = read_int (r);
+          read_string (r, label, MIN (sizeof label, label_length + 1));
+          if (label_length >= sizeof label)
+            {
+              /* Skip and silently ignore label text after the
+                 first 255 bytes.  The maximum documented length
+                 of a label is 120 bytes so this is more than
+                 generous. */
+              skip_bytes (r, sizeof label - (label_length + 1));
+            }
+
+          if (!skip && !var_add_value_label (v, &value, label))
+            sys_warn (r, _("Duplicate value label for \"%.*s\" on %s."),
+                      width, value_str (&value, width), var_get_name (v));
+        }
+    }
+}
+
+
+/* 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. */
 
@@ -1209,31 +1544,31 @@ static void partial_record (struct sfm_reader *r)
 static void read_error (struct casereader *, const struct sfm_reader *);
 
 static bool read_case_number (struct sfm_reader *, double *);
-static bool read_case_string (struct sfm_reader *, char *, size_t);
+static bool read_case_string (struct sfm_reader *, uint8_t *, size_t);
 static int read_opcode (struct sfm_reader *);
 static bool read_compressed_number (struct sfm_reader *, double *);
-static bool read_compressed_string (struct sfm_reader *, char *);
-static bool read_whole_strings (struct sfm_reader *, char *, size_t);
+static bool read_compressed_string (struct sfm_reader *, uint8_t *);
+static bool read_whole_strings (struct sfm_reader *, uint8_t *, 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->proto);
   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++)
@@ -1241,28 +1576,29 @@ sys_file_casereader_read (struct casereader *reader, void *r_,
       struct sfm_var *sv = &r->sfm_vars[i];
       union value *v = case_data_rw_idx (c, sv->case_index);
 
-      if (sv->width == 0)
+      if (sv->var_width == 0)
         {
           if (!read_case_number (r, &v->f))
             goto eof;
         }
       else
         {
-          if (!read_case_string (r, v->s + sv->offset, sv->width))
+          uint8_t *s = value_str_rw (v, sv->var_width);
+          if (!read_case_string (r, s + sv->offset, sv->segment_width))
             goto eof;
           if (!skip_whole_strings (r, ROUND_DOWN (sv->padding, 8)))
             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. */
@@ -1309,7 +1645,7 @@ read_case_number (struct sfm_reader *r, double *d)
    Returns true if successful, false if end of file is
    reached immediately. */
 static bool
-read_case_string (struct sfm_reader *r, char *s, size_t length)
+read_case_string (struct sfm_reader *r, uint8_t *s, size_t length)
 {
   size_t whole = ROUND_DOWN (length, 8);
   size_t partial = length % 8;
@@ -1322,7 +1658,7 @@ read_case_string (struct sfm_reader *r, char *s, size_t length)
 
   if (partial)
     {
-      char bounce[8];
+      uint8_t bounce[8];
       if (!read_whole_strings (r, bounce, sizeof bounce))
         {
           if (whole)
@@ -1393,7 +1729,7 @@ read_compressed_number (struct sfm_reader *r, double *d)
    Returns true if successful, false if end of file is
    reached immediately. */
 static bool
-read_compressed_string (struct sfm_reader *r, char *dst)
+read_compressed_string (struct sfm_reader *r, uint8_t *dst)
 {
   switch (read_opcode (r))
     {
@@ -1422,7 +1758,7 @@ read_compressed_string (struct sfm_reader *r, char *dst)
    Returns true if successful, false if end of file is
    reached immediately. */
 static bool
-read_whole_strings (struct sfm_reader *r, char *s, size_t length)
+read_whole_strings (struct sfm_reader *r, uint8_t *s, size_t length)
 {
   assert (length % 8 == 0);
   if (!r->compressed)
@@ -1450,7 +1786,7 @@ read_whole_strings (struct sfm_reader *r, char *s, size_t length)
 static bool
 skip_whole_strings (struct sfm_reader *r, size_t length)
 {
-  char buffer[1024];
+  uint8_t buffer[1024];
   assert (length < sizeof buffer);
   return read_whole_strings (r, buffer, length);
 }
@@ -1535,82 +1871,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..3fed2e05445135ec2ebcc300ce23c4855d0f41df 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 <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
+#include <libpspp/i18n.h>
 #include <libpspp/version.h>
 
+#include <data/attributes.h>
 #include <data/case.h>
 #include <data/casewriter-provider.h>
 #include <data/casewriter.h>
@@ -93,24 +95,34 @@ struct sfm_writer
 static const struct casewriter_class sys_file_casewriter_class;
 
 static void write_header (struct sfm_writer *, const struct dictionary *);
-static void write_variable (struct sfm_writer *, const struct variable *);
+static void write_variable (struct sfm_writer *, const struct variable *, const struct dictionary *);
 static void write_value_labels (struct sfm_writer *,
-                                struct variable *, int idx);
+                                struct variable *, int idx, const struct dictionary *);
 static void write_integer_info_record (struct sfm_writer *);
 static void write_float_info_record (struct sfm_writer *);
 
 static void write_longvar_table (struct sfm_writer *w,
                                  const struct dictionary *dict);
 
+static void write_encoding_record (struct sfm_writer *w,
+                                  const struct dictionary *);
+
 static void write_vls_length_table (struct sfm_writer *w,
                              const struct dictionary *dict);
 
+static void write_long_string_value_labels (struct sfm_writer *,
+                                            const struct dictionary *);
 
 static void write_variable_display_parameters (struct sfm_writer *w,
                                                const struct dictionary *dict);
 
 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 +132,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);
@@ -210,7 +223,7 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
   /* Write basic variable info. */
   short_names_assign (d);
   for (i = 0; i < dict_get_var_cnt (d); i++)
-    write_variable (w, dict_get_var (d, i));
+    write_variable (w, dict_get_var (d, i), d);
 
   /* Write out value labels. */
   idx = 0;
@@ -218,7 +231,7 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
     {
       struct variable *v = dict_get_var (d, i);
 
-      write_value_labels (w, v, idx);
+      write_value_labels (w, v, idx, d);
       idx += sfm_width_to_octs (var_get_width (v));
     }
 
@@ -235,6 +248,14 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
 
   write_vls_length_table (w, d);
 
+  write_long_string_value_labels (w, d);
+
+  if (attrset_count (dict_get_attributes (d)))
+    write_data_file_attributes (w, d);
+  write_variable_attributes (w, d);
+
+  write_encoding_record (w, d);
+
   /* Write end-of-headers record. */
   write_int (w, 999);
   write_int (w, 0);
@@ -245,8 +266,7 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
       return NULL;
     }
 
-  return casewriter_create (dict_get_next_value_idx (d),
-                            &sys_file_casewriter_class, w);
+  return casewriter_create (dict_get_proto (d), &sys_file_casewriter_class, w);
 
 error:
   close_writer (w);
@@ -401,12 +421,12 @@ write_variable_continuation_records (struct sfm_writer *w, int width)
 /* Write the variable record(s) for variable V to system file
    W. */
 static void
-write_variable (struct sfm_writer *w, const struct variable *v)
+write_variable (struct sfm_writer *w, const struct variable *v, const struct dictionary *dict)
 {
   int width = var_get_width (v);
   int segment_cnt = sfm_width_to_segments (width);
   int seg0_width = sfm_segment_alloc_width (width, 0);
-  const struct missing_values *mv = var_get_missing_values (v);
+  struct missing_values mv;
   int i;
 
   /* Record type. */
@@ -421,7 +441,13 @@ write_variable (struct sfm_writer *w, const struct variable *v)
   /* Number of missing values.  If there is a range, then the
      range counts as 2 missing values and causes the number to be
      negated. */
-  write_int (w, mv_has_range (mv) ? -2 - mv_n_values (mv) : mv_n_values (mv));
+  mv_copy (&mv, var_get_missing_values (v));
+  if (mv_get_width (&mv) > 8)
+    mv_resize (&mv, 8);
+  if (mv_has_range (&mv))
+    write_int (w, -2 - mv_n_values (&mv));
+  else
+    write_int (w, mv_n_values (&mv));
 
   /* Print and write formats. */
   write_format (w, *var_get_print_format (v), seg0_width);
@@ -436,25 +462,23 @@ write_variable (struct sfm_writer *w, const struct variable *v)
   if (var_has_label (v))
     {
       const char *label = var_get_label (v);
-      size_t padded_len = ROUND_UP (MIN (strlen (label), 255), 4);
+      char *l = recode_string (dict_get_encoding (dict), UTF8, label, -1);
+      size_t padded_len = ROUND_UP (MIN (strlen (l), 255), 4);
       write_int (w, padded_len);
-      write_string (w, label, padded_len);
+      write_string (w, l, padded_len);
+      free (l);
     }
 
   /* Write the missing values, if any, range first. */
-  if (mv_has_range (mv))
+  if (mv_has_range (&mv))
     {
       double x, y;
-      mv_get_range (mv, &x, &y);
+      mv_get_range (&mv, &x, &y);
       write_float (w, x);
       write_float (w, y);
     }
-  for (i = 0; i < mv_n_values (mv); i++)
-    {
-      union value value;
-      mv_get_value (mv, &value, i);
-      write_value (w, &value, seg0_width);
-    }
+  for (i = 0; i < mv_n_values (&mv); i++)
+    write_value (w, mv_get_value (&mv, i), mv_get_width (&mv));
 
   write_variable_continuation_records (w, seg0_width);
 
@@ -474,34 +498,45 @@ write_variable (struct sfm_writer *w, const struct variable *v)
 
       write_variable_continuation_records (w, seg_width);
     }
+
+  mv_destroy (&mv);
 }
 
 /* Writes the value labels for variable V having system file
-   variable index IDX to system file W. */
+   variable index IDX to system file W.
+
+   Value labels for long string variables are written separately,
+   by write_long_string_value_labels. */
 static void
-write_value_labels (struct sfm_writer *w, struct variable *v, int idx)
+write_value_labels (struct sfm_writer *w, struct variable *v, int idx, const struct dictionary *dict)
 {
   const struct val_labs *val_labs;
-  struct val_labs_iterator *i;
-  struct val_lab *vl;
+  const struct val_lab **labels;
+  size_t n_labels;
+  size_t i;
 
   val_labs = var_get_value_labels (v);
-  if (val_labs == NULL)
+  n_labels = val_labs_count (val_labs);
+  if (n_labels == 0 || var_get_width (v) > 8)
     return;
 
   /* Value label record. */
   write_int (w, 3);             /* Record type. */
   write_int (w, val_labs_count (val_labs));
-  for (vl = val_labs_first_sorted (val_labs, &i); vl != NULL;
-       vl = val_labs_next (val_labs, &i))
+  labels = val_labs_sorted (val_labs);
+  for (i = 0; i < n_labels; i++)
     {
-      uint8_t len = MIN (strlen (vl->label), 255);
+      const struct val_lab *vl = labels[i];
+      char *label = recode_string (dict_get_encoding (dict), UTF8, val_lab_get_label (vl), -1);
+      uint8_t len = MIN (strlen (label), 255);
 
-      write_value (w, &vl->value, var_get_width (v));
+      write_value (w, val_lab_get_value (vl), var_get_width (v));
       write_bytes (w, &len, 1);
-      write_bytes (w, vl->label, len);
+      write_bytes (w, label, len);
       write_zeros (w, REM_RND_UP (len + 1, 8));
+      free (label);
     }
+  free (labels);
 
   /* Value label variable record. */
   write_int (w, 4);             /* Record type. */
@@ -520,6 +555,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,
@@ -583,6 +684,89 @@ write_vls_length_table (struct sfm_writer *w,
   ds_destroy (&map);
 }
 
+
+static void
+write_long_string_value_labels (struct sfm_writer *w,
+                                const struct dictionary *dict)
+{
+  size_t n_vars = dict_get_var_cnt (dict);
+  size_t size, i;
+  off_t start UNUSED;
+
+  /* Figure out the size in advance. */
+  size = 0;
+  for (i = 0; i < n_vars; i++)
+    {
+      struct variable *var = dict_get_var (dict, i);
+      const struct val_labs *val_labs = var_get_value_labels (var);
+      int width = var_get_width (var);
+      const struct val_lab *val_lab;
+
+      if (val_labs_count (val_labs) == 0 || width < 9)
+        continue;
+
+      size += 12 + strlen (var_get_name (var));
+      for (val_lab = val_labs_first (val_labs); val_lab != NULL;
+           val_lab = val_labs_next (val_labs, val_lab))
+        size += 8 + width + strlen (val_lab_get_label (val_lab));
+    }
+  if (size == 0)
+    return;
+
+  write_int (w, 7);             /* Record type. */
+  write_int (w, 21);            /* Record subtype */
+  write_int (w, 1);             /* Data item (byte) size. */
+  write_int (w, size);          /* Number of data items. */
+
+  start = ftello (w->file);
+  for (i = 0; i < n_vars; i++)
+    {
+      struct variable *var = dict_get_var (dict, i);
+      const struct val_labs *val_labs = var_get_value_labels (var);
+      const char *var_name = var_get_name (var);
+      int width = var_get_width (var);
+      const struct val_lab *val_lab;
+
+      if (val_labs_count (val_labs) == 0 || width < 9)
+        continue;
+
+      write_int (w, strlen (var_name));
+      write_bytes (w, var_name, strlen (var_name));
+      write_int (w, width);
+      write_int (w, val_labs_count (val_labs));
+      for (val_lab = val_labs_first (val_labs); val_lab != NULL;
+           val_lab = val_labs_next (val_labs, val_lab))
+        {
+          const char *label = val_lab_get_label (val_lab);
+          size_t label_length = strlen (label);
+
+          write_int (w, width);
+          write_bytes (w, value_str (val_lab_get_value (val_lab), width),
+                       width);
+          write_int (w, label_length);
+          write_bytes (w, label, label_length);
+        }
+    }
+  assert (ftello (w->file) == start + size);
+}
+
+static void
+write_encoding_record (struct sfm_writer *w,
+                      const struct dictionary *d)
+{
+  const char *enc = dict_get_encoding (d);
+
+  if ( NULL == enc)
+    return;
+
+  write_int (w, 7);             /* Record type. */
+  write_int (w, 20);            /* Record subtype. */
+  write_int (w, 1);             /* Data item (char) size. */
+  write_int (w, strlen (enc));  /* Number of data items. */
+  write_string (w, enc, strlen (enc));
+}
+
+
 /* Writes the long variable name table. */
 static void
 write_longvar_table (struct sfm_writer *w, const struct dictionary *dict)
@@ -594,11 +778,13 @@ write_longvar_table (struct sfm_writer *w, const struct dictionary *dict)
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     {
       struct variable *v = dict_get_var (dict, i);
+      char *longname = recode_string (dict_get_encoding (dict), UTF8, var_get_name (v), -1);
 
       if (i)
         ds_put_char (&map, '\t');
       ds_put_format (&map, "%s=%s",
-                     var_get_short_name (v, 0), var_get_name (v));
+                     var_get_short_name (v, 0), longname);
+      free (longname);
     }
 
   write_int (w, 7);             /* Record type. */
@@ -671,7 +857,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 +868,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 +946,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;
 
@@ -768,12 +954,12 @@ write_case_uncompressed (struct sfm_writer *w, struct ccase *c)
     {
       struct sfm_var *v = &w->sfm_vars[i];
 
-      if (v->width == 0)
+      if (v->var_width == 0)
         write_float (w, case_num_idx (c, v->case_index));
       else
         {
           write_bytes (w, case_str_idx (c, v->case_index) + v->offset,
-                       v->width);
+                       v->segment_width);
           write_spaces (w, v->padding);
         }
     }
@@ -781,7 +967,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;
 
@@ -789,7 +975,7 @@ write_case_compressed (struct sfm_writer *w, struct ccase *c)
     {
       struct sfm_var *v = &w->sfm_vars[i];
 
-      if (v->width == 0)
+      if (v->var_width == 0)
         {
           double d = case_num_idx (c, v->case_index);
           if (d == SYSMIS)
@@ -813,7 +999,7 @@ write_case_compressed (struct sfm_writer *w, struct ccase *c)
              multiple of 8, by ensuring that the final partial
              oct (8 byte unit) is treated as padded with spaces
              on the right. */
-          for (width = v->width; width > 0; width -= 8, offset += 8)
+          for (width = v->segment_width; width > 0; width -= 8, offset += 8)
             {
               const void *data = case_str_idx (c, v->case_index) + offset;
               int chunk_size = MIN (width, 8);
@@ -936,13 +1122,13 @@ write_value (struct sfm_writer *w, const union value *value, int width)
     write_float (w, value->f);
   else
     {
-      write_bytes (w, value->s, width);
+      write_bytes (w, value_str (value, width), width);
       write_zeros (w, 8 - width);
     }
 }
 
 /* Writes null-terminated STRING in a field of the given WIDTH to
-   W.  If WIDTH is longer than WIDTH, it is truncated; if WIDTH
+   W.  If STRING is longer than WIDTH, it is truncated; if WIDTH
    is narrowed, it is padded on the right with spaces. */
 static void
 write_string (struct sfm_writer *w, const char *string, size_t width)
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 9cee8d58beaa43c876bedc1015f0d36b4b08ee5a..c8061f7bd5d7fee54c47f5e65aa739b442f10dce 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <data/data-out.h>
 #include <data/value.h>
 #include <data/variable.h>
+#include <libpspp/array.h>
 #include <libpspp/compiler.h>
-#include <libpspp/hash.h>
+#include <libpspp/hash-functions.h>
+#include <libpspp/hmap.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 
 #include "xalloc.h"
 
-static hsh_compare_func compare_int_val_lab;
-static hsh_hash_func hash_int_val_lab;
-static hsh_free_func free_int_val_lab;
-
-struct atom;
 static struct atom *atom_create (const char *string);
 static void atom_destroy (struct atom *);
-static char *atom_to_string (const struct atom *);
+static const char *atom_to_string (const struct atom *);
 
-/* A set of value labels. */
-struct val_labs
-  {
-    int width;                  /* 0=numeric, otherwise string width. */
-    struct hsh_table *labels;   /* Hash table of `struct int_val_lab's. */
-  };
+/* Returns the label in VL.  The caller must not modify or free
+   the returned value. */
+const char *
+val_lab_get_label (const struct val_lab *vl)
+{
+  return atom_to_string (vl->label);
+}
 
 /* Creates and returns a new, empty set of value labels with the
-   given WIDTH.  To actually add any value labels, WIDTH must be
-   a numeric or short string width. */
+   given WIDTH. */
 struct val_labs *
 val_labs_create (int width)
 {
-  struct val_labs *vls;
-
-  assert (width >= 0);
-
-  vls = xmalloc (sizeof *vls);
+  struct val_labs *vls = xmalloc (sizeof *vls);
   vls->width = width;
-  vls->labels = NULL;
+  hmap_init (&vls->labels);
   return vls;
 }
 
 /* Creates and returns a new set of value labels identical to
-   VLS. */
+   VLS.  Returns a null pointer if VLS is null. */
 struct val_labs *
 val_labs_clone (const struct val_labs *vls)
 {
   struct val_labs *copy;
-  struct val_labs_iterator *i;
-  struct val_lab *vl;
+  struct val_lab *label;
 
   if (vls == NULL)
     return NULL;
 
   copy = val_labs_create (vls->width);
-  for (vl = val_labs_first (vls, &i); vl != NULL;
-       vl = val_labs_next (vls, &i))
-    val_labs_add (copy, vl->value, vl->label);
+  HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
+    val_labs_add (copy, &label->value, atom_to_string (label->label));
   return copy;
 }
 
@@ -86,32 +77,28 @@ val_labs_clone (const struct val_labs *vls)
 bool
 val_labs_can_set_width (const struct val_labs *vls, int new_width)
 {
-  struct val_labs_iterator *i;
-  struct val_lab *lab;
+  struct val_lab *label;
 
-  for (lab = val_labs_first (vls, &i); lab != NULL;
-       lab = val_labs_next (vls, &i))
-    if (!value_is_resizable (&lab->value, vls->width, new_width))
-      {
-        val_labs_done (&i);
-        return false;
-      }
+  HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
+    if (!value_is_resizable (&label->value, vls->width, new_width))
+      return false;
 
   return true;
 }
 
 /* Changes the width of VLS to NEW_WIDTH.  The original and new
-   width must be both numeric or both string.  If the new width
-   is a long string width, then any value labels in VLS are
-   deleted. */
+   width must be both numeric or both string. */
 void
 val_labs_set_width (struct val_labs *vls, int new_width)
 {
   assert (val_labs_can_set_width (vls, new_width));
-
+  if (value_needs_resize (vls->width, new_width))
+    {
+      struct val_lab *label;
+      HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
+        value_resize (&label->value, vls->width, new_width);
+    }
   vls->width = new_width;
-  if (new_width > MAX_SHORT_STRING)
-    val_labs_clear (vls);
 }
 
 /* Destroys VLS. */
@@ -120,7 +107,8 @@ val_labs_destroy (struct val_labs *vls)
 {
   if (vls != NULL)
     {
-      hsh_destroy (vls->labels);
+      val_labs_clear (vls);
+      hmap_destroy (&vls->labels);
       free (vls);
     }
 }
@@ -129,328 +117,205 @@ val_labs_destroy (struct val_labs *vls)
 void
 val_labs_clear (struct val_labs *vls)
 {
-  assert (vls != NULL);
+  struct val_lab *label, *next;
 
-  hsh_destroy (vls->labels);
-  vls->labels = NULL;
+  HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
+    {
+      hmap_delete (&vls->labels, &label->node);
+      value_destroy (&label->value, vls->width);
+      atom_destroy (label->label);
+      free (label);
+    }
 }
 
-/* Returns the number of value labels in VLS. */
+/* Returns the number of value labels in VLS.
+   Returns 0 if VLS is null. */
 size_t
 val_labs_count (const struct val_labs *vls)
 {
-  return vls == NULL || vls->labels == NULL ? 0 : hsh_count (vls->labels);
+  return vls == NULL ? 0 : hmap_count (&vls->labels);
 }
 \f
-/* One value label in internal format. */
-struct int_val_lab
-  {
-    union value value;          /* The value being labeled. */
-    struct atom *label;         /* A ref-counted string. */
-  };
-
-/* Creates and returns an int_val_lab based on VALUE and
-   LABEL. */
-static struct int_val_lab *
-create_int_val_lab (struct val_labs *vls, union value value, const char *label)
+static void
+do_add_val_lab (struct val_labs *vls, const union value *value,
+                const char *label)
 {
-  struct int_val_lab *ivl;
-
-  assert (label != NULL);
-  assert (vls->width <= MAX_SHORT_STRING);
-
-  ivl = xmalloc (sizeof *ivl);
-  ivl->value = value;
-  if (vls->width > 0)
-    memset (ivl->value.s + vls->width, ' ', MAX_SHORT_STRING - vls->width);
-  ivl->label = atom_create (label);
-
-  return ivl;
+  struct val_lab *lab = xmalloc (sizeof *lab);
+  value_init (&lab->value, vls->width);
+  value_copy (&lab->value, value, vls->width);
+  lab->label = atom_create (label);
+  hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
 }
 
-/* If VLS does not already contain a value label for VALUE (and
-   VLS represents a numeric or short string set of value labels),
-   adds LABEL for it and returns true.  Otherwise, returns
-   false. */
+/* If VLS does not already contain a value label for VALUE, adds
+   LABEL for it and returns true.  Otherwise, returns false. */
 bool
-val_labs_add (struct val_labs *vls, union value value, const char *label)
+val_labs_add (struct val_labs *vls, const union value *value,
+              const char *label)
 {
-  assert (label != NULL);
-  if (vls->width < MIN_LONG_STRING)
+  const struct val_lab *lab = val_labs_lookup (vls, value);
+  if (lab == NULL)
     {
-      struct int_val_lab *ivl;
-      void **vlpp;
-
-      if (vls->labels == NULL)
-        vls->labels = hsh_create (8, compare_int_val_lab, hash_int_val_lab,
-                                  free_int_val_lab, vls);
-
-      ivl = create_int_val_lab (vls, value, label);
-      vlpp = hsh_probe (vls->labels, ivl);
-      if (*vlpp == NULL)
-        {
-          *vlpp = ivl;
-          return true;
-        }
-      free_int_val_lab (ivl, vls);
+      do_add_val_lab (vls, value, label);
+      return true;
     }
-  return false;
+  else
+    return false;
 }
 
 /* Sets LABEL as the value label for VALUE in VLS, replacing any
-   existing label for VALUE.  Has no effect if VLS has a long
-   string width. */
+   existing label for VALUE. */
 void
-val_labs_replace (struct val_labs *vls, union value value, const char *label)
+val_labs_replace (struct val_labs *vls, const union value *value,
+                  const char *label)
 {
-  if (vls->width < MIN_LONG_STRING)
+  struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value);
+  if (vl != NULL)
     {
-      if (vls->labels != NULL)
-        {
-          struct int_val_lab *new = create_int_val_lab (vls, value, label);
-          struct int_val_lab *old = hsh_replace (vls->labels, new);
-          if (old != NULL)
-            free_int_val_lab (old, vls);
-        }
-      else
-        val_labs_add (vls, value, label);
+      atom_destroy (vl->label);
+      vl->label = atom_create (label);
     }
+  else
+    do_add_val_lab (vls, value, label);
 }
 
-/* Removes any value label for VALUE within VLS.  Returns true
-   if a value label was removed. */
-bool
-val_labs_remove (struct val_labs *vls, union value value)
+/* Removes LABEL from VLS. */
+void
+val_labs_remove (struct val_labs *vls, const struct val_lab *label_)
 {
-  if (vls->width < MIN_LONG_STRING && vls->labels != NULL)
-    {
-      struct int_val_lab *ivl = create_int_val_lab (vls, value, "");
-      int deleted = hsh_delete (vls->labels, ivl);
-      free (ivl);
-      return deleted;
-    }
-  else
-    return false;
+  struct val_lab *label = (struct val_lab *) label_;
+  hmap_delete (&vls->labels, &label->node);
+  value_destroy (&label->value, vls->width);
+  atom_destroy (label->label);
+  free (label);
 }
 
 /* Searches VLS for a value label for VALUE.  If successful,
-   returns the label; otherwise, returns a null pointer.  If
-   VLS's width is greater than MAX_SHORT_STRING, always returns a
-   null pointer. */
-char *
-val_labs_find (const struct val_labs *vls, union value value)
+   returns the string used as the label; otherwise, returns a
+   null pointer.  Returns a null pointer if VLS is null. */
+const char *
+val_labs_find (const struct val_labs *vls, const union value *value)
 {
-  if (vls != NULL
-      && vls->width <= MAX_SHORT_STRING
-      && vls->labels != NULL)
-    {
-      struct int_val_lab ivl, *vlp;
-
-      ivl.value = value;
-      vlp = hsh_find (vls->labels, &ivl);
-      if (vlp != NULL)
-        return atom_to_string (vlp->label);
-    }
-  return NULL;
+  const struct val_lab *label = val_labs_lookup (vls, value);
+  return label ? atom_to_string (label->label) : NULL;
 }
-\f
-/* A value labels iterator. */
-struct val_labs_iterator
-  {
-    void **labels;              /* The labels, in order. */
-    void **lp;                  /* Current label. */
-    struct val_lab vl;          /* Structure presented to caller. */
-  };
 
-/* Sets up *IP for iterating through the value labels in VLS in
-   no particular order.  Returns the first value label or a null
-   pointer if VLS is empty.  If the return value is non-null,
-   then val_labs_next() may be used to continue iterating or
-   val_labs_done() to free up the iterator.  Otherwise, neither
-   function may be called for *IP. */
-struct val_lab *
-val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip)
+/* Searches VLS for a value label for VALUE.  If successful,
+   returns the value label; otherwise, returns a null pointer.
+   Returns a null pointer if VLS is null. */
+const struct val_lab *
+val_labs_lookup (const struct val_labs *vls, const union value *value)
 {
-  struct val_labs_iterator *i;
-
-  assert (vls != NULL);
-  assert (ip != NULL);
-
-  if (vls->labels == NULL || vls->width > MAX_SHORT_STRING)
+  if (vls != NULL)
     {
-      *ip = NULL;
-      return NULL;
+      struct val_lab *label;
+      HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node,
+                               value_hash (value, vls->width, 0), &vls->labels)
+        if (value_equal (&label->value, value, vls->width))
+          return label;
     }
-
-  i = *ip = xmalloc (sizeof *i);
-  i->labels = hsh_data_copy (vls->labels);
-  i->lp = i->labels;
-  return val_labs_next (vls, ip);
+  return NULL;
 }
-
-/* Sets up *IP for iterating through the value labels in VLS in
-   sorted order of values.  Returns the first value label or a
-   null pointer if VLS is empty.  If the return value is
-   non-null, then val_labs_next() may be used to continue
-   iterating or val_labs_done() to free up the iterator.
-   Otherwise, neither function may be called for *IP. */
-struct val_lab *
-val_labs_first_sorted (const struct val_labs *vls,
-                       struct val_labs_iterator **ip)
+\f
+/* Returns the first value label in VLS, in arbitrary order, or a
+   null pointer if VLS is empty or if VLS is a null pointer.  If
+   the return value is non-null, then val_labs_next() may be used
+   to continue iterating. */
+const struct val_lab *
+val_labs_first (const struct val_labs *vls)
 {
-  struct val_labs_iterator *i;
-
-  assert (vls != NULL);
-  assert (ip != NULL);
-
-  if (vls->labels == NULL || vls->width > MAX_SHORT_STRING)
-    {
-      *ip = NULL;
-      return NULL;
-    }
-
-  i = *ip = xmalloc (sizeof *i);
-  i->lp = i->labels = hsh_sort_copy (vls->labels);
-  return val_labs_next (vls, ip);
+  return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL;
 }
 
 /* Returns the next value label in an iteration begun by
-   val_labs_first() or val_labs_first_sorted().  If the return
-   value is non-null, then val_labs_next() may be used to
-   continue iterating or val_labs_done() to free up the iterator.
-   Otherwise, neither function may be called for *IP. */
-struct val_lab *
-val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip)
+   val_labs_first().  If the return value is non-null, then
+   val_labs_next() may be used to continue iterating. */
+const struct val_lab *
+val_labs_next (const struct val_labs *vls, const struct val_lab *label)
 {
-  struct val_labs_iterator *i;
-  struct int_val_lab *ivl;
-
-  assert (vls != NULL);
-  assert (vls->width <= MAX_SHORT_STRING);
-  assert (ip != NULL);
-  assert (*ip != NULL);
-
-  i = *ip;
-  ivl = *i->lp++;
-  if (ivl != NULL)
-    {
-      i->vl.value = ivl->value;
-      i->vl.label = atom_to_string (ivl->label);
-      return &i->vl;
-    }
-  else
-    {
-      free (i->labels);
-      free (i);
-      *ip = NULL;
-      return NULL;
-    }
+  return HMAP_NEXT (label, struct val_lab, node, &vls->labels);
 }
 
-/* Discards the state for an incomplete iteration begun by
-   val_labs_first() or val_labs_first_sorted(). */
-void
-val_labs_done (struct val_labs_iterator **ip)
-{
-  if (*ip != NULL)
-    {
-      struct val_labs_iterator *i = *ip;
-      free (i->labels);
-      free (i);
-      *ip = NULL;
-    }
-}
-\f
-/* Compares two value labels and returns a strcmp()-type result. */
-int
-compare_int_val_lab (const void *a_, const void *b_, const void *vls_)
+static int
+compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_)
 {
-  const struct int_val_lab *a = a_;
-  const struct int_val_lab *b = b_;
+  const struct val_lab *const *a = a_;
+  const struct val_lab *const *b = b_;
   const struct val_labs *vls = vls_;
-
-  if (vls->width == 0)
-    return a->value.f < b->value.f ? -1 : a->value.f > b->value.f;
-  else
-    return memcmp (a->value.s, b->value.s, vls->width);
+  return value_compare_3way (&(*a)->value, &(*b)->value, vls->width);
 }
 
-/* Hash a value label. */
-unsigned
-hash_int_val_lab (const void *vl_, const void *vls_)
+/* Allocates and returns an array of pointers to value labels
+   that is sorted in increasing order by value.  The array has
+   val_labs_count(VLS) elements.  The caller is responsible for
+   freeing the array. */
+const struct val_lab **
+val_labs_sorted (const struct val_labs *vls)
 {
-  const struct int_val_lab *vl = vl_;
-  const struct val_labs *vls = vls_;
-
-  if (vls->width == 0)
-    return hsh_hash_double (vl->value.f);
+  if (vls != NULL)
+    {
+      const struct val_lab *label;
+      const struct val_lab **labels;
+      size_t i;
+
+      labels = xmalloc (val_labs_count (vls) * sizeof *labels);
+      i = 0;
+      HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
+        labels[i++] = label;
+      assert (i == val_labs_count (vls));
+      sort (labels, val_labs_count (vls), sizeof *labels,
+            compare_labels_by_value_3way, vls);
+      return labels;
+    }
   else
-    return hsh_hash_bytes (vl->value.s, vls->width);
-}
-
-/* Free a value label. */
-void
-free_int_val_lab (void *vl_, const void *vls_ UNUSED)
-{
-  struct int_val_lab *vl = vl_;
-
-  atom_destroy (vl->label);
-  free (vl);
+    return NULL;
 }
 \f
-/* Atoms. */
+/* Atoms: reference-counted constant strings. */
 
 /* An atom. */
 struct atom
   {
+    struct hmap_node node;      /* Hash map node. */
     char *string;               /* String value. */
     unsigned ref_count;         /* Number of references. */
   };
 
-static hsh_compare_func compare_atoms;
-static hsh_hash_func hash_atom;
-static hsh_free_func free_atom;
-
 /* Hash table of atoms. */
-static struct hsh_table *atoms;
+static struct hmap atoms = HMAP_INITIALIZER (atoms);
 
-static void
-destroy_atoms (void)
-{
-  hsh_destroy (atoms);
-}
+static void free_atom (struct atom *atom);
+static void free_all_atoms (void);
 
 /* Creates and returns an atom for STRING. */
 static struct atom *
 atom_create (const char *string)
 {
-  struct atom a;
-  void **app;
+  static bool initialized;
+  struct atom *atom;
+  size_t hash;
 
   assert (string != NULL);
 
-  if (atoms == NULL)
+  if (!initialized)
     {
-      atoms = hsh_create (8, compare_atoms, hash_atom, free_atom, NULL);
-      atexit (destroy_atoms);
+      initialized = true;
+      atexit (free_all_atoms);
     }
 
-  a.string = (char *) string;
-  app = hsh_probe (atoms, &a);
-  if (*app != NULL)
-    {
-      struct atom *ap = *app;
-      ap->ref_count++;
-      return ap;
-    }
-  else
-    {
-      struct atom *ap = xmalloc (sizeof *ap);
-      ap->string = xstrdup (string);
-      ap->ref_count = 1;
-      *app = ap;
-      return ap;
-    }
+  hash = hash_string (string, 0);
+  HMAP_FOR_EACH_WITH_HASH (atom, struct atom, node, hash, &atoms)
+    if (!strcmp (atom->string, string))
+      {
+        atom->ref_count++;
+        return atom;
+      }
+
+  atom = xmalloc (sizeof *atom);
+  atom->string = xstrdup (string);
+  atom->ref_count = 1;
+  hmap_insert (&atoms, &atom->node, hash);
+  return atom;
 }
 
 /* Destroys ATOM. */
@@ -462,44 +327,32 @@ atom_destroy (struct atom *atom)
       assert (atom->ref_count > 0);
       atom->ref_count--;
       if (atom->ref_count == 0)
-        hsh_force_delete (atoms, atom);
+        {
+          hmap_delete (&atoms, &atom->node);
+          free_atom (atom);
+        }
     }
 }
 
 /* Returns the string associated with ATOM. */
-static  char *
+static const char *
 atom_to_string (const struct atom *atom)
 {
-  assert (atom != NULL);
-
   return atom->string;
 }
 
-/* A hsh_compare_func that compares A and B. */
-static int
-compare_atoms (const void *a_, const void *b_, const void *aux UNUSED)
-{
-  const struct atom *a = a_;
-  const struct atom *b = b_;
-
-  return strcmp (a->string, b->string);
-}
-
-/* A hsh_hash_func that hashes ATOM. */
-static unsigned
-hash_atom (const void *atom_, const void *aux UNUSED)
+static void
+free_atom (struct atom *atom)
 {
-  const struct atom *atom = atom_;
-
-  return hsh_hash_string (atom->string);
+  free (atom->string);
+  free (atom);
 }
 
-/* A hsh_free_func that destroys ATOM. */
 static void
-free_atom (void *atom_, const void *aux UNUSED)
+free_all_atoms (void)
 {
-  struct atom *atom = atom_;
+  struct atom *atom, *next;
 
-  free (atom->string);
-  free (atom);
+  HMAP_FOR_EACH_SAFE (atom, next, struct atom, node, &atoms)
+    free_atom (atom);
 }
index fb7ec22b57564a922562fa2c46acc557532bc6d5..53d13a389700eb586af6aaf14bd82bd540311235 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <stdbool.h>
 #include <stddef.h>
 #include <data/value.h>
+#include <libpspp/hmap.h>
 
-/* One value label. */
+/* One value label.
+
+   A value label is normally part of a struct val_labs (see
+   below). */
 struct val_lab
   {
-    union value value;
-    const char *label;
+    struct hmap_node node;      /* Node in hash map. */
+    union value value;          /* The value being labeled. */
+    struct atom *label;         /* A ref-counted string. */
+  };
+
+/* Returns the value in VL.  The caller must not modify or free
+   the returned value.
+
+   The width of the returned value cannot be determined directly
+   from VL.  It may be obtained by calling val_labs_get_width on
+   the val_labs struct that VL is in. */
+static inline const union value *val_lab_get_value (const struct val_lab *vl)
+{
+  return &vl->value;
+}
+
+const char *val_lab_get_label (const struct val_lab *);
+\f
+/* A set of value labels. */
+struct val_labs
+  {
+    int width;                  /* 0=numeric, otherwise string width. */
+    struct hmap labels;         /* Hash table of `struct int_val_lab's. */
   };
 
 /* Creating and destroying sets of value labels. */
@@ -41,28 +66,28 @@ struct val_labs *val_labs_create (int width);
 struct val_labs *val_labs_clone (const struct val_labs *);
 void val_labs_clear (struct val_labs *);
 void val_labs_destroy (struct val_labs *);
+size_t val_labs_count (const struct val_labs *);
 
 /* Looking up value labels. */
-char *val_labs_find (const struct val_labs *, union value);
+const char *val_labs_find (const struct val_labs *, const union value *);
+const struct val_lab *val_labs_lookup (const struct val_labs *,
+                                       const union value *);
 
 /* Basic properties. */
 size_t val_labs_count (const struct val_labs *);
+int val_labs_get_width (const struct val_labs *);
 bool val_labs_can_set_width (const struct val_labs *, int new_width);
 void val_labs_set_width (struct val_labs *, int new_width);
 
 /* Adding value labels. */
-bool val_labs_add (struct val_labs *, union value, const char *);
-void val_labs_replace (struct val_labs *, union value, const char *);
-bool val_labs_remove (struct val_labs *, union value);
+bool val_labs_add (struct val_labs *, const union value *, const char *);
+void val_labs_replace (struct val_labs *, const union value *, const char *);
+void val_labs_remove (struct val_labs *, const struct val_lab *);
 
 /* Iterating through value labels. */
-struct val_labs_iterator;
-struct val_lab *val_labs_first (const struct val_labs *,
-                                struct val_labs_iterator **);
-struct val_lab *val_labs_first_sorted (const struct val_labs *,
-                                       struct val_labs_iterator **);
-struct val_lab *val_labs_next (const struct val_labs *,
-                               struct val_labs_iterator **);
-void val_labs_done (struct val_labs_iterator **);
+const struct val_lab *val_labs_first (const struct val_labs *);
+const struct val_lab *val_labs_next (const struct val_labs *,
+                                     const struct val_lab *);
+const struct val_lab **val_labs_sorted (const struct val_labs *);
 
 #endif /* data/value-labels.h */
index 49555d9a4ea3b11ce15149ebdca388722af6dcb4..6dbecb11abfca8137bc8fdc763ffc1c0a8568bc2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include <data/value.h>
 
 #include <data/val-type.h>
+#include <data/variable.h>
 #include <libpspp/hash.h>
+#include <libpspp/pool.h>
 #include <libpspp/str.h>
+#include <gl/unistr.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
-/* Duplicate a value.
-   The caller is responsible for freeing the returned value. */
-union value *
-value_dup (const union value *val, int width)
+/* Copies the contents of string value SRC with width SRC_WIDTH
+   to string value DST with width DST_WIDTH.  If SRC_WIDTH is
+   greater than DST_WIDTH, then only the first DST_WIDTH bytes
+   are copied; if DST_WIDTH is greater than SRC_WIDTH, then DST
+   is padded on the right with PAD bytes.
+
+   SRC and DST must be string values; that is, SRC_WIDTH and
+   DST_WIDTH must both be positive.
+
+   It is important that SRC_WIDTH and DST_WIDTH be the actual
+   widths with which SRC and DST were initialized.  Passing,
+   e.g., smaller values in order to copy only a prefix of SRC or
+   modify only a prefix of DST will not work in every case. */
+void
+value_copy_rpad (union value *dst, int dst_width,
+                 const union value *src, int src_width,
+                 char pad)
 {
-  return xmemdup (val, MAX (width, sizeof *val));
+  u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width,
+                 value_str (src, src_width), src_width,
+                 pad);
 }
 
+/* Copies the contents of null-terminated string SRC to string
+   value DST with width DST_WIDTH.  If SRC is more than DST_WIDTH
+   bytes long, then only the first DST_WIDTH bytes are copied; if
+   DST_WIDTH is greater than the length of SRC, then DST is
+   padded on the right with PAD bytes.
 
-/* Create a value of specified width.
-   The caller is responsible for freeing the returned value. */
-union value *
-value_create (int width)
+   DST must be a string value; that is, DST_WIDTH must be
+   positive.
+
+   It is important that DST_WIDTH be the actual width with which
+   DST was initialized.  Passing, e.g., a smaller value in order
+   to modify only a prefix of DST will not work in every case. */
+void
+value_copy_str_rpad (union value *dst, int dst_width, const uint8_t *src,
+                     char pad)
 {
-  return xnmalloc (value_cnt_from_width (width), sizeof (union value));
+  value_copy_buf_rpad (dst, dst_width, src, u8_strlen (src), pad);
 }
 
+/* Copies the SRC_LEN bytes at SRC to string value DST with width
+   DST_WIDTH.  If SRC_LEN is greater than DST_WIDTH, then only
+   the first DST_WIDTH bytes are copied; if DST_WIDTH is greater
+   than SRC_LEN, then DST is padded on the right with PAD bytes.
 
-/* Compares A and B, which both have the given WIDTH, and returns
-   a strcmp()-type result.
-   Only the short string portion of longer strings are
-   compared. */
-int
-compare_values (const union value *a, const union value *b, int width)
+   DST must be a string value; that is, DST_WIDTH must be
+   positive.
+
+   It is important that DST_WIDTH be the actual width with which
+   DST was initialized.  Passing, e.g., a smaller value in order
+   to modify only a prefix of DST will not work in every case. */
+void
+value_copy_buf_rpad (union value *dst, int dst_width,
+                     const uint8_t *src, size_t src_len, char pad)
 {
-  return (width == 0
-          ? (a->f < b->f ? -1 : a->f > b->f)
-          : memcmp (a->s, b->s, MIN (MAX_SHORT_STRING, width)));
+  u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width, src, src_len, pad);
 }
 
-/* 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)
+/* Sets V to the system-missing value for data of the given
+   WIDTH. */
+void
+value_set_missing (union value *v, int width)
 {
-  return (width == 0
-          ? hsh_hash_double (v->f)
-          : hsh_hash_bytes (v->s, MIN (MAX_SHORT_STRING, width)));
+  if (width != -1)
+    {
+      if (width == 0)
+        v->f = SYSMIS;
+      else
+        memset (value_str_rw (v, width), ' ', width);
+    }
 }
 
-
+/* Compares A and B, which both have the given WIDTH, and returns
+   a strcmp()-type result. */
 int
-compare_ptr_values (const union value **v1, const union value **v2, int width)
+value_compare_3way (const union value *a, const union value *b, int width)
 {
-  return compare_values (*v1, *v2, width);
+  return (width == -1 ? 0
+          : width == 0 ? (a->f < b->f ? -1 : a->f > b->f)
+          : memcmp (value_str (a, width), value_str (b, width), width));
 }
 
-unsigned
-hash_ptr_value (const union value **v, int width)
+/* Returns true if A and B, which must both have the given WIDTH,
+   have equal contents, false if their contents differ. */
+bool
+value_equal (const union value *a, const union value *b, int width)
 {
-  return hash_value (*v, width);
+  return (width == -1 ? true
+          : width == 0 ? a->f == b->f
+          : !memcmp (value_str (a, width), value_str (b, width), width));
 }
 
-
-/* Copies SRC to DST, given that they both contain data of the
-   given WIDTH. */
-void
-value_copy (union value *dst, const union value *src, int width)
+/* Returns a hash of the data in VALUE, which must have the given
+   WIDTH, folding BASIS into the hash value calculation. */
+unsigned int
+value_hash (const union value *value, int width, unsigned int basis)
 {
-  if (width == 0)
-    dst->f = src->f;
-  else
-    memcpy (dst->s, src->s, width);
-}
-
-/* Sets V to the system-missing value for data of the given
-   WIDTH. */
-void
-value_set_missing (union value *v, int width)
-{
-  if (width == 0)
-    v->f = SYSMIS;
-  else
-    memset (v->s, ' ', width);
+  return (width == -1 ? basis
+          : width == 0 ? hash_double (value->f, basis)
+          : hash_bytes (value_str (value, width), width, basis));
 }
 
 /* Tests whether VALUE may be resized from OLD_WIDTH to
@@ -108,22 +140,97 @@ value_set_missing (union value *v, int width)
 bool
 value_is_resizable (const union value *value, int old_width, int new_width)
 {
-  int i;
-
-  if (val_type_from_width (old_width) != val_type_from_width (new_width))
+  if (old_width == new_width)
+    return true;
+  else if (val_type_from_width (old_width) != val_type_from_width (new_width))
     return false;
-  for (i = new_width; i < old_width; i++)
-    if (value->s[i] != ' ')
-      return false;
-  return true;
+  else
+    {
+      const uint8_t *str = value_str (value, old_width);
+      int i;
+
+      for (i = new_width; i < old_width; i++)
+        if (str[i] != ' ')
+          return false;
+      return true;
+    }
 }
 
 /* Resizes VALUE from OLD_WIDTH to NEW_WIDTH.  The arguments must
    satisfy the rules specified above for value_is_resizable. */
 void
 value_resize (union value *value, int old_width, int new_width)
+{
+  assert (value_is_resizable (value, old_width, new_width));
+  if (new_width != old_width)
+    {
+      union value tmp;
+      value_init (&tmp, new_width);
+      value_copy_rpad (&tmp, new_width, value, old_width, ' ');
+      value_destroy (value, old_width);
+      *value = tmp;
+    }
+}
+
+/* Returns true if resizing a value from OLD_WIDTH to NEW_WIDTH
+   actually changes anything, false otherwise.  If false is
+   returned, calls to value_resize() with the specified
+   parameters may be omitted without any ill effects.
+
+   This is generally useful only if many values can skip being
+   resized from OLD_WIDTH to NEW_WIDTH.  Otherwise you might as
+   well just call value_resize directly. */
+bool
+value_needs_resize (int old_width, int new_width)
+{
+  assert (val_type_from_width (old_width) == val_type_from_width (new_width));
+
+  /* We need to call value_resize if either the new width is
+     longer than the old width (in which case the new characters
+     must be set to spaces) or if either width is a long string.
+     (We could omit resizing if both the old and new widths were
+     long and the new width was shorter, but we choose to do so
+     anyway in hopes of saving memory.) */
+  return (old_width != new_width
+           && (new_width > old_width
+               || old_width > MAX_SHORT_STRING
+               || new_width > MAX_SHORT_STRING));
+}
+
+/* Same as value_init, except that memory for VALUE (if
+   necessary) is allocated from POOL and will be freed
+   automatically when POOL is destroyed.
+
+   VALUE must not be freed manually by calling value_destroy.  If
+   it needs to be resized, it must be done using
+   value_resize_pool instead of value_resize. */
+void
+value_init_pool (struct pool *pool, union value *value, int width)
+{
+  if (width > MAX_SHORT_STRING)
+    value->long_string = pool_alloc_unaligned (pool, width);
+}
+
+/* Same as value_resize, except that VALUE must have been
+   allocated from POOL using value_init_pool.
+
+   This function causes some memory in POOL to be wasted in some
+   cases (until the pool is freed), so it should only be done if
+   this is acceptable. */
+void
+value_resize_pool (struct pool *pool, union value *value,
+                   int old_width, int new_width)
 {
   assert (value_is_resizable (value, old_width, new_width));
   if (new_width > old_width)
-    memset (&value->s[old_width], ' ', new_width - old_width);
+    {
+      if (new_width > MAX_SHORT_STRING)
+        {
+          uint8_t *new_long_string = pool_alloc_unaligned (pool, new_width);
+          memcpy (new_long_string, value_str (value, old_width), old_width);
+          value->long_string = new_long_string;
+        }
+      memset (value_str_rw (value, new_width) + old_width, ' ',
+              new_width - old_width);
+    }
 }
index 4554a36617eb589153dc7244f19b3edf74c0bbd7..f9782e2d867d47028f134c4b406dbfd53313d68c 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
 #ifndef DATA_VALUE_H
 #define DATA_VALUE_H 1
 
-#include <libpspp/misc.h>
+#include <assert.h>
 #include <stdbool.h>
-#include <stddef.h>
-#include "minmax.h"
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include "xalloc.h"
+\f
+/* Maximum length of a "short" string, that is represented in
+   "union value" without a separate pointer.
 
-/* "Short" strings, which are generally those no more than 8
-   characters wide, can participate in more operations than
-   longer strings. */
-#define MAX_SHORT_STRING (MAX (ROUND_UP (SIZEOF_DOUBLE, 2), 8))
-#define MIN_LONG_STRING (MAX_SHORT_STRING + 1)
+   This is an implementation detail of the "union value" code.
+   There is little reason for client code to use it. */
+#define MAX_SHORT_STRING 8
 
-/* A numeric or short string value.
-   Multiple consecutive values represent a long string. */
+/* A numeric or string value.
+
+   The client is responsible for keeping track of the value's
+   width.
+
+   This structure is semi-opaque:
+
+       - If the value is a number, clients may access the 'f'
+         member directly.
+
+       - Clients should not access other members directly.
+*/
 union value
   {
     double f;
-    char s[MAX_SHORT_STRING];
+    uint8_t short_string[MAX_SHORT_STRING];
+    uint8_t *long_string;
   };
 
-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);
+static inline void value_init (union value *, int width);
+static inline bool value_needs_init (int width);
+static inline bool value_try_init (union value *, int width);
+static inline void value_destroy (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);
+static inline double value_num (const union value *);
+static inline const uint8_t *value_str (const union value *, int width);
+static inline uint8_t *value_str_rw (union value *, int width);
 
-
-static inline size_t value_cnt_from_width (int width);
-void value_copy (union value *, const union value *, int width);
+static inline void value_copy (union value *, const union value *, int width);
+void value_copy_rpad (union value *, int dst_width,
+                      const union value *, int src_width,
+                      char pad);
+void value_copy_str_rpad (union value *, int dst_width, const uint8_t *,
+                          char pad);
+void value_copy_buf_rpad (union value *dst, int dst_width,
+                          const uint8_t *src, size_t src_len, char pad);
 void value_set_missing (union value *, int width);
+int value_compare_3way (const union value *, const union value *, int width);
+bool value_equal (const union value *, const union value *, int width);
+unsigned int value_hash (const union value *, int width, unsigned int basis);
+
 bool value_is_resizable (const union value *, int old_width, int new_width);
+bool value_needs_resize (int old_width, int new_width);
 void value_resize (union value *, int old_width, int new_width);
 
-/* Number of "union value"s required for a variable of the given
-   WIDTH. */
-static inline size_t
-value_cnt_from_width (int width)
+static inline void value_swap (union value *, union value *);
+
+struct pool;
+void value_init_pool (struct pool *, union value *, int width);
+void value_resize_pool (struct pool *, union value *,
+                        int old_width, int new_width);
+\f
+/* Initializes V as a value of the given WIDTH, where 0
+   represents a numeric value and a positive integer represents a
+   string value WIDTH bytes long.
+
+   A WIDTH of -1 is ignored.
+
+   The contents of value V are indeterminate after
+   initialization. */
+static inline void
+value_init (union value *v, int width)
+{
+  if (width > MAX_SHORT_STRING)
+    v->long_string = xmalloc (width);
+}
+
+/* Returns true if a value of the given WIDTH actually needs to
+   have the value_init and value_destroy functions called, false
+   if those functions are no-ops for values of the given WIDTH.
+
+   Using this function is only a valuable optimization if a large
+   number of values of the given WIDTH are to be initialized*/
+static inline bool
+value_needs_init (int width)
+{
+  return width > MAX_SHORT_STRING;
+}
+
+/* Same as value_init, except that failure to allocate memory
+   causes it to return false instead of terminating the
+   program.  On success, returns true. */
+static inline bool
+value_try_init (union value *v, int width)
+{
+  if (width > MAX_SHORT_STRING)
+    {
+      v->long_string = malloc (width);
+      return v->long_string != NULL;
+    }
+  else
+    return true;
+}
+
+/* Frees any memory allocated by value_init for V, which must
+   have the given WIDTH. */
+static inline void
+value_destroy (union value *v, int width)
+{
+  if (width > MAX_SHORT_STRING)
+    free (v->long_string);
+}
+
+/* Returns the numeric value in V, which must have width 0. */
+static inline double
+value_num (const union value *v)
+{
+  return v->f;
+}
+
+/* Returns the string value in V, which must have width WIDTH.
+
+   The returned value is not null-terminated.
+
+   It is important that WIDTH be the actual value that was passed
+   to value_init.  Passing, e.g., a smaller value because only
+   that number of bytes will be accessed will not always work. */
+static inline const uint8_t *
+value_str (const union value *v, int width)
+{
+  assert (width > 0);
+  return (width > MAX_SHORT_STRING ? v->long_string : v->short_string);
+}
+
+/* Returns the string value in V, which must have width WIDTH.
+
+   The returned value is not null-terminated.
+
+   It is important that WIDTH be the actual value that was passed
+   to value_init.  Passing, e.g., a smaller value because only
+   that number of bytes will be accessed will not always work. */
+static inline uint8_t *
+value_str_rw (union value *v, int width)
+{
+  assert (width > 0);
+  return (width > MAX_SHORT_STRING ? v->long_string : v->short_string);
+}
+
+/* Copies SRC to DST, given that they both contain data of the
+   given WIDTH. */
+static inline void
+value_copy (union value *dst, const union value *src, int width)
+{
+  if (width <= MAX_SHORT_STRING)
+    *dst = *src;
+  else if (dst != src)
+    memcpy (dst->long_string, src->long_string, width);
+}
+
+/* Exchanges the contents of A and B. */
+static inline void
+value_swap (union value *a, union value *b)
 {
-  return width == 0 ? 1 : DIV_RND_UP (width, MAX_SHORT_STRING);
+  union value tmp = *a;
+  *a = *b;
+  *b = tmp;
 }
 
 #endif /* data/value.h */
index 35440a0de644af2fbeb3a407d7da837fe97aca1d..b4552cf17e014a6a4044b25c129f03e752ff2ce5 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
@@ -40,6 +40,7 @@ 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_resized (const struct variable *v, int old_width);
+void dict_var_display_width_changed (const struct variable *v);
 
 #endif /* data/vardict.h */
index a455e40a1f5bd72d025d30d941460d91ded5dc2a..d1e308640d05268df79f8639848cef04c618ba23 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>
@@ -35,6 +36,7 @@
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -76,6 +78,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 +113,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 +144,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 of the specified WIDTH 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, int width)
+{
+  struct variable *v = var_create ("$internal", width);
+  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 +178,12 @@ 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);
+       }
+      mv_destroy (&v->miss);
       cat_stored_values_destroy (v->obs_vals);
       var_clear_short_names (v);
       var_clear_aux (v);
@@ -292,7 +325,7 @@ hash_var_by_name (const void *v_, const void *aux UNUSED)
 {
   const struct variable *v = v_;
 
-  return hsh_hash_case_string (v->name);
+  return hash_case_string (v->name, 0);
 }
 
 /* A hsh_compare_func that orders pointers to variables A and B
@@ -307,6 +340,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
@@ -314,7 +361,7 @@ hash_var_ptr_by_name (const void *v_, const void *aux UNUSED)
 {
   struct variable *const *v = v_;
 
-  return hsh_hash_case_string (var_get_name (*v));
+  return hash_case_string (var_get_name (*v), 0);
 }
 \f
 /* Returns the type of variable V. */
@@ -338,10 +385,16 @@ var_set_width (struct variable *v, int new_width)
 {
   const int old_width = v->width;
 
+  if (old_width == new_width)
+    return;
+
   if (mv_is_resizable (&v->miss, new_width))
     mv_resize (&v->miss, new_width);
   else
-    mv_init (&v->miss, new_width);
+    {
+      mv_destroy (&v->miss);
+      mv_init (&v->miss, new_width);
+    }
 
   if (v->val_labs != NULL)
     {
@@ -358,15 +411,7 @@ var_set_width (struct variable *v, int new_width)
   fmt_resize (&v->write, new_width);
 
   v->width = new_width;
-
-  {
-    const int old_val_count = value_cnt_from_width (old_width);
-    const int new_val_count = value_cnt_from_width (new_width);
-
-    if ( old_val_count != new_val_count)
-        dict_var_resized (v, new_val_count - old_val_count);
-  }
-
+  dict_var_resized (v, old_width);
   dict_var_changed (v);
 }
 
@@ -384,30 +429,6 @@ var_is_alpha (const struct variable *v)
 {
   return var_get_type (v) == VAL_STRING;
 }
-
-/* Returns true if variable V is a short string variable, false
-   otherwise. */
-bool
-var_is_short_string (const struct variable *v)
-{
-  return v->width > 0 && v->width <= MAX_SHORT_STRING;
-}
-
-/* Returns true if variable V is a long string variable, false
-   otherwise. */
-bool
-var_is_long_string (const struct variable *v)
-{
-  return v->width > MAX_SHORT_STRING;
-}
-
-/* Returns the number of "union value"s need to store a value of
-   variable V. */
-size_t
-var_get_value_cnt (const struct variable *v)
-{
-  return value_cnt_from_width (v->width);
-}
 \f
 /* Returns variable V's missing values. */
 const struct missing_values *
@@ -426,11 +447,12 @@ var_set_missing_values (struct variable *v, const struct missing_values *miss)
   if (miss != NULL)
     {
       assert (mv_is_resizable (miss, v->width));
+      mv_destroy (&v->miss);
       mv_copy (&v->miss, miss);
       mv_resize (&v->miss, v->width);
     }
   else
-    mv_init (&v->miss, v->width);
+    mv_clear (&v->miss);
 
   dict_var_changed (v);
 }
@@ -472,7 +494,7 @@ var_is_num_missing (const struct variable *v, double d, enum mv_class class)
    S[] must contain exactly as many characters as V's width.
    V must be a string variable. */
 bool
-var_is_str_missing (const struct variable *v, const char s[],
+var_is_str_missing (const struct variable *v, const uint8_t s[],
                     enum mv_class class)
 {
   return mv_is_str_missing (&v->miss, s, class);
@@ -517,7 +539,6 @@ var_set_value_labels (struct variable *v, const struct val_labs *vls)
 static void
 alloc_value_labels (struct variable *v)
 {
-  assert (!var_is_long_string (v));
   if (v->val_labs == NULL)
     v->val_labs = val_labs_create (v->width);
 }
@@ -530,7 +551,7 @@ var_add_value_label (struct variable *v,
                      const union value *value, const char *label)
 {
   alloc_value_labels (v);
-  return val_labs_add (v->val_labs, *value, label);
+  return val_labs_add (v->val_labs, value, label);
 }
 
 /* Adds or replaces a value label with the given VALUE and LABEL
@@ -541,7 +562,7 @@ var_replace_value_label (struct variable *v,
                          const union value *value, const char *label)
 {
   alloc_value_labels (v);
-  val_labs_replace (v->val_labs, *value, label);
+  val_labs_replace (v->val_labs, value, label);
 }
 
 /* Removes V's value labels, if any. */
@@ -556,7 +577,7 @@ var_clear_value_labels (struct variable *v)
 const char *
 var_lookup_value_label (const struct variable *v, const union value *value)
 {
-  return val_labs_find (v->val_labs, *value);
+  return val_labs_find (v->val_labs, value);
 }
 
 /* Append STR with a string representing VALUE for variable V.
@@ -569,16 +590,17 @@ var_append_value_name (const struct variable *v, const union value *value,
                       struct string *str)
 {
   const char *name = var_lookup_value_label (v, value);
+  const struct dictionary *dict = var_get_vardict (v)->dict;
   if (name == NULL)
     {
-      char *s = ds_put_uninit (str, v->print.w);
-      data_out (value, &v->print, s);
+      char *s = data_out (value, dict_get_encoding (dict), &v->print);
+      ds_put_cstr (str, s);
+      free (s);
     }
   else
     ds_put_cstr (str, name);
 }
 \f
-\f
 /* Print and write formats. */
 
 /* Returns V's print format specification. */
@@ -676,8 +698,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 +760,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 +1028,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..0b619a497ed1eefa04a1dac97ec4be20afac4b80 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
@@ -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, int width);
+
 
 /* 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 *);
@@ -55,10 +59,6 @@ void var_set_width (struct variable *, int width);
 
 bool var_is_numeric (const struct variable *);
 bool var_is_alpha (const struct variable *);
-bool var_is_short_string (const struct variable *);
-bool var_is_long_string (const struct variable *);
-
-size_t var_get_value_cnt (const struct variable *);
 
 /* Variables' missing values. */
 const struct missing_values *var_get_missing_values (const struct variable *);
@@ -69,7 +69,7 @@ bool var_has_missing_values (const struct variable *);
 bool var_is_value_missing (const struct variable *, const union value *,
                            enum mv_class);
 bool var_is_num_missing (const struct variable *, double, enum mv_class);
-bool var_is_str_missing (const struct variable *, const char[], enum mv_class);
+bool var_is_str_missing (const struct variable *, const uint8_t[], enum mv_class);
 
 /* Value labels. */
 const char *var_lookup_value_label (const struct variable *,
@@ -173,6 +173,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 5dd2a30835f4e3d0eb053ecdd55a03918cc56fdd..c00d94b9c4b3597bcc2416ae0e29fd56018b229f 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
@@ -205,7 +205,7 @@ do_parse_command (struct lexer *lexer,
     }
   else if (command->function == NULL)
     {
-      msg (SE, _("%s is unimplemented."), command->name);
+      msg (SE, _("%s is not yet implemented."), command->name);
       result = CMD_NOT_IMPLEMENTED;
       goto finish;
     }
index ccf4c5560c1450a3109f66ec51083c335a2454ec..fa1bb1e88c6b2da3f683cbc49cb15449a9d24d07 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
@@ -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,14 +109,15 @@ 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, "ROC", cmd_roc)
 DEF_CMD (S_DATA, 0, "SAMPLE", cmd_sample)
 DEF_CMD (S_DATA, 0, "SAVE", cmd_save)
 DEF_CMD (S_DATA, 0, "SORT CASES", cmd_sort_cases)
@@ -127,7 +133,6 @@ DEF_CMD (S_INPUT_PROGRAM, 0, "END INPUT PROGRAM", cmd_end_input_program)
 DEF_CMD (S_INPUT_PROGRAM, 0, "REREAD", cmd_reread)
 
 /* Commands for testing PSPP. */
-DEF_CMD (S_ANY, F_TESTING, "DEBUG DATASHEET", cmd_debug_datasheet)
 DEF_CMD (S_ANY, F_TESTING, "DEBUG EVALUATE", cmd_debug_evaluate)
 DEF_CMD (S_ANY, F_TESTING, "DEBUG FORMAT GUESSER", cmd_debug_format_guesser)
 DEF_CMD (S_ANY, F_TESTING, "DEBUG MOMENTS", cmd_debug_moments)
@@ -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,12 +234,10 @@ 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")
 UNIMPL_CMD ("RMV", "Replace missing values")
-UNIMPL_CMD ("ROC", "Receiver operating characteristic")
 UNIMPL_CMD ("SAVE TRANSLATE", "Save to foriegn format")
 UNIMPL_CMD ("SCRIPT", "Run script file")
 UNIMPL_CMD ("SEASON", "Estimate seasonal factors")
@@ -255,7 +256,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..6440518
--- /dev/null
@@ -0,0 +1,894 @@
+/* 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_fields (&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_proto (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;
+  const char *file_encoding;
+
+  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);
+
+
+  /* FIXME: If the input files have different encodings, then
+     the result is undefined.
+     The correct thing to do would be to convert to an encoding
+     which can cope with all the input files (eg UTF-8).
+   */
+  file_encoding = dict_get_encoding (f->dict);
+  if ( file_encoding != NULL)
+    {
+      if ( dict_get_encoding (m) == NULL)
+       dict_set_encoding (m, file_encoding);
+      else if ( 0 != strcmp (file_encoding, dict_get_encoding (m)))
+       {
+         msg (MW,
+              _("Combining files with incompatible encodings. String data may not be represented correctly."));
+       }
+    }
+
+  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);
+  if (proc->prev_BY)
+    {
+      caseproto_destroy_values (subcase_get_proto (&proc->by_vars),
+                                proc->prev_BY);
+      free (proc->prev_BY);
+    }
+  subcase_destroy (&proc->by_vars);
+  case_unref (proc->buffered_case);
+}
+\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_proto (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_values = subcase_get_n_fields (&proc->by_vars);
+          const struct caseproto *proto = subcase_get_proto (&proc->by_vars);
+          if (proc->prev_BY == NULL)
+            {
+              proc->prev_BY = xmalloc (n_values * sizeof *proc->prev_BY);
+              caseproto_init_values (proto, proc->prev_BY);
+            }
+          caseproto_copy (subcase_get_proto (&proc->by_vars), 0, n_values,
+                          proc->prev_BY, by);
+        }
+    }
+}
+
+/* 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..d43af347a701693224980ea18281eaf72d6c0e60 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
@@ -75,8 +75,9 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
   struct dictionary *dict;
   struct data_parser *parser;
   struct dfm_reader *reader;
-  struct variable *end;
-  struct file_handle *fh;
+  struct variable *end = NULL;
+  struct file_handle *fh = NULL;
+  struct string encoding = DS_EMPTY_INITIALIZER;
 
   int table;
   enum data_parser_type type;
@@ -85,10 +86,8 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
   bool ok;
 
   dict = in_input_program () ? dataset_dict (ds) : dict_create ();
-  parser = data_parser_create ();
+  parser = data_parser_create (dict);
   reader = NULL;
-  end = NULL;
-  fh = NULL;
 
   table = -1;                /* Print table if nonzero, -1=undecided. */
   has_type = false;
@@ -103,6 +102,16 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
          if (fh == NULL)
            goto error;
        }
+      else if (lex_match_id (lexer, "ENCODING"))
+       {
+         lex_match (lexer, '=');
+         if (!lex_force_string (lexer))
+           goto error;
+
+         ds_init_string (&encoding, lex_tokstr (lexer));
+
+         lex_get (lexer);
+       }
       else if (lex_match_id (lexer, "RECORDS"))
        {
          lex_match (lexer, '=');
@@ -228,6 +237,14 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
     }
   type = data_parser_get_type (parser);
 
+  if (! ds_is_empty (&encoding))
+    {
+      if ( NULL == fh)
+       msg (MW, _("Encoding should not be specified for inline data. It will be ignored."));
+      else
+       dict_set_encoding (dict, ds_cstr (&encoding));
+    }
+
   if (fh == NULL)
     fh = fh_inline_file ();
   fh_set_default_handle (fh);
@@ -277,6 +294,7 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
     data_parser_make_active_file (parser, ds, reader, dict);
 
   fh_unref (fh);
+  ds_destroy (&encoding);
 
   return CMD_SUCCESS;
 
@@ -285,6 +303,7 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
   if (!in_input_program ())
     dict_destroy (dict);
   fh_unref (fh);
+  ds_destroy (&encoding);
   return CMD_CASCADING_FAILURE;
 }
 \f
@@ -463,14 +482,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 +504,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 a3a438b70fc63a909f474569f24a36dd35741fcb..020f8e4c7e5a20a0caabc4add9f228f98937d1b6 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
@@ -41,6 +41,7 @@
 /* Data parser for textual data like that read by DATA LIST. */
 struct data_parser
   {
+    const struct dictionary *dict; /*Dictionary of destination */
     enum data_parser_type type; /* Type of data to parse. */
     int skip_records;           /* Records to skip before first real data. */
     casenumber max_cases;       /* Max number of cases to read. */
@@ -79,7 +80,7 @@ static void set_any_sep (struct data_parser *parser);
 
 /* Creates and returns a new data parser. */
 struct data_parser *
-data_parser_create (void)
+data_parser_create (const struct dictionary *dict)
 {
   struct data_parser *parser = xmalloc (sizeof *parser);
 
@@ -91,6 +92,7 @@ data_parser_create (void)
   parser->fields = NULL;
   parser->field_cnt = 0;
   parser->field_allocated = 0;
+  parser->dict = dict;
 
   parser->span = true;
   parser->empty_line_has_field = false;
@@ -364,14 +366,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
@@ -503,7 +508,7 @@ static bool
 parse_fixed (const struct data_parser *parser, struct dfm_reader *reader,
              struct ccase *c)
 {
-  enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+  const char *encoding = dfm_reader_get_legacy_encoding (reader);
   struct field *f;
   int row;
 
@@ -529,6 +534,7 @@ parse_fixed (const struct data_parser *parser, struct dfm_reader *reader,
                             f->format.w),
                  encoding, f->format.type, f->format.d,
                  f->first_column, f->first_column + f->format.w,
+                parser->dict,
                  case_data_rw_idx (c, f->case_idx),
                  fmt_var_width (&f->format));
 
@@ -545,7 +551,7 @@ static bool
 parse_delimited_span (const struct data_parser *parser,
                       struct dfm_reader *reader, struct ccase *c)
 {
-  enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+  const char *encoding = dfm_reader_get_legacy_encoding (reader);
   struct string tmp = DS_EMPTY_INITIALIZER;
   struct field *f;
 
@@ -572,6 +578,7 @@ parse_delimited_span (const struct data_parser *parser,
 
       data_in (s, encoding, f->format.type, 0,
                first_column, last_column,
+              parser->dict,
                case_data_rw_idx (c, f->case_idx),
                fmt_var_width (&f->format));
     }
@@ -586,7 +593,7 @@ static bool
 parse_delimited_no_span (const struct data_parser *parser,
                          struct dfm_reader *reader, struct ccase *c)
 {
-  enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+  const char *encoding = dfm_reader_get_legacy_encoding (reader);
   struct string tmp = DS_EMPTY_INITIALIZER;
   struct substring s;
   struct field *f;
@@ -612,6 +619,7 @@ parse_delimited_no_span (const struct data_parser *parser,
 
       data_in (s, encoding, f->format.type, 0,
                first_column, last_column,
+              parser->dict,
                case_data_rw_idx (c, f->case_idx),
                fmt_var_width (&f->format));
     }
@@ -645,7 +653,7 @@ dump_fixed_table (const struct data_parser *parser,
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
@@ -654,9 +662,9 @@ dump_fixed_table (const struct data_parser *parser,
       int row = i + 1;
 
       tab_text (t, 0, row, TAB_LEFT, f->name);
-      tab_text (t, 1, row, TAT_PRINTF, "%d", f->record);
-      tab_text (t, 2, row, TAT_PRINTF, "%3d-%3d",
-                f->first_column, f->first_column + f->format.w - 1);
+      tab_text_format (t, 1, row, 0, "%d", f->record);
+      tab_text_format (t, 2, row, 0, "%3d-%3d",
+                       f->first_column, f->first_column + f->format.w - 1);
       tab_text (t, 3, row, TAB_LEFT | TAB_FIX,
                 fmt_to_string (&f->format, fmt_string));
     }
@@ -684,7 +692,7 @@ dump_delimited_table (const struct data_parser *parser,
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
@@ -719,7 +727,7 @@ struct data_parser_casereader
   {
     struct data_parser *parser; /* Parser. */
     struct dfm_reader *reader;  /* Data file reader. */
-    size_t value_cnt;           /* Number of `union value's in case. */
+    struct caseproto *proto;    /* Format of cases. */
   };
 
 static const struct casereader_class data_parser_casereader_class;
@@ -740,25 +748,25 @@ data_parser_make_active_file (struct data_parser *parser, struct dataset *ds,
   r = xmalloc (sizeof *r);
   r->parser = parser;
   r->reader = reader;
-  r->value_cnt = dict_get_next_value_idx (dict);
-  casereader = casereader_create_sequential (NULL, r->value_cnt,
+  r->proto = caseproto_ref (dict_get_proto (dict));
+  casereader = casereader_create_sequential (NULL, r->proto,
                                              CASENUMBER_MAX,
                                              &data_parser_casereader_class, r);
   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->proto);
+  if (data_parser_parse (r->parser, r->reader, c))
+    return c;
+  else
+    {
+      case_unref (c);
+      return NULL;
+    }
 }
 
 static void
@@ -769,6 +777,7 @@ data_parser_casereader_destroy (struct casereader *reader UNUSED, void *r_)
     casereader_force_error (reader);
   data_parser_destroy (r->parser);
   dfm_close_reader (r->reader);
+  caseproto_unref (r->proto);
   free (r);
 }
 
index 4976d3f42e69712ff52bc82f38d6432b5f5f3359..5a53a2f64ddcc3ab5c30286f438df15158014e4a 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <stdbool.h>
 #include <data/case.h>
+#include <libpspp/str.h>
 
 struct dataset;
 struct dfm_reader;
@@ -37,7 +38,7 @@ enum data_parser_type
   };
 
 /* Creating and configuring any parser. */
-struct data_parser *data_parser_create (void);
+struct data_parser *data_parser_create (const struct dictionary *dict);
 void data_parser_destroy (struct data_parser *);
 
 enum data_parser_type data_parser_get_type (const struct data_parser *);
index 24ddcf13ec61cf6749c64cd63bfd4e03268cbeea..6f620a6a948341a0dfa5db150763d20f51e92284 100644 (file)
@@ -597,7 +597,7 @@ dfm_expand_tabs (struct dfm_reader *r)
 }
 
 /* Returns the legacy character encoding of data read from READER. */
-enum legacy_encoding
+const char *
 dfm_reader_get_legacy_encoding (const struct dfm_reader *reader)
 {
   return fh_get_legacy_encoding (reader->fh);
index c7fee613d01caefe81f5edf5fe10c6f0538dc84f..308701c253147d48863a01bdb159c002e5fbf52a 100644 (file)
@@ -38,8 +38,7 @@ bool dfm_reader_error (const struct dfm_reader *);
 unsigned dfm_eof (struct dfm_reader *);
 struct substring dfm_get_record (struct dfm_reader *);
 void dfm_expand_tabs (struct dfm_reader *);
-enum legacy_encoding dfm_reader_get_legacy_encoding (
-  const struct dfm_reader *);
+const char *dfm_reader_get_legacy_encoding (const struct dfm_reader *);
 int dfm_get_percent_read (const struct dfm_reader *);
 
 /* Line control. */
index b5df59d4605f2535a690aea77e0934eb46ee6a65..85b11d4c1e553054160f8b1a57a3d1cac2704a00 100644 (file)
@@ -200,7 +200,7 @@ dfm_close_writer (struct dfm_writer *w)
 }
 
 /* Returns the legacy character encoding of data written to WRITER. */
-enum legacy_encoding
+const char *
 dfm_writer_get_legacy_encoding (const struct dfm_writer *writer)
 {
   return fh_get_legacy_encoding (writer->fh);
index 2142f2155008a8c934e471a72d837e992faae044..045db3163fd3144f2653e294d5a023e1416dc0f6 100644 (file)
@@ -27,7 +27,6 @@ struct dfm_writer *dfm_open_writer (struct file_handle *);
 bool dfm_close_writer (struct dfm_writer *);
 bool dfm_write_error (const struct dfm_writer *);
 bool dfm_put_record (struct dfm_writer *, const char *rec, size_t len);
-enum legacy_encoding dfm_writer_get_legacy_encoding (
-  const struct dfm_writer *);
+const char *dfm_writer_get_legacy_encoding (const struct dfm_writer *);
 
 #endif /* data-writer.h */
index 827dbab499e9af94a7c55fee5a42764a1ca39f89..3e053ed57262eb5f6e94d404ed5686253e640a16 100644 (file)
@@ -102,7 +102,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds)
           properties.mode = FH_MODE_VARIABLE;
           break;
         case FH_360:
-          properties.encoding = LEGACY_EBCDIC;
+          properties.encoding = "EBCDIC-US";
           if (cmd.recform == FH_FIXED || cmd.recform == FH_F)
             properties.mode = FH_MODE_FIXED;
           else if (cmd.recform == FH_VARIABLE || cmd.recform == FH_V)
index 54bb5653ecc2086456624e7923b9a02443b77b57..32202babdea8848f5c3a06b6431b0db3c4b007ff 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+   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
@@ -34,6 +34,8 @@
 #include <language/lexer/lexer.h>
 #include <libpspp/message.h>
 
+#include "xalloc.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) (msgid)
@@ -83,7 +85,7 @@ parse_get_psql (struct lexer *lexer, struct dataset *ds)
   if (!lex_force_string (lexer))
     goto error;
 
-  psql.conninfo = strdup (ds_cstr (lex_tokstr (lexer)));
+  psql.conninfo = xstrdup (ds_cstr (lex_tokstr (lexer)));
 
   lex_get (lexer);
 
@@ -151,7 +153,7 @@ parse_get_gnm (struct lexer *lexer, struct dataset *ds)
   if (!lex_force_string (lexer))
     goto error;
 
-  gri.file_name = strdup (ds_cstr (lex_tokstr (lexer)));
+  gri.file_name = xstrdup (ds_cstr (lex_tokstr (lexer)));
 
   lex_get (lexer);
 
@@ -170,7 +172,7 @@ parse_get_gnm (struct lexer *lexer, struct dataset *ds)
              if ( ! lex_force_string (lexer) )
                goto error;
 
-             gri.sheet_name = strdup (ds_cstr (lex_tokstr (lexer)));
+             gri.sheet_name = xstrdup (ds_cstr (lex_tokstr (lexer)));
              gri.sheet_index = -1;
            }
          else if (lex_match_id (lexer, "INDEX"))
@@ -194,7 +196,7 @@ parse_get_gnm (struct lexer *lexer, struct dataset *ds)
              if ( ! lex_force_string (lexer) )
                goto error;
 
-             gri.cell_range = strdup (ds_cstr (lex_tokstr (lexer)));
+             gri.cell_range = xstrdup (ds_cstr (lex_tokstr (lexer)));
            }
          else
            goto error;
@@ -269,7 +271,7 @@ static int
 parse_get_txt (struct lexer *lexer, struct dataset *ds)
 {
   struct data_parser *parser = NULL;
-  struct dictionary *dict = NULL;
+  struct dictionary *dict = dict_create ();
   struct file_handle *fh = NULL;
   struct dfm_reader *reader = NULL;
 
@@ -286,7 +288,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
   if (fh == NULL)
     goto error;
 
-  parser = data_parser_create ();
+  parser = data_parser_create (dict);
   has_type = false;
   data_parser_set_type (parser, DP_DELIMITED);
   data_parser_set_span (parser, false);
@@ -463,7 +465,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
     }
   lex_match (lexer, '=');
 
-  dict = dict_create ();
+
   record = 1;
   type = data_parser_get_type (parser);
   do
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..462289987b4044c7568272abdab35a0a4b5cd687 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,7 +70,7 @@ struct input_program_pgm
     casenumber case_nr;             /* Incremented by END CASE transformation. */
 
     struct caseinit *init;
-    size_t value_cnt;
+    struct caseproto *proto;
   };
 
 static void destroy_input_program (struct input_program_pgm *);
@@ -111,6 +111,7 @@ cmd_input_program (struct lexer *lexer, struct dataset *ds)
   inp = xmalloc (sizeof *inp);
   inp->trns_chain = NULL;
   inp->init = NULL;
+  inp->proto = NULL;
 
   inside_input_program = true;
   for (;;)
@@ -153,10 +154,10 @@ cmd_input_program (struct lexer *lexer, struct dataset *ds)
   /* Figure out how to initialize each input case. */
   inp->init = caseinit_create ();
   caseinit_mark_for_init (inp->init, dataset_dict (ds));
-  inp->value_cnt = dict_get_next_value_idx (dataset_dict (ds));
+  inp->proto = caseproto_ref (dict_get_proto (dataset_dict (ds)));
 
   proc_set_active_file_data (
-    ds, casereader_create_sequential (NULL, inp->value_cnt, CASENUMBER_MAX,
+    ds, casereader_create_sequential (NULL, inp->proto, CASENUMBER_MAX,
                                       &input_program_casereader_class, inp));
 
   return CMD_SUCCESS;
@@ -180,35 +181,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->proto);
 
   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
@@ -218,6 +218,7 @@ destroy_input_program (struct input_program_pgm *pgm)
     {
       trns_chain_destroy (pgm->trns_chain);
       caseinit_destroy (pgm->init);
+      caseproto_unref (pgm->proto);
       free (pgm);
     }
 }
@@ -251,7 +252,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 +324,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 +332,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 +369,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..c3f9b0881b959fac98a3cffac628662ffe143299 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;
@@ -704,18 +706,21 @@ list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
             if (fmt_is_string (print->type)
                 || dict_contains_var (dict, v))
              {
-                data_out (case_data (c, v), print,
-                          ds_put_uninit (&line_buffer, print->w));
+               char *s = data_out (case_data (c, v), dict_get_encoding (dict), print);
+               ds_put_cstr (&line_buffer, s);
+               free (s);
              }
             else
               {
+               char *s;
                 union value case_idx_value;
                 case_idx_value.f = case_idx;
-                data_out (&case_idx_value, print,
-                          ds_put_uninit (&line_buffer,print->w));
+                s = data_out (&case_idx_value, dict_get_encoding (dict), print);
+               ds_put_cstr (&line_buffer, s);
+               free (s);
               }
 
-           ds_put_char(&line_buffer, ' ');
+           ds_put_char (&line_buffer, ' ');
          }
 
        if (!n_lines_remaining (d))
@@ -738,20 +743,21 @@ list_case (struct ccase *c, casenumber case_idx, const struct dataset *ds)
          {
            const struct variable *v = cmd.v_variables[column];
             const struct fmt_spec *print = var_get_print_format (v);
-           char buf[256];
+           char *s = NULL;
 
             if (fmt_is_string (print->type)
                 || dict_contains_var (dict, v))
-             data_out (case_data (c, v), print, buf);
+             s = data_out (case_data (c, v), dict_get_encoding (dict), print);
             else
               {
                 union value case_idx_value;
                 case_idx_value.f = case_idx;
-                data_out (&case_idx_value, print, buf);
+                s = data_out (&case_idx_value, dict_get_encoding (dict), print);
               }
 
             fputs ("    <TD>", x->file);
-            html_put_cell_contents (d, TAB_FIX, ss_buffer (buf, print->w));
+            html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
+           free (s);
             fputs ("</TD>\n", x->file);
          }
 
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..e345477baf706f50add706bbac7e5f04b0d4057d 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
@@ -32,6 +32,7 @@
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
 #include <libpspp/assertion.h>
+#include <libpspp/i18n.h>
 #include <libpspp/compiler.h>
 #include <libpspp/ll.h>
 #include <libpspp/message.h>
@@ -83,7 +84,7 @@ struct print_trns
     struct pool *pool;          /* Stores related data. */
     bool eject;                 /* Eject page before printing? */
     bool include_prefix;        /* Prefix lines with space? */
-    enum legacy_encoding encoding; /* Encoding to use for output. */
+    const char *encoding;       /* Encoding to use for output. */
     struct dfm_writer *writer; /* Output file, NULL=listing file. */
     struct ll_list specs;       /* List of struct prt_out_specs. */
     size_t record_cnt;          /* Number of records to write. */
@@ -404,7 +405,7 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
   row = 1;
   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
     {
@@ -413,8 +414,9 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
       switch (spec->type)
         {
         case PRT_LITERAL:
-          tab_text (t, 0, row, TAB_LEFT | TAB_FIX | TAT_PRINTF, "\"%.*s\"",
-                    (int) ds_length (&spec->string), ds_data (&spec->string));
+          tab_text_format (t, 0, row, TAB_LEFT | TAB_FIX, "\"%.*s\"",
+                           (int) ds_length (&spec->string),
+                           ds_data (&spec->string));
           width = ds_length (&spec->string);
           break;
         case PRT_VAR:
@@ -426,9 +428,9 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
         default:
           NOT_REACHED ();
        }
-      tab_text (t, 1, row, TAT_PRINTF, "%d", spec->record);
-      tab_text (t, 2, row, TAT_PRINTF, "%3d-%3d",
-                spec->first_column, spec->first_column + width - 1);
+      tab_text_format (t, 1, row, 0, "%d", spec->record);
+      tab_text_format (t, 2, row, 0, "%3d-%3d",
+                       spec->first_column, spec->first_column + width - 1);
       row++;
     }
 
@@ -450,7 +452,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 +469,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);
@@ -479,12 +481,13 @@ print_trns_proc (void *trns_, struct ccase *c, casenumber case_num UNUSED)
       else
         {
           ds_put_substring (&trns->line, ds_ss (&spec->string));
-          if (trns->encoding != LEGACY_NATIVE)
+          if (0 != strcmp (trns->encoding, LEGACY_NATIVE))
             {
               size_t length = ds_length (&spec->string);
               char *data = ss_data (ds_tail (&trns->line, length));
-              legacy_recode (LEGACY_NATIVE, data,
-                             trns->encoding, data, length);
+             char *s = recode_string (trns->encoding, LEGACY_NATIVE, data, length);
+             memcpy (data, s, length);
+             free (s);
             }
         }
     }
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..ce5cc15ddf7fb7f9c63931164674750752608fd8 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
@@ -89,30 +89,16 @@ cmd_apply_dictionary (struct lexer *lexer, struct dataset *ds)
 
       if (var_has_value_labels (s))
         {
-          if (!var_is_long_string (t))
-            {
-              const struct val_labs *value_labels = var_get_value_labels (s);
-              if (val_labs_can_set_width (value_labels, var_get_width (t)))
-                var_set_value_labels (s, value_labels);
-            }
-          else
-            msg (SW, _("Cannot add value labels from source file to "
-                       "long string variable %s."),
-                 var_get_name (s));
+          const struct val_labs *value_labels = var_get_value_labels (s);
+          if (val_labs_can_set_width (value_labels, var_get_width (t)))
+            var_set_value_labels (s, value_labels);
         }
 
       if (var_has_missing_values (s))
         {
-          if (!var_is_long_string (t))
-            {
-              const struct missing_values *miss = var_get_missing_values (s);
-              if (mv_is_resizable (miss, var_get_width (t)))
-                var_set_missing_values (t, miss);
-            }
-          else
-            msg (SW, _("Cannot apply missing values from source file to "
-                       "long string variable %s."),
-                 var_get_name (s));
+          const struct missing_values *miss = var_get_missing_values (s);
+          if (mv_is_resizable (miss, var_get_width (t)))
+            var_set_missing_values (t, miss);
         }
 
       if (var_is_numeric (s))
@@ -120,12 +106,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 950abc1528115c0b123fa40568c85f60a770b2ac..819b0a9021cfe5821b4060e02b9932830b2a6120 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
@@ -26,8 +26,8 @@
 #include <data/format.h>
 #include <language/command.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/value-parser.h>
 #include <language/lexer/variable-parser.h>
-#include <language/lexer/range-parser.h>
 #include <libpspp/message.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
@@ -98,32 +98,31 @@ cmd_missing_values (struct lexer *lexer, struct dataset *ds)
             }
           else
             {
-             struct string value;
-
-              mv_init (&mv, MAX_SHORT_STRING);
+              mv_init (&mv, MV_MAX_STRING);
               while (!lex_match (lexer, ')'))
                 {
+                  uint8_t value[MV_MAX_STRING];
+                  size_t length;
+
                   if (!lex_force_string (lexer))
                     {
                       deferred_errors = true;
                       break;
                     }
 
-                 ds_init_string (&value, lex_tokstr (lexer));
-
-                  if (ds_length (&value) > MAX_SHORT_STRING)
+                  length = ds_length (lex_tokstr (lexer));
+                  if (length > MV_MAX_STRING)
                     {
-                      ds_truncate (&value, MAX_SHORT_STRING);
-                      msg (SE, _("Truncating missing value to short string "
-                                 "length (%d characters)."),
-                           MAX_SHORT_STRING);
+                      msg (SE, _("Truncating missing value to maximum "
+                                 "acceptable length (%d bytes)."),
+                           MV_MAX_STRING);
+                      length = MV_MAX_STRING;
                     }
-                  else
-                    ds_rpad (&value, MAX_SHORT_STRING, ' ');
+                  memset (value, ' ', MV_MAX_STRING);
+                  memcpy (value, ds_data (lex_tokstr (lexer)), length);
 
-                  if (!mv_add_str (&mv, ds_data (&value)))
+                  if (!mv_add_str (&mv, value))
                     deferred_errors = true;
-                 ds_destroy (&value);
 
                   lex_get (lexer);
                   lex_match (lexer, ',');
@@ -142,6 +141,8 @@ cmd_missing_values (struct lexer *lexer, struct dataset *ds)
                   deferred_errors = true;
                 }
             }
+
+          mv_destroy (&mv);
         }
 
       lex_match (lexer, '/');
index 91c27c9592c39ef9beb3814656808d9fad94487b..5d2b42d758eed2d1e305d05046d59cf0aa290877 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
@@ -78,7 +78,7 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
     return;
 
   t = tab_create (3, split_cnt + 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
   tab_vline (t, TAL_GAP, 1, 0, split_cnt);
   tab_vline (t, TAL_GAP, 2, 0, split_cnt);
   tab_text (t, 0, 0, TAB_NONE, _("Variable"));
@@ -88,17 +88,17 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
   for (i = 0; i < split_cnt; i++)
     {
       const struct variable *v = split[i];
-      char temp_buf[80];
+      char *s;
       const char *val_lab;
       const struct fmt_spec *print = var_get_print_format (v);
 
-      tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", var_get_name (v));
+      tab_text_format (t, 0, i + 1, TAB_LEFT, "%s", var_get_name (v));
 
-      data_out (case_data (c, v), print, temp_buf);
-      temp_buf[print->w] = 0;
-
-      tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", print->w, temp_buf);
+      s = data_out (case_data (c, v), dict_get_encoding (dict), print);
+      tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, s);
 
+      free (s);
+      
       val_lab = var_lookup_value_label (v, case_data (c, v));
       if (val_lab)
        tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
index b98854db6ea290a053a43fa377b6d0e4d246b305..6a0c81acac768b50ee782a3ef979ff96db57f615 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
@@ -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. */
 static void
-sysfile_info_dim (struct tab_table *t, struct outp_driver *d)
+sysfile_info_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   static const int max[] = {20, 5, 35, 3, 0};
   const int *p;
@@ -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, '=');
@@ -105,7 +108,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
     }
   casereader_destroy (reader);
 
-  t = tab_create (2, 10, 0);
+  t = tab_create (2, 11, 0);
   tab_vline (t, TAL_GAP, 1, 0, 8);
   tab_text (t, 0, 0, TAB_LEFT, _("File:"));
   tab_text (t, 1, 0, TAB_LEFT, fh_get_file_name (h));
@@ -117,8 +120,8 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
     tab_text (t, 1, 1, TAB_LEFT, label);
   }
   tab_text (t, 0, 2, TAB_LEFT, _("Created:"));
-  tab_text (t, 1, 2, TAB_LEFT | TAT_PRINTF, "%s %s by %s",
-               info.creation_date, info.creation_time, info.product);
+  tab_text_format (t, 1, 2, TAB_LEFT, "%s %s by %s",
+                   info.creation_date, info.creation_time, info.product);
   tab_text (t, 0, 3, TAB_LEFT, _("Integer Format:"));
   tab_text (t, 1, 3, TAB_LEFT,
             info.integer_format == INTEGER_MSB_FIRST ? _("Big Endian.")
@@ -133,11 +136,11 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
             : info.float_format == FLOAT_Z_LONG ? _("IBM 390 Hex Long.")
             : _("Unknown."));
   tab_text (t, 0, 5, TAB_LEFT, _("Variables:"));
-  tab_text (t, 1, 5, TAB_LEFT | TAT_PRINTF, "%zu", dict_get_var_cnt (d));
+  tab_text_format (t, 1, 5, TAB_LEFT, "%zu", dict_get_var_cnt (d));
   tab_text (t, 0, 6, TAB_LEFT, _("Cases:"));
-  tab_text (t, 1, 6, TAB_LEFT | TAT_PRINTF,
-            info.case_cnt == -1 ? _("Unknown") : "%ld",
-            (long int) info.case_cnt);
+  tab_text_format (t, 1, 6, TAB_LEFT,
+                   info.case_cnt == -1 ? _("Unknown") : "%ld",
+                   (long int) info.case_cnt);
   tab_text (t, 0, 7, TAB_LEFT, _("Type:"));
   tab_text (t, 1, 7, TAB_LEFT, _("System File."));
   tab_text (t, 0, 8, TAB_LEFT, _("Weight:"));
@@ -148,34 +151,28 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
                ? var_get_name (weight_var) : _("Not weighted.")));
   }
   tab_text (t, 0, 9, TAB_LEFT, _("Mode:"));
-  tab_text (t, 1, 9, TAB_LEFT | TAT_PRINTF,
-               _("Compression %s."), info.compressed ? _("on") : _("off"));
-  tab_dim (t, tab_natural_dimensions);
-  tab_submit (t);
+  tab_text_format (t, 1, 9, TAB_LEFT,
+                   _("Compression %s."), info.compressed ? _("on") : _("off"));
+
+
+  tab_text (t, 0, 10, TAB_LEFT, _("Charset:"));
+  tab_text_format (t, 1, 10, TAB_LEFT,
+                   dict_get_encoding(d) ? dict_get_encoding(d) : _("Unknown"));
 
-  nr = 1 + 2 * dict_get_var_cnt (d);
 
-  t = tab_create (4, nr, 1);
-  tab_dim (t, sysfile_info_dim);
+  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_submit (t);
+
+  t = tab_create (4, 1 + 2 * dict_get_var_cnt (d), 1);
+  tab_dim (t, sysfile_info_dim, NULL);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
   tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
   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 +194,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 +226,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,25 +339,27 @@ 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. */
 static void
-variables_dim (struct tab_table *t, struct outp_driver *d)
+variables_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   int pc;
   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,168 +367,235 @@ 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);
+  tab_dim (t, variables_dim, NULL);
 
-  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_format (t, c, r, TAB_LEFT, "%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, NULL);
+  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_format (t, pc, r, 0, "%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_format (t, 1, r, 2, r, TAB_LEFT,
+                                 _("Format: %s"), fmt_to_string (print, str));
+          r++;
+        }
+      else
+        {
+          char str[FMT_STRING_LEN_MAX + 1];
+          tab_joint_text_format (t, 1, r, 2, r, TAB_LEFT,
+                                 _("Print Format: %s"),
+                                 fmt_to_string (print, str));
+          r++;
+          tab_joint_text_format (t, 1, r, 2, r, TAB_LEFT,
+                                 _("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];
-      tab_joint_text (t, 1, r, 2, r, TAB_LEFT | TAT_PRINTF,
-                     _("Print Format: %s"), fmt_to_string (print, str));
+      enum measure m = var_get_measure (v);
+      enum alignment a = var_get_alignment (v);
+
+      tab_joint_text_format (t, 1, r, 2, r, TAB_LEFT,
+                             _("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));
+      tab_joint_text_format (t, 1, r, 2, r, TAB_LEFT,
+                             _("Display Alignment: %s"),
+                             a == ALIGN_LEFT ? _("Left")
+                             : a == ALIGN_CENTRE ? _("Center")
+                             : _("Right"));
+      r++;
+      tab_joint_text_format (t, 1, r, 2, r, TAB_LEFT,
+                             _("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))
     {
+      const struct missing_values *mv = var_get_missing_values (v);
       char buf[128];
       char *cp;
-      struct missing_values mv;
       int cnt = 0;
+      int i;
 
       cp = stpcpy (buf, _("Missing Values: "));
 
-      mv_copy (&mv, var_get_missing_values (v));
-      if (mv_has_range (&mv))
+      if (mv_has_range (mv))
         {
           double x, y;
-          mv_pop_range (&mv, &x, &y);
+          mv_get_range (mv, &x, &y);
           if (x == LOWEST)
             cp += sprintf (cp, "LOWEST THRU %g", y);
           else if (y == HIGHEST)
@@ -529,19 +604,21 @@ describe_variable (const struct variable *v, struct tab_table *t, int r, int as)
             cp += sprintf (cp, "%g THRU %g", x, y);
           cnt++;
         }
-      while (mv_has_value (&mv))
+      for (i = 0; i < mv_n_values (mv); i++)
         {
-          union value value;
-          mv_pop_value (&mv, &value);
+          const union value *value = mv_get_value (mv, i);
           if (cnt++ > 0)
             cp += sprintf (cp, "; ");
           if (var_is_numeric (v))
-            cp += sprintf (cp, "%g", value.f);
+            cp += sprintf (cp, "%g", value->f);
           else
             {
+              int width = var_get_width (v);
+              int mv_width = MIN (width, MV_MAX_STRING);
+
               *cp++ = '"';
-             memcpy (cp, value.s, var_get_width (v));
-             cp += var_get_width (v);
+             memcpy (cp, value_str (value, width), mv_width);
+             cp += mv_width;
              *cp++ = '"';
               *cp = '\0';
             }
@@ -552,12 +629,13 @@ 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;
-      struct val_lab *vl;
+      size_t n_labels = val_labs_count (val_labs);
+      const struct val_lab **labels;
       int orig_r = r;
+      size_t i;
 
 #if 0
       tab_text (t, 1, r, TAB_LEFT, _("Value"));
@@ -566,29 +644,42 @@ describe_variable (const struct variable *v, struct tab_table *t, int r, int as)
 #endif
 
       tab_hline (t, TAL_1, 1, 2, r);
-      for (vl = val_labs_first_sorted (val_labs, &i); vl != NULL;
-           vl = val_labs_next (val_labs, &i))
+
+      labels = val_labs_sorted (val_labs);
+      for (i = 0; i < n_labels; i++)
         {
-         char buf[128];
+          const struct val_lab *vl = labels[i];
+         char buf[MAX_STRING + 1];
 
          if (var_is_alpha (v))
            {
-             memcpy (buf, vl->value.s, var_get_width (v));
-             buf[var_get_width (v)] = 0;
+              int width = var_get_width (v);
+             memcpy (buf, value_str (&vl->value, width), width);
+             buf[width] = 0;
            }
          else
            sprintf (buf, "%g", vl->value.f);
 
          tab_text (t, 1, r, TAB_NONE, buf);
-         tab_text (t, 2, r, TAB_LEFT, vl->label);
+         tab_text (t, 2, r, TAB_LEFT, val_lab_get_label (vl));
          r++;
        }
+      free (labels);
 
       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;
 }
@@ -625,7 +716,7 @@ display_vectors (const struct dictionary *dict, int sorted)
   t = tab_create (4, nrow + 1, 0);
   tab_headers (t, 0, 0, 1, 0);
   tab_columns (t, TAB_COL_DOWN, 1);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, nrow);
   tab_box (t, -1, -1, -1, TAL_1, 0, 0, 3, nrow);
   tab_hline (t, TAL_2, 0, 3, 1);
@@ -650,7 +741,7 @@ display_vectors (const struct dictionary *dict, int sorted)
           char fmt_string[FMT_STRING_LEN_MAX + 1];
           fmt_to_string (var_get_print_format (var), fmt_string);
 
-          tab_text (t, 1, row, TAB_RIGHT | TAT_PRINTF, "%zu", j + 1);
+          tab_text_format (t, 1, row, TAB_RIGHT, "%zu", j + 1);
           tab_text (t, 2, row, TAB_LEFT, var_get_name (var));
           tab_text (t, 3, row, TAB_LEFT, fmt_string);
           row++;
index 4b7c03961783c0109b30c8e5a32bcb13b43658b3..5e3ea11f24654d0af73ff4cc1113123134bca00b 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
@@ -24,6 +24,7 @@
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/value-parser.h>
 #include <language/lexer/variable-parser.h>
 #include <libpspp/hash.h>
 #include <libpspp/message.h>
@@ -38,7 +39,6 @@
 
 static int do_value_labels (struct lexer *,
                            const struct dictionary *dict, bool);
-static int verify_val_labs (struct variable **vars, size_t var_cnt);
 static void erase_labels (struct variable **vars, size_t var_cnt);
 static int get_label (struct lexer *, struct variable **vars, size_t var_cnt);
 \f
@@ -70,14 +70,12 @@ do_value_labels (struct lexer *lexer, const struct dictionary *dict, bool erase)
   while (lex_token (lexer) != '.')
     {
       parse_err = !parse_variables (lexer, dict, &vars, &var_cnt,
-                                   PV_SAME_TYPE) ;
+                                   PV_SAME_WIDTH);
       if (var_cnt < 1)
        {
          free(vars);
          return CMD_FAILURE;
        }
-      if (!verify_val_labs (vars, var_cnt))
-        goto lossage;
       if (erase)
         erase_labels (vars, var_cnt);
       while (lex_token (lexer) != '/' && lex_token (lexer) != '.')
@@ -105,27 +103,6 @@ do_value_labels (struct lexer *lexer, const struct dictionary *dict, bool erase)
   return CMD_FAILURE;
 }
 
-/* Verifies that none of the VAR_CNT variables in VARS are long
-   string variables. */
-static int
-verify_val_labs (struct variable **vars, size_t var_cnt)
-{
-  size_t i;
-
-  for (i = 0; i < var_cnt; i++)
-    {
-      const struct variable *vp = vars[i];
-
-      if (var_is_long_string (vp))
-       {
-         msg (SE, _("It is not possible to assign value labels to long "
-                    "string variables such as %s."), var_get_name (vp));
-         return 0;
-       }
-    }
-  return 1;
-}
-
 /* Erases all the labels for the VAR_CNT variables in VARS. */
 static void
 erase_labels (struct variable **vars, size_t var_cnt)
@@ -145,37 +122,26 @@ get_label (struct lexer *lexer, struct variable **vars, size_t var_cnt)
   /* Parse all the labels and add them to the variables. */
   do
     {
+      int width = var_get_width (vars[0]);
       union value value;
       struct string label;
       size_t i;
 
       /* Set value. */
-      if (var_is_alpha (vars[0]))
-       {
-         if (lex_token (lexer) != T_STRING)
-           {
-              lex_error (lexer, _("expecting string"));
-             return 0;
-           }
-         buf_copy_str_rpad (value.s, MAX_SHORT_STRING, ds_cstr (lex_tokstr (lexer)));
-       }
-      else
-       {
-         if (!lex_is_number (lexer))
-           {
-             lex_error (lexer, _("expecting integer"));
-             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);
+      value_init (&value, width);
+      if (!parse_value (lexer, &value, width))
+        {
+          value_destroy (&value, width);
+          return 0;
+        }
       lex_match (lexer, ',');
 
       /* Set label. */
-      if (!lex_force_string (lexer))
-       return 0;
+      if (lex_token (lexer) != T_ID && !lex_force_string (lexer))
+        {
+          value_destroy (&value, width);
+          return 0;
+        }
 
       ds_init_string (&label, lex_tokstr (lexer));
 
@@ -189,6 +155,7 @@ get_label (struct lexer *lexer, struct variable **vars, size_t var_cnt)
         var_replace_value_label (vars[i], &value, ds_cstr (&label));
 
       ds_destroy (&label);
+      value_destroy (&value, width);
 
       lex_get (lexer);
       lex_match (lexer, ',');
index 277db48e56b2eb53bd292c5819ebb71fd849b529..83df065b99728e58745ffc72b08742a562b62e55 100644 (file)
@@ -27,6 +27,7 @@
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
 #include "gettext.h"
index fc5b74a3ab57b7727e96226c785640e5a3e8ee43..dea521a8fa25554638644866a915c5804720c937 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
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "private.h"
+#include "evaluate.h"
 
 #include <ctype.h>
 #include <libpspp/assertion.h>
 #include <libpspp/message.h>
-#include "helpers.h"
-#include "evaluate.h"
+#include <language/expressions/helpers.h>
+#include <language/expressions/private.h>
+#include <language/lexer/value-parser.h>
 #include <libpspp/pool.h>
 
 #include "xalloc.h"
@@ -98,7 +99,7 @@ expr_evaluate_str (struct expression *e, const struct ccase *c, int case_idx,
   assert ((dst == NULL) == (dst_size == 0));
   expr_evaluate (e, c, case_idx, &s);
 
-  buf_copy_rpad (dst, dst_size, s.string, s.length);
+  buf_copy_rpad (dst, dst_size, s.string, s.length, ' ');
 }
 \f
 #include <language/lexer/lexer.h>
@@ -170,19 +171,12 @@ 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_proto (d));
           else
-            case_resize (c, dict_get_next_value_idx (d));
+            c = case_unshare_and_resize (c, dict_get_proto (d));
 
-          if (lex_is_number (lexer))
-            case_data_rw (c, v)->f = lex_tokval (lexer);
-          else
-            memcpy (case_data_rw (c, v)->s, ds_data (lex_tokstr (lexer)),
-                    var_get_width (v));
-          lex_get (lexer);
+          if (!parse_value (lexer, case_data_rw (c, v), var_get_width (v)))
+            NOT_REACHED ();
 
           if (!lex_force_match (lexer, ')'))
             goto done;
@@ -255,11 +249,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..d283867214811ea142d83cafd39d9b69faf2a0a0 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
@@ -573,7 +573,7 @@ string function RTRIM (string s, string c)
 function NUMBER (string s, ni_format f)
 {
   union value out;
-  data_in (ss_head (s, f->w), LEGACY_NATIVE, f->type, f->d, 0, 0, &out, 0);
+  data_in (ss_head (s, f->w), LEGACY_NATIVE, f->type, f->d, 0, 0, NULL, &out, 0);
   return out.f;
 }
 
@@ -582,11 +582,15 @@ absorb_miss string function STRING (x, no_format f)
 {
   union value v;
   struct substring dst;
+  char *s;
 
   v.f = x;
-  dst = alloc_string (e, f->w);
+
   assert (!fmt_is_string (f->type));
-  data_out (&v, f, dst.string);
+  s = data_out (&v, LEGACY_NATIVE, f);
+  dst = alloc_string (e, strlen (s));
+  strcpy (dst.string, s);
+  free (s);
   return dst;
 }
 
@@ -988,7 +992,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 +1005,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 +1019,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 +1030,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 0ce371afb8b6c37c36b81478c76108ec70eab3fb..aff3f2a2879d97869affded7a2f260401e657751 100644 (file)
@@ -7,8 +7,8 @@ language_lexer_sources = \
        src/language/lexer/subcommand-list.h \
        src/language/lexer/format-parser.c \
        src/language/lexer/format-parser.h \
-       src/language/lexer/range-parser.c \
-       src/language/lexer/range-parser.h \
+       src/language/lexer/value-parser.c \
+       src/language/lexer/value-parser.h \
        src/language/lexer/variable-parser.c \
        src/language/lexer/variable-parser.h
 
index 1c9542d78ed54ccf5271811b5cc27be54337aeae..8b3f2a48d00474811a09c090f8a82d1b75ed7546 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,10 @@ lex_get (struct lexer *lexer)
             }
           else
             {
-              if (isgraph ((unsigned char) *lexer->prog))
-                msg (SE, _("Bad character in input: `%c'."), *lexer->prog++);
-              else
-                msg (SE, _("Bad character in input: `\\%o'."), *lexer->prog++);
+              unsigned char c = *lexer->prog++;
+              char *c_name = xasprintf (c_isgraph (c) ? "%c" : "\\%o", c);
+              msg (SE, _("Bad character in input: `%s'."), c_name);
+              free (c_name);
               continue;
             }
         }
@@ -690,7 +692,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 +867,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 +952,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 +1166,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 +1188,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 +1306,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..be0f29c5216ac392209e3e075a6df469208b54b2 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,45 @@ 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) + 1);
+  char *d = dest;
+
+  while (*s)
+    {
+      if (*s == hyphen_proxy)
+       *d = '-';
+      else
+       *d = *s;
+      s++;
+      d++;
+    }
+  *d = '\0';
+
+  return dest;
+}
+
 /* Reads a token from the input file. */
 static int
 lex_get (void)
@@ -398,9 +437,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 +1412,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;
 }
diff --git a/src/language/lexer/range-parser.c b/src/language/lexer/range-parser.c
deleted file mode 100644 (file)
index 4cf21cf..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2005, 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 "range-parser.h"
-#include <stdbool.h>
-#include <data/data-in.h>
-#include <libpspp/message.h>
-#include "lexer.h"
-#include <libpspp/str.h>
-#include <data/value.h>
-#include <data/format.h>
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-static bool parse_number (struct lexer *, double *, const enum fmt_type *);
-
-/* Parses and stores a numeric value, or a range of the form "x
-   THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
-   y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
-   value and returns success.
-
-   Numeric values are always accepted.  If FORMAT is nonnull,
-   then string values are also accepted, and converted to numeric
-   values using *FORMAT. */
-bool
-parse_num_range (struct lexer *lexer,
-                 double *x, double *y, const enum fmt_type *format)
-{
-  if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
-    *x = LOWEST;
-  else if (!parse_number (lexer, x, format))
-    return false;
-
-  if (lex_match_id (lexer, "THRU"))
-    {
-      if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
-        *y = HIGHEST;
-      else if (!parse_number (lexer, y, format))
-        return false;
-
-      if (*y < *x)
-        {
-          double t;
-          msg (SW, _("Low end of range (%g) is below high end (%g).  "
-                     "The range will be treated as reversed."),
-               *x, *y);
-          t = *x;
-          *x = *y;
-          *y = t;
-        }
-      else if (*x == *y)
-        msg (SW, _("Ends of range are equal (%g)."), *x);
-
-      return true;
-    }
-  else
-    {
-      if (*x == LOWEST)
-        {
-          msg (SE, _("LO or LOWEST must be part of a range."));
-          return false;
-        }
-      *y = *x;
-    }
-
-  return true;
-}
-
-/* Parses a number and stores it in *X.  Returns success.
-
-   Numeric values are always accepted.  If FORMAT is nonnull,
-   then string values are also accepted, and converted to numeric
-   values using *FORMAT. */
-static bool
-parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
-{
-  if (lex_is_number (lexer))
-    {
-      *x = lex_number (lexer);
-      lex_get (lexer);
-      return true;
-    }
-  else if (lex_token (lexer) == T_STRING && format != NULL)
-    {
-      union value v;
-      data_in (ds_ss (lex_tokstr (lexer)), LEGACY_NATIVE,
-               *format, 0, 0, 0, &v, 0);
-      lex_get (lexer);
-      *x = v.f;
-      if (*x == SYSMIS)
-        {
-          msg (SE, _("System-missing value is not valid here."));
-          return false;
-        }
-      return true;
-    }
-  else
-    {
-      if (format != NULL)
-        lex_error (lexer, _("expecting number or data string"));
-      else
-        lex_force_num (lexer);
-      return false;
-    }
-}
diff --git a/src/language/lexer/range-parser.h b/src/language/lexer/range-parser.h
deleted file mode 100644 (file)
index 8cff0e1..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2005 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef RANGE_PRS_H
-#define RANGE_PRS_H 1
-
-#include <stdbool.h>
-
-struct lexer;
-enum fmt_type;
-bool parse_num_range (struct lexer *,
-                      double *x, double *y, const enum fmt_type *fmt);
-
-#endif /* range-prs.h */
diff --git a/src/language/lexer/value-parser.c b/src/language/lexer/value-parser.c
new file mode 100644 (file)
index 0000000..c780d86
--- /dev/null
@@ -0,0 +1,146 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 "value-parser.h"
+#include <stdbool.h>
+#include <data/data-in.h>
+#include <libpspp/message.h>
+#include "lexer.h"
+#include <libpspp/str.h>
+#include <data/value.h>
+#include <data/format.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+static bool parse_number (struct lexer *, double *, const enum fmt_type *);
+
+/* Parses and stores a numeric value, or a range of the form "x
+   THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
+   y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
+   value and returns success.
+
+   Numeric values are always accepted.  If FORMAT is nonnull,
+   then string values are also accepted, and converted to numeric
+   values using *FORMAT. */
+bool
+parse_num_range (struct lexer *lexer,
+                 double *x, double *y, const enum fmt_type *format)
+{
+  if (lex_match_id (lexer, "LO") || lex_match_id (lexer, "LOWEST"))
+    *x = LOWEST;
+  else if (!parse_number (lexer, x, format))
+    return false;
+
+  if (lex_match_id (lexer, "THRU"))
+    {
+      if (lex_match_id (lexer, "HI") || lex_match_id (lexer, "HIGHEST"))
+        *y = HIGHEST;
+      else if (!parse_number (lexer, y, format))
+        return false;
+
+      if (*y < *x)
+        {
+          double t;
+          msg (SW, _("Low end of range (%g) is below high end (%g).  "
+                     "The range will be treated as reversed."),
+               *x, *y);
+          t = *x;
+          *x = *y;
+          *y = t;
+        }
+      else if (*x == *y)
+        msg (SW, _("Ends of range are equal (%g)."), *x);
+
+      return true;
+    }
+  else
+    {
+      if (*x == LOWEST)
+        {
+          msg (SE, _("LO or LOWEST must be part of a range."));
+          return false;
+        }
+      *y = *x;
+    }
+
+  return true;
+}
+
+/* Parses a number and stores it in *X.  Returns success.
+
+   Numeric values are always accepted.  If FORMAT is nonnull,
+   then string values are also accepted, and converted to numeric
+   values using *FORMAT. */
+static bool
+parse_number (struct lexer *lexer, double *x, const enum fmt_type *format)
+{
+  if (lex_is_number (lexer))
+    {
+      *x = lex_number (lexer);
+      lex_get (lexer);
+      return true;
+    }
+  else if (lex_token (lexer) == T_STRING && format != NULL)
+    {
+      union value v;
+      assert (! (fmt_get_category (*format) & ( FMT_CAT_STRING )));
+      data_in (ds_ss (lex_tokstr (lexer)), LEGACY_NATIVE,
+               *format, 0, 0, 0, NULL, &v, 0);
+      lex_get (lexer);
+      *x = v.f;
+      if (*x == SYSMIS)
+        {
+          msg (SE, _("System-missing value is not valid here."));
+          return false;
+        }
+      return true;
+    }
+  else
+    {
+      if (format != NULL)
+        lex_error (lexer, _("expecting number or data string"));
+      else
+        lex_force_num (lexer);
+      return false;
+    }
+}
+
+/* Parses the current token from LEXER into value V, which must
+   already have been initialized with the specified WIDTH.
+   Returns true if successful, false otherwise. */
+bool
+parse_value (struct lexer *lexer, union value *v, int width)
+{
+  if (width == 0)
+    {
+      if (!lex_force_num (lexer))
+       return false;
+      v->f = lex_tokval (lexer);
+    }
+  else
+    {
+      if (!lex_force_string (lexer))
+       return false;
+      value_copy_str_rpad (v, width, ds_cstr (lex_tokstr (lexer)), ' ');
+    }
+
+  lex_get (lexer);
+
+  return true;
+}
diff --git a/src/language/lexer/value-parser.h b/src/language/lexer/value-parser.h
new file mode 100644 (file)
index 0000000..4f9004a
--- /dev/null
@@ -0,0 +1,29 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 VALUE_PARSER_H
+#define VALUE_PARSER_H 1
+
+#include <stdbool.h>
+
+struct lexer;
+enum fmt_type;
+union value;
+bool parse_num_range (struct lexer *,
+                      double *x, double *y, const enum fmt_type *fmt);
+bool parse_value (struct lexer *, union value *, int width);
+
+#endif /* value-parser.h */
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..08d2f5e15c7d06176f1787b5e81017c7da54ad83 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],
@@ -267,7 +274,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
          so TEMPORARY is moot. */
       proc_cancel_temporary_transformations (ds);
       proc_discard_output (ds);
-      output = autopaging_writer_create (dict_get_next_value_idx (agr.dict));
+      output = autopaging_writer_create (dict_get_proto (agr.dict));
     }
   else
     {
@@ -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,25 @@ 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;
+
+              cout = case_create (casewriter_get_proto (iter->writer));
+
+             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;
@@ -763,8 +793,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MAX | FSTRING:
-           if (memcmp (iter->string, v->s, src_width) < 0)
-             memcpy (iter->string, v->s, src_width);
+           if (memcmp (iter->string, value_str (v, src_width), src_width) < 0)
+             memcpy (iter->string, value_str (v, src_width), src_width);
            iter->int1 = 1;
            break;
          case MIN:
@@ -772,8 +802,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case MIN | FSTRING:
-           if (memcmp (iter->string, v->s, src_width) > 0)
-             memcpy (iter->string, v->s, src_width);
+           if (memcmp (iter->string, value_str (v, src_width), src_width) > 0)
+             memcpy (iter->string, value_str (v, src_width), src_width);
            iter->int1 = 1;
            break;
          case FGT:
@@ -784,7 +814,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FGT | FSTRING:
          case PGT | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) < 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -796,7 +827,8 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FLT | FSTRING:
          case PLT | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) > 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) > 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -808,8 +840,10 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FIN | FSTRING:
          case PIN | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) <= 0
-                && memcmp (iter->arg[1].c, v->s, src_width) >= 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) <= 0
+                && memcmp (iter->arg[1].c,
+                           value_str (v, src_width), src_width) >= 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -821,8 +855,10 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
             break;
          case FOUT | FSTRING:
          case POUT | FSTRING:
-            if (memcmp (iter->arg[0].c, v->s, src_width) > 0
-                || memcmp (iter->arg[1].c, v->s, src_width) < 0)
+            if (memcmp (iter->arg[0].c,
+                        value_str (v, src_width), src_width) > 0
+                || memcmp (iter->arg[1].c,
+                           value_str (v, src_width), src_width) < 0)
               iter->dbl[0] += weight;
             iter->dbl[1] += weight;
             break;
@@ -844,7 +880,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
          case FIRST | FSTRING:
            if (iter->int1 == 0)
              {
-               memcpy (iter->string, v->s, src_width);
+               memcpy (iter->string, value_str (v, src_width), src_width);
                iter->int1 = 1;
              }
            break;
@@ -853,7 +889,7 @@ accumulate_aggregate_info (struct agr_proc *agr, const struct ccase *input)
            iter->int1 = 1;
            break;
          case LAST | FSTRING:
-           memcpy (iter->string, v->s, src_width);
+           memcpy (iter->string, value_str (v, src_width), src_width);
            iter->int1 = 1;
            break;
           case NMISS:
@@ -885,9 +921,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_proto (agr->dict));
 
   {
     int value_idx = 0;
@@ -896,11 +930,10 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
     for (i = 0; i < agr->break_var_cnt; i++)
       {
         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),
-                sizeof (union value) * value_cnt);
-        value_idx += value_cnt;
+        value_copy (case_data_rw_idx (c, value_idx),
+                    case_data (agr->break_case, v),
+                    var_get_width (v));
+        value_idx++;
       }
   }
 
@@ -909,16 +942,15 @@ 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);
+        int width = var_get_width (i->dest);
 
        if (agr->missing == COLUMNWISE && i->saw_missing
            && (i->function & FUNC) != N && (i->function & FUNC) != NU
            && (i->function & FUNC) != NMISS && (i->function & FUNC) != NUMISS)
          {
-           if (var_is_alpha (i->dest))
-             memset (v->s, ' ', var_get_width (i->dest));
-           else
-             v->f = SYSMIS;
+            value_set_missing (v, width);
+           casewriter_destroy (i->writer);
            continue;
          }
 
@@ -930,6 +962,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;
@@ -950,9 +1001,9 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case MAX | FSTRING:
          case MIN | FSTRING:
            if (i->int1)
-             memcpy (v->s, i->string, var_get_width (i->dest));
+             memcpy (value_str_rw (v, width), i->string, width);
            else
-             memset (v->s, ' ', var_get_width (i->dest));
+              value_set_missing (v, width);
            break;
          case FGT:
          case FGT | FSTRING:
@@ -989,9 +1040,9 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case FIRST | FSTRING:
          case LAST | FSTRING:
            if (i->int1)
-             memcpy (v->s, i->string, var_get_width (i->dest));
+             memcpy (value_str_rw (v, width), i->string, width);
            else
-             memset (v->s, ' ', var_get_width (i->dest));
+              value_set_missing (v, width);
            break;
          case N_NO_VARS:
            v->f = i->dbl[0];
@@ -1013,7 +1064,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 +1073,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 +1095,29 @@ 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 caseproto *proto;
+            struct subcase ordering;
+
+            proto = caseproto_create ();
+            proto = caseproto_add_width (proto, 0);
+            proto = caseproto_add_width (proto, 0);
+
+           if ( ! iter->subject)
+             iter->subject = var_create_internal (0, 0);
+
+           if ( ! iter->weight)
+             iter->weight = var_create_internal (1, 0);
+
+            subcase_init_var (&ordering, iter->subject, SC_ASCEND);
+           iter->writer = sort_create_writer (&ordering, proto);
+            subcase_destroy (&ordering);
+            caseproto_unref (proto);
+
+           iter->cc = 0;
+         }
+         break;
         case SD:
           if (iter->moments == NULL)
             iter->moments = moments1_create (MOMENT_VARIANCE);
index d60cb0dbdb6a15a59fcd31312a147f6b4da15d9e..1a68b906bfe313181353dc472bb48e3f202e6c72 100644 (file)
@@ -3,7 +3,6 @@
 AM_CPPFLAGS += -I$(top_srcdir)/src/language/stats
 
 src_language_stats_built_sources = \
-       src/language/stats/correlations.c \
        src/language/stats/crosstabs.c \
        src/language/stats/examine.c \
        src/language/stats/frequencies.c \
@@ -13,6 +12,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 +31,13 @@ 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/roc.c \
+       src/language/stats/roc.h \
+       src/language/stats/sign.c \
+       src/language/stats/sign.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..887b12230d48222f805a97c37de6298aa15f7971 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;
 }
@@ -353,7 +355,7 @@ hash_alpha_value (const void *a_, const void *v_)
   const union arc_value *a = a_;
   const struct variable *v = v_;
 
-  return hsh_hash_bytes (a->c, var_get_width (v));
+  return hash_bytes (a->c, var_get_width (v), 0);
 }
 
 static int
@@ -370,5 +372,5 @@ hash_numeric_value (const void *a_, const void *aux UNUSED)
 {
   const union arc_value *a = a_;
 
-  return hsh_hash_double (a->f);
+  return hash_double (a->f, 0);
 }
index 7a0ac72292e802587312f43cfe8eac18894c21cc..26e0257ca14030ca3d58a2ae10acd8f960b45d2c 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,48 @@ 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))
+  for (; (c = casereader_read (input)) != NULL; case_unref (c))
     {
       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);
-          int width = var_get_width (var);
+         double value = case_num (c, var);
 
-         if (var_is_value_missing (var, value, exclude))
-           break;
+         if (var_is_num_missing (var, value, exclude))
+           continue;
 
-         if ( NULL == cat1[v].value )
+         if (bst->cutpoint != SYSMIS)
            {
-             cat1[v].value = value_dup (value, width);
-             cat1[v].count = w;
+             if ( cat1[v].value.f >= value )
+                 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 ( SYSMIS == cat1[v].value.f )
+               {
+                 cat1[v].value.f = value;
+                 cat1[v].count = w;
+               }
+             else if ( cat1[v].value.f == value )
+               cat1[v].count += w;
+             else if ( SYSMIS == cat2[v].value.f )
+               {
+                 cat2[v].value.f = value;
+                 cat2[v].count = w;
+               }
+             else if ( cat2[v].value.f == value )
+               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);
     }
   return casereader_destroy (input);
 }
@@ -144,33 +151,37 @@ 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);
   const struct binomial_test *bst = (const struct binomial_test *) test;
   const struct one_sample_test *ost = (const struct one_sample_test*) test;
 
-  struct freq_mutable *cat1 = xzalloc (sizeof (*cat1) * ost->n_vars);
-  struct freq_mutable *cat2 = xzalloc (sizeof (*cat1) * ost->n_vars);
+  struct freq_mutable *cat[2];
+  int i;
 
-  assert ((bst->category1 == SYSMIS) == (bst->category2 == SYSMIS) );
+  assert ((bst->category1 == SYSMIS) == (bst->category2 == SYSMIS) || bst->cutpoint != SYSMIS);
 
-  if ( bst->category1 != SYSMIS )
+  for (i = 0; i < 2; i++)
     {
-      union value v;
-      v.f = bst->category1;
-      cat1->value = value_dup (&v, 0);
-    }
-
-  if ( bst->category2 != SYSMIS )
-    {
-      union value v;
-      v.f = bst->category2;
-      cat2->value = value_dup (&v, 0);
+      double value;
+      if (i == 0)
+        value = bst->cutpoint != SYSMIS ? bst->cutpoint : bst->category1;
+      else
+        value = bst->category2;
+
+      cat[i] = xnmalloc (ost->n_vars, sizeof *cat[i]);
+      for (v = 0; v < ost->n_vars; v++)
+        {
+          cat[i][v].value.f = value;
+          cat[i][v].count = 0;
+        }
     }
 
-  if (do_binomial (dict, input, bst, cat1, cat2, exclude))
+  if (do_binomial (dataset_dict (ds), input, bst, cat[0], cat[1], exclude))
     {
       const struct variable *wvar = dict_get_weight (dict);
       const struct fmt_spec *wfmt = wvar ?
@@ -178,7 +189,7 @@ binomial_execute (const struct dataset *ds,
 
       struct tab_table *table = tab_create (7, ost->n_vars * 3 + 1, 0);
 
-      tab_dim (table, tab_natural_dimensions);
+      tab_dim (table, tab_natural_dimensions, NULL);
 
       tab_title (table, _("Binomial Test"));
 
@@ -190,15 +201,21 @@ binomial_execute (const struct dataset *ds,
       for (v = 0 ; v < ost->n_vars; ++v)
         {
           double n_total, sig;
-         struct string catstr1;
-         struct string catstr2;
+         struct string catstr[2];
           const struct variable *var = ost->vars[v];
 
-         ds_init_empty (&catstr1);
-         ds_init_empty (&catstr2);
+         ds_init_empty (&catstr[0]);
+         ds_init_empty (&catstr[1]);
 
-         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 (&catstr[0], "<= %g", bst->cutpoint);
+           }
+          else
+            {
+              var_append_value_name (var, &cat[0][v].value, &catstr[0]);
+              var_append_value_name (var, &cat[1][v].value, &catstr[1]);
+            }
 
           tab_hline (table, TAL_1, 0, tab_nc (table) -1, 1 + v * 3);
 
@@ -212,31 +229,31 @@ binomial_execute (const struct dataset *ds,
           tab_double (table, 5, 1 + v * 3, TAB_NONE, bst->p, NULL);
 
           /* Category labels */
-          tab_text (table, 2, 1 + v * 3, TAB_NONE, ds_cstr (&catstr1));
-         tab_text (table, 2, 2 + v * 3, TAB_NONE, ds_cstr (&catstr2));
+          tab_text (table, 2, 1 + v * 3, TAB_NONE, ds_cstr (&catstr[0]));
+         tab_text (table, 2, 2 + v * 3, TAB_NONE, ds_cstr (&catstr[1]));
 
           /* Observed N */
-          tab_double (table, 3, 1 + v * 3, TAB_NONE, cat1[v].count, wfmt);
-          tab_double (table, 3, 2 + v * 3, TAB_NONE, cat2[v].count, wfmt);
+          tab_double (table, 3, 1 + v * 3, TAB_NONE, cat[0][v].count, wfmt);
+          tab_double (table, 3, 2 + v * 3, TAB_NONE, cat[1][v].count, wfmt);
 
-          n_total = cat1[v].count + cat2[v].count;
+          n_total = cat[0][v].count + cat[1][v].count;
           tab_double (table, 3, 3 + v * 3, TAB_NONE, n_total, wfmt);
 
           /* Observed Proportions */
           tab_double (table, 4, 1 + v * 3, TAB_NONE,
-                     cat1[v].count / n_total, NULL);
+                     cat[0][v].count / n_total, NULL);
           tab_double (table, 4, 2 + v * 3, TAB_NONE,
-                     cat2[v].count / n_total, NULL);
+                     cat[1][v].count / n_total, NULL);
 
           tab_double (table, 4, 3 + v * 3, TAB_NONE,
-                     (cat1[v].count + cat2[v].count) / n_total, wfmt);
+                     (cat[0][v].count + cat[1][v].count) / n_total, NULL);
 
           /* Significance */
-          sig = calculate_binomial (cat1[v].count, cat2[v].count, bst->p);
+          sig = calculate_binomial (cat[0][v].count, cat[1][v].count, bst->p);
           tab_double (table, 6, 1 + v * 3, TAB_NONE, sig, NULL);
 
-         ds_destroy (&catstr1);
-         ds_destroy (&catstr2);
+         ds_destroy (&catstr[0]);
+         ds_destroy (&catstr[1]);
         }
 
       tab_text (table,  2, 0,  TAB_CENTER, _("Category"));
@@ -244,19 +261,14 @@ binomial_execute (const struct dataset *ds,
       tab_text (table,  4, 0,  TAB_CENTER, _("Observed Prop."));
       tab_text (table,  5, 0,  TAB_CENTER, _("Test Prop."));
 
-      tab_text (table,  6, 0,  TAB_CENTER | TAT_PRINTF,
-                _("Exact Sig. (%d-tailed)"),
-                bst->p == 0.5 ? 2: 1);
+      tab_text_format (table,  6, 0,  TAB_CENTER,
+                       _("Exact Sig. (%d-tailed)"),
+                       bst->p == 0.5 ? 2 : 1);
 
       tab_vline (table, TAL_2, 2, 0, tab_nr (table) -1);
       tab_submit (table);
     }
 
-  for (v = 0; v < ost->n_vars; v++)
-    {
-      free (cat1[v].value);
-      free (cat2[v].value);
-    }
-  free (cat1);
-  free (cat2);
+  for (i = 0; i < 2; i++)
+    free (cat[i]);
 }
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..4593df4116eda81be5230a64ee2eee6c5f9fa15f 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,
@@ -68,47 +68,22 @@ create_freq_hash_with_range (const struct dictionary *dict,
   /* Populate the hash with zero entries */
   for (i_d = trunc (lo); i_d <= trunc (hi); i_d += 1.0 )
     {
-      union value the_value;
       struct freq_mutable *fr = xmalloc (sizeof (*fr));
-
-      the_value.f = i_d;
-
-      fr->value = value_dup (&the_value, 0);
+      value_init (&fr->value, 0);
+      fr->value.f = i_d;
       fr->count = 0;
-
       hsh_insert (freq_hash, fr);
     }
 
-  while (casereader_read (input, &c))
+  for (; (c = casereader_read (input)) != NULL; case_unref (c))
     {
-      union value obs_value;
-      struct freq **existing_fr;
-      struct freq *fr = xmalloc(sizeof  (*fr));
-      fr->value = case_data (&c, var);
-
-      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);
-         continue;
-       }
-
-      fr->value = &obs_value;
-
-      existing_fr = (struct freq **) hsh_probe (freq_hash, fr);
-
-      /* This must exist in the hash, because we previously populated it
-        with zero counts */
-      assert (*existing_fr);
-
-      (*existing_fr)->count += fr->count;
-      free (fr);
-
-      case_destroy (&c);
+      struct freq_mutable fr;
+      fr.value.f = trunc (case_num (c, var));
+      if (fr.value.f >= lo && fr.value.f <= hi)
+        {
+          struct freq_mutable *existing_fr = hsh_force_find (freq_hash, &fr);
+          existing_fr->count += dict_get_case_weight (dict, c, &warn);
+        }
     }
   if (casereader_destroy (input))
     return freq_hash;
@@ -130,33 +105,36 @@ create_freq_hash (const struct dictionary *dict,
                  struct casereader *input,
                  const struct variable *var)
 {
+  int width = var_get_width (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);
+      struct freq_mutable fr;
+      void **p;
 
-      fr->count = dict_get_case_weight (dict, &c, &warn);
+      fr.value = *case_data (c, var);
+      fr.count = dict_get_case_weight (dict, c, &warn);
 
-      existing_fr = (struct freq **) hsh_probe (freq_hash, fr);
-      if ( *existing_fr)
-       {
-         (*existing_fr)->count += fr->count;
-         free (fr);
-       }
+      p = hsh_probe (freq_hash, &fr);
+      if (*p == NULL)
+        {
+          struct freq_mutable *new_fr = *p = xmalloc (sizeof *new_fr);
+          value_init (&new_fr->value, width);
+          value_copy (&new_fr->value, &fr.value, width);
+          new_fr->count = fr.count;
+        }
       else
-       {
-          *existing_fr = fr;
-          fr->value = value_dup (fr->value, var_get_width (var));
-       }
+        {
+          struct freq *existing_fr = *p;
+          existing_fr->count += fr.count;
+        }
     }
   if (casereader_destroy (input))
     return freq_hash;
@@ -202,7 +180,7 @@ create_variable_frequency_table (const struct dictionary *dict,
     }
 
   table = tab_create(4, n_cells + 2, 0);
-  tab_dim (table, tab_natural_dimensions);
+  tab_dim (table, tab_natural_dimensions, NULL);
 
   tab_title (table, var_to_string(var));
   tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
@@ -238,7 +216,7 @@ create_combo_frequency_table (const struct chisquare_test *test)
   int n_cells = test->hi - test->lo + 1;
 
   table = tab_create(1 + ost->n_vars * 4, n_cells + 3, 0);
-  tab_dim (table, tab_natural_dimensions);
+  tab_dim (table, tab_natural_dimensions, NULL);
 
   tab_title (table, _("Frequencies"));
   for ( i = 0 ; i < ost->n_vars ; ++i )
@@ -294,7 +272,7 @@ create_stats_table (const struct chisquare_test *test)
 
   struct tab_table *table;
   table = tab_create (1 + ost->n_vars, 4, 0);
-  tab_dim (table, tab_natural_dimensions);
+  tab_dim (table, tab_natural_dimensions, NULL);
   tab_title (table, _("Test Statistics"));
   tab_headers (table, 1, 0, 1, 0);
 
@@ -321,7 +299,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 +328,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);
 
@@ -368,7 +349,7 @@ chisquare_execute (const struct dataset *ds,
            {
              struct string str;
              double exp;
-             const union value *observed_value = ff[i]->value;
+             const union value *observed_value = &ff[i]->value;
 
              ds_init_empty (&str);
              var_append_value_name (ost->vars[v], observed_value, &str);
@@ -418,7 +399,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);
@@ -440,7 +422,7 @@ chisquare_execute (const struct dataset *ds,
              struct string str;
              double exp;
 
-             const union value *observed_value = ff[i]->value;
+             const union value *observed_value = &ff[i]->value;
 
              ds_init_empty (&str);
              var_append_value_name (ost->vars[v], observed_value, &str);
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);
 
 
 
diff --git a/src/language/stats/correlations.q b/src/language/stats/correlations.q
deleted file mode 100644 (file)
index 5d54309..0000000
+++ /dev/null
@@ -1,179 +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/>. */
-
-#include <config.h>
-
-#include <stdlib.h>
-
-#include <data/dictionary.h>
-#include <data/file-handle-def.h>
-#include <data/procedure.h>
-#include <data/variable.h>
-#include <language/command.h>
-#include <language/data-io/file-handle.h>
-#include <language/lexer/lexer.h>
-#include <language/lexer/variable-parser.h>
-#include <libpspp/compiler.h>
-
-#include "xalloc.h"
-
-/* (headers) */
-
-struct cor_set
-  {
-    struct cor_set *next;
-    const struct variable **v1, **v2;
-    size_t nv1, nv2;
-  };
-
-static struct cor_set *cor_list, *cor_last;
-
-static struct file_handle *matrix_file;
-
-static void free_correlations_state (void);
-static int internal_cmd_correlations (struct lexer *lexer, struct dataset *ds);
-
-int
-cmd_correlations (struct lexer *lexer, struct dataset *ds)
-{
-  int result = internal_cmd_correlations (lexer, ds);
-  free_correlations_state ();
-  return result;
-}
-
-/* (specification)
-   "CORRELATIONS" (cor_):
-     *variables=custom;
-     missing=miss:!pairwise/listwise,
-            inc:include/exclude;
-     +print=tail:!twotail/onetail,
-           sig:!sig/nosig;
-     +format=fmt:!matrix/serial;
-     +matrix=custom;
-     +statistics[st_]=descriptives,xprod,all.
-*/
-/* (declarations) */
-/* (functions) */
-
-int
-internal_cmd_correlations (struct lexer *lexer, struct dataset *ds)
-{
-  struct cmd_correlations cmd;
-
-  cor_list = cor_last = NULL;
-  matrix_file = NULL;
-
-  if (!parse_correlations (lexer, ds, &cmd, NULL))
-    {
-      fh_unref (matrix_file);
-      return CMD_FAILURE;
-    }
-
-  free_correlations (&cmd);
-  fh_unref (matrix_file);
-
-  return CMD_SUCCESS;
-}
-
-static int
-cor_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_correlations *cmd UNUSED, void *aux UNUSED)
-{
-  const struct variable **v1, **v2;
-  size_t nv1, nv2;
-  struct cor_set *cor;
-
-  /* Ensure that this is a VARIABLES subcommand. */
-  if (!lex_match_id (lexer, "VARIABLES")
-      && (lex_token (lexer) != T_ID || dict_lookup_var (dataset_dict (ds), lex_tokid (lexer)) != NULL)
-      && lex_token (lexer) != T_ALL)
-    return 2;
-  lex_match (lexer, '=');
-
-  if (!parse_variables_const (lexer, dataset_dict (ds), &v1, &nv1,
-                       PV_NO_DUPLICATE | PV_NUMERIC))
-    return 0;
-
-  if (lex_match (lexer, T_WITH))
-    {
-      if (!parse_variables_const (lexer, dataset_dict (ds), &v2, &nv2,
-                           PV_NO_DUPLICATE | PV_NUMERIC))
-       {
-         free (v1);
-         return 0;
-       }
-    }
-  else
-    {
-      nv2 = nv1;
-      v2 = v1;
-    }
-
-  cor = xmalloc (sizeof *cor);
-  cor->next = NULL;
-  cor->v1 = v1;
-  cor->v2 = v2;
-  cor->nv1 = nv1;
-  cor->nv2 = nv2;
-  if (cor_list)
-    cor_last = cor_last->next = cor;
-  else
-    cor_list = cor_last = cor;
-
-  return 1;
-}
-
-static int
-cor_custom_matrix (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_correlations *cmd UNUSED, void *aux UNUSED)
-{
-  if (!lex_force_match (lexer, '('))
-    return 0;
-
-  if (lex_match (lexer, '*'))
-    matrix_file = NULL;
-  else
-    {
-      fh_unref (matrix_file);
-      matrix_file = fh_parse (lexer, FH_REF_FILE);
-      if (matrix_file == NULL)
-        return 0;
-    }
-
-  if (!lex_force_match (lexer, ')'))
-    return 0;
-
-  return 1;
-}
-
-static void
-free_correlations_state (void)
-{
-  struct cor_set *cor, *next;
-
-  for (cor = cor_list; cor != NULL; cor = next)
-    {
-      next = cor->next;
-      if (cor->v1 != cor->v2)
-       free (cor->v2);
-      free (cor->v1);
-      free (cor);
-    }
-}
-
-/*
-  Local Variables:
-  mode: c
-  End:
-*/
index 186ee12b995551b51b66851b502445ec008fe0b1..99fa41d9c056a039ef9d92fdc9090758539c9d15 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
@@ -50,6 +50,8 @@
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
+#include <libpspp/hmap.h>
+#include <libpspp/hmapx.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
@@ -59,7 +61,7 @@
 
 #include "minmax.h"
 #include "xalloc.h"
-#include "xmalloca.h"
+#include "xsize.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -79,8 +81,8 @@
             tabl:!tables/notables,
             box:!box/nobox,
             pivot:!pivot/nopivot;
-     +cells[cl_]=count,none,expected,row,column,total,residual,sresidual,
-                asresidual,all;
+     +cells[cl_]=count,expected,row,column,total,residual,sresidual,
+                asresidual,all,none;
      +statistics[st_]=chisq,phi,cc,lambda,uc,none,btau,ctau,risk,gamma,d,
                      kappa,eta,corr,all.
 */
 /* A single table entry for general mode. */
 struct table_entry
   {
-    int table;         /* Flattened table number. */
-    union
-      {
-       double freq;    /* Frequency count. */
-       double *data;   /* Crosstabulation table for integer mode. */
-      }
-    u;
+    struct hmap_node node;      /* Entry in hash table. */
+    double freq;                /* Frequency count. */
     union value values[1];     /* Values. */
   };
 
-/* A crosstabulation. */
-struct crosstab
+static size_t
+table_entry_size (size_t n_values)
+{
+  return (offsetof (struct table_entry, values)
+          + n_values * sizeof (union value));
+}
+
+/* Indexes into the 'vars' member of struct pivot_table and
+   struct crosstab member. */
+enum
+  {
+    ROW_VAR = 0,                /* Row variable. */
+    COL_VAR = 1                 /* Column variable. */
+    /* Higher indexes cause multiple tables to be output. */
+  };
+
+/* A crosstabulation of 2 or more variables. */
+struct pivot_table
   {
-    int nvar;                  /* Number of variables. */
-    double missing;            /* Missing cases count. */
-    int ofs;                   /* Integer mode: Offset into sorted_tab[]. */
-    const struct variable *vars[2];    /* At least two variables; sorted by
-                                  larger indices first. */
+    struct fmt_spec weight_format; /* Format for weight variable. */
+    double missing;             /* Weight of missing cases. */
+
+    /* Variables (2 or more). */
+    int n_vars;
+    const struct variable **vars;
+
+    /* Constants (0 or more). */
+    int n_consts;
+    const struct variable **const_vars;
+    union value *const_values;
+
+    /* Data. */
+    struct hmap data;
+    struct table_entry **entries;
+    size_t n_entries;
+
+    /* Column values, number of columns. */
+    union value *cols;
+    int n_cols;
+
+    /* Row values, number of rows. */
+    union value *rows;
+    int n_rows;
+
+    /* Number of statistically interesting columns/rows
+       (columns/rows with data in them). */
+    int ns_cols, ns_rows;
+
+    /* Matrix contents. */
+    double *mat;               /* Matrix proper. */
+    double *row_tot;           /* Row totals. */
+    double *col_tot;           /* Column totals. */
+    double total;              /* Grand total. */
   };
 
 /* Integer mode variable info. */
@@ -133,192 +175,198 @@ get_var_range (const struct variable *v)
   return var_get_aux (v);
 }
 
-/* Indexes into crosstab.v. */
-enum
+struct crosstabs_proc
   {
-    ROW_VAR = 0,
-    COL_VAR = 1
+    const struct dictionary *dict;
+    enum { INTEGER, GENERAL } mode;
+    enum mv_class exclude;
+    bool pivot;
+    bool bad_warn;
+    struct fmt_spec weight_format;
+
+    /* Variables specifies on VARIABLES. */
+    const struct variable **variables;
+    size_t n_variables;
+
+    /* TABLES. */
+    struct pivot_table *pivots;
+    int n_pivots;
+
+    /* CELLS. */
+    int n_cells;               /* Number of cells requested. */
+    unsigned int cells;         /* Bit k is 1 if cell k is requested. */
+    int a_cells[CRS_CL_count];  /* 0...n_cells-1 are the requested cells. */
+
+    /* STATISTICS. */
+    unsigned int statistics;    /* Bit k is 1 if statistic k is requested. */
   };
 
-/* General mode crosstabulation table. */
-static struct hsh_table *gen_tab;      /* Hash table. */
-static int n_sorted_tab;               /* Number of entries in sorted_tab. */
-static struct table_entry **sorted_tab;        /* Sorted table. */
-
-/* Variables specifies on VARIABLES. */
-static const struct variable **variables;
-static size_t variables_cnt;
-
-/* TABLES. */
-static struct crosstab **xtab;
-static int nxtab;
-
-/* Integer or general mode? */
-enum
-  {
-    INTEGER,
-    GENERAL
-  };
-static int mode;
-
-/* CELLS. */
-static int num_cells;          /* Number of cells requested. */
-static int cells[8];           /* Cells requested. */
-
-/* WRITE. */
-static int write_style;                /* One of WR_* that specifies the WRITE style. */
+static void
+init_proc (struct crosstabs_proc *proc, struct dataset *ds)
+{
+  const struct variable *wv = dict_get_weight (dataset_dict (ds));
+  proc->dict = dataset_dict (ds);
+  proc->bad_warn = true;
+  proc->variables = NULL;
+  proc->n_variables = 0;
+  proc->pivots = NULL;
+  proc->n_pivots = 0;
+  proc->weight_format = wv ? *var_get_print_format (wv) : F_8_0;
+}
 
-/* Command parsing info. */
-static struct cmd_crosstabs cmd;
+static void
+free_proc (struct crosstabs_proc *proc)
+{
+  struct pivot_table *pt;
 
-/* Pools. */
-static struct pool *pl_tc;     /* For table cells. */
-static struct pool *pl_col;    /* For column data. */
+  free (proc->variables);
+  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+    {
+      free (pt->vars);
+      free (pt->const_vars);
+      /* We must not call value_destroy on const_values because
+         it is a wild pointer; it never pointed to anything owned
+         by the pivot_table.
 
-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 postcalc (const struct dataset *);
-static void submit (struct tab_table *);
+         The rest of the data was allocated and destroyed at a
+         lower level already. */
+      free (pt);
+    }
+}
 
-static void format_short (char *s, const struct fmt_spec *fp,
-                         const union value *v);
+static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds,
+                                   struct crosstabs_proc *);
+static bool should_tabulate_case (const struct pivot_table *,
+                                  const struct ccase *, enum mv_class exclude);
+static void tabulate_general_case (struct pivot_table *, const struct ccase *,
+                                   double weight);
+static void tabulate_integer_case (struct pivot_table *, const struct ccase *,
+                                   double weight);
+static void postcalc (struct crosstabs_proc *);
+static void submit (struct crosstabs_proc *, struct pivot_table *,
+                    struct tab_table *);
 
 /* Parse and execute CROSSTABS, then clean up. */
 int
 cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 {
-  int result = internal_cmd_crosstabs (lexer, ds);
-  int i;
-
-  free (variables);
-  pool_destroy (pl_tc);
-  pool_destroy (pl_col);
+  struct crosstabs_proc proc;
+  int result;
 
-  for (i = 0; i < nxtab; i++)
-    free (xtab[i]);
-  free (xtab);
+  init_proc (&proc, ds);
+  result = internal_cmd_crosstabs (lexer, ds, &proc);
+  free_proc (&proc);
 
   return result;
 }
 
 /* Parses and executes the CROSSTABS procedure. */
 static int
-internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
+internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds,
+                        struct crosstabs_proc *proc)
 {
   struct casegrouper *grouper;
   struct casereader *input, *group;
+  struct cmd_crosstabs cmd;
+  struct pivot_table *pt;
   bool ok;
   int i;
 
-  variables = NULL;
-  variables_cnt = 0;
-  xtab = NULL;
-  nxtab = 0;
-  pl_tc = pool_create ();
-  pl_col = pool_create ();
-
-  if (!parse_crosstabs (lexer, ds, &cmd, NULL))
+  if (!parse_crosstabs (lexer, ds, &cmd, proc))
     return CMD_FAILURE;
 
-  mode = variables ? INTEGER : GENERAL;
+  proc->mode = proc->n_variables ? INTEGER : GENERAL;
 
   /* CELLS. */
   if (!cmd.sbc_cells)
-    {
-      cmd.a_cells[CRS_CL_COUNT] = 1;
-    }
+    proc->cells = 1u << CRS_CL_COUNT;
+  else if (cmd.a_cells[CRS_CL_ALL])
+    proc->cells = UINT_MAX;
   else
     {
-      int count = 0;
-
+      proc->cells = 0;
       for (i = 0; i < CRS_CL_count; i++)
        if (cmd.a_cells[i])
-         count++;
-      if (count == 0)
-       {
-         cmd.a_cells[CRS_CL_COUNT] = 1;
-         cmd.a_cells[CRS_CL_ROW] = 1;
-         cmd.a_cells[CRS_CL_COLUMN] = 1;
-         cmd.a_cells[CRS_CL_TOTAL] = 1;
-       }
-      if (cmd.a_cells[CRS_CL_ALL])
-       {
-         for (i = 0; i < CRS_CL_count; i++)
-           cmd.a_cells[i] = 1;
-         cmd.a_cells[CRS_CL_ALL] = 0;
-       }
-      cmd.a_cells[CRS_CL_NONE] = 0;
+         proc->cells |= 1u << i;
+      if (proc->cells == 0)
+        proc->cells = ((1u << CRS_CL_COUNT)
+                       | (1u << CRS_CL_ROW)
+                       | (1u << CRS_CL_COLUMN)
+                       | (1u << CRS_CL_TOTAL));
     }
-  for (num_cells = i = 0; i < CRS_CL_count; i++)
-    if (cmd.a_cells[i])
-      cells[num_cells++] = i;
+  proc->cells &= ((1u << CRS_CL_count) - 1);
+  proc->cells &= ~((1u << CRS_CL_NONE) | (1u << CRS_CL_ALL));
+  proc->n_cells = 0;
+  for (i = 0; i < CRS_CL_count; i++)
+    if (proc->cells & (1u << i))
+      proc->a_cells[proc->n_cells++] = i;
 
   /* STATISTICS. */
-  if (cmd.sbc_statistics)
+  if (cmd.a_statistics[CRS_ST_ALL])
+    proc->statistics = UINT_MAX;
+  else if (cmd.sbc_statistics)
     {
       int i;
-      int count = 0;
 
+      proc->statistics = 0;
       for (i = 0; i < CRS_ST_count; i++)
        if (cmd.a_statistics[i])
-         count++;
-      if (count == 0)
-       cmd.a_statistics[CRS_ST_CHISQ] = 1;
-      if (cmd.a_statistics[CRS_ST_ALL])
-       for (i = 0; i < CRS_ST_count; i++)
-         cmd.a_statistics[i] = 1;
+         proc->statistics |= 1u << i;
+      if (proc->statistics == 0)
+        proc->statistics |= 1u << CRS_ST_CHISQ;
     }
+  else
+    proc->statistics = 0;
 
   /* MISSING. */
-  if (cmd.miss == CRS_REPORT && mode == GENERAL)
+  proc->exclude = (cmd.miss == CRS_TABLE ? MV_ANY
+                   : cmd.miss == CRS_INCLUDE ? MV_SYSTEM
+                   : MV_NEVER);
+  if (proc->mode == GENERAL && proc->mode == MV_NEVER)
     {
       msg (SE, _("Missing mode REPORT not allowed in general mode.  "
                 "Assuming MISSING=TABLE."));
-      cmd.miss = CRS_TABLE;
+      proc->mode = MV_ANY;
     }
 
-  /* WRITE. */
-  if (cmd.a_write[CRS_WR_ALL] && cmd.a_write[CRS_WR_CELLS])
-    cmd.a_write[CRS_WR_ALL] = 0;
-  if (cmd.a_write[CRS_WR_ALL] && mode == GENERAL)
-    {
-      msg (SE, _("Write mode ALL not allowed in general mode.  "
-                "Assuming WRITE=CELLS."));
-      cmd.a_write[CRS_WR_CELLS] = 1;
-    }
-  if (cmd.sbc_write
-      && (cmd.a_write[CRS_WR_NONE]
-         + cmd.a_write[CRS_WR_ALL]
-         + cmd.a_write[CRS_WR_CELLS] == 0))
-    cmd.a_write[CRS_WR_CELLS] = 1;
-  if (cmd.a_write[CRS_WR_CELLS])
-    write_style = CRS_WR_CELLS;
-  else if (cmd.a_write[CRS_WR_ALL])
-    write_style = CRS_WR_ALL;
-  else
-    write_style = CRS_WR_NONE;
+  /* PIVOT. */
+  proc->pivot = cmd.pivot == CRS_PIVOT;
 
   input = casereader_create_filter_weight (proc_open (ds), dataset_dict (ds),
                                            NULL, NULL);
   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))
+      /* Output SPLIT FILE variables. */
+      c = casereader_peek (group, 0);
+      if (c != NULL)
         {
-          if (mode == GENERAL)
-            calc_general (&c, ds);
-          else
-            calc_integer (&c, ds);
+          output_split_file_values (ds, c);
+          case_unref (c);
         }
+
+      /* Tabulate. */
+      for (; (c = casereader_read (group)) != NULL; case_unref (c))
+        for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+          {
+            double weight = dict_get_case_weight (dataset_dict (ds), c,
+                                                  &proc->bad_warn);
+            if (should_tabulate_case (pt, c, proc->exclude))
+              {
+                if (proc->mode == GENERAL)
+                  tabulate_general_case (pt, c, weight);
+                else
+                  tabulate_integer_case (pt, c, weight);
+              }
+            else
+              pt->missing += weight;
+          }
       casereader_destroy (group);
 
-      postcalc (ds);
+      /* Output. */
+      postcalc (proc);
     }
   ok = casegrouper_destroy (grouper);
   ok = proc_commit (ds) && ok;
@@ -328,14 +376,18 @@ internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
 
 /* Parses the TABLES subcommand. */
 static int
-crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
+crs_custom_tables (struct lexer *lexer, struct dataset *ds,
+                   struct cmd_crosstabs *cmd UNUSED, void *proc_)
 {
+  struct crosstabs_proc *proc = proc_;
   struct const_var_set *var_set;
   int n_by;
   const struct variable ***by = NULL;
+  int *by_iter;
   size_t *by_nvar = NULL;
   size_t nx = 1;
-  int success = 0;
+  bool ok = false;
+  int i;
 
   /* Ensure that this is a TABLES subcommand. */
   if (!lex_match_id (lexer, "TABLES")
@@ -345,8 +397,9 @@ crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs
     return 2;
   lex_match (lexer, '=');
 
-  if (variables != NULL)
-    var_set = const_var_set_create_from_array (variables, variables_cnt);
+  if (proc->variables != NULL)
+    var_set = const_var_set_create_from_array (proc->variables,
+                                               proc->n_variables);
   else
     var_set = const_var_set_create_from_dict (dataset_dict (ds));
   assert (var_set != NULL);
@@ -356,7 +409,7 @@ crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs
       by = xnrealloc (by, n_by + 1, sizeof *by);
       by_nvar = xnrealloc (by_nvar, n_by + 1, sizeof *by_nvar);
       if (!parse_const_var_set_vars (lexer, var_set, &by[n_by], &by_nvar[n_by],
-                               PV_NO_DUPLICATE | PV_NO_SCRATCH))
+                                     PV_NO_DUPLICATE | PV_NO_SCRATCH))
        goto done;
       if (xalloc_oversized (nx, by_nvar[n_by]))
         {
@@ -378,64 +431,57 @@ crs_custom_tables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs
        }
     }
 
-  {
-    int *by_iter = xcalloc (n_by, sizeof *by_iter);
-    int i;
-
-    xtab = xnrealloc (xtab, nxtab + nx, sizeof *xtab);
-    for (i = 0; i < nx; i++)
-      {
-       struct crosstab *x;
-
-       x = xmalloc (sizeof *x + sizeof (struct variable *) * (n_by - 2));
-       x->nvar = n_by;
-       x->missing = 0.;
-
-       {
-         int i;
-
-          for (i = 0; i < n_by; i++)
-            x->vars[i] = by[i][by_iter[i]];
-       }
-
-       {
-         int i;
-
-         for (i = n_by - 1; i >= 0; i--)
-           {
-             if (++by_iter[i] < by_nvar[i])
-               break;
-             by_iter[i] = 0;
-           }
-       }
-
-       xtab[nxtab++] = x;
-      }
-    free (by_iter);
-  }
-  success = 1;
+  by_iter = xcalloc (n_by, sizeof *by_iter);
+  proc->pivots = xnrealloc (proc->pivots,
+                            proc->n_pivots + nx, sizeof *proc->pivots);
+  for (i = 0; i < nx; i++)
+    {
+      struct pivot_table *pt = &proc->pivots[proc->n_pivots++];
+      int j;
+
+      pt->weight_format = proc->weight_format;
+      pt->missing = 0.;
+      pt->n_vars = n_by;
+      pt->vars = xmalloc (n_by * sizeof *pt->vars);
+      pt->n_consts = 0;
+      pt->const_vars = NULL;
+      pt->const_values = NULL;
+      hmap_init (&pt->data);
+      pt->entries = NULL;
+      pt->n_entries = 0;
+
+      for (j = 0; j < n_by; j++)
+        pt->vars[j] = by[j][by_iter[j]];
+
+      for (j = n_by - 1; j >= 0; j--)
+        {
+          if (++by_iter[j] < by_nvar[j])
+            break;
+          by_iter[j] = 0;
+        }
+    }
+  free (by_iter);
+  ok = true;
 
- done:
+done:
   /* All return paths lead here. */
-  {
-    int i;
-
-    for (i = 0; i < n_by; i++)
-      free (by[i]);
-    free (by);
-    free (by_nvar);
-  }
+  for (i = 0; i < n_by; i++)
+    free (by[i]);
+  free (by);
+  free (by_nvar);
 
   const_var_set_destroy (var_set);
 
-  return success;
+  return ok;
 }
 
 /* Parses the VARIABLES subcommand. */
 static int
-crs_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_crosstabs *cmd UNUSED, void *aux UNUSED)
+crs_custom_variables (struct lexer *lexer, struct dataset *ds,
+                      struct cmd_crosstabs *cmd UNUSED, void *proc_)
 {
-  if (nxtab)
+  struct crosstabs_proc *proc = proc_;
+  if (proc->n_pivots)
     {
       msg (SE, _("VARIABLES must be specified before TABLES."));
       return 0;
@@ -445,15 +491,15 @@ crs_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_crosst
 
   for (;;)
     {
-      size_t orig_nv = variables_cnt;
+      size_t orig_nv = proc->n_variables;
       size_t i;
 
       long min, max;
 
       if (!parse_variables_const (lexer, dataset_dict (ds),
-                           &variables, &variables_cnt,
-                           (PV_APPEND | PV_NUMERIC
-                            | PV_NO_DUPLICATE | PV_NO_SCRATCH)))
+                                  &proc->variables, &proc->n_variables,
+                                  (PV_APPEND | PV_NUMERIC
+                                   | PV_NO_DUPLICATE | PV_NO_SCRATCH)))
        return 0;
 
       if (lex_token (lexer) != '(')
@@ -488,13 +534,13 @@ crs_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_crosst
        }
       lex_get (lexer);
 
-      for (i = orig_nv; i < variables_cnt; i++)
+      for (i = orig_nv; i < proc->n_variables; i++)
         {
           struct var_range *vr = xmalloc (sizeof *vr);
           vr->min = min;
          vr->max = max + 1.;
          vr->count = max - min + 1;
-          var_attach_aux (variables[i], vr, var_dtor_free);
+          var_attach_aux (proc->variables[i], vr, var_dtor_free);
        }
 
       if (lex_token (lexer) == '/')
@@ -504,360 +550,293 @@ crs_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_crosst
   return 1;
 
  lossage:
-  free (variables);
-  variables = NULL;
+  free (proc->variables);
+  proc->variables = NULL;
+  proc->n_variables = 0;
   return 0;
 }
 \f
 /* Data file processing. */
 
-static int compare_table_entry (const void *, const void *, const void *);
-static unsigned hash_table_entry (const void *, const void *);
+static bool
+should_tabulate_case (const struct pivot_table *pt, const struct ccase *c,
+                      enum mv_class exclude)
+{
+  int j;
+  for (j = 0; j < pt->n_vars; j++)
+    {
+      const struct variable *var = pt->vars[j];
+      struct var_range *range = get_var_range (var);
+
+      if (var_is_value_missing (var, case_data (c, var), exclude))
+        return false;
+
+      if (range != NULL)
+        {
+          double num = case_num (c, var);
+          if (num < range->min || num > range->max)
+            return false;
+        }
+    }
+  return true;
+}
 
-/* Set up the crosstabulation tables for processing. */
 static void
-precalc (struct casereader *input, const struct dataset *ds)
+tabulate_integer_case (struct pivot_table *pt, const struct ccase *c,
+                       double weight)
 {
-  struct ccase c;
+  struct table_entry *te;
+  size_t hash;
+  int j;
 
-  if (casereader_peek (input, 0, &c))
+  hash = 0;
+  for (j = 0; j < pt->n_vars; j++)
     {
-      output_split_file_values (ds, &c);
-      case_destroy (&c);
+      /* Throw away fractional parts of values. */
+      hash = hash_int (case_num (c, pt->vars[j]), hash);
     }
 
-  if (mode == GENERAL)
-    {
-      gen_tab = hsh_create (512, compare_table_entry, hash_table_entry,
-                           NULL, NULL);
-    }
-  else
+  HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
     {
-      int i;
-
-      sorted_tab = NULL;
-      n_sorted_tab = 0;
-
-      for (i = 0; i < nxtab; i++)
-       {
-         struct crosstab *x = xtab[i];
-         int count = 1;
-         int *v;
-         int j;
-
-         x->ofs = n_sorted_tab;
-
-         for (j = 2; j < x->nvar; j++)
-            count *= get_var_range (x->vars[j - 2])->count;
-
-         sorted_tab = xnrealloc (sorted_tab,
-                                  n_sorted_tab + count, sizeof *sorted_tab);
-         v = xmalloca (sizeof *v * x->nvar);
-         for (j = 2; j < x->nvar; j++)
-            v[j] = get_var_range (x->vars[j])->min;
-         for (j = 0; j < count; j++)
-           {
-             struct table_entry *te;
-             int k;
-
-             te = sorted_tab[n_sorted_tab++]
-               = xmalloc (sizeof *te + sizeof (union value) * (x->nvar - 1));
-             te->table = i;
-
-             {
-                int row_cnt = get_var_range (x->vars[0])->count;
-                int col_cnt = get_var_range (x->vars[1])->count;
-               const int mat_size = row_cnt * col_cnt;
-               int m;
-
-               te->u.data = xnmalloc (mat_size, sizeof *te->u.data);
-               for (m = 0; m < mat_size; m++)
-                 te->u.data[m] = 0.;
-             }
+      for (j = 0; j < pt->n_vars; j++)
+        if ((int) case_num (c, pt->vars[j]) != (int) te->values[j].f)
+          goto no_match;
 
-             for (k = 2; k < x->nvar; k++)
-               te->values[k].f = v[k];
-             for (k = 2; k < x->nvar; k++)
-                {
-                  struct var_range *vr = get_var_range (x->vars[k]);
-                  if (++v[k] >= vr->max)
-                    v[k] = vr->min;
-                  else
-                    break;
-                }
-           }
-         freea (v);
-       }
+      /* Found an existing entry. */
+      te->freq += weight;
+      return;
 
-      sorted_tab = xnrealloc (sorted_tab,
-                              n_sorted_tab + 1, sizeof *sorted_tab);
-      sorted_tab[n_sorted_tab] = NULL;
+    no_match: ;
     }
 
+  /* No existing entry.  Create a new one. */
+  te = xmalloc (table_entry_size (pt->n_vars));
+  te->freq = weight;
+  for (j = 0; j < pt->n_vars; j++)
+    te->values[j].f = (int) case_num (c, pt->vars[j]);
+  hmap_insert (&pt->data, &te->node, hash);
 }
 
-/* Form crosstabulations for general mode. */
 static void
-calc_general (struct ccase *c, const struct dataset *ds)
+tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
+                       double weight)
 {
-  /* Missing values to exclude. */
-  enum mv_class exclude = (cmd.miss == CRS_TABLE ? MV_ANY
-                           : cmd.miss == CRS_INCLUDE ? MV_SYSTEM
-                           : MV_NEVER);
+  struct table_entry *te;
+  size_t hash;
+  int j;
 
-  /* Case weight. */
-  double weight = dict_get_case_weight (dataset_dict (ds), c, NULL);
-
-  /* Flattened current table index. */
-  int t;
-
-  for (t = 0; t < nxtab; t++)
+  hash = 0;
+  for (j = 0; j < pt->n_vars; j++)
     {
-      struct crosstab *x = xtab[t];
-      const size_t entry_size = (sizeof (struct table_entry)
-                                + sizeof (union value) * (x->nvar - 1));
-      struct table_entry *te = xmalloca (entry_size);
-
-      /* Construct table entry for the current record and table. */
-      te->table = t;
-      {
-       int j;
-
-       assert (x != NULL);
-       for (j = 0; j < x->nvar; j++)
-         {
-            const union value *v = case_data (c, x->vars[j]);
-            if (var_is_value_missing (x->vars[j], v, exclude))
-             {
-               x->missing += weight;
-               goto next_crosstab;
-             }
-
-           if (var_is_numeric (x->vars[j]))
-             te->values[j].f = case_num (c, x->vars[j]);
-           else
-             {
-                size_t n = var_get_width (x->vars[j]);
-                if (n > MAX_SHORT_STRING)
-                  n = MAX_SHORT_STRING;
-               memcpy (te->values[j].s, case_str (c, x->vars[j]), n);
-
-               /* Necessary in order to simplify comparisons. */
-               memset (&te->values[j].s[var_get_width (x->vars[j])], 0,
-                       sizeof (union value) - n);
-             }
-         }
-      }
+      const struct variable *var = pt->vars[j];
+      hash = value_hash (case_data (c, var), var_get_width (var), hash);
+    }
 
-      /* Add record to hash table. */
-      {
-       struct table_entry **tepp
-          = (struct table_entry **) hsh_probe (gen_tab, te);
-       if (*tepp == NULL)
-         {
-           struct table_entry *tep = pool_alloc (pl_tc, entry_size);
+  HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
+    {
+      for (j = 0; j < pt->n_vars; j++)
+        {
+          const struct variable *var = pt->vars[j];
+          if (!value_equal (case_data (c, var), &te->values[j],
+                            var_get_width (var)))
+            goto no_match;
+        }
 
-           te->u.freq = weight;
-           memcpy (tep, te, entry_size);
+      /* Found an existing entry. */
+      te->freq += weight;
+      return;
 
-           *tepp = tep;
-         }
-       else
-         (*tepp)->u.freq += weight;
-      }
+    no_match: ;
+    }
 
-    next_crosstab:
-      freea (te);
+  /* No existing entry.  Create a new one. */
+  te = xmalloc (table_entry_size (pt->n_vars));
+  te->freq = weight;
+  for (j = 0; j < pt->n_vars; j++)
+    {
+      const struct variable *var = pt->vars[j];
+      int width = var_get_width (var);
+      value_init (&te->values[j], width);
+      value_copy (&te->values[j], case_data (c, var), width);
     }
+  hmap_insert (&pt->data, &te->node, hash);
 }
+\f
+/* Post-data reading calculations. */
+
+static int compare_table_entry_vars_3way (const struct table_entry *a,
+                                          const struct table_entry *b,
+                                          const struct pivot_table *pt,
+                                          int idx0, int idx1);
+static int compare_table_entry_3way (const void *ap_, const void *bp_,
+                                     const void *pt_);
+static void enum_var_values (const struct pivot_table *, int var_idx,
+                             union value **valuesp, int *n_values);
+static void output_pivot_table (struct crosstabs_proc *,
+                                struct pivot_table *);
+static void make_pivot_table_subset (struct pivot_table *pt,
+                                     size_t row0, size_t row1,
+                                     struct pivot_table *subset);
+static void make_summary_table (struct crosstabs_proc *);
+static bool find_crosstab (struct pivot_table *, size_t *row0p, size_t *row1p);
 
 static void
-calc_integer (struct ccase *c, const struct dataset *ds)
+postcalc (struct crosstabs_proc *proc)
 {
-  bool bad_warn = true;
-
-  /* Case weight. */
-  double weight = dict_get_case_weight (dataset_dict (ds), c, &bad_warn);
-
-  /* Flattened current table index. */
-  int t;
+  struct pivot_table *pt;
 
-  for (t = 0; t < nxtab; t++)
+  /* Convert hash tables into sorted arrays of entries. */
+  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
     {
-      struct crosstab *x = xtab[t];
-      int i, fact, ofs;
+      struct table_entry *e;
+      size_t i;
 
-      fact = i = 1;
-      ofs = x->ofs;
-      for (i = 0; i < x->nvar; i++)
-       {
-         const struct variable *const v = x->vars[i];
-          struct var_range *vr = get_var_range (v);
-         double value = case_num (c, v);
-
-         /* Note that the first test also rules out SYSMIS. */
-         if ((value < vr->min || value >= vr->max)
-             || (cmd.miss == CRS_TABLE
-                  && var_is_num_missing (v, value, MV_USER)))
-           {
-             x->missing += weight;
-             goto next_crosstab;
-           }
+      pt->n_entries = hmap_count (&pt->data);
+      pt->entries = xnmalloc (pt->n_entries, sizeof *pt->entries);
+      i = 0;
+      HMAP_FOR_EACH (e, struct table_entry, node, &pt->data)
+        pt->entries[i++] = e;
+      hmap_destroy (&pt->data);
 
-         if (i > 1)
-           {
-             ofs += fact * ((int) value - vr->min);
-             fact *= vr->count;
-           }
-       }
+      sort (pt->entries, pt->n_entries, sizeof *pt->entries,
+            compare_table_entry_3way, pt);
+    }
 
-      {
-        const struct variable *row_var = x->vars[ROW_VAR];
-       const int row = case_num (c, row_var) - get_var_range (row_var)->min;
+  make_summary_table (proc);
 
-        const struct variable *col_var = x->vars[COL_VAR];
-       const int col = case_num (c, col_var) - get_var_range (col_var)->min;
+  /* Output each pivot table. */
+  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+    {
+      if (proc->pivot || pt->n_vars == 2)
+        output_pivot_table (proc, pt);
+      else
+        {
+          size_t row0 = 0, row1 = 0;
+          while (find_crosstab (pt, &row0, &row1))
+            {
+              struct pivot_table subset;
+              make_pivot_table_subset (pt, row0, row1, &subset);
+              output_pivot_table (proc, &subset);
+            }
+        }
+    }
 
-       const int col_dim = get_var_range (col_var)->count;
+  /* Free output and prepare for next split file. */
+  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+    {
+      size_t i;
 
-       sorted_tab[ofs]->u.data[col + row * col_dim] += weight;
-      }
+      pt->missing = 0.0;
+
+      /* Free only the members that were allocated in this
+         function.  The other pointer members are either both
+         allocated and destroyed at a lower level (in
+         output_pivot_table), or both allocated and destroyed at
+         a higher level (in crs_custom_tables and free_proc,
+         respectively). */
+      for (i = 0; i < pt->n_entries; i++)
+        free (pt->entries[i]);
+      free (pt->entries);
+    }
+}
 
-    next_crosstab: ;
+static void
+make_pivot_table_subset (struct pivot_table *pt, size_t row0, size_t row1,
+                         struct pivot_table *subset)
+{
+  *subset = *pt;
+  if (pt->n_vars > 2)
+    {
+      assert (pt->n_consts == 0);
+      subset->missing = pt->missing;
+      subset->n_vars = 2;
+      subset->vars = pt->vars;
+      subset->n_consts = pt->n_vars - 2;
+      subset->const_vars = pt->vars + 2;
+      subset->const_values = &pt->entries[row0]->values[2];
     }
+  subset->entries = &pt->entries[row0];
+  subset->n_entries = row1 - row0;
 }
 
-/* Compare the table_entry's at A and B and return a strcmp()-type
-   result. */
 static int
-compare_table_entry (const void *a_, const void *b_, const void *aux UNUSED)
+compare_table_entry_var_3way (const struct table_entry *a,
+                              const struct table_entry *b,
+                              const struct pivot_table *pt,
+                              int idx)
 {
-  const struct table_entry *a = a_;
-  const struct table_entry *b = b_;
-
-  if (a->table > b->table)
-    return 1;
-  else if (a->table < b->table)
-    return -1;
-
-  {
-    const struct crosstab *x = xtab[a->table];
-    int i;
-
-    for (i = x->nvar - 1; i >= 0; i--)
-      if (var_is_numeric (x->vars[i]))
-       {
-         const double diffnum = a->values[i].f - b->values[i].f;
-         if (diffnum < 0)
-           return -1;
-         else if (diffnum > 0)
-           return 1;
-       }
-      else
-        {
-          const int diffstr = strncmp (a->values[i].s, b->values[i].s,
-                                       var_get_width (x->vars[i]));
-          if (diffstr)
-            return diffstr;
-        }
-  }
-
-  return 0;
+  return value_compare_3way (&a->values[idx], &b->values[idx],
+                             var_get_width (pt->vars[idx]));
 }
 
-/* Calculate a hash value from table_entry A. */
-static unsigned
-hash_table_entry (const void *a_, const void *aux UNUSED)
+static int
+compare_table_entry_vars_3way (const struct table_entry *a,
+                               const struct table_entry *b,
+                               const struct pivot_table *pt,
+                               int idx0, int idx1)
 {
-  const struct table_entry *a = a_;
-  unsigned long hash;
   int i;
 
-  hash = a->table;
-  for (i = 0; i < xtab[a->table]->nvar; i++)
-    hash ^= hsh_hash_bytes (&a->values[i], sizeof a->values[i]);
-
-  return hash;
+  for (i = idx1 - 1; i >= idx0; i--)
+    {
+      int cmp = compare_table_entry_var_3way (a, b, pt, i);
+      if (cmp != 0)
+        return cmp;
+    }
+  return 0;
 }
-\f
-/* Post-data reading calculations. */
 
-static struct table_entry **find_pivot_extent (struct table_entry **,
-                                               int *cnt, int pivot);
-static void enum_var_values (struct table_entry **entries, int entry_cnt,
-                             int var_idx,
-                             union value **values, int *value_cnt);
-static void output_pivot_table (struct table_entry **, struct table_entry **,
-                               const struct dictionary *,
-                               double **, double **, double **,
-                               int *, int *, int *);
-static void make_summary_table (const struct dictionary *);
+/* Compare the struct table_entry at *AP to the one at *BP and
+   return a strcmp()-type result. */
+static int
+compare_table_entry_3way (const void *ap_, const void *bp_, const void *pt_)
+{
+  const struct table_entry *const *ap = ap_;
+  const struct table_entry *const *bp = bp_;
+  const struct table_entry *a = *ap;
+  const struct table_entry *b = *bp;
+  const struct pivot_table *pt = pt_;
+  int cmp;
+
+  cmp = compare_table_entry_vars_3way (a, b, pt, 2, pt->n_vars);
+  if (cmp != 0)
+    return cmp;
+
+  cmp = compare_table_entry_var_3way (a, b, pt, ROW_VAR);
+  if (cmp != 0)
+    return cmp;
+
+  return compare_table_entry_var_3way (a, b, pt, COL_VAR);
+}
 
-static void
-postcalc (const struct dataset *ds)
+static int
+find_first_difference (const struct pivot_table *pt, size_t row)
 {
-  if (mode == GENERAL)
+  if (row == 0)
+    return pt->n_vars - 1;
+  else
     {
-      n_sorted_tab = hsh_count (gen_tab);
-      sorted_tab = (struct table_entry **) hsh_sort (gen_tab);
-    }
-
-  make_summary_table (dataset_dict (ds));
-
-  /* Identify all the individual crosstabulation tables, and deal with
-     them. */
-  {
-    struct table_entry **pb = sorted_tab, **pe;        /* Pivot begin, pivot end. */
-    int pc = n_sorted_tab;                     /* Pivot count. */
-
-    double *mat = NULL, *row_tot = NULL, *col_tot = NULL;
-    int maxrows = 0, maxcols = 0, maxcells = 0;
-
-    for (;;)
-      {
-       pe = find_pivot_extent (pb, &pc, cmd.pivot == CRS_PIVOT);
-       if (pe == NULL)
-         break;
+      const struct table_entry *a = pt->entries[row];
+      const struct table_entry *b = pt->entries[row - 1];
+      int col;
 
-       output_pivot_table (pb, pe, dataset_dict (ds),
-                           &mat, &row_tot, &col_tot,
-                           &maxrows, &maxcols, &maxcells);
-
-       pb = pe;
-      }
-    free (mat);
-    free (row_tot);
-    free (col_tot);
-  }
-
-  hsh_destroy (gen_tab);
-  if (mode == INTEGER)
-    {
-      int i;
-      for (i = 0; i < n_sorted_tab; i++)
-        {
-          free (sorted_tab[i]->u.data);
-          free (sorted_tab[i]);
-        }
-      free (sorted_tab);
+      for (col = pt->n_vars - 1; col >= 0; col--)
+        if (compare_table_entry_var_3way (a, b, pt, col))
+          return col;
+      NOT_REACHED ();
     }
 }
 
-static void insert_summary (struct tab_table *, int tab_index,
-                           const struct dictionary *,
-                           double valid);
-
 /* Output a table summarizing the cases processed. */
 static void
-make_summary_table (const struct dictionary *dict)
+make_summary_table (struct crosstabs_proc *proc)
 {
   struct tab_table *summary;
+  struct pivot_table *pt;
+  struct string name;
+  int i;
 
-  struct table_entry **pb = sorted_tab, **pe;
-  int pc = n_sorted_tab;
-  int cur_tab = 0;
-
-  summary = tab_create (7, 3 + nxtab, 1);
+  summary = tab_create (7, 3 + proc->n_pivots, 1);
   tab_title (summary, _("Summary."));
   tab_headers (summary, 1, 0, 3, 0);
   tab_joint_text (summary, 1, 0, 6, 0, TAB_CENTER, _("Cases"));
@@ -868,638 +847,506 @@ make_summary_table (const struct dictionary *dict)
   tab_hline (summary, TAL_1, 1, 6, 2);
   tab_vline (summary, TAL_1, 3, 1, 1);
   tab_vline (summary, TAL_1, 5, 1, 1);
-  {
-    int i;
-
-    for (i = 0; i < 3; i++)
-      {
-       tab_text (summary, 1 + i * 2, 2, TAB_RIGHT, _("N"));
-       tab_text (summary, 2 + i * 2, 2, TAB_RIGHT, _("Percent"));
-      }
-  }
+  for (i = 0; i < 3; i++)
+    {
+      tab_text (summary, 1 + i * 2, 2, TAB_RIGHT, _("N"));
+      tab_text (summary, 2 + i * 2, 2, TAB_RIGHT, _("Percent"));
+    }
   tab_offset (summary, 0, 3);
 
-  for (;;)
+  ds_init_empty (&name);
+  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
     {
       double valid;
+      double n[3];
+      size_t i;
 
-      pe = find_pivot_extent (pb, &pc, cmd.pivot == CRS_PIVOT);
-      if (pe == NULL)
-       break;
-
-      while (cur_tab < (*pb)->table)
-       insert_summary (summary, cur_tab++, dict, 0.);
+      tab_hline (summary, TAL_1, 0, 6, 0);
 
-      if (mode == GENERAL)
-       for (valid = 0.; pb < pe; pb++)
-         valid += (*pb)->u.freq;
-      else
-       {
-         const struct crosstab *const x = xtab[(*pb)->table];
-         const int n_cols = get_var_range (x->vars[COL_VAR])->count;
-         const int n_rows = get_var_range (x->vars[ROW_VAR])->count;
-         const int count = n_cols * n_rows;
+      ds_clear (&name);
+      for (i = 0; i < pt->n_vars; i++)
+        {
+          if (i > 0)
+            ds_put_cstr (&name, " * ");
+          ds_put_cstr (&name, var_to_string (pt->vars[i]));
+        }
+      tab_text (summary, 0, 0, TAB_LEFT, ds_cstr (&name));
 
-         for (valid = 0.; pb < pe; pb++)
-           {
-             const double *data = (*pb)->u.data;
-             int i;
+      valid = 0.;
+      for (i = 0; i < pt->n_entries; i++)
+        valid += pt->entries[i]->freq;
 
-             for (i = 0; i < count; i++)
-               valid += *data++;
-           }
-       }
-      insert_summary (summary, cur_tab++, dict, valid);
+      n[0] = valid;
+      n[1] = pt->missing;
+      n[2] = n[0] + n[1];
+      for (i = 0; i < 3; i++)
+        {
+          tab_double (summary, i * 2 + 1, 0, TAB_RIGHT, n[i],
+                      &proc->weight_format);
+          tab_text_format (summary, i * 2 + 2, 0, TAB_RIGHT, "%.1f%%",
+                           n[i] / n[2] * 100.);
+        }
 
-      pb = pe;
+      tab_next_row (summary);
     }
+  ds_destroy (&name);
 
-  while (cur_tab < nxtab)
-    insert_summary (summary, cur_tab++, dict, 0.);
-
-  submit (summary);
-}
-
-/* Inserts a line into T describing the crosstabulation at index
-   TAB_INDEX, which has VALID valid observations. */
-static void
-insert_summary (struct tab_table *t, int tab_index,
-               const struct dictionary *dict,
-               double valid)
-{
-  struct crosstab *x = xtab[tab_index];
-
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
-
-  tab_hline (t, TAL_1, 0, 6, 0);
-
-  /* Crosstabulation name. */
-  {
-    char *buf = xmalloca (128 * x->nvar);
-    char *cp = buf;
-    int i;
-
-    for (i = 0; i < x->nvar; i++)
-      {
-       if (i > 0)
-         cp = stpcpy (cp, " * ");
-
-       cp = stpcpy (cp, var_to_string (x->vars[i]));
-      }
-    tab_text (t, 0, 0, TAB_LEFT, buf);
-
-    freea (buf);
-  }
-
-  /* Counts and percentages. */
-  {
-    double n[3];
-    int i;
-
-    n[0] = valid;
-    n[1] = x->missing;
-    n[2] = n[0] + n[1];
-
-
-    for (i = 0; i < 3; i++)
-      {
-       tab_double (t, i * 2 + 1, 0, TAB_RIGHT, n[i], wfmt);
-       tab_text (t, i * 2 + 2, 0, TAB_RIGHT | TAT_PRINTF, "%.1f%%",
-                 n[i] / n[2] * 100.);
-      }
-  }
-
-  tab_next_row (t);
+  submit (proc, NULL, summary);
 }
 \f
 /* Output. */
 
-/* Tables. */
-static struct tab_table *table;        /* Crosstabulation table. */
-static struct tab_table *chisq;        /* Chi-square table. */
-static struct tab_table *sym;          /* Symmetric measures table. */
-static struct tab_table *risk;         /* Risk estimate table. */
-static struct tab_table *direct;       /* Directional measures table. */
-
-/* Statistics. */
-static int chisq_fisher;       /* Did any rows include Fisher's exact test? */
-
-/* Column values, number of columns. */
-static union value *cols;
-static int n_cols;
-
-/* Row values, number of rows. */
-static union value *rows;
-static int n_rows;
-
-/* Number of statistically interesting columns/rows (columns/rows with
-   data in them). */
-static int ns_cols, ns_rows;
-
-/* Crosstabulation. */
-static const struct crosstab *x;
-
-/* Number of variables from the crosstabulation to consider.  This is
-   either x->nvar, if pivoting is on, or 2, if pivoting is off. */
-static int nvar;
-
-/* Matrix contents. */
-static double *mat;            /* Matrix proper. */
-static double *row_tot;                /* Row totals. */
-static double *col_tot;                /* Column totals. */
-static double W;               /* Grand total. */
-
-static void display_dimensions (struct tab_table *, int first_difference,
-                               struct table_entry *);
-static void display_crosstabulation (void);
-static void display_chisq (const struct dictionary *);
-static void display_symmetric (const struct dictionary *);
-static void display_risk (const struct dictionary *);
-static void display_directional (void);
-static void crosstabs_dim (struct tab_table *, struct outp_driver *);
-static void table_value_missing (struct tab_table *table, int c, int r,
+static struct tab_table *create_crosstab_table (struct crosstabs_proc *,
+                                                struct pivot_table *);
+static struct tab_table *create_chisq_table (struct pivot_table *);
+static struct tab_table *create_sym_table (struct pivot_table *);
+static struct tab_table *create_risk_table (struct pivot_table *);
+static struct tab_table *create_direct_table (struct pivot_table *);
+static void display_dimensions (struct crosstabs_proc *, struct pivot_table *,
+                                struct tab_table *, int first_difference);
+static void display_crosstabulation (struct crosstabs_proc *,
+                                     struct pivot_table *,
+                                     struct tab_table *);
+static void display_chisq (struct pivot_table *, struct tab_table *,
+                           bool *showed_fisher);
+static void display_symmetric (struct crosstabs_proc *, struct pivot_table *,
+                               struct tab_table *);
+static void display_risk (struct pivot_table *, struct tab_table *);
+static void display_directional (struct crosstabs_proc *, struct pivot_table *,
+                                 struct tab_table *);
+static void crosstabs_dim (struct tab_table *, struct outp_driver *,
+                           void *proc);
+static void table_value_missing (struct crosstabs_proc *proc,
+                                 struct tab_table *table, int c, int r,
                                 unsigned char opt, const union value *v,
                                 const struct variable *var);
-static void delete_missing (void);
+static void delete_missing (struct pivot_table *);
+static void build_matrix (struct pivot_table *);
 
 /* Output pivot table beginning at PB and continuing until PE,
    exclusive.  For efficiency, *MATP is a pointer to a matrix that can
    hold *MAXROWS entries. */
 static void
-output_pivot_table (struct table_entry **pb, struct table_entry **pe,
-                   const struct dictionary *dict,
-                   double **matp, double **row_totp, double **col_totp,
-                   int *maxrows, int *maxcols, int *maxcells)
+output_pivot_table (struct crosstabs_proc *proc, struct pivot_table *pt)
 {
-  /* Subtable. */
-  struct table_entry **tb = pb, **te;  /* Table begin, table end. */
-  int tc = pe - pb;            /* Table count. */
-
-  /* Table entry for header comparison. */
-  struct table_entry *cmp = NULL;
-
-  x = xtab[(*pb)->table];
-  enum_var_values (pb, pe - pb, COL_VAR, &cols, &n_cols);
-
-  nvar = cmd.pivot == CRS_PIVOT ? x->nvar : 2;
-
-  /* Crosstabulation table initialization. */
-  if (num_cells)
-    {
-      table = tab_create (nvar + n_cols,
-                         (pe - pb) / n_cols * 3 / 2 * num_cells + 10, 1);
-      tab_headers (table, nvar - 1, 0, 2, 0);
-
-      /* First header line. */
-      tab_joint_text (table, nvar - 1, 0, (nvar - 1) + (n_cols - 1), 0,
-                     TAB_CENTER | TAT_TITLE, var_get_name (x->vars[COL_VAR]));
-
-      tab_hline (table, TAL_1, nvar - 1, nvar + n_cols - 2, 1);
-
-      /* Second header line. */
-      {
-       int i;
-
-       for (i = 2; i < nvar; i++)
-         tab_joint_text (table, nvar - i - 1, 0, nvar - i - 1, 1,
-                         TAB_RIGHT | TAT_TITLE, var_to_string (x->vars[i]));
-       tab_text (table, nvar - 2, 1, TAB_RIGHT | TAT_TITLE,
-                 var_get_name (x->vars[ROW_VAR]));
-       for (i = 0; i < n_cols; i++)
-         table_value_missing (table, nvar + i - 1, 1, TAB_RIGHT, &cols[i],
-                              x->vars[COL_VAR]);
-       tab_text (table, nvar + n_cols - 1, 1, TAB_CENTER, _("Total"));
-      }
-
-      tab_hline (table, TAL_1, 0, nvar + n_cols - 1, 2);
-      tab_vline (table, TAL_1, nvar + n_cols - 1, 0, 1);
+  struct tab_table *table = NULL; /* Crosstabulation table. */
+  struct tab_table *chisq = NULL; /* Chi-square table. */
+  bool showed_fisher = false;
+  struct tab_table *sym = NULL;   /* Symmetric measures table. */
+  struct tab_table *risk = NULL;  /* Risk estimate table. */
+  struct tab_table *direct = NULL; /* Directional measures table. */
+  size_t row0, row1;
+
+  enum_var_values (pt, COL_VAR, &pt->cols, &pt->n_cols);
+
+  if (proc->cells)
+    table = create_crosstab_table (proc, pt);
+  if (proc->statistics & (1u << CRS_ST_CHISQ))
+    chisq = create_chisq_table (pt);
+  if (proc->statistics & ((1u << CRS_ST_PHI) | (1u << CRS_ST_CC)
+                          | (1u << CRS_ST_BTAU) | (1u << CRS_ST_CTAU)
+                          | (1u << CRS_ST_GAMMA) | (1u << CRS_ST_CORR)
+                          | (1u << CRS_ST_KAPPA)))
+    sym = create_sym_table (pt);
+  if (proc->statistics & (1u << CRS_ST_RISK))
+    risk = create_risk_table (pt);
+  if (proc->statistics & ((1u << CRS_ST_LAMBDA) | (1u << CRS_ST_UC)
+                          | (1u << CRS_ST_D) | (1u << CRS_ST_ETA)))
+    direct = create_direct_table (pt);
+
+  row0 = row1 = 0;
+  while (find_crosstab (pt, &row0, &row1))
+    {
+      struct pivot_table x;
+      int first_difference;
+
+      make_pivot_table_subset (pt, row0, row1, &x);
 
-      /* Title. */
-      {
-       char *title = xmalloca (x->nvar * 64 + 128);
-       char *cp = title;
-       int i;
-
-       if (cmd.pivot == CRS_PIVOT)
-         for (i = 0; i < nvar; i++)
-           {
-             if (i)
-               cp = stpcpy (cp, " by ");
-             cp = stpcpy (cp, var_get_name (x->vars[i]));
-           }
-       else
-         {
-           cp = spprintf (cp, "%s by %s for",
-                           var_get_name (x->vars[0]),
-                           var_get_name (x->vars[1]));
-           for (i = 2; i < nvar; i++)
-             {
-               char buf[64], *bufp;
-
-               if (i > 2)
-                 *cp++ = ',';
-               *cp++ = ' ';
-               cp = stpcpy (cp, var_get_name (x->vars[i]));
-               *cp++ = '=';
-               format_short (buf, var_get_print_format (x->vars[i]),
-                              &(*pb)->values[i]);
-               for (bufp = buf; isspace ((unsigned char) *bufp); bufp++)
-                 ;
-               cp = stpcpy (cp, bufp);
-             }
-         }
-
-       cp = stpcpy (cp, " [");
-       for (i = 0; i < num_cells; i++)
-         {
-           struct tuple
-             {
-               int value;
-               const char *name;
-             };
-
-           static const struct tuple cell_names[] =
-             {
-               {CRS_CL_COUNT, N_("count")},
-               {CRS_CL_ROW, N_("row %")},
-               {CRS_CL_COLUMN, N_("column %")},
-               {CRS_CL_TOTAL, N_("total %")},
-               {CRS_CL_EXPECTED, N_("expected")},
-               {CRS_CL_RESIDUAL, N_("residual")},
-               {CRS_CL_SRESIDUAL, N_("std. resid.")},
-               {CRS_CL_ASRESIDUAL, N_("adj. resid.")},
-               {-1, NULL},
-             };
-
-           const struct tuple *t;
-
-           for (t = cell_names; t->value != cells[i]; t++)
-             assert (t->value != -1);
-           if (i)
-             cp = stpcpy (cp, ", ");
-           cp = stpcpy (cp, gettext (t->name));
-         }
-       strcpy (cp, "].");
+      /* Find all the row variable values. */
+      enum_var_values (&x, ROW_VAR, &x.rows, &x.n_rows);
 
-       tab_title (table, "%s", title);
-       freea (title);
-      }
+      if (size_overflow_p (xtimes (xtimes (x.n_rows, x.n_cols),
+                                   sizeof (double))))
+        xalloc_die ();
+      x.row_tot = xmalloc (x.n_rows * sizeof *x.row_tot);
+      x.col_tot = xmalloc (x.n_cols * sizeof *x.col_tot);
+      x.mat = xmalloc (x.n_rows * x.n_cols * sizeof *x.mat);
 
-      tab_offset (table, 0, 2);
-    }
-  else
-    table = NULL;
-
-  /* Chi-square table initialization. */
-  if (cmd.a_statistics[CRS_ST_CHISQ])
-    {
-      chisq = tab_create (6 + (nvar - 2),
-                         (pe - pb) / n_cols * 3 / 2 * N_CHISQ + 10, 1);
-      tab_headers (chisq, 1 + (nvar - 2), 0, 1, 0);
-
-      tab_title (chisq, _("Chi-square tests."));
-
-      tab_offset (chisq, nvar - 2, 0);
-      tab_text (chisq, 0, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
-      tab_text (chisq, 1, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
-      tab_text (chisq, 2, 0, TAB_RIGHT | TAT_TITLE, _("df"));
-      tab_text (chisq, 3, 0, TAB_RIGHT | TAT_TITLE,
-               _("Asymp. Sig. (2-sided)"));
-      tab_text (chisq, 4, 0, TAB_RIGHT | TAT_TITLE,
-               _("Exact. Sig. (2-sided)"));
-      tab_text (chisq, 5, 0, TAB_RIGHT | TAT_TITLE,
-               _("Exact. Sig. (1-sided)"));
-      chisq_fisher = 0;
-      tab_offset (chisq, 0, 1);
-    }
-  else
-    chisq = NULL;
-
-  /* Symmetric measures. */
-  if (cmd.a_statistics[CRS_ST_PHI] || cmd.a_statistics[CRS_ST_CC]
-      || cmd.a_statistics[CRS_ST_BTAU] || cmd.a_statistics[CRS_ST_CTAU]
-      || cmd.a_statistics[CRS_ST_GAMMA] || cmd.a_statistics[CRS_ST_CORR]
-      || cmd.a_statistics[CRS_ST_KAPPA])
-    {
-      sym = tab_create (6 + (nvar - 2), (pe - pb) / n_cols * 7 + 10, 1);
-      tab_headers (sym, 2 + (nvar - 2), 0, 1, 0);
-      tab_title (sym, _("Symmetric measures."));
-
-      tab_offset (sym, nvar - 2, 0);
-      tab_text (sym, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
-      tab_text (sym, 1, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
-      tab_text (sym, 2, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
-      tab_text (sym, 3, 0, TAB_RIGHT | TAT_TITLE, _("Asymp. Std. Error"));
-      tab_text (sym, 4, 0, TAB_RIGHT | TAT_TITLE, _("Approx. T"));
-      tab_text (sym, 5, 0, TAB_RIGHT | TAT_TITLE, _("Approx. Sig."));
-      tab_offset (sym, 0, 1);
-    }
-  else
-    sym = NULL;
-
-  /* Risk estimate. */
-  if (cmd.a_statistics[CRS_ST_RISK])
-    {
-      risk = tab_create (4 + (nvar - 2), (pe - pb) / n_cols * 4 + 10, 1);
-      tab_headers (risk, 1 + nvar - 2, 0, 2, 0);
-      tab_title (risk, _("Risk estimate."));
-
-      tab_offset (risk, nvar - 2, 0);
-      tab_joint_text (risk, 2, 0, 3, 0, TAB_CENTER | TAT_TITLE | TAT_PRINTF,
-                     _("95%% Confidence Interval"));
-      tab_text (risk, 0, 1, TAB_LEFT | TAT_TITLE, _("Statistic"));
-      tab_text (risk, 1, 1, TAB_RIGHT | TAT_TITLE, _("Value"));
-      tab_text (risk, 2, 1, TAB_RIGHT | TAT_TITLE, _("Lower"));
-      tab_text (risk, 3, 1, TAB_RIGHT | TAT_TITLE, _("Upper"));
-      tab_hline (risk, TAL_1, 2, 3, 1);
-      tab_vline (risk, TAL_1, 2, 0, 1);
-      tab_offset (risk, 0, 2);
-    }
-  else
-    risk = NULL;
-
-  /* Directional measures. */
-  if (cmd.a_statistics[CRS_ST_LAMBDA] || cmd.a_statistics[CRS_ST_UC]
-      || cmd.a_statistics[CRS_ST_D] || cmd.a_statistics[CRS_ST_ETA])
-    {
-      direct = tab_create (7 + (nvar - 2), (pe - pb) / n_cols * 7 + 10, 1);
-      tab_headers (direct, 3 + (nvar - 2), 0, 1, 0);
-      tab_title (direct, _("Directional measures."));
-
-      tab_offset (direct, nvar - 2, 0);
-      tab_text (direct, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
-      tab_text (direct, 1, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
-      tab_text (direct, 2, 0, TAB_LEFT | TAT_TITLE, _("Type"));
-      tab_text (direct, 3, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
-      tab_text (direct, 4, 0, TAB_RIGHT | TAT_TITLE, _("Asymp. Std. Error"));
-      tab_text (direct, 5, 0, TAB_RIGHT | TAT_TITLE, _("Approx. T"));
-      tab_text (direct, 6, 0, TAB_RIGHT | TAT_TITLE, _("Approx. Sig."));
-      tab_offset (direct, 0, 1);
-    }
-  else
-    direct = NULL;
+      /* Allocate table space for the matrix. */
+      if (table
+          && tab_row (table) + (x.n_rows + 1) * proc->n_cells > tab_nr (table))
+       tab_realloc (table, -1,
+                    MAX (tab_nr (table) + (x.n_rows + 1) * proc->n_cells,
+                         tab_nr (table) * pt->n_entries / x.n_entries));
 
-  for (;;)
-    {
-      /* Find pivot subtable if applicable. */
-      te = find_pivot_extent (tb, &tc, 0);
-      if (te == NULL)
-       break;
+      build_matrix (&x);
 
-      /* Find all the row variable values. */
-      enum_var_values (tb, te - tb, ROW_VAR, &rows, &n_rows);
+      /* Find the first variable that differs from the last subtable. */
+      first_difference = find_first_difference (pt, row0);
+      if (table)
+        {
+          display_dimensions (proc, &x, table, first_difference);
+          display_crosstabulation (proc, &x, table);
+        }
 
-      /* Allocate memory space for the column and row totals. */
-      if (n_rows > *maxrows)
-       {
-         *row_totp = xnrealloc (*row_totp, n_rows, sizeof **row_totp);
-         row_tot = *row_totp;
-         *maxrows = n_rows;
-       }
-      if (n_cols > *maxcols)
-       {
-         *col_totp = xnrealloc (*col_totp, n_cols, sizeof **col_totp);
-         col_tot = *col_totp;
-         *maxcols = n_cols;
-       }
+      if (proc->exclude == MV_NEVER)
+       delete_missing (&x);
 
-      /* Allocate table space for the matrix. */
-      if (table && tab_row (table) + (n_rows + 1) * num_cells > tab_nr (table))
-       tab_realloc (table, -1,
-                    MAX (tab_nr (table) + (n_rows + 1) * num_cells,
-                         tab_nr (table) * (pe - pb) / (te - tb)));
+      if (chisq)
+        {
+          display_dimensions (proc, &x, chisq, first_difference);
+          display_chisq (&x, chisq, &showed_fisher);
+        }
+      if (sym)
+        {
+          display_dimensions (proc, &x, sym, first_difference);
+          display_symmetric (proc, &x, sym);
+        }
+      if (risk)
+        {
+          display_dimensions (proc, &x, risk, first_difference);
+          display_risk (&x, risk);
+        }
+      if (direct)
+        {
+          display_dimensions (proc, &x, direct, first_difference);
+          display_directional (proc, &x, direct);
+        }
 
-      if (mode == GENERAL)
-       {
-         /* Allocate memory space for the matrix. */
-         if (n_cols * n_rows > *maxcells)
-           {
-             *matp = xnrealloc (*matp, n_cols * n_rows, sizeof **matp);
-             *maxcells = n_cols * n_rows;
-           }
+      /* Free the parts of x that are not owned by pt.  In
+         particular we must not free x.cols, which is the same as
+         pt->cols, which is freed at the end of this function. */
+      free (x.rows);
 
-         mat = *matp;
+      free (x.mat);
+      free (x.row_tot);
+      free (x.col_tot);
+    }
 
-         /* Build the matrix and calculate column totals. */
-         {
-           union value *cur_col = cols;
-           union value *cur_row = rows;
-           double *mp = mat;
-           double *cp = col_tot;
-           struct table_entry **p;
-
-           *cp = 0.;
-           for (p = &tb[0]; p < te; p++)
-             {
-               for (; memcmp (cur_col, &(*p)->values[COL_VAR], sizeof *cur_col);
-                    cur_row = rows)
-                 {
-                   *++cp = 0.;
-                   for (; cur_row < &rows[n_rows]; cur_row++)
-                     {
-                       *mp = 0.;
-                       mp += n_cols;
-                     }
-                   cur_col++;
-                   mp = &mat[cur_col - cols];
-                 }
+  submit (proc, NULL, table);
 
-               for (; memcmp (cur_row, &(*p)->values[ROW_VAR], sizeof *cur_row);
-                    cur_row++)
-                 {
-                   *mp = 0.;
-                   mp += n_cols;
-                 }
+  if (chisq)
+    {
+      if (!showed_fisher)
+       tab_resize (chisq, 4 + (pt->n_vars - 2), -1);
+      submit (proc, pt, chisq);
+    }
 
-               *cp += *mp = (*p)->u.freq;
-               mp += n_cols;
-               cur_row++;
-             }
+  submit (proc, pt, sym);
+  submit (proc, pt, risk);
+  submit (proc, pt, direct);
 
-           /* Zero out the rest of the matrix. */
-           for (; cur_row < &rows[n_rows]; cur_row++)
-             {
-               *mp = 0.;
-               mp += n_cols;
-             }
-           cur_col++;
-           if (cur_col < &cols[n_cols])
-             {
-               const int rem_cols = n_cols - (cur_col - cols);
-               int c, r;
+  free (pt->cols);
+}
 
-               for (c = 0; c < rem_cols; c++)
-                 *++cp = 0.;
-               mp = &mat[cur_col - cols];
-               for (r = 0; r < n_rows; r++)
-                 {
-                   for (c = 0; c < rem_cols; c++)
-                     *mp++ = 0.;
-                   mp += n_cols - rem_cols;
-                 }
-             }
-         }
-       }
-      else
-       {
-         int r, c;
-         double *tp = col_tot;
+static void
+build_matrix (struct pivot_table *x)
+{
+  const int col_var_width = var_get_width (x->vars[COL_VAR]);
+  const int row_var_width = var_get_width (x->vars[ROW_VAR]);
+  int col, row;
+  double *mp;
+  struct table_entry **p;
 
-         assert (mode == INTEGER);
-         mat = (*tb)->u.data;
-         ns_cols = n_cols;
+  mp = x->mat;
+  col = row = 0;
+  for (p = x->entries; p < &x->entries[x->n_entries]; p++)
+    {
+      const struct table_entry *te = *p;
 
-         /* Calculate column totals. */
-         for (c = 0; c < n_cols; c++)
-           {
-             double cum = 0.;
-             double *cp = &mat[c];
+      while (!value_equal (&x->rows[row], &te->values[ROW_VAR], row_var_width))
+        {
+          for (; col < x->n_cols; col++)
+            *mp++ = 0.0;
+          col = 0;
+          row++;
+        }
 
-             for (r = 0; r < n_rows; r++)
-               cum += cp[r * n_cols];
-             *tp++ = cum;
-           }
-       }
+      while (!value_equal (&x->cols[col], &te->values[COL_VAR], col_var_width))
+        {
+          *mp++ = 0.0;
+          col++;
+        }
 
-      {
-       double *cp;
+      *mp++ = te->freq;
+      if (++col >= x->n_cols)
+        {
+          col = 0;
+          row++;
+        }
+    }
+  while (mp < &x->mat[x->n_cols * x->n_rows])
+    *mp++ = 0.0;
+  assert (mp == &x->mat[x->n_cols * x->n_rows]);
+
+  /* Column totals, row totals, ns_rows. */
+  mp = x->mat;
+  for (col = 0; col < x->n_cols; col++)
+    x->col_tot[col] = 0.0;
+  for (row = 0; row < x->n_rows; row++)
+    x->row_tot[row] = 0.0;
+  x->ns_rows = 0;
+  for (row = 0; row < x->n_rows; row++)
+    {
+      bool row_is_empty = true;
+      for (col = 0; col < x->n_cols; col++)
+        {
+          if (*mp != 0.0)
+            {
+              row_is_empty = false;
+              x->col_tot[col] += *mp;
+              x->row_tot[row] += *mp;
+            }
+          mp++;
+        }
+      if (!row_is_empty)
+        x->ns_rows++;
+    }
+  assert (mp == &x->mat[x->n_cols * x->n_rows]);
 
-       for (ns_cols = 0, cp = col_tot; cp < &col_tot[n_cols]; cp++)
-         ns_cols += *cp != 0.;
-      }
+  /* ns_cols. */
+  x->ns_cols = 0;
+  for (col = 0; col < x->n_cols; col++)
+    for (row = 0; row < x->n_rows; row++)
+      if (x->mat[col + row * x->n_cols] != 0.0)
+        {
+          x->ns_cols++;
+          break;
+        }
 
-      /* Calculate row totals. */
-      {
-       double *mp = mat;
-       double *rp = row_tot;
-       int r, c;
+  /* Grand total. */
+  x->total = 0.0;
+  for (col = 0; col < x->n_cols; col++)
+    x->total += x->col_tot[col];
+}
 
-       for (ns_rows = 0, r = n_rows; r--; )
-         {
-           double cum = 0.;
-           for (c = n_cols; c--; )
-             cum += *mp++;
-           *rp++ = cum;
-           if (cum != 0.)
-             ns_rows++;
-         }
-      }
+static struct tab_table *
+create_crosstab_table (struct crosstabs_proc *proc, struct pivot_table *pt)
+{
+  struct tuple
+    {
+      int value;
+      const char *name;
+    };
+  static const struct tuple names[] =
+    {
+      {CRS_CL_COUNT, N_("count")},
+      {CRS_CL_ROW, N_("row %")},
+      {CRS_CL_COLUMN, N_("column %")},
+      {CRS_CL_TOTAL, N_("total %")},
+      {CRS_CL_EXPECTED, N_("expected")},
+      {CRS_CL_RESIDUAL, N_("residual")},
+      {CRS_CL_SRESIDUAL, N_("std. resid.")},
+      {CRS_CL_ASRESIDUAL, N_("adj. resid.")},
+    };
+  const int n_names = sizeof names / sizeof *names;
+  const struct tuple *t;
 
-      /* Calculate grand total. */
-      {
-       double *tp;
-       double cum = 0.;
-       int n;
+  struct tab_table *table;
+  struct string title;
+  int i;
 
-       if (n_rows < n_cols)
-         tp = row_tot, n = n_rows;
-       else
-         tp = col_tot, n = n_cols;
-       while (n--)
-         cum += *tp++;
-       W = cum;
-      }
+  table = tab_create (pt->n_consts + 1 + pt->n_cols + 1,
+                      (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10,
+                      true);
+  tab_headers (table, pt->n_consts + 1, 0, 2, 0);
+
+  /* First header line. */
+  tab_joint_text (table, pt->n_consts + 1, 0,
+                  (pt->n_consts + 1) + (pt->n_cols - 1), 0,
+                  TAB_CENTER | TAT_TITLE, var_get_name (pt->vars[COL_VAR]));
+
+  tab_hline (table, TAL_1, pt->n_consts + 1,
+             pt->n_consts + 2 + pt->n_cols - 2, 1);
+
+  /* Second header line. */
+  for (i = 2; i < pt->n_consts + 2; i++)
+    tab_joint_text (table, pt->n_consts + 2 - i - 1, 0,
+                    pt->n_consts + 2 - i - 1, 1,
+                    TAB_RIGHT | TAT_TITLE, var_to_string (pt->vars[i]));
+  tab_text (table, pt->n_consts + 2 - 2, 1, TAB_RIGHT | TAT_TITLE,
+            var_get_name (pt->vars[ROW_VAR]));
+  for (i = 0; i < pt->n_cols; i++)
+    table_value_missing (proc, table, pt->n_consts + 2 + i - 1, 1, TAB_RIGHT,
+                         &pt->cols[i], pt->vars[COL_VAR]);
+  tab_text (table, pt->n_consts + 2 + pt->n_cols - 1, 1, TAB_CENTER, _("Total"));
+
+  tab_hline (table, TAL_1, 0, pt->n_consts + 2 + pt->n_cols - 1, 2);
+  tab_vline (table, TAL_1, pt->n_consts + 2 + pt->n_cols - 1, 0, 1);
+
+  /* Title. */
+  ds_init_empty (&title);
+  for (i = 0; i < pt->n_consts + 2; i++)
+    {
+      if (i)
+        ds_put_cstr (&title, " * ");
+      ds_put_cstr (&title, var_get_name (pt->vars[i]));
+    }
+  for (i = 0; i < pt->n_consts; i++)
+    {
+      const struct variable *var = pt->const_vars[i];
+      size_t ofs;
+      char *s = NULL;
+
+      ds_put_format (&title, ", %s=", var_get_name (var));
+
+      /* Insert the formatted value of the variable, then trim
+         leading spaces in what was just inserted. */
+      ofs = ds_length (&title);
+      s = data_out (&pt->const_values[i], dict_get_encoding (proc->dict), var_get_print_format (var));
+      ds_put_cstr (&title, s);
+      free (s);
+      ds_remove (&title, ofs, ss_cspan (ds_substr (&title, ofs, SIZE_MAX),
+                                        ss_cstr (" ")));
+    }
 
-      /* Find the first variable that differs from the last subtable,
-        then display the values of the dimensioning variables for
-        each table that needs it. */
+  ds_put_cstr (&title, " [");
+  i = 0;
+  for (t = names; t < &names[n_names]; t++)
+    if (proc->cells & (1u << t->value))
       {
-       int first_difference = nvar - 1;
-
-       if (tb != pb)
-         for (; ; first_difference--)
-           {
-             assert (first_difference >= 2);
-             if (memcmp (&cmp->values[first_difference],
-                         &(*tb)->values[first_difference],
-                          sizeof *cmp->values))
-               break;
-           }
-       cmp = *tb;
-
-       if (table)
-         display_dimensions (table, first_difference, *tb);
-       if (chisq)
-         display_dimensions (chisq, first_difference, *tb);
-       if (sym)
-         display_dimensions (sym, first_difference, *tb);
-       if (risk)
-         display_dimensions (risk, first_difference, *tb);
-       if (direct)
-         display_dimensions (direct, first_difference, *tb);
+        if (i++)
+          ds_put_cstr (&title, ", ");
+        ds_put_cstr (&title, gettext (t->name));
       }
+  ds_put_cstr (&title, "].");
 
-      if (table)
-       display_crosstabulation ();
-      if (cmd.miss == CRS_REPORT)
-       delete_missing ();
-      if (chisq)
-       display_chisq (dict);
-      if (sym)
-       display_symmetric (dict);
-      if (risk)
-       display_risk (dict);
-      if (direct)
-       display_directional ();
+  tab_title (table, "%s", ds_cstr (&title));
+  ds_destroy (&title);
 
-      tb = te;
-      free (rows);
-    }
+  tab_offset (table, 0, 2);
+  return table;
+}
 
-  submit (table);
+static struct tab_table *
+create_chisq_table (struct pivot_table *pt)
+{
+  struct tab_table *chisq;
+
+  chisq = tab_create (6 + (pt->n_vars - 2),
+                      pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10,
+                      1);
+  tab_headers (chisq, 1 + (pt->n_vars - 2), 0, 1, 0);
+
+  tab_title (chisq, _("Chi-square tests."));
+
+  tab_offset (chisq, pt->n_vars - 2, 0);
+  tab_text (chisq, 0, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
+  tab_text (chisq, 1, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
+  tab_text (chisq, 2, 0, TAB_RIGHT | TAT_TITLE, _("df"));
+  tab_text (chisq, 3, 0, TAB_RIGHT | TAT_TITLE,
+            _("Asymp. Sig. (2-sided)"));
+  tab_text (chisq, 4, 0, TAB_RIGHT | TAT_TITLE,
+            _("Exact Sig. (2-sided)"));
+  tab_text (chisq, 5, 0, TAB_RIGHT | TAT_TITLE,
+            _("Exact Sig. (1-sided)"));
+  tab_offset (chisq, 0, 1);
+
+  return chisq;
+}
 
-  if (chisq)
-    {
-      if (!chisq_fisher)
-       tab_resize (chisq, 4 + (nvar - 2), -1);
-      submit (chisq);
-    }
+/* Symmetric measures. */
+static struct tab_table *
+create_sym_table (struct pivot_table *pt)
+{
+  struct tab_table *sym;
+
+  sym = tab_create (6 + (pt->n_vars - 2),
+                    pt->n_entries / pt->n_cols * 7 + 10, 1);
+  tab_headers (sym, 2 + (pt->n_vars - 2), 0, 1, 0);
+  tab_title (sym, _("Symmetric measures."));
+
+  tab_offset (sym, pt->n_vars - 2, 0);
+  tab_text (sym, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
+  tab_text (sym, 1, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
+  tab_text (sym, 2, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
+  tab_text (sym, 3, 0, TAB_RIGHT | TAT_TITLE, _("Asymp. Std. Error"));
+  tab_text (sym, 4, 0, TAB_RIGHT | TAT_TITLE, _("Approx. T"));
+  tab_text (sym, 5, 0, TAB_RIGHT | TAT_TITLE, _("Approx. Sig."));
+  tab_offset (sym, 0, 1);
+
+  return sym;
+}
 
-  submit (sym);
-  submit (risk);
-  submit (direct);
+/* Risk estimate. */
+static struct tab_table *
+create_risk_table (struct pivot_table *pt)
+{
+  struct tab_table *risk;
+
+  risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10,
+                     1);
+  tab_headers (risk, 1 + pt->n_vars - 2, 0, 2, 0);
+  tab_title (risk, _("Risk estimate."));
+
+  tab_offset (risk, pt->n_vars - 2, 0);
+  tab_joint_text_format (risk, 2, 0, 3, 0, TAB_CENTER | TAT_TITLE,
+                         _("95%% Confidence Interval"));
+  tab_text (risk, 0, 1, TAB_LEFT | TAT_TITLE, _("Statistic"));
+  tab_text (risk, 1, 1, TAB_RIGHT | TAT_TITLE, _("Value"));
+  tab_text (risk, 2, 1, TAB_RIGHT | TAT_TITLE, _("Lower"));
+  tab_text (risk, 3, 1, TAB_RIGHT | TAT_TITLE, _("Upper"));
+  tab_hline (risk, TAL_1, 2, 3, 1);
+  tab_vline (risk, TAL_1, 2, 0, 1);
+  tab_offset (risk, 0, 2);
+
+  return risk;
+}
 
-  free (cols);
+/* Directional measures. */
+static struct tab_table *
+create_direct_table (struct pivot_table *pt)
+{
+  struct tab_table *direct;
+
+  direct = tab_create (7 + (pt->n_vars - 2),
+                       pt->n_entries / pt->n_cols * 7 + 10, 1);
+  tab_headers (direct, 3 + (pt->n_vars - 2), 0, 1, 0);
+  tab_title (direct, _("Directional measures."));
+
+  tab_offset (direct, pt->n_vars - 2, 0);
+  tab_text (direct, 0, 0, TAB_LEFT | TAT_TITLE, _("Category"));
+  tab_text (direct, 1, 0, TAB_LEFT | TAT_TITLE, _("Statistic"));
+  tab_text (direct, 2, 0, TAB_LEFT | TAT_TITLE, _("Type"));
+  tab_text (direct, 3, 0, TAB_RIGHT | TAT_TITLE, _("Value"));
+  tab_text (direct, 4, 0, TAB_RIGHT | TAT_TITLE, _("Asymp. Std. Error"));
+  tab_text (direct, 5, 0, TAB_RIGHT | TAT_TITLE, _("Approx. T"));
+  tab_text (direct, 6, 0, TAB_RIGHT | TAT_TITLE, _("Approx. Sig."));
+  tab_offset (direct, 0, 1);
+
+  return direct;
 }
 
+
 /* Delete missing rows and columns for statistical analysis when
    /MISSING=REPORT. */
 static void
-delete_missing (void)
+delete_missing (struct pivot_table *pt)
 {
-  {
-    int r;
-
-    for (r = 0; r < n_rows; r++)
-      if (var_is_num_missing (x->vars[ROW_VAR], rows[r].f, MV_USER))
-       {
-         int c;
-
-         for (c = 0; c < n_cols; c++)
-           mat[c + r * n_cols] = 0.;
-         ns_rows--;
-       }
-  }
+  int r, c;
 
-  {
-    int c;
+  for (r = 0; r < pt->n_rows; r++)
+    if (var_is_num_missing (pt->vars[ROW_VAR], pt->rows[r].f, MV_USER))
+      {
+        for (c = 0; c < pt->n_cols; c++)
+          pt->mat[c + r * pt->n_cols] = 0.;
+        pt->ns_rows--;
+      }
 
-    for (c = 0; c < n_cols; c++)
-      if (var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER))
-       {
-         int r;
 
-         for (r = 0; r < n_rows; r++)
-           mat[c + r * n_cols] = 0.;
-         ns_cols--;
-       }
-  }
+  for (c = 0; c < pt->n_cols; c++)
+    if (var_is_num_missing (pt->vars[COL_VAR], pt->cols[c].f, MV_USER))
+      {
+        for (r = 0; r < pt->n_rows; r++)
+          pt->mat[c + r * pt->n_cols] = 0.;
+        pt->ns_cols--;
+      }
 }
 
 /* Prepare table T for submission, and submit it. */
 static void
-submit (struct tab_table *t)
+submit (struct crosstabs_proc *proc, struct pivot_table *pt,
+        struct tab_table *t)
 {
   int i;
 
@@ -1513,30 +1360,31 @@ submit (struct tab_table *t)
       return;
     }
   tab_offset (t, 0, 0);
-  if (t != table)
-    for (i = 2; i < nvar; i++)
-      tab_text (t, nvar - i - 1, 0, TAB_RIGHT | TAT_TITLE,
-                var_to_string (x->vars[i]));
+  if (pt != NULL)
+    for (i = 2; i < pt->n_vars; i++)
+      tab_text (t, pt->n_vars - i - 1, 0, TAB_RIGHT | TAT_TITLE,
+                var_to_string (pt->vars[i]));
   tab_box (t, TAL_2, TAL_2, -1, -1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
   tab_box (t, -1, -1, -1, TAL_1, tab_l (t), tab_t (t) - 1, tab_nc (t) - 1,
           tab_nr (t) - 1);
   tab_box (t, -1, -1, -1, TAL_GAP, 0, tab_t (t), tab_l (t) - 1,
           tab_nr (t) - 1);
   tab_vline (t, TAL_2, tab_l (t), 0, tab_nr (t) - 1);
-  tab_dim (t, crosstabs_dim);
+  tab_dim (t, crosstabs_dim, proc);
   tab_submit (t);
 }
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-crosstabs_dim (struct tab_table *t, struct outp_driver *d)
+crosstabs_dim (struct tab_table *t, struct outp_driver *d, void *proc_)
 {
+  struct crosstabs_proc *proc = proc_;
   int i;
 
   /* Width of a numerical column. */
   int c = outp_string_width (d, "0.000000", OUTP_PROPORTIONAL);
-  if (cmd.miss == CRS_REPORT)
+  if (proc->exclude == MV_NEVER)
     c += outp_string_width (d, "M", OUTP_PROPORTIONAL);
 
   /* Set width for header columns. */
@@ -1567,142 +1415,93 @@ crosstabs_dim (struct tab_table *t, struct outp_driver *d)
     t->h[i] = tab_natural_height (t, d, i);
 }
 
-static struct table_entry **find_pivot_extent_general (struct table_entry **tp,
-                                               int *cnt, int pivot);
-static struct table_entry **find_pivot_extent_integer (struct table_entry **tp,
-                                               int *cnt, int pivot);
-
-/* Calls find_pivot_extent_general or find_pivot_extent_integer, as
-   appropriate. */
-static struct table_entry **
-find_pivot_extent (struct table_entry **tp, int *cnt, int pivot)
-{
-  return (mode == GENERAL
-         ? find_pivot_extent_general (tp, cnt, pivot)
-         : find_pivot_extent_integer (tp, cnt, pivot));
-}
-
-/* Find the extent of a region in TP that contains one table.  If
-   PIVOT != 0 that means a set of table entries with identical table
-   number; otherwise they also have to have the same values for every
-   dimension after the row and column dimensions.  The table that is
-   searched starts at TP and has length CNT.  Returns the first entry
-   after the last one in the table; sets *CNT to the number of
-   remaining values.  If there are no entries in TP at all, returns
-   NULL.  A yucky interface, admittedly, but it works. */
-static struct table_entry **
-find_pivot_extent_general (struct table_entry **tp, int *cnt, int pivot)
+static bool
+find_crosstab (struct pivot_table *pt, size_t *row0p, size_t *row1p)
 {
-  struct table_entry *fp = *tp;
-  struct crosstab *x;
-
-  if (*cnt == 0)
-    return NULL;
-  x = xtab[(*tp)->table];
-  for (;;)
-    {
-      tp++;
-      if (--*cnt == 0)
-       break;
-      assert (*cnt > 0);
-
-      if ((*tp)->table != fp->table)
-       break;
-      if (pivot)
-       continue;
-
-      if (memcmp (&(*tp)->values[2], &fp->values[2], sizeof (union value) * (x->nvar - 2)))
-       break;
-    }
-
-  return tp;
-}
-
-/* Integer mode correspondent to find_pivot_extent_general().  This
-   could be optimized somewhat, but I just don't give a crap about
-   CROSSTABS performance in integer mode, which is just a
-   CROSSTABS wart as far as I'm concerned.
+  size_t row0 = *row1p;
+  size_t row1;
 
-   That said, feel free to send optimization patches to me. */
-static struct table_entry **
-find_pivot_extent_integer (struct table_entry **tp, int *cnt, int pivot)
-{
-  struct table_entry *fp = *tp;
-  struct crosstab *x;
+  if (row0 >= pt->n_entries)
+    return false;
 
-  if (*cnt == 0)
-    return NULL;
-  x = xtab[(*tp)->table];
-  for (;;)
+  for (row1 = row0 + 1; row1 < pt->n_entries; row1++)
     {
-      tp++;
-      if (--*cnt == 0)
-       break;
-      assert (*cnt > 0);
-
-      if ((*tp)->table != fp->table)
-       break;
-      if (pivot)
-       continue;
-
-      if (memcmp (&(*tp)->values[2], &fp->values[2],
-                  sizeof (union value) * (x->nvar - 2)))
-       break;
+      struct table_entry *a = pt->entries[row0];
+      struct table_entry *b = pt->entries[row1];
+      if (compare_table_entry_vars_3way (a, b, pt, 2, pt->n_vars) != 0)
+        break;
     }
-
-  return tp;
+  *row0p = row0;
+  *row1p = row1;
+  return true;
 }
 
 /* Compares `union value's A_ and B_ and returns a strcmp()-like
    result.  WIDTH_ points to an int which is either 0 for a
    numeric value or a string width for a string value. */
 static int
-compare_value (const void *a_, const void *b_, const void *width_)
+compare_value_3way (const void *a_, const void *b_, const void *width_)
 {
   const union value *a = a_;
   const union value *b = b_;
-  const int *pwidth = width_;
-  const int width = *pwidth;
+  const int *width = width_;
 
-  if (width == 0)
-    return (a->f < b->f) ? -1 : (a->f > b->f);
-  else
-    return strncmp (a->s, b->s, width);
+  return value_compare_3way (a, b, *width);
 }
 
 /* Given an array of ENTRY_CNT table_entry structures starting at
    ENTRIES, creates a sorted list of the values that the variable
    with index VAR_IDX takes on.  The values are returned as a
-   malloc()'darray stored in *VALUES, with the number of values
+   malloc()'d array stored in *VALUES, with the number of values
    stored in *VALUE_CNT.
    */
 static void
-enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
-                 union value **values, int *value_cnt)
+enum_var_values (const struct pivot_table *pt, int var_idx,
+                 union value **valuesp, int *n_values)
 {
-  const struct variable *v = xtab[(*entries)->table]->vars[var_idx];
+  const struct variable *var = pt->vars[var_idx];
+  struct var_range *range = get_var_range (var);
+  union value *values;
+  size_t i;
 
-  if (mode == GENERAL)
+  if (range)
     {
-      int width = MIN (var_get_width (v), MAX_SHORT_STRING);
-      int i;
-
-      *values = xnmalloc (entry_cnt, sizeof **values);
-      for (i = 0; i < entry_cnt; i++)
-        (*values)[i] = entries[i]->values[var_idx];
-      *value_cnt = sort_unique (*values, entry_cnt, sizeof **values,
-                                compare_value, &width);
+      values = *valuesp = xnmalloc (range->count, sizeof *values);
+      *n_values = range->count;
+      for (i = 0; i < range->count; i++)
+        values[i].f = range->min + i;
     }
   else
     {
-      struct var_range *vr = get_var_range (v);
-      int i;
+      int width = var_get_width (var);
+      struct hmapx_node *node;
+      const union value *iter;
+      struct hmapx set;
+
+      hmapx_init (&set);
+      for (i = 0; i < pt->n_entries; i++)
+        {
+          const struct table_entry *te = pt->entries[i];
+          const union value *value = &te->values[var_idx];
+          size_t hash = value_hash (value, width, 0);
+
+          HMAPX_FOR_EACH_WITH_HASH (iter, node, hash, &set)
+            if (value_equal (iter, value, width))
+              goto next_entry;
+
+          hmapx_insert (&set, (union value *) value, hash);
+
+        next_entry: ;
+        }
+
+      *n_values = hmapx_count (&set);
+      values = *valuesp = xnmalloc (*n_values, sizeof *values);
+      i = 0;
+      HMAPX_FOR_EACH (iter, node, &set)
+        values[i++] = *iter;
+      hmapx_destroy (&set);
 
-      assert (mode == INTEGER);
-      *values = xnmalloc (vr->count, sizeof **values);
-      for (i = 0; i < vr->count; i++)
-       (*values)[i].f = i + vr->min;
-      *value_cnt = vr->count;
+      sort (values, *n_values, sizeof *values, compare_value_3way, &width);
     }
 }
 
@@ -1710,7 +1509,8 @@ enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
    from V, displayed with print format spec from variable VAR.  When
    in REPORT missing-value mode, missing values have an M appended. */
 static void
-table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
+table_value_missing (struct crosstabs_proc *proc,
+                     struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
   struct substring s;
@@ -1723,10 +1523,9 @@ table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
       return;
     }
 
-  s.string = tab_alloc (table, print->w);
-  format_short (s.string, print, v);
-  s.length = strlen (s.string);
-  if (cmd.miss == CRS_REPORT && var_is_num_missing (var, v->f, MV_USER))
+  s = ss_cstr (data_out_pool (v, dict_get_encoding (proc->dict), print,
+                            table->container));
+  if (proc->exclude == MV_NEVER && var_is_num_missing (var, v->f, MV_USER))
     s.string[s.length++] = 'M';
   while (s.length && *s.string == ' ')
     {
@@ -1737,19 +1536,20 @@ table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
 }
 
 /* Draws a line across TABLE at the current row to indicate the most
-   major dimension variable with index FIRST_DIFFERENCE out of NVAR
+   major dimension variable with index FIRST_DIFFERENCE out of N_VARS
    that changed, and puts the values that changed into the table.  TB
-   and X must be the corresponding table_entry and crosstab,
+   and PT must be the corresponding table_entry and crosstab,
    respectively. */
 static void
-display_dimensions (struct tab_table *table, int first_difference, struct table_entry *tb)
+display_dimensions (struct crosstabs_proc *proc, struct pivot_table *pt,
+                    struct tab_table *table, int first_difference)
 {
-  tab_hline (table, TAL_1, nvar - first_difference - 1, tab_nc (table) - 1, 0);
+  tab_hline (table, TAL_1, pt->n_vars - first_difference - 1, tab_nc (table) - 1, 0);
 
   for (; first_difference >= 2; first_difference--)
-    table_value_missing (table, nvar - first_difference - 1, 0,
-                        TAB_RIGHT, &tb->values[first_difference],
-                        x->vars[first_difference]);
+    table_value_missing (proc, table, pt->n_vars - first_difference - 1, 0,
+                        TAB_RIGHT, &pt->entries[0]->values[first_difference],
+                        pt->vars[first_difference]);
 }
 
 /* Put VALUE into cell (C,R) of TABLE, suffixed with character
@@ -1757,16 +1557,15 @@ display_dimensions (struct tab_table *table, int first_difference, struct table_
    additionally suffixed with a letter `M'. */
 static void
 format_cell_entry (struct tab_table *table, int c, int r, double value,
-                   char suffix, bool mark_missing)
+                   char suffix, bool mark_missing, const struct dictionary *dict)
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
   struct substring s;
 
-  s.length = 10;
-  s.string = tab_alloc (table, 16);
   v.f = value;
-  data_out (&v, &f, s.string);
+  s = ss_cstr (data_out_pool (&v, dict_get_encoding (dict), &f, table->container));
+
   while (*s.string == ' ')
     {
       s.length--;
@@ -1782,208 +1581,196 @@ format_cell_entry (struct tab_table *table, int c, int r, double value,
 
 /* Displays the crosstabulation table. */
 static void
-display_crosstabulation (void)
+display_crosstabulation (struct crosstabs_proc *proc, struct pivot_table *pt,
+                         struct tab_table *table)
 {
-  {
-    int r;
+  int last_row;
+  int r, c, i;
+  double *mp;
 
-    for (r = 0; r < n_rows; r++)
-      table_value_missing (table, nvar - 2, r * num_cells,
-                          TAB_RIGHT, &rows[r], x->vars[ROW_VAR]);
-  }
-  tab_text (table, nvar - 2, n_rows * num_cells,
+  for (r = 0; r < pt->n_rows; r++)
+    table_value_missing (proc, table, pt->n_vars - 2, r * proc->n_cells,
+                         TAB_RIGHT, &pt->rows[r], pt->vars[ROW_VAR]);
+
+  tab_text (table, pt->n_vars - 2, pt->n_rows * proc->n_cells,
            TAB_LEFT, _("Total"));
 
   /* Put in the actual cells. */
-  {
-    double *mp = mat;
-    int r, c, i;
-
-    tab_offset (table, nvar - 1, -1);
-    for (r = 0; r < n_rows; r++)
-      {
-       if (num_cells > 1)
-         tab_hline (table, TAL_1, -1, n_cols, 0);
-       for (c = 0; c < n_cols; c++)
-         {
-            bool mark_missing = false;
-            double expected_value = row_tot[r] * col_tot[c] / W;
-            if (cmd.miss == CRS_REPORT
-                && (var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER)
-                    || var_is_num_missing (x->vars[ROW_VAR], rows[r].f,
-                                           MV_USER)))
-              mark_missing = true;
-           for (i = 0; i < num_cells; i++)
-             {
-               double v;
-                int suffix = 0;
-
-               switch (cells[i])
-                 {
-                 case CRS_CL_COUNT:
-                   v = *mp;
-                   break;
-                 case CRS_CL_ROW:
-                   v = *mp / row_tot[r] * 100.;
-                    suffix = '%';
-                   break;
-                 case CRS_CL_COLUMN:
-                   v = *mp / col_tot[c] * 100.;
-                    suffix = '%';
-                   break;
-                 case CRS_CL_TOTAL:
-                   v = *mp / W * 100.;
-                    suffix = '%';
-                   break;
-                 case CRS_CL_EXPECTED:
-                   v = expected_value;
-                   break;
-                 case CRS_CL_RESIDUAL:
-                   v = *mp - expected_value;
-                   break;
-                 case CRS_CL_SRESIDUAL:
-                   v = (*mp - expected_value) / sqrt (expected_value);
-                   break;
-                 case CRS_CL_ASRESIDUAL:
-                   v = ((*mp - expected_value)
-                        / sqrt (expected_value
-                                * (1. - row_tot[r] / W)
-                                * (1. - col_tot[c] / W)));
-                   break;
-                 default:
-                    NOT_REACHED ();
-                 }
-
-                format_cell_entry (table, c, i, v, suffix, mark_missing);
-             }
+  mp = pt->mat;
+  tab_offset (table, pt->n_vars - 1, -1);
+  for (r = 0; r < pt->n_rows; r++)
+    {
+      if (proc->n_cells > 1)
+        tab_hline (table, TAL_1, -1, pt->n_cols, 0);
+      for (c = 0; c < pt->n_cols; c++)
+        {
+          bool mark_missing = false;
+          double expected_value = pt->row_tot[r] * pt->col_tot[c] / pt->total;
+          if (proc->exclude == MV_NEVER
+              && (var_is_num_missing (pt->vars[COL_VAR], pt->cols[c].f, MV_USER)
+                  || var_is_num_missing (pt->vars[ROW_VAR], pt->rows[r].f,
+                                         MV_USER)))
+            mark_missing = true;
+          for (i = 0; i < proc->n_cells; i++)
+            {
+              double v;
+              int suffix = 0;
+
+              switch (proc->a_cells[i])
+                {
+                case CRS_CL_COUNT:
+                  v = *mp;
+                  break;
+                case CRS_CL_ROW:
+                  v = *mp / pt->row_tot[r] * 100.;
+                  suffix = '%';
+                  break;
+                case CRS_CL_COLUMN:
+                  v = *mp / pt->col_tot[c] * 100.;
+                  suffix = '%';
+                  break;
+                case CRS_CL_TOTAL:
+                  v = *mp / pt->total * 100.;
+                  suffix = '%';
+                  break;
+                case CRS_CL_EXPECTED:
+                  v = expected_value;
+                  break;
+                case CRS_CL_RESIDUAL:
+                  v = *mp - expected_value;
+                  break;
+                case CRS_CL_SRESIDUAL:
+                  v = (*mp - expected_value) / sqrt (expected_value);
+                  break;
+                case CRS_CL_ASRESIDUAL:
+                  v = ((*mp - expected_value)
+                       / sqrt (expected_value
+                               * (1. - pt->row_tot[r] / pt->total)
+                               * (1. - pt->col_tot[c] / pt->total)));
+                  break;
+                default:
+                  NOT_REACHED ();
+                }
+              format_cell_entry (table, c, i, v, suffix, mark_missing, proc->dict);
+            }
 
-           mp++;
-         }
+          mp++;
+        }
 
-       tab_offset (table, -1, tab_row (table) + num_cells);
-      }
-  }
+      tab_offset (table, -1, tab_row (table) + proc->n_cells);
+    }
 
   /* Row totals. */
-  {
-    int r, i;
-
-    tab_offset (table, -1, tab_row (table) - num_cells * n_rows);
-    for (r = 0; r < n_rows; r++)
-      {
-        bool mark_missing = false;
-
-        if (cmd.miss == CRS_REPORT
-            && var_is_num_missing (x->vars[ROW_VAR], rows[r].f, MV_USER))
-          mark_missing = true;
-
-        for (i = 0; i < num_cells; i++)
-          {
-            char suffix = 0;
-            double v;
+  tab_offset (table, -1, tab_row (table) - proc->n_cells * pt->n_rows);
+  for (r = 0; r < pt->n_rows; r++)
+    {
+      bool mark_missing = false;
 
-            switch (cells[i])
-              {
-              case CRS_CL_COUNT:
-                v = row_tot[r];
-                break;
-              case CRS_CL_ROW:
-                v = 100.0;
-                suffix = '%';
-                break;
-              case CRS_CL_COLUMN:
-                v = row_tot[r] / W * 100.;
-                suffix = '%';
-                break;
-              case CRS_CL_TOTAL:
-                v = row_tot[r] / W * 100.;
-                suffix = '%';
-                break;
-              case CRS_CL_EXPECTED:
-              case CRS_CL_RESIDUAL:
-              case CRS_CL_SRESIDUAL:
-              case CRS_CL_ASRESIDUAL:
-                v = 0.;
-                break;
-              default:
-                NOT_REACHED ();
-              }
+      if (proc->exclude == MV_NEVER
+          && var_is_num_missing (pt->vars[ROW_VAR], pt->rows[r].f, MV_USER))
+        mark_missing = true;
 
-            format_cell_entry (table, n_cols, 0, v, suffix, mark_missing);
-            tab_next_row (table);
-          }
-      }
-  }
+      for (i = 0; i < proc->n_cells; i++)
+        {
+          char suffix = 0;
+          double v;
+
+          switch (proc->a_cells[i])
+            {
+            case CRS_CL_COUNT:
+              v = pt->row_tot[r];
+              break;
+            case CRS_CL_ROW:
+              v = 100.0;
+              suffix = '%';
+              break;
+            case CRS_CL_COLUMN:
+              v = pt->row_tot[r] / pt->total * 100.;
+              suffix = '%';
+              break;
+            case CRS_CL_TOTAL:
+              v = pt->row_tot[r] / pt->total * 100.;
+              suffix = '%';
+              break;
+            case CRS_CL_EXPECTED:
+            case CRS_CL_RESIDUAL:
+            case CRS_CL_SRESIDUAL:
+            case CRS_CL_ASRESIDUAL:
+              v = 0.;
+              break;
+            default:
+              NOT_REACHED ();
+            }
+
+          format_cell_entry (table, pt->n_cols, 0, v, suffix, mark_missing, proc->dict);
+          tab_next_row (table);
+        }
+    }
 
   /* Column totals, grand total. */
-  {
-    int c;
-    int last_row = 0;
-
-    if (num_cells > 1)
-      tab_hline (table, TAL_1, -1, n_cols, 0);
-    for (c = 0; c <= n_cols; c++)
-      {
-       double ct = c < n_cols ? col_tot[c] : W;
-        bool mark_missing = false;
-        int i;
-
-        if (cmd.miss == CRS_REPORT && c < n_cols
-            && var_is_num_missing (x->vars[COL_VAR], cols[c].f, MV_USER))
-          mark_missing = true;
-
-        for (i = 0; i < num_cells; i++)
-         {
-            char suffix = 0;
-           double v;
-
-           switch (cells[i])
-             {
-             case CRS_CL_COUNT:
-               v = ct;
-               break;
-             case CRS_CL_ROW:
-               v = ct / W * 100.;
-                suffix = '%';
-               break;
-             case CRS_CL_COLUMN:
-               v = 100.;
-                suffix = '%';
-               break;
-             case CRS_CL_TOTAL:
-               v = ct / W * 100.;
-                suffix = '%';
-               break;
-             case CRS_CL_EXPECTED:
-             case CRS_CL_RESIDUAL:
-             case CRS_CL_SRESIDUAL:
-             case CRS_CL_ASRESIDUAL:
-               continue;
-             default:
-                NOT_REACHED ();
-             }
+  last_row = 0;
+  if (proc->n_cells > 1)
+    tab_hline (table, TAL_1, -1, pt->n_cols, 0);
+  for (c = 0; c <= pt->n_cols; c++)
+    {
+      double ct = c < pt->n_cols ? pt->col_tot[c] : pt->total;
+      bool mark_missing = false;
+      int i;
 
-           format_cell_entry (table, c, i, v, suffix, mark_missing);
-         }
-        last_row = i;
-      }
+      if (proc->exclude == MV_NEVER && c < pt->n_cols
+          && var_is_num_missing (pt->vars[COL_VAR], pt->cols[c].f, MV_USER))
+        mark_missing = true;
 
-    tab_offset (table, -1, tab_row (table) + last_row);
-  }
+      for (i = 0; i < proc->n_cells; i++)
+        {
+          char suffix = 0;
+          double v;
+
+          switch (proc->a_cells[i])
+            {
+            case CRS_CL_COUNT:
+              v = ct;
+              break;
+            case CRS_CL_ROW:
+              v = ct / pt->total * 100.;
+              suffix = '%';
+              break;
+            case CRS_CL_COLUMN:
+              v = 100.;
+              suffix = '%';
+              break;
+            case CRS_CL_TOTAL:
+              v = ct / pt->total * 100.;
+              suffix = '%';
+              break;
+            case CRS_CL_EXPECTED:
+            case CRS_CL_RESIDUAL:
+            case CRS_CL_SRESIDUAL:
+            case CRS_CL_ASRESIDUAL:
+              continue;
+            default:
+              NOT_REACHED ();
+            }
+
+          format_cell_entry (table, c, i, v, suffix, mark_missing, proc->dict);
+        }
+      last_row = i;
+    }
 
+  tab_offset (table, -1, tab_row (table) + last_row);
   tab_offset (table, 0, -1);
 }
 
-static void calc_r (double *X, double *Y, double *, double *, double *);
-static void calc_chisq (double[N_CHISQ], int[N_CHISQ], double *, double *);
+static void calc_r (struct pivot_table *,
+                    double *PT, double *Y, double *, double *, double *);
+static void calc_chisq (struct pivot_table *,
+                        double[N_CHISQ], int[N_CHISQ], double *, double *);
 
 /* Display chi-square statistics. */
 static void
-display_chisq (const struct dictionary *dict)
+display_chisq (struct pivot_table *pt, struct tab_table *chisq,
+               bool *showed_fisher)
 {
-  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 *chisq_stats[N_CHISQ] =
     {
       N_("Pearson Chi-Square"),
@@ -1995,32 +1782,30 @@ display_chisq (const struct dictionary *dict)
   double chisq_v[N_CHISQ];
   double fisher1, fisher2;
   int df[N_CHISQ];
-  int s = 0;
 
   int i;
 
-  calc_chisq (chisq_v, df, &fisher1, &fisher2);
+  calc_chisq (pt, chisq_v, df, &fisher1, &fisher2);
 
-  tab_offset (chisq, nvar - 2, -1);
+  tab_offset (chisq, pt->n_vars - 2, -1);
 
   for (i = 0; i < N_CHISQ; i++)
     {
       if ((i != 2 && chisq_v[i] == SYSMIS)
          || (i == 2 && fisher1 == SYSMIS))
        continue;
-      s = 1;
 
       tab_text (chisq, 0, 0, TAB_LEFT, gettext (chisq_stats[i]));
       if (i != 2)
        {
          tab_double (chisq, 1, 0, TAB_RIGHT, chisq_v[i], NULL);
-         tab_double (chisq, 2, 0, TAB_RIGHT, df[i], wfmt);
+         tab_double (chisq, 2, 0, TAB_RIGHT, df[i], &pt->weight_format);
          tab_double (chisq, 3, 0, TAB_RIGHT,
                     gsl_cdf_chisq_Q (chisq_v[i], df[i]), NULL);
        }
       else
        {
-         chisq_fisher = 1;
+         *showed_fisher = true;
          tab_double (chisq, 4, 0, TAB_RIGHT, fisher2, NULL);
          tab_double (chisq, 5, 0, TAB_RIGHT, fisher1, NULL);
        }
@@ -2028,22 +1813,22 @@ display_chisq (const struct dictionary *dict)
     }
 
   tab_text (chisq, 0, 0, TAB_LEFT, _("N of Valid Cases"));
-  tab_double (chisq, 1, 0, TAB_RIGHT, W, wfmt);
+  tab_double (chisq, 1, 0, TAB_RIGHT, pt->total, &pt->weight_format);
   tab_next_row (chisq);
 
   tab_offset (chisq, 0, -1);
 }
 
-static int calc_symmetric (double[N_SYMMETRIC], double[N_SYMMETRIC],
-                          double[N_SYMMETRIC]);
+static int calc_symmetric (struct crosstabs_proc *, struct pivot_table *,
+                           double[N_SYMMETRIC], double[N_SYMMETRIC],
+                          double[N_SYMMETRIC],
+                           double[3], double[3], double[3]);
 
 /* Display symmetric measures. */
 static void
-display_symmetric (const struct dictionary *dict)
+display_symmetric (struct crosstabs_proc *proc, struct pivot_table *pt,
+                   struct tab_table *sym)
 {
-  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 *categories[] =
     {
       N_("Nominal by Nominal"),
@@ -2072,12 +1857,14 @@ display_symmetric (const struct dictionary *dict)
 
   int last_cat = -1;
   double sym_v[N_SYMMETRIC], sym_ase[N_SYMMETRIC], sym_t[N_SYMMETRIC];
+  double somers_d_v[3], somers_d_ase[3], somers_d_t[3];
   int i;
 
-  if (!calc_symmetric (sym_v, sym_ase, sym_t))
+  if (!calc_symmetric (proc, pt, sym_v, sym_ase, sym_t,
+                       somers_d_v, somers_d_ase, somers_d_t))
     return;
 
-  tab_offset (sym, nvar - 2, -1);
+  tab_offset (sym, pt->n_vars - 2, -1);
 
   for (i = 0; i < N_SYMMETRIC; i++)
     {
@@ -2101,80 +1888,85 @@ display_symmetric (const struct dictionary *dict)
     }
 
   tab_text (sym, 0, 0, TAB_LEFT, _("N of Valid Cases"));
-  tab_double (sym, 2, 0, TAB_RIGHT, W, wfmt);
+  tab_double (sym, 2, 0, TAB_RIGHT, pt->total, &pt->weight_format);
   tab_next_row (sym);
 
   tab_offset (sym, 0, -1);
 }
 
-static int calc_risk (double[], double[], double[], union value *);
+static int calc_risk (struct pivot_table *,
+                      double[], double[], double[], union value *);
 
 /* Display risk estimate. */
 static void
-display_risk (const struct dictionary *dict)
+display_risk (struct pivot_table *pt, struct tab_table *risk)
 {
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
-
   char buf[256];
   double risk_v[3], lower[3], upper[3];
   union value c[2];
   int i;
 
-  if (!calc_risk (risk_v, upper, lower, c))
+  if (!calc_risk (pt, risk_v, upper, lower, c))
     return;
 
-  tab_offset (risk, nvar - 2, -1);
+  tab_offset (risk, pt->n_vars - 2, -1);
 
   for (i = 0; i < 3; i++)
     {
+      const struct variable *cv = pt->vars[COL_VAR];
+      const struct variable *rv = pt->vars[ROW_VAR];
+      int cvw = var_get_width (cv);
+      int rvw = var_get_width (rv);
+
       if (risk_v[i] == SYSMIS)
        continue;
 
       switch (i)
        {
        case 0:
-         if (var_is_numeric (x->vars[COL_VAR]))
+         if (var_is_numeric (cv))
            sprintf (buf, _("Odds Ratio for %s (%g / %g)"),
-                    var_get_name (x->vars[COL_VAR]), c[0].f, c[1].f);
+                    var_get_name (cv), c[0].f, c[1].f);
          else
            sprintf (buf, _("Odds Ratio for %s (%.*s / %.*s)"),
-                    var_get_name (x->vars[COL_VAR]),
-                    var_get_width (x->vars[COL_VAR]), c[0].s,
-                    var_get_width (x->vars[COL_VAR]), c[1].s);
+                    var_get_name (cv),
+                    cvw, value_str (&c[0], cvw),
+                    cvw, value_str (&c[1], cvw));
          break;
        case 1:
        case 2:
-         if (var_is_numeric (x->vars[ROW_VAR]))
+         if (var_is_numeric (rv))
            sprintf (buf, _("For cohort %s = %g"),
-                    var_get_name (x->vars[ROW_VAR]), rows[i - 1].f);
+                    var_get_name (rv), pt->rows[i - 1].f);
          else
            sprintf (buf, _("For cohort %s = %.*s"),
-                    var_get_name (x->vars[ROW_VAR]),
-                    var_get_width (x->vars[ROW_VAR]), rows[i - 1].s);
+                    var_get_name (rv),
+                    rvw, value_str (&pt->rows[i - 1], rvw));
          break;
        }
 
       tab_text (risk, 0, 0, TAB_LEFT, buf);
       tab_double (risk, 1, 0, TAB_RIGHT, risk_v[i], NULL);
-      tab_double (risk, 2, 0, TAB_RIGHT, lower[i],  NULL);
-      tab_double (risk, 3, 0, TAB_RIGHT, upper[i],  NULL);
+      tab_double (risk, 2, 0, TAB_RIGHT, lower[i], NULL);
+      tab_double (risk, 3, 0, TAB_RIGHT, upper[i], NULL);
       tab_next_row (risk);
     }
 
   tab_text (risk, 0, 0, TAB_LEFT, _("N of Valid Cases"));
-  tab_double (risk, 1, 0, TAB_RIGHT, W, wfmt);
+  tab_double (risk, 1, 0, TAB_RIGHT, pt->total, &pt->weight_format);
   tab_next_row (risk);
 
   tab_offset (risk, 0, -1);
 }
 
-static int calc_directional (double[N_DIRECTIONAL], double[N_DIRECTIONAL],
+static int calc_directional (struct crosstabs_proc *, struct pivot_table *,
+                             double[N_DIRECTIONAL], double[N_DIRECTIONAL],
                             double[N_DIRECTIONAL]);
 
 /* Display directional measures. */
 static void
-display_directional (void)
+display_directional (struct crosstabs_proc *proc, struct pivot_table *pt,
+                     struct tab_table *direct)
 {
   static const char *categories[] =
     {
@@ -2239,10 +2031,10 @@ display_directional (void)
 
   int i;
 
-  if (!calc_directional (direct_v, direct_ase, direct_t))
+  if (!calc_directional (proc, pt, direct_v, direct_ase, direct_t))
     return;
 
-  tab_offset (direct, nvar - 2, -1);
+  tab_offset (direct, pt->n_vars - 2, -1);
 
   for (i = 0; i < N_DIRECTIONAL; i++)
     {
@@ -2266,12 +2058,12 @@ display_directional (void)
                  if (k == 0)
                    string = NULL;
                  else if (k == 1)
-                   string = var_get_name (x->vars[0]);
+                   string = var_get_name (pt->vars[0]);
                  else
-                   string = var_get_name (x->vars[1]);
+                   string = var_get_name (pt->vars[1]);
 
-                 tab_text (direct, j, 0, TAB_LEFT | TAT_PRINTF,
-                           gettext (stats_names[j][k]), string);
+                 tab_text_format (direct, j, 0, TAB_LEFT,
+                                   gettext (stats_names[j][k]), string);
                }
            }
       }
@@ -2291,14 +2083,14 @@ display_directional (void)
 /* Statistical calculations. */
 
 /* Returns the value of the gamma (factorial) function for an integer
-   argument X. */
+   argument PT. */
 static double
-gamma_int (double x)
+gamma_int (double pt)
 {
   double r = 1;
   int i;
 
-  for (i = 2; i < x; i++)
+  for (i = 2; i < pt; i++)
     r *= i;
   return r;
 }
@@ -2329,7 +2121,7 @@ swap (int *a, int *b)
 static void
 calc_fisher (int a, int b, int c, int d, double *fisher1, double *fisher2)
 {
-  int x;
+  int pt;
 
   if (MIN (c, d) < MIN (a, b))
     swap (&a, &c), swap (&b, &d);
@@ -2344,19 +2136,20 @@ calc_fisher (int a, int b, int c, int d, double *fisher1, double *fisher2)
     }
 
   *fisher1 = 0.;
-  for (x = 0; x <= a; x++)
-    *fisher1 += Pr (a - x, b + x, c + x, d - x);
+  for (pt = 0; pt <= a; pt++)
+    *fisher1 += Pr (a - pt, b + pt, c + pt, d - pt);
 
   *fisher2 = *fisher1;
-  for (x = 1; x <= b; x++)
-    *fisher2 += Pr (a + x, b - x, c - x, d + x);
+  for (pt = 1; pt <= b; pt++)
+    *fisher2 += Pr (a + pt, b - pt, c - pt, d + pt);
 }
 
 /* Calculates chi-squares into CHISQ.  MAT is a matrix with N_COLS
    columns with values COLS and N_ROWS rows with values ROWS.  Values
-   in the matrix sum to W. */
+   in the matrix sum to pt->total. */
 static void
-calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
+calc_chisq (struct pivot_table *pt,
+            double chisq[N_CHISQ], int df[N_CHISQ],
            double *fisher1, double *fisher2)
 {
   int r, c;
@@ -2365,19 +2158,19 @@ calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
   chisq[2] = chisq[3] = chisq[4] = SYSMIS;
   *fisher1 = *fisher2 = SYSMIS;
 
-  df[0] = df[1] = (ns_cols - 1) * (ns_rows - 1);
+  df[0] = df[1] = (pt->ns_cols - 1) * (pt->ns_rows - 1);
 
-  if (ns_rows <= 1 || ns_cols <= 1)
+  if (pt->ns_rows <= 1 || pt->ns_cols <= 1)
     {
       chisq[0] = chisq[1] = SYSMIS;
       return;
     }
 
-  for (r = 0; r < n_rows; r++)
-    for (c = 0; c < n_cols; c++)
+  for (r = 0; r < pt->n_rows; r++)
+    for (c = 0; c < pt->n_cols; c++)
       {
-       const double expected = row_tot[r] * col_tot[c] / W;
-       const double freq = mat[n_cols * r + c];
+       const double expected = pt->row_tot[r] * pt->col_tot[c] / pt->total;
+       const double freq = pt->mat[pt->n_cols * r + c];
        const double residual = freq - expected;
 
         chisq[0] += residual * residual / expected;
@@ -2394,7 +2187,7 @@ calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
     chisq[1] = SYSMIS;
 
   /* Calculate Yates and Fisher exact test. */
-  if (ns_cols == 2 && ns_rows == 2)
+  if (pt->ns_cols == 2 && pt->ns_rows == 2)
     {
       double f11, f12, f21, f22;
 
@@ -2402,8 +2195,8 @@ calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
        int nz_cols[2];
        int i, j;
 
-       for (i = j = 0; i < n_cols; i++)
-         if (col_tot[i] != 0.)
+       for (i = j = 0; i < pt->n_cols; i++)
+         if (pt->col_tot[i] != 0.)
            {
              nz_cols[j++] = i;
              if (j == 2)
@@ -2412,18 +2205,18 @@ calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
 
        assert (j == 2);
 
-       f11 = mat[nz_cols[0]];
-       f12 = mat[nz_cols[1]];
-       f21 = mat[nz_cols[0] + n_cols];
-       f22 = mat[nz_cols[1] + n_cols];
+       f11 = pt->mat[nz_cols[0]];
+       f12 = pt->mat[nz_cols[1]];
+       f21 = pt->mat[nz_cols[0] + pt->n_cols];
+       f22 = pt->mat[nz_cols[1] + pt->n_cols];
       }
 
       /* Yates. */
       {
-       const double x = fabs (f11 * f22 - f12 * f21) - 0.5 * W;
+       const double pt_ = fabs (f11 * f22 - f12 * f21) - 0.5 * pt->total;
 
-       if (x > 0.)
-         chisq[3] = (W * x * x
+       if (pt_ > 0.)
+         chisq[3] = (pt->total * pow2 (pt_)
                      / (f11 + f12) / (f21 + f22)
                      / (f11 + f21) / (f12 + f22));
        else
@@ -2438,21 +2231,22 @@ calc_chisq (double chisq[N_CHISQ], int df[N_CHISQ],
     }
 
   /* Calculate Mantel-Haenszel. */
-  if (var_is_numeric (x->vars[ROW_VAR]) && var_is_numeric (x->vars[COL_VAR]))
+  if (var_is_numeric (pt->vars[ROW_VAR]) && var_is_numeric (pt->vars[COL_VAR]))
     {
       double r, ase_0, ase_1;
-      calc_r ((double *) rows, (double *) cols, &r, &ase_0, &ase_1);
+      calc_r (pt, (double *) pt->rows, (double *) pt->cols, &r, &ase_0, &ase_1);
 
-      chisq[4] = (W - 1.) * r * r;
+      chisq[4] = (pt->total - 1.) * r * r;
       df[4] = 1;
     }
 }
 
 /* Calculate the value of Pearson's r.  r is stored into R, ase_1 into
    ASE_1, and ase_0 into ASE_0.  The row and column values must be
-   passed in X and Y. */
+   passed in PT and Y. */
 static void
-calc_r (double *X, double *Y, double *r, double *ase_0, double *ase_1)
+calc_r (struct pivot_table *pt,
+        double *PT, double *Y, double *r, double *ase_0, double *ase_1)
 {
   double SX, SY, S, T;
   double Xbar, Ybar;
@@ -2461,52 +2255,52 @@ calc_r (double *X, double *Y, double *r, double *ase_0, double *ase_1)
   double sum_Yc, sum_Y2c;
   int i, j;
 
-  for (sum_X2Y2f = sum_XYf = 0., i = 0; i < n_rows; i++)
-    for (j = 0; j < n_cols; j++)
+  for (sum_X2Y2f = sum_XYf = 0., i = 0; i < pt->n_rows; i++)
+    for (j = 0; j < pt->n_cols; j++)
       {
-       double fij = mat[j + i * n_cols];
-       double product = X[i] * Y[j];
+       double fij = pt->mat[j + i * pt->n_cols];
+       double product = PT[i] * Y[j];
        double temp = fij * product;
        sum_XYf += temp;
        sum_X2Y2f += temp * product;
       }
 
-  for (sum_Xr = sum_X2r = 0., i = 0; i < n_rows; i++)
+  for (sum_Xr = sum_X2r = 0., i = 0; i < pt->n_rows; i++)
     {
-      sum_Xr += X[i] * row_tot[i];
-      sum_X2r += X[i] * X[i] * row_tot[i];
+      sum_Xr += PT[i] * pt->row_tot[i];
+      sum_X2r += pow2 (PT[i]) * pt->row_tot[i];
     }
-  Xbar = sum_Xr / W;
+  Xbar = sum_Xr / pt->total;
 
-  for (sum_Yc = sum_Y2c = 0., i = 0; i < n_cols; i++)
+  for (sum_Yc = sum_Y2c = 0., i = 0; i < pt->n_cols; i++)
     {
-      sum_Yc += Y[i] * col_tot[i];
-      sum_Y2c += Y[i] * Y[i] * col_tot[i];
+      sum_Yc += Y[i] * pt->col_tot[i];
+      sum_Y2c += Y[i] * Y[i] * pt->col_tot[i];
     }
-  Ybar = sum_Yc / W;
+  Ybar = sum_Yc / pt->total;
 
-  S = sum_XYf - sum_Xr * sum_Yc / W;
-  SX = sum_X2r - sum_Xr * sum_Xr / W;
-  SY = sum_Y2c - sum_Yc * sum_Yc / W;
+  S = sum_XYf - sum_Xr * sum_Yc / pt->total;
+  SX = sum_X2r - pow2 (sum_Xr) / pt->total;
+  SY = sum_Y2c - pow2 (sum_Yc) / pt->total;
   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) / pt->total) / (sum_X2r * sum_Y2c));
 
   {
     double s, c, y, t;
 
-    for (s = c = 0., i = 0; i < n_rows; i++)
-      for (j = 0; j < n_cols; j++)
+    for (s = c = 0., i = 0; i < pt->n_rows; i++)
+      for (j = 0; j < pt->n_cols; j++)
        {
          double Xresid, Yresid;
          double temp;
 
-         Xresid = X[i] - Xbar;
+         Xresid = PT[i] - Xbar;
          Yresid = Y[j] - Ybar;
          temp = (T * Xresid * Yresid
                  - ((S / (2. * T))
                     * (Xresid * Xresid * SY + Yresid * Yresid * SX)));
-         y = mat[j + i * n_cols] * temp * temp - c;
+         y = pt->mat[j + i * pt->n_cols] * temp * temp - c;
          t = s + y;
          c = (t - s) - y;
          s = t;
@@ -2515,88 +2309,73 @@ calc_r (double *X, double *Y, double *r, double *ase_0, double *ase_1)
   }
 }
 
-static double somers_d_v[3];
-static double somers_d_ase[3];
-static double somers_d_t[3];
-
 /* Calculate symmetric statistics and their asymptotic standard
    errors.  Returns 0 if none could be calculated. */
 static int
-calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
-               double t[N_SYMMETRIC])
+calc_symmetric (struct crosstabs_proc *proc, struct pivot_table *pt,
+                double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
+               double t[N_SYMMETRIC],
+                double somers_d_v[3], double somers_d_ase[3],
+                double somers_d_t[3])
 {
-  int q = MIN (ns_rows, ns_cols);
+  int q, i;
 
+  q = MIN (pt->ns_rows, pt->ns_cols);
   if (q <= 1)
     return 0;
 
-  {
-    int i;
-
-    if (v)
-      for (i = 0; i < N_SYMMETRIC; i++)
-       v[i] = ase[i] = t[i] = SYSMIS;
-  }
+  for (i = 0; i < N_SYMMETRIC; i++)
+    v[i] = ase[i] = t[i] = SYSMIS;
 
   /* Phi, Cramer's V, contingency coefficient. */
-  if (cmd.a_statistics[CRS_ST_PHI] || cmd.a_statistics[CRS_ST_CC])
+  if (proc->statistics & ((1u << CRS_ST_PHI) | (1u << CRS_ST_CC)))
     {
       double Xp = 0.;  /* Pearson chi-square. */
+      int r, c;
 
-      {
-       int r, c;
-
-       for (r = 0; r < n_rows; r++)
-         for (c = 0; c < n_cols; c++)
-           {
-             const double expected = row_tot[r] * col_tot[c] / W;
-             const double freq = mat[n_cols * r + c];
-             const double residual = freq - expected;
+      for (r = 0; r < pt->n_rows; r++)
+        for (c = 0; c < pt->n_cols; c++)
+          {
+            const double expected = pt->row_tot[r] * pt->col_tot[c] / pt->total;
+            const double freq = pt->mat[pt->n_cols * r + c];
+            const double residual = freq - expected;
 
-              Xp += residual * residual / expected;
-           }
-      }
+            Xp += residual * residual / expected;
+          }
 
-      if (cmd.a_statistics[CRS_ST_PHI])
+      if (proc->statistics & (1u << CRS_ST_PHI))
        {
-         v[0] = sqrt (Xp / W);
-         v[1] = sqrt (Xp / (W * (q - 1)));
+         v[0] = sqrt (Xp / pt->total);
+         v[1] = sqrt (Xp / (pt->total * (q - 1)));
        }
-      if (cmd.a_statistics[CRS_ST_CC])
-       v[2] = sqrt (Xp / (Xp + W));
+      if (proc->statistics & (1u << CRS_ST_CC))
+       v[2] = sqrt (Xp / (Xp + pt->total));
     }
 
-  if (cmd.a_statistics[CRS_ST_BTAU] || cmd.a_statistics[CRS_ST_CTAU]
-      || cmd.a_statistics[CRS_ST_GAMMA] || cmd.a_statistics[CRS_ST_D])
+  if (proc->statistics & ((1u << CRS_ST_BTAU) | (1u << CRS_ST_CTAU)
+                          | (1u << CRS_ST_GAMMA) | (1u << CRS_ST_D)))
     {
       double *cum;
       double Dr, Dc;
       double P, Q;
       double btau_cum, ctau_cum, gamma_cum, d_yx_cum, d_xy_cum;
       double btau_var;
+      int r, c;
 
-      {
-       int r, c;
-
-       Dr = Dc = W * W;
-       for (r = 0; r < n_rows; r++)
-         Dr -= row_tot[r] * row_tot[r];
-       for (c = 0; c < n_cols; c++)
-         Dc -= col_tot[c] * col_tot[c];
-      }
-
-      {
-       int r, c;
+      Dr = Dc = pow2 (pt->total);
+      for (r = 0; r < pt->n_rows; r++)
+        Dr -= pow2 (pt->row_tot[r]);
+      for (c = 0; c < pt->n_cols; c++)
+        Dc -= pow2 (pt->col_tot[c]);
 
-       cum = xnmalloc (n_cols * n_rows, sizeof *cum);
-       for (c = 0; c < n_cols; c++)
-         {
-           double ct = 0.;
+      cum = xnmalloc (pt->n_cols * pt->n_rows, sizeof *cum);
+      for (c = 0; c < pt->n_cols; c++)
+        {
+          double ct = 0.;
 
-           for (r = 0; r < n_rows; r++)
-             cum[c + r * n_cols] = ct += mat[c + r * n_cols];
-         }
-      }
+          for (r = 0; r < pt->n_rows; r++)
+            cum[c + r * pt->n_cols] = ct += pt->mat[c + r * pt->n_cols];
+        }
 
       /* P and Q. */
       {
@@ -2604,44 +2383,44 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
        double Cij, Dij;
 
        P = Q = 0.;
-       for (i = 0; i < n_rows; i++)
+       for (i = 0; i < pt->n_rows; i++)
          {
            Cij = Dij = 0.;
 
-           for (j = 1; j < n_cols; j++)
-             Cij += col_tot[j] - cum[j + i * n_cols];
+           for (j = 1; j < pt->n_cols; j++)
+             Cij += pt->col_tot[j] - cum[j + i * pt->n_cols];
 
            if (i > 0)
-             for (j = 1; j < n_cols; j++)
-               Dij += cum[j + (i - 1) * n_cols];
+             for (j = 1; j < pt->n_cols; j++)
+               Dij += cum[j + (i - 1) * pt->n_cols];
 
            for (j = 0;;)
              {
-               double fij = mat[j + i * n_cols];
+               double fij = pt->mat[j + i * pt->n_cols];
                P += fij * Cij;
                Q += fij * Dij;
 
-               if (++j == n_cols)
+               if (++j == pt->n_cols)
                  break;
-               assert (j < n_cols);
+               assert (j < pt->n_cols);
 
-               Cij -= col_tot[j] - cum[j + i * n_cols];
-               Dij += col_tot[j - 1] - cum[j - 1 + i * n_cols];
+               Cij -= pt->col_tot[j] - cum[j + i * pt->n_cols];
+               Dij += pt->col_tot[j - 1] - cum[j - 1 + i * pt->n_cols];
 
                if (i > 0)
                  {
-                   Cij += cum[j - 1 + (i - 1) * n_cols];
-                   Dij -= cum[j + (i - 1) * n_cols];
+                   Cij += cum[j - 1 + (i - 1) * pt->n_cols];
+                   Dij -= cum[j + (i - 1) * pt->n_cols];
                  }
              }
          }
       }
 
-      if (cmd.a_statistics[CRS_ST_BTAU])
+      if (proc->statistics & (1u << CRS_ST_BTAU))
        v[3] = (P - Q) / sqrt (Dr * Dc);
-      if (cmd.a_statistics[CRS_ST_CTAU])
-       v[4] = (q * (P - Q)) / ((W * W) * (q - 1));
-      if (cmd.a_statistics[CRS_ST_GAMMA])
+      if (proc->statistics & (1u << CRS_ST_CTAU))
+       v[4] = (q * (P - Q)) / (pow2 (pt->total) * (q - 1));
+      if (proc->statistics & (1u << CRS_ST_GAMMA))
        v[5] = (P - Q) / (P + Q);
 
       /* ASE for tau-b, tau-c, gamma.  Calculations could be
@@ -2651,26 +2430,26 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
        double Cij, Dij;
 
        btau_cum = ctau_cum = gamma_cum = d_yx_cum = d_xy_cum = 0.;
-       for (i = 0; i < n_rows; i++)
+       for (i = 0; i < pt->n_rows; i++)
          {
            Cij = Dij = 0.;
 
-           for (j = 1; j < n_cols; j++)
-             Cij += col_tot[j] - cum[j + i * n_cols];
+           for (j = 1; j < pt->n_cols; j++)
+             Cij += pt->col_tot[j] - cum[j + i * pt->n_cols];
 
            if (i > 0)
-             for (j = 1; j < n_cols; j++)
-               Dij += cum[j + (i - 1) * n_cols];
+             for (j = 1; j < pt->n_cols; j++)
+               Dij += cum[j + (i - 1) * pt->n_cols];
 
            for (j = 0;;)
              {
-               double fij = mat[j + i * n_cols];
+               double fij = pt->mat[j + i * pt->n_cols];
 
-               if (cmd.a_statistics[CRS_ST_BTAU])
+               if (proc->statistics & (1u << CRS_ST_BTAU))
                  {
                    const double temp = (2. * sqrt (Dr * Dc) * (Cij - Dij)
-                                        + v[3] * (row_tot[i] * Dc
-                                                  + col_tot[j] * Dr));
+                                        + v[3] * (pt->row_tot[i] * Dc
+                                                  + pt->col_tot[j] * Dr));
                    btau_cum += fij * temp * temp;
                  }
 
@@ -2679,84 +2458,84 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
                  ctau_cum += fij * temp * temp;
                }
 
-               if (cmd.a_statistics[CRS_ST_GAMMA])
+               if (proc->statistics & (1u << CRS_ST_GAMMA))
                  {
                    const double temp = Q * Cij - P * Dij;
                    gamma_cum += fij * temp * temp;
                  }
 
-               if (cmd.a_statistics[CRS_ST_D])
+               if (proc->statistics & (1u << CRS_ST_D))
                  {
                    d_yx_cum += fij * pow2 (Dr * (Cij - Dij)
-                                            - (P - Q) * (W - row_tot[i]));
+                                            - (P - Q) * (pt->total - pt->row_tot[i]));
                    d_xy_cum += fij * pow2 (Dc * (Dij - Cij)
-                                            - (Q - P) * (W - col_tot[j]));
+                                            - (Q - P) * (pt->total - pt->col_tot[j]));
                  }
 
-               if (++j == n_cols)
+               if (++j == pt->n_cols)
                  break;
-               assert (j < n_cols);
+               assert (j < pt->n_cols);
 
-               Cij -= col_tot[j] - cum[j + i * n_cols];
-               Dij += col_tot[j - 1] - cum[j - 1 + i * n_cols];
+               Cij -= pt->col_tot[j] - cum[j + i * pt->n_cols];
+               Dij += pt->col_tot[j - 1] - cum[j - 1 + i * pt->n_cols];
 
                if (i > 0)
                  {
-                   Cij += cum[j - 1 + (i - 1) * n_cols];
-                   Dij -= cum[j + (i - 1) * n_cols];
+                   Cij += cum[j - 1 + (i - 1) * pt->n_cols];
+                   Dij -= cum[j + (i - 1) * pt->n_cols];
                  }
              }
          }
       }
 
       btau_var = ((btau_cum
-                  - (W * pow2 (W * (P - Q) / sqrt (Dr * Dc) * (Dr + Dc))))
+                  - (pt->total * pow2 (pt->total * (P - Q) / sqrt (Dr * Dc) * (Dr + Dc))))
                  / pow2 (Dr * Dc));
-      if (cmd.a_statistics[CRS_ST_BTAU])
+      if (proc->statistics & (1u << CRS_ST_BTAU))
        {
          ase[3] = sqrt (btau_var);
-         t[3] = v[3] / (2 * sqrt ((ctau_cum - (P - Q) * (P - Q) / W)
+         t[3] = v[3] / (2 * sqrt ((ctau_cum - (P - Q) * (P - Q) / pt->total)
                                   / (Dr * Dc)));
        }
-      if (cmd.a_statistics[CRS_ST_CTAU])
+      if (proc->statistics & (1u << CRS_ST_CTAU))
        {
-         ase[4] = ((2 * q / ((q - 1) * W * W))
-                   * sqrt (ctau_cum - (P - Q) * (P - Q) / W));
+         ase[4] = ((2 * q / ((q - 1) * pow2 (pt->total)))
+                   * sqrt (ctau_cum - (P - Q) * (P - Q) / pt->total));
          t[4] = v[4] / ase[4];
        }
-      if (cmd.a_statistics[CRS_ST_GAMMA])
+      if (proc->statistics & (1u << CRS_ST_GAMMA))
        {
          ase[5] = ((4. / ((P + Q) * (P + Q))) * sqrt (gamma_cum));
          t[5] = v[5] / (2. / (P + Q)
-                        * sqrt (ctau_cum - (P - Q) * (P - Q) / W));
+                        * sqrt (ctau_cum - (P - Q) * (P - Q) / pt->total));
        }
-      if (cmd.a_statistics[CRS_ST_D])
+      if (proc->statistics & (1u << CRS_ST_D))
        {
          somers_d_v[0] = (P - Q) / (.5 * (Dc + Dr));
          somers_d_ase[0] = 2. * btau_var / (Dr + Dc) * sqrt (Dr * Dc);
          somers_d_t[0] = (somers_d_v[0]
                           / (4 / (Dc + Dr)
-                             * sqrt (ctau_cum - pow2 (P - Q) / W)));
+                             * sqrt (ctau_cum - pow2 (P - Q) / pt->total)));
          somers_d_v[1] = (P - Q) / Dc;
          somers_d_ase[1] = 2. / pow2 (Dc) * sqrt (d_xy_cum);
          somers_d_t[1] = (somers_d_v[1]
                           / (2. / Dc
-                             * sqrt (ctau_cum - pow2 (P - Q) / W)));
+                             * sqrt (ctau_cum - pow2 (P - Q) / pt->total)));
          somers_d_v[2] = (P - Q) / Dr;
          somers_d_ase[2] = 2. / pow2 (Dr) * sqrt (d_yx_cum);
          somers_d_t[2] = (somers_d_v[2]
                           / (2. / Dr
-                             * sqrt (ctau_cum - pow2 (P - Q) / W)));
+                             * sqrt (ctau_cum - pow2 (P - Q) / pt->total)));
        }
 
       free (cum);
     }
 
   /* Spearman correlation, Pearson's r. */
-  if (cmd.a_statistics[CRS_ST_CORR])
+  if (proc->statistics & (1u << CRS_ST_CORR))
     {
-      double *R = xmalloca (sizeof *R * n_rows);
-      double *C = xmalloca (sizeof *C * n_cols);
+      double *R = xmalloc (sizeof *R * pt->n_rows);
+      double *C = xmalloc (sizeof *C * pt->n_cols);
 
       {
        double y, t, c = 0., s = 0.;
@@ -2764,14 +2543,14 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
 
        for (;;)
          {
-           R[i] = s + (row_tot[i] + 1.) / 2.;
-           y = row_tot[i] - c;
+           R[i] = s + (pt->row_tot[i] + 1.) / 2.;
+           y = pt->row_tot[i] - c;
            t = s + y;
            c = (t - s) - y;
            s = t;
-           if (++i == n_rows)
+           if (++i == pt->n_rows)
              break;
-           assert (i < n_rows);
+           assert (i < pt->n_rows);
          }
       }
 
@@ -2781,73 +2560,73 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
 
        for (;;)
          {
-           C[j] = s + (col_tot[j] + 1.) / 2;
-           y = col_tot[j] - c;
+           C[j] = s + (pt->col_tot[j] + 1.) / 2;
+           y = pt->col_tot[j] - c;
            t = s + y;
            c = (t - s) - y;
            s = t;
-           if (++j == n_cols)
+           if (++j == pt->n_cols)
              break;
-           assert (j < n_cols);
+           assert (j < pt->n_cols);
          }
       }
 
-      calc_r (R, C, &v[6], &t[6], &ase[6]);
+      calc_r (pt, R, C, &v[6], &t[6], &ase[6]);
       t[6] = v[6] / t[6];
 
-      freea (R);
-      freea (C);
+      free (R);
+      free (C);
 
-      calc_r ((double *) rows, (double *) cols, &v[7], &t[7], &ase[7]);
+      calc_r (pt, (double *) pt->rows, (double *) pt->cols, &v[7], &t[7], &ase[7]);
       t[7] = v[7] / t[7];
     }
 
   /* Cohen's kappa. */
-  if (cmd.a_statistics[CRS_ST_KAPPA] && ns_rows == ns_cols)
+  if (proc->statistics & (1u << CRS_ST_KAPPA) && pt->ns_rows == pt->ns_cols)
     {
       double sum_fii, sum_rici, sum_fiiri_ci, sum_fijri_ci2, sum_riciri_ci;
       int i, j;
 
       for (sum_fii = sum_rici = sum_fiiri_ci = sum_riciri_ci = 0., i = j = 0;
-          i < ns_rows; i++, j++)
+          i < pt->ns_rows; i++, j++)
        {
          double prod, sum;
 
-         while (col_tot[j] == 0.)
+         while (pt->col_tot[j] == 0.)
            j++;
 
-         prod = row_tot[i] * col_tot[j];
-         sum = row_tot[i] + col_tot[j];
+         prod = pt->row_tot[i] * pt->col_tot[j];
+         sum = pt->row_tot[i] + pt->col_tot[j];
 
-         sum_fii += mat[j + i * n_cols];
+         sum_fii += pt->mat[j + i * pt->n_cols];
          sum_rici += prod;
-         sum_fiiri_ci += mat[j + i * n_cols] * sum;
+         sum_fiiri_ci += pt->mat[j + i * pt->n_cols] * sum;
          sum_riciri_ci += prod * sum;
        }
-      for (sum_fijri_ci2 = 0., i = 0; i < ns_rows; i++)
-       for (j = 0; j < ns_cols; j++)
+      for (sum_fijri_ci2 = 0., i = 0; i < pt->ns_rows; i++)
+       for (j = 0; j < pt->ns_cols; j++)
          {
-           double sum = row_tot[i] + col_tot[j];
-           sum_fijri_ci2 += mat[j + i * n_cols] * sum * sum;
+           double sum = pt->row_tot[i] + pt->col_tot[j];
+           sum_fijri_ci2 += pt->mat[j + i * pt->n_cols] * sum * sum;
          }
 
-      v[8] = (W * sum_fii - sum_rici) / (W * W - sum_rici);
+      v[8] = (pt->total * sum_fii - sum_rici) / (pow2 (pt->total) - sum_rici);
 
-      ase[8] = sqrt ((W * W * sum_rici
+      ase[8] = sqrt ((pow2 (pt->total) * sum_rici
                      + sum_rici * sum_rici
-                     - W * sum_riciri_ci)
-                    / (W * (W * W - sum_rici) * (W * W - sum_rici)));
+                     - pt->total * sum_riciri_ci)
+                    / (pt->total * (pow2 (pt->total) - sum_rici) * (pow2 (pt->total) - sum_rici)));
 #if 0
-      t[8] = v[8] / sqrt (W * (((sum_fii * (W - sum_fii))
-                               / pow2 (W * W - sum_rici))
-                              + ((2. * (W - sum_fii)
+      t[8] = v[8] / sqrt (pt->total * (((sum_fii * (pt->total - sum_fii))
+                               / pow2 (pow2 (pt->total) - sum_rici))
+                              + ((2. * (pt->total - sum_fii)
                                   * (2. * sum_fii * sum_rici
-                                     - W * sum_fiiri_ci))
-                                 / cube (W * W - sum_rici))
-                              + (pow2 (W - sum_fii)
-                                 * (W * sum_fijri_ci2 - 4.
+                                     - pt->total * sum_fiiri_ci))
+                                 / cube (pow2 (pt->total) - sum_rici))
+                              + (pow2 (pt->total - sum_fii)
+                                 * (pt->total * sum_fijri_ci2 - 4.
                                     * sum_rici * sum_rici)
-                                 / pow4 (W * W - sum_rici))));
+                                 / pow4 (pow2 (pt->total) - sum_rici))));
 #else
       t[8] = v[8] / ase[8];
 #endif
@@ -2858,7 +2637,8 @@ calc_symmetric (double v[N_SYMMETRIC], double ase[N_SYMMETRIC],
 
 /* Calculate risk estimate. */
 static int
-calc_risk (double *value, double *upper, double *lower, union value *c)
+calc_risk (struct pivot_table *pt,
+           double *value, double *upper, double *lower, union value *c)
 {
   double f11, f12, f21, f22;
   double v;
@@ -2870,15 +2650,15 @@ calc_risk (double *value, double *upper, double *lower, union value *c)
       value[i] = upper[i] = lower[i] = SYSMIS;
   }
 
-  if (ns_rows != 2 || ns_cols != 2)
+  if (pt->ns_rows != 2 || pt->ns_cols != 2)
     return 0;
 
   {
     int nz_cols[2];
     int i, j;
 
-    for (i = j = 0; i < n_cols; i++)
-      if (col_tot[i] != 0.)
+    for (i = j = 0; i < pt->n_cols; i++)
+      if (pt->col_tot[i] != 0.)
        {
          nz_cols[j++] = i;
          if (j == 2)
@@ -2887,13 +2667,13 @@ calc_risk (double *value, double *upper, double *lower, union value *c)
 
     assert (j == 2);
 
-    f11 = mat[nz_cols[0]];
-    f12 = mat[nz_cols[1]];
-    f21 = mat[nz_cols[0] + n_cols];
-    f22 = mat[nz_cols[1] + n_cols];
+    f11 = pt->mat[nz_cols[0]];
+    f12 = pt->mat[nz_cols[1]];
+    f21 = pt->mat[nz_cols[0] + pt->n_cols];
+    f22 = pt->mat[nz_cols[1] + pt->n_cols];
 
-    c[0] = cols[nz_cols[0]];
-    c[1] = cols[nz_cols[1]];
+    c[0] = pt->cols[nz_cols[0]];
+    c[1] = pt->cols[nz_cols[1]];
   }
 
   value[0] = (f11 * f22) / (f12 * f21);
@@ -2918,7 +2698,8 @@ calc_risk (double *value, double *upper, double *lower, union value *c)
 
 /* Calculate directional measures. */
 static int
-calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
+calc_directional (struct crosstabs_proc *proc, struct pivot_table *pt,
+                  double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
                  double t[N_DIRECTIONAL])
 {
   {
@@ -2929,27 +2710,27 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
   }
 
   /* Lambda. */
-  if (cmd.a_statistics[CRS_ST_LAMBDA])
+  if (proc->statistics & (1u << CRS_ST_LAMBDA))
     {
-      double *fim = xnmalloc (n_rows, sizeof *fim);
-      int *fim_index = xnmalloc (n_rows, sizeof *fim_index);
-      double *fmj = xnmalloc (n_cols, sizeof *fmj);
-      int *fmj_index = xnmalloc (n_cols, sizeof *fmj_index);
+      double *fim = xnmalloc (pt->n_rows, sizeof *fim);
+      int *fim_index = xnmalloc (pt->n_rows, sizeof *fim_index);
+      double *fmj = xnmalloc (pt->n_cols, sizeof *fmj);
+      int *fmj_index = xnmalloc (pt->n_cols, sizeof *fmj_index);
       double sum_fim, sum_fmj;
       double rm, cm;
       int rm_index, cm_index;
       int i, j;
 
       /* Find maximum for each row and their sum. */
-      for (sum_fim = 0., i = 0; i < n_rows; i++)
+      for (sum_fim = 0., i = 0; i < pt->n_rows; i++)
        {
-         double max = mat[i * n_cols];
+         double max = pt->mat[i * pt->n_cols];
          int index = 0;
 
-         for (j = 1; j < n_cols; j++)
-           if (mat[j + i * n_cols] > max)
+         for (j = 1; j < pt->n_cols; j++)
+           if (pt->mat[j + i * pt->n_cols] > max)
              {
-               max = mat[j + i * n_cols];
+               max = pt->mat[j + i * pt->n_cols];
                index = j;
              }
 
@@ -2958,15 +2739,15 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        }
 
       /* Find maximum for each column. */
-      for (sum_fmj = 0., j = 0; j < n_cols; j++)
+      for (sum_fmj = 0., j = 0; j < pt->n_cols; j++)
        {
-         double max = mat[j];
+         double max = pt->mat[j];
          int index = 0;
 
-         for (i = 1; i < n_rows; i++)
-           if (mat[j + i * n_cols] > max)
+         for (i = 1; i < pt->n_rows; i++)
+           if (pt->mat[j + i * pt->n_cols] > max)
              {
-               max = mat[j + i * n_cols];
+               max = pt->mat[j + i * pt->n_cols];
                index = i;
              }
 
@@ -2975,83 +2756,83 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        }
 
       /* Find maximum row total. */
-      rm = row_tot[0];
+      rm = pt->row_tot[0];
       rm_index = 0;
-      for (i = 1; i < n_rows; i++)
-       if (row_tot[i] > rm)
+      for (i = 1; i < pt->n_rows; i++)
+       if (pt->row_tot[i] > rm)
          {
-           rm = row_tot[i];
+           rm = pt->row_tot[i];
            rm_index = i;
          }
 
       /* Find maximum column total. */
-      cm = col_tot[0];
+      cm = pt->col_tot[0];
       cm_index = 0;
-      for (j = 1; j < n_cols; j++)
-       if (col_tot[j] > cm)
+      for (j = 1; j < pt->n_cols; j++)
+       if (pt->col_tot[j] > cm)
          {
-           cm = col_tot[j];
+           cm = pt->col_tot[j];
            cm_index = j;
          }
 
-      v[0] = (sum_fim + sum_fmj - cm - rm) / (2. * W - rm - cm);
-      v[1] = (sum_fmj - rm) / (W - rm);
-      v[2] = (sum_fim - cm) / (W - cm);
+      v[0] = (sum_fim + sum_fmj - cm - rm) / (2. * pt->total - rm - cm);
+      v[1] = (sum_fmj - rm) / (pt->total - rm);
+      v[2] = (sum_fim - cm) / (pt->total - cm);
 
-      /* ASE1 for Y given X. */
+      /* ASE1 for Y given PT. */
       {
        double accum;
 
-       for (accum = 0., i = 0; i < n_rows; i++)
-         for (j = 0; j < n_cols; j++)
+       for (accum = 0., i = 0; i < pt->n_rows; i++)
+         for (j = 0; j < pt->n_cols; j++)
            {
              const int deltaj = j == cm_index;
-             accum += (mat[j + i * n_cols]
+             accum += (pt->mat[j + i * pt->n_cols]
                        * pow2 ((j == fim_index[i])
                               - deltaj
                               + v[0] * deltaj));
            }
 
-       ase[2] = sqrt (accum - W * v[0]) / (W - cm);
+       ase[2] = sqrt (accum - pt->total * v[0]) / (pt->total - cm);
       }
 
-      /* ASE0 for Y given X. */
+      /* ASE0 for Y given PT. */
       {
        double accum;
 
-       for (accum = 0., i = 0; i < n_rows; i++)
+       for (accum = 0., i = 0; i < pt->n_rows; i++)
          if (cm_index != fim_index[i])
-           accum += (mat[i * n_cols + fim_index[i]]
-                     + mat[i * n_cols + cm_index]);
-       t[2] = v[2] / (sqrt (accum - pow2 (sum_fim - cm) / W) / (W - cm));
+           accum += (pt->mat[i * pt->n_cols + fim_index[i]]
+                     + pt->mat[i * pt->n_cols + cm_index]);
+       t[2] = v[2] / (sqrt (accum - pow2 (sum_fim - cm) / pt->total) / (pt->total - cm));
       }
 
-      /* ASE1 for X given Y. */
+      /* ASE1 for PT given Y. */
       {
        double accum;
 
-       for (accum = 0., i = 0; i < n_rows; i++)
-         for (j = 0; j < n_cols; j++)
+       for (accum = 0., i = 0; i < pt->n_rows; i++)
+         for (j = 0; j < pt->n_cols; j++)
            {
              const int deltaj = i == rm_index;
-             accum += (mat[j + i * n_cols]
+             accum += (pt->mat[j + i * pt->n_cols]
                        * pow2 ((i == fmj_index[j])
                               - deltaj
                               + v[0] * deltaj));
            }
 
-       ase[1] = sqrt (accum - W * v[0]) / (W - rm);
+       ase[1] = sqrt (accum - pt->total * v[0]) / (pt->total - rm);
       }
 
-      /* ASE0 for X given Y. */
+      /* ASE0 for PT given Y. */
       {
        double accum;
 
-       for (accum = 0., j = 0; j < n_cols; j++)
+       for (accum = 0., j = 0; j < pt->n_cols; j++)
          if (rm_index != fmj_index[j])
-           accum += (mat[j + n_cols * fmj_index[j]]
-                     + mat[j + n_cols * rm_index]);
-       t[1] = v[1] / (sqrt (accum - pow2 (sum_fmj - rm) / W) / (W - rm));
+           accum += (pt->mat[j + pt->n_cols * fmj_index[j]]
+                     + pt->mat[j + pt->n_cols * rm_index]);
+       t[1] = v[1] / (sqrt (accum - pow2 (sum_fmj - rm) / pt->total) / (pt->total - rm));
       }
 
       /* Symmetric ASE0 and ASE1. */
@@ -3059,18 +2840,18 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        double accum0;
        double accum1;
 
-       for (accum0 = accum1 = 0., i = 0; i < n_rows; i++)
-         for (j = 0; j < n_cols; j++)
+       for (accum0 = accum1 = 0., i = 0; i < pt->n_rows; i++)
+         for (j = 0; j < pt->n_cols; j++)
            {
              int temp0 = (fmj_index[j] == i) + (fim_index[i] == j);
              int temp1 = (i == rm_index) + (j == cm_index);
-             accum0 += mat[j + i * n_cols] * pow2 (temp0 - temp1);
-             accum1 += (mat[j + i * n_cols]
+             accum0 += pt->mat[j + i * pt->n_cols] * pow2 (temp0 - temp1);
+             accum1 += (pt->mat[j + i * pt->n_cols]
                         * pow2 (temp0 + (v[0] - 1.) * temp1));
            }
-       ase[0] = sqrt (accum1 - 4. * W * v[0] * v[0]) / (2. * W - rm - cm);
-       t[0] = v[0] / (sqrt (accum0 - pow2 ((sum_fim + sum_fmj - cm - rm) / W))
-                      / (2. * W - rm - cm));
+       ase[0] = sqrt (accum1 - 4. * pt->total * v[0] * v[0]) / (2. * pt->total - rm - cm);
+       t[0] = v[0] / (sqrt (accum0 - pow2 ((sum_fim + sum_fmj - cm - rm) / pt->total))
+                      / (2. * pt->total - rm - cm));
       }
 
       free (fim);
@@ -3082,123 +2863,131 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        double sum_fij2_ri, sum_fij2_ci;
        double sum_ri2, sum_cj2;
 
-       for (sum_fij2_ri = sum_fij2_ci = 0., i = 0; i < n_rows; i++)
-         for (j = 0; j < n_cols; j++)
+       for (sum_fij2_ri = sum_fij2_ci = 0., i = 0; i < pt->n_rows; i++)
+         for (j = 0; j < pt->n_cols; j++)
            {
-             double temp = pow2 (mat[j + i * n_cols]);
-             sum_fij2_ri += temp / row_tot[i];
-             sum_fij2_ci += temp / col_tot[j];
+             double temp = pow2 (pt->mat[j + i * pt->n_cols]);
+             sum_fij2_ri += temp / pt->row_tot[i];
+             sum_fij2_ci += temp / pt->col_tot[j];
            }
 
-       for (sum_ri2 = 0., i = 0; i < n_rows; i++)
-         sum_ri2 += row_tot[i] * row_tot[i];
+       for (sum_ri2 = 0., i = 0; i < pt->n_rows; i++)
+         sum_ri2 += pow2 (pt->row_tot[i]);
 
-       for (sum_cj2 = 0., j = 0; j < n_cols; j++)
-         sum_cj2 += col_tot[j] * col_tot[j];
+       for (sum_cj2 = 0., j = 0; j < pt->n_cols; j++)
+         sum_cj2 += pow2 (pt->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);
+       v[3] = (pt->total * sum_fij2_ci - sum_ri2) / (pow2 (pt->total) - sum_ri2);
+       v[4] = (pt->total * sum_fij2_ri - sum_cj2) / (pow2 (pt->total) - sum_cj2);
       }
     }
 
-  if (cmd.a_statistics[CRS_ST_UC])
+  if (proc->statistics & (1u << CRS_ST_UC))
     {
       double UX, UY, UXY, P;
       double ase1_yx, ase1_xy, ase1_sym;
       int i, j;
 
-      for (UX = 0., i = 0; i < n_rows; i++)
-       if (row_tot[i] > 0.)
-         UX -= row_tot[i] / W * log (row_tot[i] / W);
+      for (UX = 0., i = 0; i < pt->n_rows; i++)
+       if (pt->row_tot[i] > 0.)
+         UX -= pt->row_tot[i] / pt->total * log (pt->row_tot[i] / pt->total);
 
-      for (UY = 0., j = 0; j < n_cols; j++)
-       if (col_tot[j] > 0.)
-         UY -= col_tot[j] / W * log (col_tot[j] / W);
+      for (UY = 0., j = 0; j < pt->n_cols; j++)
+       if (pt->col_tot[j] > 0.)
+         UY -= pt->col_tot[j] / pt->total * log (pt->col_tot[j] / pt->total);
 
-      for (UXY = P = 0., i = 0; i < n_rows; i++)
-       for (j = 0; j < n_cols; j++)
+      for (UXY = P = 0., i = 0; i < pt->n_rows; i++)
+       for (j = 0; j < pt->n_cols; j++)
          {
-           double entry = mat[j + i * n_cols];
+           double entry = pt->mat[j + i * pt->n_cols];
 
            if (entry <= 0.)
              continue;
 
-           P += entry * pow2 (log (col_tot[j] * row_tot[i] / (W * entry)));
-           UXY -= entry / W * log (entry / W);
+           P += entry * pow2 (log (pt->col_tot[j] * pt->row_tot[i] / (pt->total * entry)));
+           UXY -= entry / pt->total * log (entry / pt->total);
          }
 
-      for (ase1_yx = ase1_xy = ase1_sym = 0., i = 0; i < n_rows; i++)
-       for (j = 0; j < n_cols; j++)
+      for (ase1_yx = ase1_xy = ase1_sym = 0., i = 0; i < pt->n_rows; i++)
+       for (j = 0; j < pt->n_cols; j++)
          {
-           double entry = mat[j + i * n_cols];
+           double entry = pt->mat[j + i * pt->n_cols];
 
            if (entry <= 0.)
              continue;
 
-           ase1_yx += entry * pow2 (UY * log (entry / row_tot[i])
-                                   + (UX - UXY) * log (col_tot[j] / W));
-           ase1_xy += entry * pow2 (UX * log (entry / col_tot[j])
-                                   + (UY - UXY) * log (row_tot[i] / W));
+           ase1_yx += entry * pow2 (UY * log (entry / pt->row_tot[i])
+                                   + (UX - UXY) * log (pt->col_tot[j] / pt->total));
+           ase1_xy += entry * pow2 (UX * log (entry / pt->col_tot[j])
+                                   + (UY - UXY) * log (pt->row_tot[i] / pt->total));
            ase1_sym += entry * pow2 ((UXY
-                                     * log (row_tot[i] * col_tot[j] / (W * W)))
-                                    - (UX + UY) * log (entry / W));
+                                     * log (pt->row_tot[i] * pt->col_tot[j] / pow2 (pt->total)))
+                                    - (UX + UY) * log (entry / pt->total));
          }
 
       v[5] = 2. * ((UX + UY - UXY) / (UX + UY));
-      ase[5] = (2. / (W * pow2 (UX + UY))) * sqrt (ase1_sym);
-      t[5] = v[5] / ((2. / (W * (UX + UY)))
-                    * sqrt (P - pow2 (UX + UY - UXY) / W));
+      ase[5] = (2. / (pt->total * pow2 (UX + UY))) * sqrt (ase1_sym);
+      t[5] = v[5] / ((2. / (pt->total * (UX + UY)))
+                    * sqrt (P - pow2 (UX + UY - UXY) / pt->total));
 
       v[6] = (UX + UY - UXY) / UX;
-      ase[6] = sqrt (ase1_xy) / (W * UX * UX);
-      t[6] = v[6] / (sqrt (P - W * pow2 (UX + UY - UXY)) / (W * UX));
+      ase[6] = sqrt (ase1_xy) / (pt->total * UX * UX);
+      t[6] = v[6] / (sqrt (P - pt->total * pow2 (UX + UY - UXY)) / (pt->total * UX));
 
       v[7] = (UX + UY - UXY) / UY;
-      ase[7] = sqrt (ase1_yx) / (W * UY * UY);
-      t[7] = v[7] / (sqrt (P - W * pow2 (UX + UY - UXY)) / (W * UY));
+      ase[7] = sqrt (ase1_yx) / (pt->total * UY * UY);
+      t[7] = v[7] / (sqrt (P - pt->total * pow2 (UX + UY - UXY)) / (pt->total * UY));
     }
 
   /* Somers' D. */
-  if (cmd.a_statistics[CRS_ST_D])
+  if (proc->statistics & (1u << CRS_ST_D))
     {
-      int i;
+      double v_dummy[N_SYMMETRIC];
+      double ase_dummy[N_SYMMETRIC];
+      double t_dummy[N_SYMMETRIC];
+      double somers_d_v[3];
+      double somers_d_ase[3];
+      double somers_d_t[3];
 
-      if (!sym)
-       calc_symmetric (NULL, NULL, NULL);
-      for (i = 0; i < 3; i++)
-       {
-         v[8 + i] = somers_d_v[i];
-         ase[8 + i] = somers_d_ase[i];
-         t[8 + i] = somers_d_t[i];
-       }
+      if (calc_symmetric (proc, pt, v_dummy, ase_dummy, t_dummy,
+                          somers_d_v, somers_d_ase, somers_d_t))
+        {
+          int i;
+          for (i = 0; i < 3; i++)
+            {
+              v[8 + i] = somers_d_v[i];
+              ase[8 + i] = somers_d_ase[i];
+              t[8 + i] = somers_d_t[i];
+            }
+        }
     }
 
   /* Eta. */
-  if (cmd.a_statistics[CRS_ST_ETA])
+  if (proc->statistics & (1u << CRS_ST_ETA))
     {
       {
        double sum_Xr, sum_X2r;
        double SX, SXW;
        int i, j;
 
-       for (sum_Xr = sum_X2r = 0., i = 0; i < n_rows; i++)
+       for (sum_Xr = sum_X2r = 0., i = 0; i < pt->n_rows; i++)
          {
-           sum_Xr += rows[i].f * row_tot[i];
-           sum_X2r += rows[i].f * rows[i].f * row_tot[i];
+           sum_Xr += pt->rows[i].f * pt->row_tot[i];
+           sum_X2r += pow2 (pt->rows[i].f) * pt->row_tot[i];
          }
-       SX = sum_X2r - sum_Xr * sum_Xr / W;
+       SX = sum_X2r - pow2 (sum_Xr) / pt->total;
 
-       for (SXW = 0., j = 0; j < n_cols; j++)
+       for (SXW = 0., j = 0; j < pt->n_cols; j++)
          {
            double cum;
 
-           for (cum = 0., i = 0; i < n_rows; i++)
+           for (cum = 0., i = 0; i < pt->n_rows; i++)
              {
-               SXW += rows[i].f * rows[i].f * mat[j + i * n_cols];
-               cum += rows[i].f * mat[j + i * n_cols];
+               SXW += pow2 (pt->rows[i].f) * pt->mat[j + i * pt->n_cols];
+               cum += pt->rows[i].f * pt->mat[j + i * pt->n_cols];
              }
 
-           SXW -= cum * cum / col_tot[j];
+           SXW -= cum * cum / pt->col_tot[j];
          }
        v[11] = sqrt (1. - SXW / SX);
       }
@@ -3208,24 +2997,24 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
        double SY, SYW;
        int i, j;
 
-       for (sum_Yc = sum_Y2c = 0., i = 0; i < n_cols; i++)
+       for (sum_Yc = sum_Y2c = 0., i = 0; i < pt->n_cols; i++)
          {
-           sum_Yc += cols[i].f * col_tot[i];
-           sum_Y2c += cols[i].f * cols[i].f * col_tot[i];
+           sum_Yc += pt->cols[i].f * pt->col_tot[i];
+           sum_Y2c += pow2 (pt->cols[i].f) * pt->col_tot[i];
          }
-       SY = sum_Y2c - sum_Yc * sum_Yc / W;
+       SY = sum_Y2c - sum_Yc * sum_Yc / pt->total;
 
-       for (SYW = 0., i = 0; i < n_rows; i++)
+       for (SYW = 0., i = 0; i < pt->n_rows; i++)
          {
            double cum;
 
-           for (cum = 0., j = 0; j < n_cols; j++)
+           for (cum = 0., j = 0; j < pt->n_cols; j++)
              {
-               SYW += cols[j].f * cols[j].f * mat[j + i * n_cols];
-               cum += cols[j].f * mat[j + i * n_cols];
+               SYW += pow2 (pt->cols[j].f) * pt->mat[j + i * pt->n_cols];
+               cum += pt->cols[j].f * pt->mat[j + i * pt->n_cols];
              }
 
-           SYW -= cum * cum / row_tot[i];
+           SYW -= cum * cum / pt->row_tot[i];
          }
        v[12] = sqrt (1. - SYW / SY);
       }
@@ -3234,34 +3023,6 @@ calc_directional (double v[N_DIRECTIONAL], double ase[N_DIRECTIONAL],
   return 1;
 }
 
-/* A wrapper around data_out() that limits string output to short
-   string width and null terminates the result. */
-static void
-format_short (char *s, const struct fmt_spec *fp, const union value *v)
-{
-  struct fmt_spec fmt_subst;
-
-  /* Limit to short string width. */
-  if (fmt_is_string (fp->type))
-    {
-      fmt_subst = *fp;
-
-      assert (fmt_subst.type == FMT_A || fmt_subst.type == FMT_AHEX);
-      if (fmt_subst.type == FMT_A)
-        fmt_subst.w = MIN (8, fmt_subst.w);
-      else
-        fmt_subst.w = MIN (16, fmt_subst.w);
-
-      fp = &fmt_subst;
-    }
-
-  /* Format. */
-  data_out (v, fp, s);
-
-  /* Null terminate. */
-  s[fp->w] = '\0';
-}
-
 /*
    Local Variables:
    mode: c
index e26eadf93e064f5b48a3654ab95b24a9552bf617..7b30a6013908c6692a5ba0b6977b3046a534f33c 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
@@ -560,7 +560,7 @@ dump_z_table (struct dsc_proc *dsc)
   tab_hline (t, TAL_2, 0, 1, 1);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   {
     size_t i, y;
@@ -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;
@@ -877,7 +879,7 @@ display (struct dsc_proc *dsc)
   tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   nc = 0;
   tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
@@ -903,9 +905,9 @@ display (struct dsc_proc *dsc)
 
       nc = 0;
       tab_text (t, nc++, i + 1, TAB_LEFT, var_get_name (dv->v));
-      tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
+      tab_text_format (t, nc++, i + 1, 0, "%g", dv->valid);
       if (dsc->format == DSC_SERIAL)
-       tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
+       tab_text_format (t, nc++, i + 1, 0, "%g", dv->missing);
 
       for (j = 0; j < DSC_N_STATS; j++)
        if (dsc->show_stats & (1ul << j))
index febb60fedeeb5d5146253c5b3c434bece64ff79e..e8333b1ceeaa5257d922b9be4700bc6aec8ef7d1 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);
+      int i;
 
-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);
+       }
+
+      for (i = 0; i < 2; i++)
+        if (fctr->indep_var[i])
+          value_destroy (&result->value[i],
+                         var_get_width (fctr->indep_var[i]));
+      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 +244,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 +272,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 +299,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 +748,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 +766,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 +778,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 +800,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 +819,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,342 +871,381 @@ 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));
+  int i;
+
+  for (i = 0; i < 2; i++)
+    if (factor->indep_var[i])
+      value_init (&result->value[i], var_get_width (factor->indep_var[i]));
 
+  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);
 
-static void populate_descriptives (struct tab_table *t, int col, int row,
-                                  const struct variable *,
-                                  const struct metrics *fs);
+  c = casereader_peek (reader, 0);
+  if (c != NULL)
+    {
+      if ( level > 0)
+        for (i = 0; i < 2; i++)
+          if (factor->indep_var[i])
+            value_copy (&result->value[i], case_data (c, factor->indep_var[i]),
+                        var_get_width (factor->indep_var[i]));
+      case_unref (c);
+    }
 
-static void populate_extremes (struct tab_table *t, int col, int row, int n,
-                              const struct variable *var,
-                              const struct metrics *m);
+  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_proto (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_proto (reader));
+       }
 
-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)
+       {
+          int n_vals = caseproto_get_n_widths (casereader_get_proto (reader));
+         const casenumber loc = case_data_idx (c, n_vals - 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);
 
-/* 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;
+         moments1_add (result->metrics[v].moments,
+                       value->f,
+                       weight);
 
-  while ( fctr)
-    {
-      struct factor_statistics **foo ;
-      union value *indep_vals[2] ;
+         result->metrics[v].n += weight;
 
-      indep_vals[0] = value_dup (
-                                case_data (c, fctr->indep_var[0]),
-                                var_get_width (fctr->indep_var[0])
-                                );
+         if ( ! var_is_value_missing (dependent_vars[v], value, MV_ANY) )
+           result->metrics[v].n_valid += weight;
 
-      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].maxima,
+                      value->f,
+                      weight,
+                      loc);
 
-      assert (fctr->fstats);
+         extrema_add (result->metrics[v].minima,
+                      value->f,
+                      weight,
+                      loc);
 
-      foo = ( struct factor_statistics ** )
-       hsh_probe (fctr->fstats, (void *) &indep_vals);
+         casewriter_write (writer, c);
+       }
+      casereader_destroy (input);
+      result->metrics[v].up_reader = casewriter_make_reader (writer);
+    }
 
-      if ( !*foo )
+  /* 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 = create_factor_statistics (n_dependent_vars,
-                                         indep_vals[0],
-                                         indep_vals[1]);
+         metric->n_ptiles = percentile_list.n_data;
 
-         for ( v =  0 ; v  < n_dependent_vars ; ++v )
+         metric->ptl = xcalloc (metric->n_ptiles,
+                                sizeof (struct percentile *));
+
+         metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
+
+         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_valid, metric->cmin);
+         metric->trimmed_mean = trimmed_mean_create (metric->n_valid, 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];
+          int n_vals = caseproto_get_n_widths (casereader_get_proto (
+                                                 metric->up_reader));
+
+         metric->box_whisker =
+           box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
+                               cmd->v_id, n_vals - 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);
 
-      fctr = fctr->next;
+             while (casegrouper_get_next_group (grouper2, &group2))
+               {
+                 examine_group (cmd, group2, 2, dict, factor);
+                 n_groups++;
+               }
+             casegrouper_destroy (grouper2);
+           }
+
+         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;
 
-  if ( fctr )
+  assert (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);
+  tab_dim (tbl, tab_natural_dimensions, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -979,12 +1269,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 +1283,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 +1316,884 @@ 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;
+      const 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 ||
+                  !value_equal (last_value, &result->value[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,
-                           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_format (tbl, heading_columns + 1,
+                           heading_rows + j + v * ll_count (&fctr->result_list),
+                           TAB_RIGHT,
+                           "%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_format (tbl, heading_columns + 3,
+                           heading_rows + j + v * ll_count (&fctr->result_list),
+                           TAB_RIGHT,
+                           "%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_format (tbl, heading_columns + 5,
+                           heading_rows + j + v * ll_count (&fctr->result_list),
+                           TAB_RIGHT,
+                           "%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);
+  tab_dim (tbl, tab_natural_dimensions, NULL);
 
-  /* 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 );
+         const struct factor_result *result =
+           ll_data (ll, struct factor_result, ll);
 
+         const double t =
+           gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0) / 2.0,
+                                      result->metrics[v].n - 1);
 
-             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);
-               }
-
-             populate_extremes (tbl, heading_columns - 2,
-                                row, n_extremities,
-                                dependent_var[i],
-                                & (*fs)->m[i]);
-
-             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 ;
+         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_value (t, col + 3, row + extremity + j  + n,
-                    TAB_RIGHT,
-                    &wv->v, var_get_print_format (var));
+             ds_destroy (&vstr);
+           }
 
-         tab_fixed (t, col + 2, row + extremity + j  + n,
-                     TAB_RIGHT,
-                     cn->num, 10, 0);
 
-         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_format (tbl, n_cols - 4,
+                           heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
+                           TAB_LEFT,
+                           _("%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, _("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, 0, heading_rows, 0);
 
-  tab_headers (tbl, heading_columns + 1, 0, heading_rows, 0);
-
-  tab_dim (tbl, tab_natural_dimensions);
+  tab_dim (tbl, tab_natural_dimensions, NULL);
 
-  /* 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_format (tbl, n_cols - 3,
+                               heading_rows + row_var_start + row_result_start + e - 1,
+                               TAB_RIGHT,
+                               "%d", e);
+
+             tab_text_format (tbl, n_cols - 3,
+                               heading_rows + row_var_start + row_result_start + cmd.st_n + e - 1,
+                               TAB_RIGHT,
+                               "%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, heading_columns, 0, heading_rows, 0);
 
-  tab_headers (tbl, n_heading_columns + 1, 0, n_heading_rows, 0);
-
-  tab_dim (tbl, tab_natural_dimensions);
+  tab_dim (tbl, tab_natural_dimensions, NULL);
 
-  /* 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"));
 
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
+  tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
 
-  {
-    /* Put in the percentile break points as headings */
+  if ( fctr->indep_var[0])
+    tab_text (tbl, 1, 1, TAT_TITLE, var_to_string (fctr->indep_var[0]));
 
-    struct percentile **p = (struct percentile **) hsh_sort (ptiles);
+  if ( fctr->indep_var[1])
+    tab_text (tbl, 2, 1, TAT_TITLE, var_to_string (fctr->indep_var[1]));
 
-    i = 0;
-    while ( (*p)  )
-      {
-       tab_fixed (tbl, n_heading_columns + i++ , 1,
-                   TAB_CENTER,
-                   (*p)->p,
-                   8, 0);
-       p++;
-      }
-
-  }
-
-  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_format (tbl, n_cols - n_percentiles + i, 1,
+                       TAB_CENTER | TAT_TITLE,
+                       _("%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..3ca2413fcf35f2d56bba7b2200cde2934bb92985 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
 #define _(msgid) gettext (msgid)
 
 /* List of variable names. */
-struct varname
+struct var_names
   {
-    struct varname *next;
-    char name[SHORT_NAME_LEN + 1];
+    const char **names;
+    size_t n_names, allocated_names;
   };
 
+static void var_names_init (struct var_names *);
+static void var_names_add (struct pool *, struct var_names *, const char *);
+
 /* Represents a FLIP input program. */
 struct flip_pgm
   {
     struct pool *pool;          /* Pool containing FLIP data. */
-    const struct variable **var;      /* Variables to transpose. */
-    size_t var_cnt;             /* Number of elements in `var'. */
-    int case_cnt;               /* Pre-flip case count. */
+    size_t n_vars;              /* Pre-flip number of variables. */
+    int n_cases;                /* Pre-flip number of cases. */
 
-    struct variable *new_names; /* Variable containing new variable names. */
-    struct varname *new_names_head; /* First new variable. */
-    struct varname *new_names_tail; /* Last new variable. */
+    struct variable *new_names_var; /* Variable with new variable names. */
+    struct var_names old_names; /* Variable names before FLIP. */
+    struct var_names new_names; /* Variable names after FLIP. */
 
     FILE *file;                 /* Temporary file containing data. */
-    union value *input_buf;     /* Input buffer for temporary file. */
     size_t cases_read;          /* Number of cases already read. */
     bool error;                 /* Error reading temporary file? */
   };
@@ -77,18 +78,17 @@ static const struct casereader_class flip_casereader_class;
 
 static void destroy_flip_pgm (struct flip_pgm *);
 static bool flip_file (struct flip_pgm *);
-static bool build_dictionary (struct dictionary *, struct flip_pgm *);
-static bool write_flip_case (struct flip_pgm *, const struct ccase *);
+static void make_new_var (struct dictionary *, const char *name);
 
 /* Parses and executes FLIP. */
 int
 cmd_flip (struct lexer *lexer, struct dataset *ds)
 {
   struct dictionary *dict = dataset_dict (ds);
+  const struct variable **vars;
   struct flip_pgm *flip;
   struct casereader *input, *reader;
-  union value *output_buf;
-  struct ccase c;
+  struct ccase *c;
   size_t i;
   bool ok;
 
@@ -97,14 +97,12 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
                "Temporary transformations will be made permanent."));
 
   flip = pool_create_container (struct flip_pgm, pool);
-  flip->var = NULL;
-  flip->var_cnt = 0;
-  flip->case_cnt = 0;
-  flip->new_names = NULL;
-  flip->new_names_head = NULL;
-  flip->new_names_tail = NULL;
+  flip->n_vars = 0;
+  flip->n_cases = 0;
+  flip->new_names_var = NULL;
+  var_names_init (&flip->old_names);
+  var_names_init (&flip->new_names);
   flip->file = NULL;
-  flip->input_buf = NULL;
   flip->cases_read = 0;
   flip->error = false;
 
@@ -112,39 +110,37 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
   if (lex_match_id (lexer, "VARIABLES"))
     {
       lex_match (lexer, '=');
-      if (!parse_variables_const (lexer, dict, &flip->var, &flip->var_cnt,
+      if (!parse_variables_const (lexer, dict, &vars, &flip->n_vars,
                                   PV_NO_DUPLICATE))
        goto error;
       lex_match (lexer, '/');
     }
   else
-    dict_get_vars (dict, &flip->var, &flip->var_cnt, DC_SYSTEM);
-  pool_register (flip->pool, free, flip->var);
+    dict_get_vars (dict, &vars, &flip->n_vars, DC_SYSTEM);
+  pool_register (flip->pool, free, vars);
 
   lex_match (lexer, '/');
   if (lex_match_id (lexer, "NEWNAMES"))
     {
       lex_match (lexer, '=');
-      flip->new_names = parse_variable (lexer, dict);
-      if (!flip->new_names)
+      flip->new_names_var = parse_variable (lexer, dict);
+      if (!flip->new_names_var)
         goto error;
     }
   else
-    flip->new_names = dict_lookup_var (dict, "CASE_LBL");
+    flip->new_names_var = dict_lookup_var (dict, "CASE_LBL");
 
-  if (flip->new_names)
+  if (flip->new_names_var)
     {
-      for (i = 0; i < flip->var_cnt; i++)
-       if (flip->var[i] == flip->new_names)
+      for (i = 0; i < flip->n_vars; i++)
+       if (vars[i] == flip->new_names_var)
          {
-            remove_element (flip->var, flip->var_cnt, sizeof *flip->var, i);
-           flip->var_cnt--;
+            remove_element (vars, flip->n_vars, sizeof *vars, i);
+           flip->n_vars--;
            break;
          }
     }
 
-  output_buf = pool_nalloc (flip->pool, flip->var_cnt, sizeof *output_buf);
-
   flip->file = pool_tmpfile (flip->pool);
   if (flip->file == NULL)
     {
@@ -152,27 +148,46 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
       goto error;
     }
 
-  /* Write variable names as first case. */
-  for (i = 0; i < flip->var_cnt; i++)
-    buf_copy_str_rpad (output_buf[i].s, MAX_SHORT_STRING,
-                       var_get_name (flip->var[i]));
-  if (fwrite (output_buf, sizeof *output_buf,
-              flip->var_cnt, flip->file) != (size_t) flip->var_cnt)
-    {
-      msg (SE, _("Error writing FLIP file: %s."), strerror (errno));
-      goto error;
-    }
-
-  flip->case_cnt = 1;
+  /* Save old variable names for use as values of CASE_LBL
+     variable in flipped file. */
+  for (i = 0; i < flip->n_vars; i++)
+    var_names_add (flip->pool, &flip->old_names,
+                   pool_strdup (flip->pool, var_get_name (vars[i])));
 
   /* Read the active file into a flip_sink. */
   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);
+      flip->n_cases++;
+      for (i = 0; i < flip->n_vars; i++)
+        {
+          const struct variable *v = vars[i];
+          double out = var_is_numeric (v) ? case_num (c, v) : SYSMIS;
+          fwrite (&out, sizeof out, 1, flip->file);
+        }
+      if (flip->new_names_var != NULL)
+        {
+          const union value *value = case_data (c, flip->new_names_var);
+          const char *name;
+          if (var_is_numeric (flip->new_names_var))
+            {
+              double f = value->f;
+              name = (f == SYSMIS ? "VSYSMIS"
+                      : f < INT_MIN ? "VNEGINF"
+                      : f > INT_MAX ? "VPOSINF"
+                      : pool_asprintf (flip->pool, "V%d", (int) f));
+            }
+          else
+            {
+              int width = var_get_width (flip->new_names_var);
+              name = pool_strdup0 (flip->pool,
+                                   value_str (value, width), width);
+            }
+          var_names_add (flip->pool, &flip->new_names, name);
+        }
+      case_unref (c);
     }
   ok = casereader_destroy (input);
   ok = proc_commit (ds) && ok;
@@ -186,15 +201,20 @@ cmd_flip (struct lexer *lexer, struct dataset *ds)
 
   /* Flip the dictionary. */
   dict_clear (dict);
-  if (!build_dictionary (dict, flip))
-    {
-      proc_discard_active_file (ds);
-      goto error;
-    }
+  dict_create_var_assert (dict, "CASE_LBL", 8);
+  for (i = 0; i < flip->n_cases; i++)
+    if (flip->new_names.n_names)
+      make_new_var (dict, flip->new_names.names[i]);
+    else
+      {
+        char s[VAR_NAME_LEN + 1];
+        sprintf (s, "VAR%03d", i);
+        dict_create_var_assert (dict, s, 0);
+      }
 
   /* Set up flipped data for reading. */
-  reader = casereader_create_sequential (NULL, dict_get_next_value_idx (dict),
-                                         flip->var_cnt,
+  reader = casereader_create_sequential (NULL, dict_get_proto (dict),
+                                         flip->n_vars,
                                          &flip_casereader_class, flip);
   proc_set_active_file_data (ds, reader);
   return lex_end_of_command (lexer);
@@ -213,10 +233,11 @@ destroy_flip_pgm (struct flip_pgm *flip)
 }
 
 /* Make a new variable with base name NAME, which is bowdlerized and
-   mangled until acceptable, and returns success. */
-static int
-make_new_var (struct dictionary *dict, char name[])
+   mangled until acceptable. */
+static void
+make_new_var (struct dictionary *dict, const char *name_)
 {
+  char *name = xstrdup (name_);
   char *cp;
 
   /* Trim trailing spaces. */
@@ -225,7 +246,7 @@ make_new_var (struct dictionary *dict, char name[])
     *--cp = '\0';
 
   /* Fix invalid characters. */
-  for (cp = name; *cp && cp < name + SHORT_NAME_LEN; cp++)
+  for (cp = name; *cp && cp < name + VAR_NAME_LEN; cp++)
     if (cp == name)
       {
         if (!lex_is_id1 (*cp) || *cp == '$')
@@ -239,115 +260,24 @@ make_new_var (struct dictionary *dict, char name[])
   *cp = '\0';
   str_uppercase (name);
 
-  if (dict_create_var (dict, name, 0))
-    return 1;
-
-  /* Add numeric extensions until acceptable. */
-  {
-    const int len = (int) strlen (name);
-    char n[SHORT_NAME_LEN + 1];
-    int i;
-
-    for (i = 1; i < 10000000; i++)
-      {
-       int ofs = MIN (7 - intlog10 (i), len);
-       memcpy (n, name, ofs);
-       sprintf (&n[ofs], "%d", i);
-
-       if (dict_create_var (dict, n, 0))
-         return 1;
-      }
-  }
-
-  msg (SE, _("Could not create acceptable variant for variable %s."), name);
-  return 0;
-}
-
-/* Make a new dictionary for all the new variable names. */
-static bool
-build_dictionary (struct dictionary *dict, struct flip_pgm *flip)
-{
-  dict_create_var_assert (dict, "CASE_LBL", 8);
-
-  if (flip->new_names_head == NULL)
+  /* Use the mangled name, if it is available, or add numeric
+     extensions until we find one that is. */
+  if (!dict_create_var (dict, name, 0))
     {
+      int len = strlen (name);
       int i;
-
-      if (flip->case_cnt > 99999)
-       {
-         msg (SE, _("Cannot create more than 99999 variable names."));
-         return false;
-       }
-
-      for (i = 0; i < flip->case_cnt - 1; i++)
-       {
-          struct variable *v;
-         char s[SHORT_NAME_LEN + 1];
-
-         sprintf (s, "VAR%03d", i);
-         v = dict_create_var_assert (dict, s, 0);
-       }
-    }
-  else
-    {
-      struct varname *v;
-
-      for (v = flip->new_names_head; v; v = v->next)
-        if (!make_new_var (dict, v->name))
-          return false;
-    }
-
-  return true;
-}
-
-/* Writes case C to the FLIP sink.
-   Returns true if successful, false if an I/O error occurred. */
-static bool
-write_flip_case (struct flip_pgm *flip, const struct ccase *c)
-{
-  size_t i;
-
-  flip->case_cnt++;
-
-  if (flip->new_names != NULL)
-    {
-      struct varname *v = pool_alloc (flip->pool, sizeof *v);
-      v->next = NULL;
-      if (var_is_numeric (flip->new_names))
+      for (i = 1; ; i++)
         {
-          double f = case_num (c, flip->new_names);
-
-          if (f == SYSMIS)
-            strcpy (v->name, "VSYSMIS");
-          else if (f < INT_MIN)
-            strcpy (v->name, "VNEGINF");
-          else if (f > INT_MAX)
-            strcpy (v->name, "VPOSINF");
-          else
-            snprintf (v->name, sizeof v->name, "V%d", (int) f);
-        }
-      else
-       {
-         int width = MIN (var_get_width (flip->new_names), MAX_SHORT_STRING);
-         memcpy (v->name, case_str (c, flip->new_names), width);
-         v->name[width] = 0;
-       }
-
-      if (flip->new_names_head == NULL)
-       flip->new_names_head = v;
-      else
-       flip->new_names_tail->next = v;
-      flip->new_names_tail = v;
-    }
+          char n[VAR_NAME_LEN + 1];
+          int ofs = MIN (VAR_NAME_LEN - 1 - intlog10 (i), len);
+          memcpy (n, name, ofs);
+          sprintf (&n[ofs], "%d", i);
 
-  /* Write to external file. */
-  for (i = 0; i < flip->var_cnt; i++)
-    {
-      const struct variable *v = flip->var[i];
-      double out = var_is_numeric (v) ? case_num (c, v) : SYSMIS;
-      fwrite (&out, sizeof out, 1, flip->file);
+          if (dict_create_var (dict, n, 0))
+            break;
+        }
     }
-  return true;
+  free (name);
 }
 
 /* Transposes the external file into a new file. */
@@ -357,14 +287,14 @@ flip_file (struct flip_pgm *flip)
   size_t case_bytes;
   size_t case_capacity;
   size_t case_idx;
-  union value *input_buf, *output_buf;
+  double *input_buf, *output_buf;
   FILE *input_file, *output_file;
 
   /* Allocate memory for many cases. */
-  case_bytes = flip->var_cnt * sizeof *input_buf;
+  case_bytes = flip->n_vars * sizeof *input_buf;
   case_capacity = settings_get_workspace () / case_bytes;
-  if (case_capacity > flip->case_cnt * 2)
-    case_capacity = flip->case_cnt * 2;
+  if (case_capacity > flip->n_cases * 2)
+    case_capacity = flip->n_cases * 2;
   if (case_capacity < 2)
     case_capacity = 2;
   for (;;)
@@ -386,7 +316,7 @@ flip_file (struct flip_pgm *flip)
   /* Use half the allocated memory for input_buf, half for
      output_buf. */
   case_capacity /= 2;
-  output_buf = input_buf + flip->var_cnt * case_capacity;
+  output_buf = input_buf + flip->n_vars * case_capacity;
 
   input_file = flip->file;
   if (fseek (input_file, 0, SEEK_SET) != 0)
@@ -402,9 +332,9 @@ flip_file (struct flip_pgm *flip)
       return false;
     }
 
-  for (case_idx = 0; case_idx < flip->case_cnt; )
+  for (case_idx = 0; case_idx < flip->n_cases; )
     {
-      unsigned long read_cases = MIN (flip->case_cnt - case_idx,
+      unsigned long read_cases = MIN (flip->n_cases - case_idx,
                                       case_capacity);
       size_t i;
 
@@ -417,16 +347,16 @@ flip_file (struct flip_pgm *flip)
           return false;
         }
 
-      for (i = 0; i < flip->var_cnt; i++)
+      for (i = 0; i < flip->n_vars; i++)
        {
          unsigned long j;
 
          for (j = 0; j < read_cases; j++)
-           output_buf[j] = input_buf[i + j * flip->var_cnt];
+           output_buf[j] = input_buf[i + j * flip->n_vars];
 
          if (fseeko (output_file,
                       sizeof *input_buf * (case_idx
-                                           + (off_t) i * flip->case_cnt),
+                                           + (off_t) i * flip->n_cases),
                       SEEK_SET) != 0)
             {
               msg (SE, _("Error seeking FLIP source file: %s."),
@@ -464,26 +394,27 @@ 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, void *flip_)
 {
   struct flip_pgm *flip = flip_;
+  struct ccase *c;
   size_t i;
 
-  if (flip->error || flip->cases_read >= flip->var_cnt)
+  if (flip->error || flip->cases_read >= flip->n_vars)
     return false;
 
-  case_create (c, flip->case_cnt);
-  for (i = 0; i < flip->case_cnt; i++)
+  c = case_create (casereader_get_proto (reader));
+  value_copy_str_rpad (case_data_rw_idx (c, 0), 8,
+                       flip->old_names.names[flip->cases_read], ' ');
+  for (i = 0; i < flip->n_cases; 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 +423,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;
+      case_data_rw_idx (c, i + 1)->f = in;
     }
 
   flip->cases_read++;
 
-  return true;
+  return c;
 }
 
 /* Destroys the source.
@@ -521,3 +452,21 @@ static const struct casereader_class flip_casereader_class =
     NULL,
     NULL,
   };
+\f
+static void
+var_names_init (struct var_names *vn)
+{
+  vn->names = NULL;
+  vn->n_names = 0;
+  vn->allocated_names = 0;
+}
+
+static void
+var_names_add (struct pool *pool, struct var_names *vn, const char *name)
+{
+  if (vn->n_names >= vn->allocated_names)
+    vn->names = pool_2nrealloc (pool, vn->names, &vn->allocated_names,
+                                sizeof *vn->names);
+  vn->names[vn->n_names++] = name;
+}
+
index f2054380b8807fff50107d3a5b8902a484a6e742..1a0ecd498636561b9fb225f96baa8d9bdc82e409 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
@@ -30,24 +30,24 @@ 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 value_compare_3way (&f1->value, &f2->value, var_get_width (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 value_hash (&f->value, var_get_width (var), 0);
 }
 
 /* Free function to be used on FR whose value parameter has been copied */
 void
-free_freq_mutable_hash (void *fr, const void *var UNUSED)
+free_freq_mutable_hash (void *fr, const void *var_)
 {
+  const struct variable *var = var_;
   struct freq_mutable *freq = fr;
-  free (freq->value);
+  value_destroy (&freq->value, var_get_width (var));
   free (freq);
 }
 
index 98af8a6f0483785c375783ed4c4ddea2706ab3d6..b06a36f6b2fb14f5fbc47f7f9db6d6d924dab893 100644 (file)
@@ -21,14 +21,14 @@ union value ;
 /* Frequency table entry. */
 struct freq
   {
-    const union value *value;  /* The value. */
+    const union value value;   /* The value. */
     double count;              /* The number of occurrences of the value. */
   };
 
 /* Non const version of frequency table entry. */
 struct freq_mutable
   {
-    union value *value;                /* The value. */
+    union value value;         /* The value. */
     double count;              /* The number of occurrences of the value. */
   };
 
index a09ecc100759b76d2ecf4d682a050c9999be79f5..83b864c9f695c4494c5b5f2204b9d4ecd0054c46 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
@@ -205,10 +205,12 @@ static struct pool *syntax_pool;        /* For syntax-related data. */
 struct freq_tab
   {
     struct hsh_table *data;    /* Undifferentiated data. */
-    struct freq *valid;         /* Valid freqs. */
+    struct freq_mutable *valid; /* Valid freqs. */
     int n_valid;               /* Number of total freqs. */
+    const struct dictionary *dict; /* The dict from whence entries in the table
+                                     come */
 
-    struct freq *missing;      /* Missing freqs. */
+    struct freq_mutable *missing; /* Missing freqs. */
     int n_missing;             /* Number of missing freqs. */
 
     /* Statistics. */
@@ -230,11 +232,7 @@ struct var_freqs
     /* Statistics. */
     double stat[frq_n_stats];
 
-    /* Width and format for analysis and display.
-       This is normally the same as "width" and "print" in struct
-       variable, but in SPSS-compatible mode only the first
-       MAX_SHORT_STRING bytes of long string variables are
-       included. */
+    /* Variable attributes. */
     int width;
     struct fmt_spec print;
   };
@@ -268,7 +266,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 +374,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);
@@ -510,21 +508,20 @@ calc (const struct ccase *c, const struct dataset *ds)
       struct var_freqs *vf = get_var_freqs (v);
       struct freq_tab *ft = &vf->tab;
 
-      struct freq target;
-      struct freq **fpp;
+      struct freq_mutable target;
+      struct freq_mutable **fpp;
 
-      target.value = (union value *) val;
-      fpp = (struct freq **) hsh_probe (ft->data, &target);
+      target.value = *val;
+      fpp = (struct freq_mutable **) hsh_probe (ft->data, &target);
 
       if (*fpp != NULL)
         (*fpp)->count += weight;
       else
         {
-          struct freq *fp = pool_alloc (data_pool, sizeof *fp);
+          struct freq_mutable *fp = pool_alloc (data_pool, sizeof *fp);
           fp->count = weight;
-          fp->value = pool_clone (data_pool,
-                                  val,
-                                  MAX (MAX_SHORT_STRING, vf->width));
+          value_init_pool (data_pool, &fp->value, vf->width);
+          value_copy (&fp->value, val, vf->width);
           *fpp = fp;
         }
     }
@@ -535,13 +532,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);
@@ -605,34 +603,29 @@ postcalc (const struct dataset *ds)
 
 
 
-      if ( chart == GFT_HIST)
+      if ( chart == GFT_HIST && var_is_numeric (v) )
        {
          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);
 
     }
@@ -660,15 +653,15 @@ get_freq_comparator (int frq_sort, enum val_type val_type)
     }
 }
 
-/* Returns true iff the value in struct freq F is non-missing
+/* Returns true iff the value in struct freq_mutable F is non-missing
    for variable V. */
 static bool
 not_missing (const void *f_, const void *v_)
 {
-  const struct freq *f = f_;
+  const struct freq_mutable *f = f_;
   const struct variable *v = v_;
 
-  return !var_is_value_missing (v, f->value, MV_ANY);
+  return !var_is_value_missing (v, &f->value, MV_ANY);
 }
 
 /* Summarizes the frequency table data for variable V. */
@@ -679,7 +672,7 @@ postprocess_freq_tab (const struct variable *v)
   struct freq_tab *ft;
   size_t count;
   void *const *data;
-  struct freq *freqs, *f;
+  struct freq_mutable *freqs, *f;
   size_t i;
 
   ft = &get_var_freqs (v)->tab;
@@ -693,7 +686,7 @@ postprocess_freq_tab (const struct variable *v)
   freqs = xnmalloc (count, sizeof *freqs);
   for (i = 0; i < count; i++)
     {
-      struct freq *f = data[i];
+      struct freq_mutable *f = data[i];
       freqs[i] = *f;
     }
 
@@ -764,16 +757,11 @@ frq_custom_variables (struct lexer *lexer, struct dataset *ds, struct cmd_freque
        }
       vf = var_attach_aux (v, xmalloc (sizeof *vf), var_dtor_free);
       vf->tab.valid = vf->tab.missing = NULL;
+      vf->tab.dict = dataset_dict (ds);
       vf->n_groups = 0;
       vf->groups = NULL;
       vf->width = var_get_width (v);
       vf->print = *var_get_print_format (v);
-      if (vf->width > MAX_SHORT_STRING && settings_get_algorithm () == COMPATIBLE)
-        {
-          enum fmt_type type = var_get_print_format (v)->type;
-          vf->width = MAX_SHORT_STRING;
-          vf->print.w = MAX_SHORT_STRING * (type == FMT_AHEX ? 2 : 1);
-        }
     }
   return 1;
 }
@@ -895,12 +883,12 @@ add_percentile (double x)
 static int
 compare_value_numeric_a (const void *a_, const void *b_, const void *aux UNUSED)
 {
-  const struct freq *a = a_;
-  const struct freq *b = b_;
+  const struct freq_mutable *a = a_;
+  const struct freq_mutable *b = b_;
 
-  if (a->value[0].f > b->value[0].f)
+  if (a->value.f > b->value.f)
     return 1;
-  else if (a->value[0].f < b->value[0].f)
+  else if (a->value.f < b->value.f)
     return -1;
   else
     return 0;
@@ -910,12 +898,12 @@ compare_value_numeric_a (const void *a_, const void *b_, const void *aux UNUSED)
 static int
 compare_value_alpha_a (const void *a_, const void *b_, const void *v_)
 {
-  const struct freq *a = a_;
-  const struct freq *b = b_;
+  const struct freq_mutable *a = a_;
+  const struct freq_mutable *b = b_;
   const struct variable *v = v_;
   struct var_freqs *vf = get_var_freqs (v);
 
-  return memcmp (a->value[0].s, b->value[0].s, vf->width);
+  return value_compare_3way (&a->value, &b->value, vf->width);
 }
 
 /* Descending numeric compare of values. */
@@ -937,17 +925,17 @@ compare_value_alpha_d (const void *a, const void *b, const void *v)
 static int
 compare_freq_numeric_a (const void *a_, const void *b_, const void *aux UNUSED)
 {
-  const struct freq *a = a_;
-  const struct freq *b = b_;
+  const struct freq_mutable *a = a_;
+  const struct freq_mutable *b = b_;
 
   if (a->count > b->count)
     return 1;
   else if (a->count < b->count)
     return -1;
 
-  if (a->value[0].f > b->value[0].f)
+  if (a->value.f > b->value.f)
     return 1;
-  else if (a->value[0].f < b->value[0].f)
+  else if (a->value.f < b->value.f)
     return -1;
   else
     return 0;
@@ -958,8 +946,8 @@ compare_freq_numeric_a (const void *a_, const void *b_, const void *aux UNUSED)
 static int
 compare_freq_alpha_a (const void *a_, const void *b_, const void *v_)
 {
-  const struct freq *a = a_;
-  const struct freq *b = b_;
+  const struct freq_mutable *a = a_;
+  const struct freq_mutable *b = b_;
   const struct variable *v = v_;
   struct var_freqs *vf = get_var_freqs (v);
 
@@ -968,7 +956,7 @@ compare_freq_alpha_a (const void *a_, const void *b_, const void *v_)
   else if (a->count < b->count)
     return -1;
   else
-    return memcmp (a->value[0].s, b->value[0].s, vf->width);
+    return value_compare_3way (&a->value, &b->value, vf->width);
 }
 
 /* Descending numeric compare of frequency;
@@ -976,17 +964,17 @@ compare_freq_alpha_a (const void *a_, const void *b_, const void *v_)
 static int
 compare_freq_numeric_d (const void *a_, const void *b_, const void *aux UNUSED)
 {
-  const struct freq *a = a_;
-  const struct freq *b = b_;
+  const struct freq_mutable *a = a_;
+  const struct freq_mutable *b = b_;
 
   if (a->count > b->count)
     return -1;
   else if (a->count < b->count)
     return 1;
 
-  if (a->value[0].f > b->value[0].f)
+  if (a->value.f > b->value.f)
     return 1;
-  else if (a->value[0].f < b->value[0].f)
+  else if (a->value.f < b->value.f)
     return -1;
   else
     return 0;
@@ -997,8 +985,8 @@ compare_freq_numeric_d (const void *a_, const void *b_, const void *aux UNUSED)
 static int
 compare_freq_alpha_d (const void *a_, const void *b_, const void *v_)
 {
-  const struct freq *a = a_;
-  const struct freq *b = b_;
+  const struct freq_mutable *a = a_;
+  const struct freq_mutable *b = b_;
   const struct variable *v = v_;
   struct var_freqs *vf = get_var_freqs (v);
 
@@ -1007,7 +995,7 @@ compare_freq_alpha_d (const void *a_, const void *b_, const void *v_)
   else if (a->count < b->count)
     return 1;
   else
-    return memcmp (a->value[0].s, b->value[0].s, vf->width);
+    return value_compare_3way (&a->value, &b->value, vf->width);
 }
 \f
 /* Frequency table display. */
@@ -1015,7 +1003,7 @@ compare_freq_alpha_d (const void *a_, const void *b_, const void *v_)
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-full_dim (struct tab_table *t, struct outp_driver *d)
+full_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   int i = 0;
   int columns = 5;
@@ -1042,7 +1030,7 @@ dump_full (const struct variable *v, const struct variable *wv)
   int n_categories;
   struct var_freqs *vf;
   struct freq_tab *ft;
-  struct freq *f;
+  struct freq_mutable *f;
   struct tab_table *t;
   int r;
   double cum_total = 0.0;
@@ -1079,7 +1067,7 @@ dump_full (const struct variable *v, const struct variable *wv)
   n_categories = ft->n_valid + ft->n_missing;
   t = tab_create (5 + lab, n_categories + 3, 0);
   tab_headers (t, 0, 0, 2, 0);
-  tab_dim (t, full_dim);
+  tab_dim (t, full_dim, NULL);
 
   if (lab)
     tab_text (t, 0, 1, TAB_CENTER | TAT_TITLE, _("Value Label"));
@@ -1101,12 +1089,12 @@ dump_full (const struct variable *v, const struct variable *wv)
 
       if (lab)
        {
-         const char *label = var_lookup_value_label (v, &f->value[0]);
+         const char *label = var_lookup_value_label (v, &f->value);
          if (label != NULL)
            tab_text (t, 0, r, TAB_LEFT, label);
        }
 
-      tab_value (t, 0 + lab, r, TAB_NONE, f->value, &vf->print);
+      tab_value (t, 0 + lab, r, TAB_NONE, &f->value, ft->dict, &vf->print);
       tab_double (t, 1 + lab, r, TAB_NONE, f->count, wfmt);
       tab_double (t, 2 + lab, r, TAB_NONE, percent, NULL);
       tab_double (t, 3 + lab, r, TAB_NONE, valid_percent, NULL);
@@ -1119,12 +1107,12 @@ dump_full (const struct variable *v, const struct variable *wv)
 
       if (lab)
        {
-         const char *label = var_lookup_value_label (v, &f->value[0]);
+         const char *label = var_lookup_value_label (v, &f->value);
          if (label != NULL)
            tab_text (t, 0, r, TAB_LEFT, label);
        }
 
-      tab_value (t, 0 + lab, r, TAB_NONE, f->value, &vf->print);
+      tab_value (t, 0 + lab, r, TAB_NONE, &f->value, ft->dict, &vf->print);
       tab_double (t, 1 + lab, r, TAB_NONE, f->count, wfmt);
       tab_double (t, 2 + lab, r, TAB_NONE,
                     f->count / ft->total_cases * 100.0, NULL);
@@ -1150,7 +1138,7 @@ dump_full (const struct variable *v, const struct variable *wv)
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-condensed_dim (struct tab_table *t, struct outp_driver *d)
+condensed_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   int cum_w = MAX (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
                   MAX (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
@@ -1174,7 +1162,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   int n_categories;
   struct var_freqs *vf;
   struct freq_tab *ft;
-  struct freq *f;
+  struct freq_mutable *f;
   struct tab_table *t;
   int r;
   double cum_total = 0.0;
@@ -1190,7 +1178,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Cum"));
   tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
-  tab_dim (t, condensed_dim);
+  tab_dim (t, condensed_dim, NULL);
 
   r = 2;
   for (f = ft->valid; f < ft->missing; f++)
@@ -1200,7 +1188,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
       percent = f->count / ft->total_cases * 100.0;
       cum_total += f->count / ft->valid_cases * 100.0;
 
-      tab_value (t, 0, r, TAB_NONE, f->value, &vf->print);
+      tab_value (t, 0, r, TAB_NONE, &f->value, ft->dict, &vf->print);
       tab_double (t, 1, r, TAB_NONE, f->count, wfmt);
       tab_double (t, 2, r, TAB_NONE, percent, NULL);
       tab_double (t, 3, r, TAB_NONE, cum_total, NULL);
@@ -1208,7 +1196,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
     }
   for (; f < &ft->valid[n_categories]; f++)
     {
-      tab_value (t, 0, r, TAB_NONE, f->value, &vf->print);
+      tab_value (t, 0, r, TAB_NONE, &f->value, ft->dict, &vf->print);
       tab_double (t, 1, r, TAB_NONE, f->count, wfmt);
       tab_double (t, 2, r, TAB_NONE,
                 f->count / ft->total_cases * 100.0, NULL);
@@ -1234,7 +1222,7 @@ calc_stats (const struct variable *v, double d[frq_n_stats])
   struct freq_tab *ft = &get_var_freqs (v)->tab;
   double W = ft->valid_cases;
   struct moments *m;
-  struct freq *f=0;
+  struct freq_mutable *f=0;
   int most_often;
   double X_mode;
 
@@ -1270,7 +1258,7 @@ calc_stats (const struct variable *v, double d[frq_n_stats])
 
          if ( percentiles[i].flag )
            {
-             percentiles[i].x2 = f->value[0].f;
+             percentiles[i].x2 = f->value.f;
              percentiles[i].x1 = prev_value;
              percentiles[i].flag2 = 1;
              continue;
@@ -1280,7 +1268,7 @@ calc_stats (const struct variable *v, double d[frq_n_stats])
          {
            if ( f->count > 1 && rank - (f->count - 1) > tp )
              {
-               percentiles[i].x2 = percentiles[i].x1 = f->value[0].f;
+               percentiles[i].x2 = percentiles[i].x1 = f->value.f;
                percentiles[i].flag2 = 1;
              }
            else
@@ -1291,14 +1279,14 @@ calc_stats (const struct variable *v, double d[frq_n_stats])
            continue;
          }
         }
-      prev_value = f->value[0].f;
+      prev_value = f->value.f;
     }
 
   for (i = 0; i < n_percentiles; i++)
     {
       /* Catches the case when p == 100% */
       if ( ! percentiles[i].flag2 )
-       percentiles[i].x1 = percentiles[i].x2 = f->value[0].f;
+       percentiles[i].x1 = percentiles[i].x2 = f->value.f;
 
       /*
       printf("percentile %d (p==%.2f); X1 = %g; X2 = %g\n",
@@ -1334,7 +1322,7 @@ calc_stats (const struct variable *v, double d[frq_n_stats])
       if (most_often < f->count)
         {
           most_often = f->count;
-          X_mode = f->value[0].f;
+          X_mode = f->value.f;
         }
       else if (most_often == f->count)
         {
@@ -1347,16 +1335,16 @@ calc_stats (const struct variable *v, double d[frq_n_stats])
   /* Calculate moments. */
   m = moments_create (MOMENT_KURTOSIS);
   for (f = ft->valid; f < ft->missing; f++)
-    moments_pass_one (m, f->value[0].f, f->count);
+    moments_pass_one (m, f->value.f, f->count);
   for (f = ft->valid; f < ft->missing; f++)
-    moments_pass_two (m, f->value[0].f, f->count);
+    moments_pass_two (m, f->value.f, f->count);
   moments_calculate (m, NULL, &d[frq_mean], &d[frq_variance],
                      &d[frq_skew], &d[frq_kurt]);
   moments_destroy (m);
 
   /* Formulas below are taken from _SPSS Statistical Algorithms_. */
-  d[frq_min] = ft->valid[0].value[0].f;
-  d[frq_max] = ft->valid[ft->n_valid - 1].value[0].f;
+  d[frq_min] = ft->valid[0].value.f;
+  d[frq_max] = ft->valid[ft->n_valid - 1].value.f;
   d[frq_mode] = X_mode;
   d[frq_range] = d[frq_max] - d[frq_min];
   d[frq_sum] = d[frq_mean] * W;
@@ -1389,7 +1377,7 @@ dump_statistics (const struct variable *v, bool show_varname,
   calc_stats (v, stat_value);
 
   t = tab_create (3, n_stats + n_percentiles + 2, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   tab_box (t, TAL_1, TAL_1, -1, -1 , 0 , 0 , 2, tab_nr(t) - 1) ;
 
@@ -1442,39 +1430,39 @@ 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;
   struct hsh_table *fh = ft->data;
-  struct freq *frq;
+  struct freq_mutable *frq;
 
   /* Find out the extremes of the x value */
   for ( frq = hsh_first(fh, &hi); frq != 0; frq = hsh_next(fh, &hi) )
     {
-      if (var_is_value_missing(var, frq->value, MV_ANY))
+      if (var_is_value_missing(var, &frq->value, MV_ANY))
        continue;
 
-      if ( frq->value[0].f < x_min ) x_min = frq->value[0].f ;
-      if ( frq->value[0].f > x_max ) x_max = frq->value[0].f ;
+      if ( frq->value.f < x_min ) x_min = frq->value.f ;
+      if ( frq->value.f > x_max ) x_max = frq->value.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.f, frq->count);
     }
 
-  return hist;
+  return (struct histogram *)hist;
 }
 
 
@@ -1502,10 +1490,10 @@ freq_tab_to_slice_array(const struct freq_tab *frq_tab,
 
   for (i = 0 ; i < *n_slices ; ++i )
     {
-      const struct freq *frq = &frq_tab->valid[i];
+      const struct freq_mutable *frq = &frq_tab->valid[i];
 
       ds_init_empty (&slices[i].label);
-      var_append_value_name (var, frq->value, &slices[i].label);
+      var_append_value_name (var, &frq->value, &slices[i].label);
       slices[i].magnetude = frq->count;
     }
 
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..d626dffac5f72075a582c370cb5940ec3845e2d1 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);
 
@@ -86,11 +86,6 @@ npar_summary_calc_descriptives (struct descriptives *desc,
 }
 
 
-void
-do_summary_box (const struct descriptives *desc,
-               const struct variable *const *vv,
-               int n_vars);
-
 
 void
 do_summary_box (const struct descriptives *desc,
@@ -109,7 +104,7 @@ do_summary_box (const struct descriptives *desc,
 
   table = tab_create (columns, 2 + n_vars, 0);
 
-  tab_dim (table, tab_natural_dimensions);
+  tab_dim (table, tab_natural_dimensions, NULL);
 
   tab_title (table, _("Descriptive Statistics"));
 
index 0e52b8023355cf81c6f76ec73451d1b10c489231..a8b12797c1c8b6667de481a41c5f05644a6a08d7 100644 (file)
@@ -30,6 +30,11 @@ struct descriptives
   double max;
 };
 
+void
+do_summary_box (const struct descriptives *desc,
+               const struct variable *const *vv,
+               int n_vars);
+
 void npar_summary_calc_descriptives (struct descriptives *desc,
                                     struct casereader *input,
                                     const struct dictionary *dict,
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..bbccce671f075b161b1e95170d07197c6a3ac69d 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, 2009 Free Software Foundation, Inc.
 
    This 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,8 @@
 #include <language/lexer/variable-parser.h>
 #include <language/stats/binomial.h>
 #include <language/stats/chisquare.h>
+#include <language/stats/wilcoxon.h>
+#include <language/stats/sign.h>
 #include <libpspp/hash.h>
 #include <libpspp/pool.h>
 #include <libpspp/taint.h>
@@ -53,7 +55,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 +73,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 +109,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 +137,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 +145,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) )
     {
@@ -149,7 +160,7 @@ cmd_npar_tests (struct lexer *lexer, struct dataset *ds)
       test->insert_variables (test, var_hash);
     }
 
-  npar_specs.vv = (const struct variable **) const_hsh_data (var_hash);
+  npar_specs.vv = (const struct variable **) const_hsh_sort (var_hash);
   npar_specs.n_vars = const_hsh_count (var_hash);
 
   if ( cmd.sbc_statistics )
@@ -183,10 +194,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 +217,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 +229,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 +323,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 +350,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 +412,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 +465,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 +477,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 +492,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 +511,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) )
@@ -542,8 +558,7 @@ npar_custom_sign (struct lexer *lexer, struct dataset *ds,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
-  ((struct npar_test *)tp)->execute = NULL;
-
+  ((struct npar_test *) tp)->execute = sign_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..0f6b20a1db04cdf9503e4a21cd67cb2f22ecf6bf 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,9 +259,9 @@ show_anova_table(void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
 
   tab_box (t,
@@ -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,14 +365,14 @@ 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);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
 
   /* Put a frame around the entire box, and vertical lines inside */
@@ -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,11 @@ 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_format (t, 6, 0, 7, 0, TAB_CENTER | TAT_TITLE,
+                         _("%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 +409,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 +423,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 +473,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 +503,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,9 +517,9 @@ show_homogeneity(void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 1, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -533,28 +529,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);
 
@@ -577,15 +572,14 @@ 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);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -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,20 +611,23 @@ 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)
     {
+      double *group_value_p;
+      union value group_value;
       int i;
       struct string vstr;
-      group_value = group_values[count];
 
       ds_init_empty (&vstr);
 
-      var_append_value_name (indep_var, group_value, &vstr);
+      group_value_p = group_values[count];
+      group_value.f = *group_value_p;
+      var_append_value_name (indep_var, &group_value, &vstr);
 
       tab_text (t, count + 2, 1, TAB_CENTER | TAT_TITLE,
                ds_cstr (&vstr));
@@ -638,16 +635,15 @@ 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_format (t, 1, i + 2, TAB_CENTER, "%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_format (t, count + 2, i + 2, TAB_RIGHT, "%g",
+                             subc_list_double_at (&cmd.dl_contrast[i], count));
        }
     }
 
@@ -657,7 +653,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,9 +661,9 @@ show_contrast_tests(short *bad_contrast)
 
   struct tab_table *t;
 
-  t = tab_create (n_cols,n_rows,0);
+  t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 3, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -677,34 +673,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 +711,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)
             }
          */
@@ -744,36 +740,37 @@ show_contrast_tests(short *bad_contrast)
                        _("Does not assume equal"));
            }
 
-         tab_text (t,  2, (v * lines_per_variable) + i + 1,
-                   TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1);
+         tab_text_format (t,  2, (v * lines_per_variable) + i + 1,
+                           TAB_CENTER | TAT_TITLE, "%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_text_format (t,  2,
+                           (v * lines_per_variable) + i + 1 + cmd.sbc_contrast,
+                           TAB_CENTER | TAT_TITLE, "%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 +788,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 +809,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 +817,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 +835,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 +847,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,27 +868,38 @@ 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;
     }
 }
 
+static int
+compare_double_3way (const void *a_, const void *b_, const void *aux UNUSED)
+{
+  const double *a = a_;
+  const double *b = b_;
+  return *a < *b ? -1 : *a > *b;
+}
+
+static unsigned
+do_hash_double (const void *value_, const void *aux UNUSED)
+{
+  const double *value = value_;
+  return hash_double (*value, 0);
+}
+
 static void
-free_value (void *value_, const void *aux UNUSED)
+free_double (void *value_, const void *aux UNUSED)
 {
-  union value *value = value_;
+  double *value = value_;
   free (value);
 }
 
@@ -909,80 +912,84 @@ 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_double_3way,
+                                 do_hash_double,
+                                 free_double,
+                                 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);
-      void **p = hsh_probe (global_group_hash, indep_val);
+      const union value *indep_val = case_data (c, indep_var);
+      void **p = hsh_probe (global_group_hash, &indep_val->f);
       if (*p == NULL)
-        *p = value_dup (indep_val, var_get_width (indep_var));
+        {
+          double *value = *p = xmalloc (sizeof *value);
+          *value = indep_val->f;
+        }
 
-      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 +997,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 +1014,7 @@ run_oneway (struct cmd_oneway *cmd,
     }
   casereader_destroy (reader);
 
-  postcalc(cmd);
+  postcalc (cmd);
 
 
   if ( stat_tables & STAT_HOMO )
@@ -1028,10 +1035,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 +1046,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->mean = gs->sum / gs->n;
+         gs->s_std_dev = sqrt (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->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..c225370e459269cc572430f87e533f7a591662c4 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;
@@ -249,25 +249,22 @@ rank_cmd (struct dataset *ds, const struct case_ordering *sc,
 
       proc_discard_output (ds);
       split_grouper = casegrouper_create_splits (proc_open (ds), d);
-      output = autopaging_writer_create (dict_get_next_value_idx (d));
+      output = autopaging_writer_create (dict_get_proto (d));
 
       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.
@@ -715,48 +714,44 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
 
                  if ( rank_specs[i].rfunc == NORMAL ||
                       rank_specs[i].rfunc == PROPORTION )
-                   tab_output_text (TAT_PRINTF,
-                                    _("%s into %s(%s of %s using %s BY %s)"),
-                                    var_get_name (src_vars[v]),
-                                    var_get_name (rank_specs[i].destvars[v]),
-                                    function_name[rank_specs[i].rfunc],
-                                    var_get_name (src_vars[v]),
-                                    fraction_name(),
-                                    ds_cstr (&varlist)
-                                    );
+                   tab_output_text_format (0,
+                                            _("%s into %s(%s of %s using %s BY %s)"),
+                                            var_get_name (src_vars[v]),
+                                            var_get_name (rank_specs[i].destvars[v]),
+                                            function_name[rank_specs[i].rfunc],
+                                            var_get_name (src_vars[v]),
+                                            fraction_name(),
+                                            ds_cstr (&varlist));
 
                  else
-                   tab_output_text (TAT_PRINTF,
-                                    _("%s into %s(%s of %s BY %s)"),
-                                    var_get_name (src_vars[v]),
-                                    var_get_name (rank_specs[i].destvars[v]),
-                                    function_name[rank_specs[i].rfunc],
-                                    var_get_name (src_vars[v]),
-                                    ds_cstr (&varlist)
-                                    );
+                   tab_output_text_format (0,
+                                            _("%s into %s(%s of %s BY %s)"),
+                                            var_get_name (src_vars[v]),
+                                            var_get_name (rank_specs[i].destvars[v]),
+                                            function_name[rank_specs[i].rfunc],
+                                            var_get_name (src_vars[v]),
+                                            ds_cstr (&varlist));
                  ds_destroy (&varlist);
                }
              else
                {
                  if ( rank_specs[i].rfunc == NORMAL ||
                       rank_specs[i].rfunc == PROPORTION )
-                   tab_output_text (TAT_PRINTF,
-                                    _("%s into %s(%s of %s using %s)"),
-                                    var_get_name (src_vars[v]),
-                                    var_get_name (rank_specs[i].destvars[v]),
-                                    function_name[rank_specs[i].rfunc],
-                                    var_get_name (src_vars[v]),
-                                    fraction_name()
-                                    );
+                   tab_output_text_format (0,
+                                            _("%s into %s(%s of %s using %s)"),
+                                            var_get_name (src_vars[v]),
+                                            var_get_name (rank_specs[i].destvars[v]),
+                                            function_name[rank_specs[i].rfunc],
+                                            var_get_name (src_vars[v]),
+                                            fraction_name());
 
                  else
-                   tab_output_text (TAT_PRINTF,
-                                    _("%s into %s(%s of %s)"),
-                                    var_get_name (src_vars[v]),
-                                    var_get_name (rank_specs[i].destvars[v]),
-                                    function_name[rank_specs[i].rfunc],
-                                    var_get_name (src_vars[v])
-                                    );
+                   tab_output_text_format (0,
+                                            _("%s into %s(%s of %s)"),
+                                            var_get_name (src_vars[v]),
+                                            var_get_name (rank_specs[i].destvars[v]),
+                                            function_name[rank_specs[i].rfunc],
+                                            var_get_name (src_vars[v]));
                }
            }
        }
@@ -773,17 +768,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 +803,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 +839,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 +853,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..2c259d0f904c1834c7d7e1614fe4f47084bab5ee 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
@@ -150,7 +150,7 @@ reg_stats_r (pspp_linreg_cache * c)
   adjrsq = 1.0 - (1.0 - rsq) * (c->n_obs - 1.0) / (c->n_obs - c->n_indeps);
   std_error = sqrt (pspp_linreg_mse (c));
   t = tab_create (n_cols, n_rows, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -193,7 +193,7 @@ reg_stats_coeff (pspp_linreg_cache * c)
 
   t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -290,7 +290,7 @@ reg_stats_anova (pspp_linreg_cache * c)
   assert (c != NULL);
   t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
 
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
 
@@ -315,9 +315,9 @@ reg_stats_anova (pspp_linreg_cache * c)
 
 
   /* Degrees of freedom */
-  tab_text (t, 3, 1, TAB_RIGHT | TAT_PRINTF, "%g", c->dfm);
-  tab_text (t, 3, 2, TAB_RIGHT | TAT_PRINTF, "%g", c->dfe);
-  tab_text (t, 3, 3, TAB_RIGHT | TAT_PRINTF, "%g", c->dft);
+  tab_text_format (t, 3, 1, TAB_RIGHT, "%g", c->dfm);
+  tab_text_format (t, 3, 2, TAB_RIGHT, "%g", c->dfe);
+  tab_text_format (t, 3, 3, TAB_RIGHT, "%g", c->dft);
 
   /* Mean Squares */
   tab_double (t, 4, 1, TAB_RIGHT, msm, NULL);
@@ -381,7 +381,7 @@ reg_stats_bcov (pspp_linreg_cache * c)
   n_rows = 2 * (c->n_indeps + 1);
   t = tab_create (n_cols, n_rows, 0);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions);
+  tab_dim (t, tab_natural_dimensions, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -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, n_indep);
          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..681669d
--- /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 <math.h>
+
+#include <data/case.h>
+#include <data/casegrouper.h>
+#include <data/casereader.h>
+#include <data/dictionary.h>
+#include <data/procedure.h>
+#include <data/variable.h>
+#include <language/command.h>
+#include <libpspp/misc.h>
+#include <math/moments.h>
+#include <output/manager.h>
+#include <output/table.h>
+
+#include "xalloc.h"
+#include "xmalloca.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+/* (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 = caseproto_get_n_widths (casereader_get_proto (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, NULL);
+    tab_flags (tab, SOMF_NO_TITLE );
+
+    tab_text_format (tab, 0, 0, 0, "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, NULL);
+
+  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, NULL);
+
+  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, NULL);
+
+  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, _("%"));
+
+  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:
+*/
diff --git a/src/language/stats/roc.c b/src/language/stats/roc.c
new file mode 100644 (file)
index 0000000..1d61a55
--- /dev/null
@@ -0,0 +1,1227 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <data/procedure.h>
+#include <language/lexer/variable-parser.h>
+#include <language/lexer/value-parser.h>
+#include <language/command.h>
+#include <language/lexer/lexer.h>
+
+#include <data/casegrouper.h>
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <data/dictionary.h>
+#include <data/format.h>
+#include <math/sort.h>
+#include <data/subcase.h>
+
+
+#include <libpspp/misc.h>
+
+#include <gsl/gsl_cdf.h>
+#include <output/table.h>
+
+#include <output/charts/plot-chart.h>
+#include <output/charts/cartesian.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+struct cmd_roc
+{
+  size_t n_vars;
+  const struct variable **vars;
+  const struct dictionary *dict;
+
+  const struct variable *state_var ;
+  union value state_value;
+
+  /* Plot the roc curve */
+  bool curve;
+  /* Plot the reference line */
+  bool reference;
+
+  double ci;
+
+  bool print_coords;
+  bool print_se;
+  bool bi_neg_exp; /* True iff the bi-negative exponential critieria
+                     should be used */
+  enum mv_class exclude;
+
+  bool invert ; /* True iff a smaller test result variable indicates
+                  a positive result */
+
+  double pos;
+  double neg;
+  double pos_weighted;
+  double neg_weighted;
+};
+
+static int run_roc (struct dataset *ds, struct cmd_roc *roc);
+
+int
+cmd_roc (struct lexer *lexer, struct dataset *ds)
+{
+  struct cmd_roc roc ;
+  const struct dictionary *dict = dataset_dict (ds);
+
+  roc.vars = NULL;
+  roc.n_vars = 0;
+  roc.print_se = false;
+  roc.print_coords = false;
+  roc.exclude = MV_ANY;
+  roc.curve = true;
+  roc.reference = false;
+  roc.ci = 95;
+  roc.bi_neg_exp = false;
+  roc.invert = false;
+  roc.pos = roc.pos_weighted = 0;
+  roc.neg = roc.neg_weighted = 0;
+  roc.dict = dataset_dict (ds);
+
+  if (!parse_variables_const (lexer, dict, &roc.vars, &roc.n_vars,
+                             PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
+    goto error;
+
+  if ( ! lex_force_match (lexer, T_BY))
+    {
+      goto error;
+    }
+
+  roc.state_var = parse_variable (lexer, dict);
+
+  if ( !lex_force_match (lexer, '('))
+    {
+      goto error;
+    }
+
+  parse_value (lexer, &roc.state_value, var_get_width (roc.state_var));
+
+
+  if ( !lex_force_match (lexer, ')'))
+    {
+      goto error;
+    }
+
+
+  while (lex_token (lexer) != '.')
+    {
+      lex_match (lexer, '/');
+      if (lex_match_id (lexer, "MISSING"))
+        {
+          lex_match (lexer, '=');
+          while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+            {
+             if (lex_match_id (lexer, "INCLUDE"))
+               {
+                 roc.exclude = MV_SYSTEM;
+               }
+             else if (lex_match_id (lexer, "EXCLUDE"))
+               {
+                 roc.exclude = MV_ANY;
+               }
+             else
+               {
+                  lex_error (lexer, NULL);
+                 goto error;
+               }
+           }
+       }
+      else if (lex_match_id (lexer, "PLOT"))
+       {
+         lex_match (lexer, '=');
+         if (lex_match_id (lexer, "CURVE"))
+           {
+             roc.curve = true;
+             if (lex_match (lexer, '('))
+               {
+                 roc.reference = true;
+                 lex_force_match_id (lexer, "REFERENCE");
+                 lex_force_match (lexer, ')');
+               }
+           }
+         else if (lex_match_id (lexer, "NONE"))
+           {
+             roc.curve = false;
+           }
+         else
+           {
+             lex_error (lexer, NULL);
+             goto error;
+           }
+       }
+      else if (lex_match_id (lexer, "PRINT"))
+       {
+         lex_match (lexer, '=');
+          while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+           {
+             if (lex_match_id (lexer, "SE"))
+               {
+                 roc.print_se = true;
+               }
+             else if (lex_match_id (lexer, "COORDINATES"))
+               {
+                 roc.print_coords = true;
+               }
+             else
+               {
+                 lex_error (lexer, NULL);
+                 goto error;
+               }
+           }
+       }
+      else if (lex_match_id (lexer, "CRITERIA"))
+       {
+         lex_match (lexer, '=');
+          while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+           {
+             if (lex_match_id (lexer, "CUTOFF"))
+               {
+                 lex_force_match (lexer, '(');
+                 if (lex_match_id (lexer, "INCLUDE"))
+                   {
+                     roc.exclude = MV_SYSTEM;
+                   }
+                 else if (lex_match_id (lexer, "EXCLUDE"))
+                   {
+                     roc.exclude = MV_USER | MV_SYSTEM;
+                   }
+                 else
+                   {
+                     lex_error (lexer, NULL);
+                     goto error;
+                   }
+                 lex_force_match (lexer, ')');
+               }
+             else if (lex_match_id (lexer, "TESTPOS"))
+               {
+                 lex_force_match (lexer, '(');
+                 if (lex_match_id (lexer, "LARGE"))
+                   {
+                     roc.invert = false;
+                   }
+                 else if (lex_match_id (lexer, "SMALL"))
+                   {
+                     roc.invert = true;
+                   }
+                 else
+                   {
+                     lex_error (lexer, NULL);
+                     goto error;
+                   }
+                 lex_force_match (lexer, ')');
+               }
+             else if (lex_match_id (lexer, "CI"))
+               {
+                 lex_force_match (lexer, '(');
+                 lex_force_num (lexer);
+                 roc.ci = lex_number (lexer);
+                 lex_get (lexer);
+                 lex_force_match (lexer, ')');
+               }
+             else if (lex_match_id (lexer, "DISTRIBUTION"))
+               {
+                 lex_force_match (lexer, '(');
+                 if (lex_match_id (lexer, "FREE"))
+                   {
+                     roc.bi_neg_exp = false;
+                   }
+                 else if (lex_match_id (lexer, "NEGEXPO"))
+                   {
+                     roc.bi_neg_exp = true;
+                   }
+                 else
+                   {
+                     lex_error (lexer, NULL);
+                     goto error;
+                   }
+                 lex_force_match (lexer, ')');
+               }
+             else
+               {
+                 lex_error (lexer, NULL);
+                 goto error;
+               }
+           }
+       }
+      else
+       {
+         lex_error (lexer, NULL);
+         break;
+       }
+    }
+
+  if ( ! run_roc (ds, &roc)) 
+    goto error;
+
+  free (roc.vars);
+  return CMD_SUCCESS;
+
+ error:
+  free (roc.vars);
+  return CMD_FAILURE;
+}
+
+
+
+
+static void
+do_roc (struct cmd_roc *roc, struct casereader *group, struct dictionary *dict);
+
+
+static int
+run_roc (struct dataset *ds, struct cmd_roc *roc)
+{
+  struct dictionary *dict = dataset_dict (ds);
+  bool ok;
+  struct casereader *group;
+
+  struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds), dict);
+  while (casegrouper_get_next_group (grouper, &group))
+    {
+      do_roc (roc, group, dataset_dict (ds));
+    }
+  ok = casegrouper_destroy (grouper);
+  ok = proc_commit (ds) && ok;
+
+  return ok;
+}
+
+#if 0
+static void
+dump_casereader (struct casereader *reader)
+{
+  struct ccase *c;
+  struct casereader *r = casereader_clone (reader);
+
+  for ( ; (c = casereader_read (r) ); case_unref (c))
+    {
+      int i;
+      for (i = 0 ; i < case_get_value_cnt (c); ++i)
+       {
+         printf ("%g ", case_data_idx (c, i)->f);
+       }
+      printf ("\n");
+    }
+
+  casereader_destroy (r);
+}
+#endif
+
+
+/* 
+   Return true iff the state variable indicates that C has positive actual state.
+
+   As a side effect, this function also accumulates the roc->{pos,neg} and 
+   roc->{pos,neg}_weighted counts.
+ */
+static bool
+match_positives (const struct ccase *c, void *aux)
+{
+  struct cmd_roc *roc = aux;
+  const struct variable *wv = dict_get_weight (roc->dict);
+  const double weight = wv ? case_data (c, wv)->f : 1.0;
+
+  const bool positive =
+  ( 0 == value_compare_3way (case_data (c, roc->state_var), &roc->state_value,
+    var_get_width (roc->state_var)));
+
+  if ( positive )
+    {
+      roc->pos++;
+      roc->pos_weighted += weight;
+    }
+  else
+    {
+      roc->neg++;
+      roc->neg_weighted += weight;
+    }
+
+  return positive;
+}
+
+
+#define VALUE  0
+#define N_EQ   1
+#define N_PRED 2
+
+/* Some intermediate state for calculating the cutpoints and the 
+   standard error values */
+struct roc_state
+{
+  double auc;  /* Area under the curve */
+
+  double n1;  /* total weight of positives */
+  double n2;  /* total weight of negatives */
+
+  /* intermediates for standard error */
+  double q1hat; 
+  double q2hat;
+
+  /* intermediates for cutpoints */
+  struct casewriter *cutpoint_wtr;
+  struct casereader *cutpoint_rdr;
+  double prev_result;
+  double min;
+  double max;
+};
+
+#define CUTPOINT 0
+#define TP 1
+#define FN 2
+#define TN 3
+#define FP 4
+
+
+/* 
+   Return a new casereader based upon CUTPOINT_RDR.
+   The number of "positive" cases are placed into
+   the position TRUE_INDEX, and the number of "negative" cases
+   into FALSE_INDEX.
+   POS_COND and RESULT determine the semantics of what is 
+   "positive".
+   WEIGHT is the value of a single count.
+ */
+static struct casereader *
+accumulate_counts (struct casereader *cutpoint_rdr, 
+                  double result, double weight, 
+                  bool (*pos_cond) (double, double),
+                  int true_index, int false_index)
+{
+  const struct caseproto *proto = casereader_get_proto (cutpoint_rdr);
+  struct casewriter *w =
+    autopaging_writer_create (proto);
+  struct casereader *r = casereader_clone (cutpoint_rdr);
+  struct ccase *cpc;
+  double prev_cp = SYSMIS;
+
+  for ( ; (cpc = casereader_read (r) ); case_unref (cpc))
+    {
+      struct ccase *new_case;
+      const double cp = case_data_idx (cpc, CUTPOINT)->f;
+
+      assert (cp != SYSMIS);
+
+      /* We don't want duplicates here */
+      if ( cp == prev_cp )
+       continue;
+
+      new_case = case_clone (cpc);
+
+      if ( pos_cond (result, cp))
+       case_data_rw_idx (new_case, true_index)->f += weight;
+      else
+       case_data_rw_idx (new_case, false_index)->f += weight;
+
+      prev_cp = cp;
+
+      casewriter_write (w, new_case);
+    }
+  casereader_destroy (r);
+
+  return casewriter_make_reader (w);
+}
+
+
+
+static void output_roc (struct roc_state *rs, const struct cmd_roc *roc);
+
+/*
+  This function does 3 things:
+
+  1. Counts the number of cases which are equal to every other case in READER,
+  and those cases for which the relationship between it and every other case
+  satifies PRED (normally either > or <).  VAR is variable defining a case's value
+  for this purpose.
+
+  2. Counts the number of true and false cases in reader, and populates
+  CUTPOINT_RDR accordingly.  TRUE_INDEX and FALSE_INDEX are the indices
+  which receive these values.  POS_COND is the condition defining true
+  and false.
+  
+  3. CC is filled with the cumulative weight of all cases of READER.
+*/
+static struct casereader *
+process_group (const struct variable *var, struct casereader *reader,
+              bool (*pred) (double, double),
+              const struct dictionary *dict,
+              double *cc,
+              struct casereader **cutpoint_rdr, 
+              bool (*pos_cond) (double, double),
+              int true_index,
+              int false_index)
+{
+  const struct variable *w = dict_get_weight (dict);
+
+  struct casereader *r1 =
+    casereader_create_distinct (sort_execute_1var (reader, var), var, w);
+
+  const int weight_idx  = w ? var_get_case_index (w) :
+    caseproto_get_n_widths (casereader_get_proto (r1)) - 1;
+  
+  struct ccase *c1;
+
+  struct casereader *rclone = casereader_clone (r1);
+  struct casewriter *wtr;
+  struct caseproto *proto = caseproto_create ();
+
+  proto = caseproto_add_width (proto, 0);
+  proto = caseproto_add_width (proto, 0);
+  proto = caseproto_add_width (proto, 0);
+
+  wtr = autopaging_writer_create (proto);  
+
+  *cc = 0;
+
+  for ( ; (c1 = casereader_read (r1) ); case_unref (c1))
+    {
+      struct ccase *new_case = case_create (proto);
+      struct ccase *c2;
+      struct casereader *r2 = casereader_clone (rclone);
+
+      const double weight1 = case_data_idx (c1, weight_idx)->f;
+      const double d1 = case_data (c1, var)->f;
+      double n_eq = 0.0;
+      double n_pred = 0.0;
+
+      *cutpoint_rdr = accumulate_counts (*cutpoint_rdr, d1, weight1,
+                                        pos_cond,
+                                        true_index, false_index);
+
+      *cc += weight1;
+
+      for ( ; (c2 = casereader_read (r2) ); case_unref (c2))
+       {
+         const double d2 = case_data (c2, var)->f;
+         const double weight2 = case_data_idx (c2, weight_idx)->f;
+
+         if ( d1 == d2 )
+           {
+             n_eq += weight2;
+             continue;
+           }
+         else  if ( pred (d2, d1))
+           {
+             n_pred += weight2;
+           }
+       }
+
+      case_data_rw_idx (new_case, VALUE)->f = d1;
+      case_data_rw_idx (new_case, N_EQ)->f = n_eq;
+      case_data_rw_idx (new_case, N_PRED)->f = n_pred;
+
+      casewriter_write (wtr, new_case);
+
+      casereader_destroy (r2);
+    }
+
+  casereader_destroy (r1);
+  casereader_destroy (rclone);
+
+  return casewriter_make_reader (wtr);
+}
+
+/* Some more indeces into case data */
+#define N_POS_EQ 1  /* number of positive cases with values equal to n */
+#define N_POS_GT 2  /* number of postive cases with values greater than n */
+#define N_NEG_EQ 3  /* number of negative cases with values equal to n */
+#define N_NEG_LT 4  /* number of negative cases with values less than n */
+
+static bool
+gt (double d1, double d2)
+{
+  return d1 > d2;
+}
+
+
+static bool
+ge (double d1, double d2)
+{
+  return d1 > d2;
+}
+
+static bool
+lt (double d1, double d2)
+{
+  return d1 < d2;
+}
+
+
+/*
+  Return a casereader with width 3,
+  populated with cases based upon READER.
+  The cases will have the values:
+  (N, number of cases equal to N, number of cases greater than N)
+  As a side effect, update RS->n1 with the number of positive cases.
+*/
+static struct casereader *
+process_positive_group (const struct variable *var, struct casereader *reader,
+                       const struct dictionary *dict,
+                       struct roc_state *rs)
+{
+  return process_group (var, reader, gt, dict, &rs->n1,
+                       &rs->cutpoint_rdr,
+                       ge,
+                       TP, FN);
+}
+
+/*
+  Return a casereader with width 3,
+  populated with cases based upon READER.
+  The cases will have the values:
+  (N, number of cases equal to N, number of cases less than N)
+  As a side effect, update RS->n2 with the number of negative cases.
+*/
+static struct casereader *
+process_negative_group (const struct variable *var, struct casereader *reader,
+                       const struct dictionary *dict,
+                       struct roc_state *rs)
+{
+  return process_group (var, reader, lt, dict, &rs->n2,
+                       &rs->cutpoint_rdr,
+                       lt,
+                       TN, FP);
+}
+
+
+
+
+static void
+append_cutpoint (struct casewriter *writer, double cutpoint)
+{
+  struct ccase *cc = case_create (casewriter_get_proto (writer));
+
+  case_data_rw_idx (cc, CUTPOINT)->f = cutpoint;
+  case_data_rw_idx (cc, TP)->f = 0;
+  case_data_rw_idx (cc, FN)->f = 0;
+  case_data_rw_idx (cc, TN)->f = 0;
+  case_data_rw_idx (cc, FP)->f = 0;
+
+  casewriter_write (writer, cc);
+}
+
+
+/* 
+   Create and initialise the rs[x].cutpoint_rdr casereaders.  That is, the readers will
+   be created with width 5, ready to take the values (cutpoint, TP, FN, TN, FP), and the
+   reader will be populated with its final number of cases.
+   However on exit from this function, only CUTPOINT entries will be set to their final
+   value.  The other entries will be initialised to zero.
+*/
+static void
+prepare_cutpoints (struct cmd_roc *roc, struct roc_state *rs, struct casereader *input)
+{
+  int i;
+  struct casereader *r = casereader_clone (input);
+  struct ccase *c;
+  struct caseproto *proto = caseproto_create ();
+
+  struct subcase ordering;
+  subcase_init (&ordering, CUTPOINT, 0, SC_ASCEND);
+
+  proto = caseproto_add_width (proto, 0); /* cutpoint */
+  proto = caseproto_add_width (proto, 0); /* TP */
+  proto = caseproto_add_width (proto, 0); /* FN */
+  proto = caseproto_add_width (proto, 0); /* TN */
+  proto = caseproto_add_width (proto, 0); /* FP */
+
+  for (i = 0 ; i < roc->n_vars; ++i)
+    {
+      rs[i].cutpoint_wtr = sort_create_writer (&ordering, proto);
+      rs[i].prev_result = SYSMIS;
+      rs[i].max = -DBL_MAX;
+      rs[i].min = DBL_MAX;
+    }
+
+  for (; (c = casereader_read (r)) != NULL; case_unref (c))
+    {
+      for (i = 0 ; i < roc->n_vars; ++i)
+       {
+         const union value *v = case_data (c, roc->vars[i]); 
+         const double result = v->f;
+
+         if ( mv_is_value_missing (var_get_missing_values (roc->vars[i]), v, roc->exclude))
+           continue;
+
+         minimize (&rs[i].min, result);
+         maximize (&rs[i].max, result);
+
+         if ( rs[i].prev_result != SYSMIS && rs[i].prev_result != result )
+           {
+             const double mean = (result + rs[i].prev_result ) / 2.0;
+             append_cutpoint (rs[i].cutpoint_wtr, mean);
+           }
+
+         rs[i].prev_result = result;
+       }
+    }
+  casereader_destroy (r);
+
+
+  /* Append the min and max cutpoints */
+  for (i = 0 ; i < roc->n_vars; ++i)
+    {
+      append_cutpoint (rs[i].cutpoint_wtr, rs[i].min - 1);
+      append_cutpoint (rs[i].cutpoint_wtr, rs[i].max + 1);
+
+      rs[i].cutpoint_rdr = casewriter_make_reader (rs[i].cutpoint_wtr);
+    }
+}
+
+static void
+do_roc (struct cmd_roc *roc, struct casereader *reader, struct dictionary *dict)
+{
+  int i;
+
+  struct roc_state *rs = xcalloc (roc->n_vars, sizeof *rs);
+
+  struct casereader *negatives = NULL;
+  struct casereader *positives = NULL;
+
+  struct caseproto *n_proto = caseproto_create ();
+
+  struct subcase up_ordering;
+  struct subcase down_ordering;
+
+  struct casewriter *neg_wtr = NULL;
+
+  struct casereader *input = casereader_create_filter_missing (reader,
+                                                              roc->vars, roc->n_vars,
+                                                              roc->exclude,
+                                                              NULL,
+                                                              NULL);
+
+  input = casereader_create_filter_missing (input,
+                                           &roc->state_var, 1,
+                                           roc->exclude,
+                                           NULL,
+                                           NULL);
+
+  neg_wtr = autopaging_writer_create (casereader_get_proto (input));
+
+  prepare_cutpoints (roc, rs, input);
+
+
+  /* Separate the positive actual state cases from the negative ones */
+  positives = 
+    casereader_create_filter_func (input,
+                                  match_positives,
+                                  NULL,
+                                  roc,
+                                  neg_wtr);
+
+  n_proto = caseproto_create ();
+      
+  n_proto = caseproto_add_width (n_proto, 0);
+  n_proto = caseproto_add_width (n_proto, 0);
+  n_proto = caseproto_add_width (n_proto, 0);
+  n_proto = caseproto_add_width (n_proto, 0);
+  n_proto = caseproto_add_width (n_proto, 0);
+
+  subcase_init (&up_ordering, VALUE, 0, SC_ASCEND);
+  subcase_init (&down_ordering, VALUE, 0, SC_DESCEND);
+
+  for (i = 0 ; i < roc->n_vars; ++i)
+    {
+      struct casewriter *w = NULL;
+      struct casereader *r = NULL;
+
+      struct ccase *c;
+
+      struct ccase *cpos;
+      struct casereader *n_neg ;
+      const struct variable *var = roc->vars[i];
+
+      struct casereader *neg ;
+      struct casereader *pos = casereader_clone (positives);
+
+
+      struct casereader *n_pos =
+       process_positive_group (var, pos, dict, &rs[i]);
+
+      if ( negatives == NULL)
+       {
+         negatives = casewriter_make_reader (neg_wtr);
+       }
+
+      neg = casereader_clone (negatives);
+
+      n_neg = process_negative_group (var, neg, dict, &rs[i]);
+
+
+      /* Merge the n_pos and n_neg casereaders */
+      w = sort_create_writer (&up_ordering, n_proto);
+      for ( ; (cpos = casereader_read (n_pos) ); case_unref (cpos))
+       {
+         struct ccase *pos_case = case_create (n_proto);
+         struct ccase *cneg;
+         const double jpos = case_data_idx (cpos, VALUE)->f;
+
+         while ((cneg = casereader_read (n_neg)))
+           {
+             struct ccase *nc = case_create (n_proto);
+
+             const double jneg = case_data_idx (cneg, VALUE)->f;
+
+             case_data_rw_idx (nc, VALUE)->f = jneg;
+             case_data_rw_idx (nc, N_POS_EQ)->f = 0;
+
+             case_data_rw_idx (nc, N_POS_GT)->f = SYSMIS;
+
+             *case_data_rw_idx (nc, N_NEG_EQ) = *case_data_idx (cneg, N_EQ);
+             *case_data_rw_idx (nc, N_NEG_LT) = *case_data_idx (cneg, N_PRED);
+
+             casewriter_write (w, nc);
+
+             case_unref (cneg);
+             if ( jneg > jpos)
+               break;
+           }
+
+         case_data_rw_idx (pos_case, VALUE)->f = jpos;
+         *case_data_rw_idx (pos_case, N_POS_EQ) = *case_data_idx (cpos, N_EQ);
+         *case_data_rw_idx (pos_case, N_POS_GT) = *case_data_idx (cpos, N_PRED);
+         case_data_rw_idx (pos_case, N_NEG_EQ)->f = 0;
+         case_data_rw_idx (pos_case, N_NEG_LT)->f = SYSMIS;
+
+         casewriter_write (w, pos_case);
+       }
+
+/* These aren't used anymore */
+#undef N_EQ
+#undef N_PRED
+
+      r = casewriter_make_reader (w);
+
+      /* Propagate the N_POS_GT values from the positive cases
+        to the negative ones */
+      {
+       double prev_pos_gt = rs[i].n1;
+       w = sort_create_writer (&down_ordering, n_proto);
+
+       for ( ; (c = casereader_read (r) ); case_unref (c))
+         {
+           double n_pos_gt = case_data_idx (c, N_POS_GT)->f;
+           struct ccase *nc = case_clone (c);
+
+           if ( n_pos_gt == SYSMIS)
+             {
+               n_pos_gt = prev_pos_gt;
+               case_data_rw_idx (nc, N_POS_GT)->f = n_pos_gt;
+             }
+           
+           casewriter_write (w, nc);
+           prev_pos_gt = n_pos_gt;
+         }
+
+       r = casewriter_make_reader (w);
+      }
+
+      /* Propagate the N_NEG_LT values from the negative cases
+        to the positive ones */
+      {
+       double prev_neg_lt = rs[i].n2;
+       w = sort_create_writer (&up_ordering, n_proto);
+
+       for ( ; (c = casereader_read (r) ); case_unref (c))
+         {
+           double n_neg_lt = case_data_idx (c, N_NEG_LT)->f;
+           struct ccase *nc = case_clone (c);
+
+           if ( n_neg_lt == SYSMIS)
+             {
+               n_neg_lt = prev_neg_lt;
+               case_data_rw_idx (nc, N_NEG_LT)->f = n_neg_lt;
+             }
+           
+           casewriter_write (w, nc);
+           prev_neg_lt = n_neg_lt;
+         }
+
+       r = casewriter_make_reader (w);
+      }
+
+      {
+       struct ccase *prev_case = NULL;
+       for ( ; (c = casereader_read (r) ); case_unref (c))
+         {
+           const struct ccase *next_case = casereader_peek (r, 0);
+
+           const double j = case_data_idx (c, VALUE)->f;
+           double n_pos_eq = case_data_idx (c, N_POS_EQ)->f;
+           double n_pos_gt = case_data_idx (c, N_POS_GT)->f;
+           double n_neg_eq = case_data_idx (c, N_NEG_EQ)->f;
+           double n_neg_lt = case_data_idx (c, N_NEG_LT)->f;
+
+           if ( prev_case && j == case_data_idx (prev_case, VALUE)->f)
+             {
+               if ( 0 ==  case_data_idx (c, N_POS_EQ)->f)
+                 {
+                   n_pos_eq = case_data_idx (prev_case, N_POS_EQ)->f;
+                   n_pos_gt = case_data_idx (prev_case, N_POS_GT)->f;
+                 }
+
+               if ( 0 ==  case_data_idx (c, N_NEG_EQ)->f)
+                 {
+                   n_neg_eq = case_data_idx (prev_case, N_NEG_EQ)->f;
+                   n_neg_lt = case_data_idx (prev_case, N_NEG_LT)->f;
+                 }
+             }
+
+           if ( NULL == next_case || j != case_data_idx (next_case, VALUE)->f)
+             {
+               rs[i].auc += n_pos_gt * n_neg_eq + (n_pos_eq * n_neg_eq) / 2.0;
+
+               rs[i].q1hat +=
+                 n_neg_eq * ( pow2 (n_pos_gt) + n_pos_gt * n_pos_eq + pow2 (n_pos_eq) / 3.0);
+               rs[i].q2hat +=
+                 n_pos_eq * ( pow2 (n_neg_lt) + n_neg_lt * n_neg_eq + pow2 (n_neg_eq) / 3.0);
+
+             }
+
+           case_unref (prev_case);
+           prev_case = case_clone (c);
+         }
+
+       rs[i].auc /=  rs[i].n1 * rs[i].n2; 
+       if ( roc->invert ) 
+         rs[i].auc = 1 - rs[i].auc;
+
+       if ( roc->bi_neg_exp )
+         {
+           rs[i].q1hat = rs[i].auc / ( 2 - rs[i].auc);
+           rs[i].q2hat = 2 * pow2 (rs[i].auc) / ( 1 + rs[i].auc);
+         }
+       else
+         {
+           rs[i].q1hat /= rs[i].n2 * pow2 (rs[i].n1);
+           rs[i].q2hat /= rs[i].n1 * pow2 (rs[i].n2);
+         }
+      }
+    }
+
+  casereader_destroy (positives);
+  casereader_destroy (negatives);
+
+  output_roc (rs, roc);
+
+  free (rs);
+}
+
+static void
+show_auc  (struct roc_state *rs, const struct cmd_roc *roc)
+{
+  int i;
+  const int n_fields = roc->print_se ? 5 : 1;
+  const int n_cols = roc->n_vars > 1 ? n_fields + 1: n_fields;
+  const int n_rows = 2 + roc->n_vars;
+  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+
+  if ( roc->n_vars > 1)
+    tab_title (tbl, _("Area Under the Curve"));
+  else
+    tab_title (tbl, _("Area Under the Curve (%s)"), var_to_string (roc->vars[0]));
+
+  tab_headers (tbl, n_cols - n_fields, 0, 1, 0);
+
+  tab_dim (tbl, tab_natural_dimensions, NULL);
+
+  tab_text (tbl, n_cols - n_fields, 1, TAT_TITLE, _("Area"));
+
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, 2);
+
+  tab_box (tbl,
+          TAL_2, TAL_2,
+          -1, TAL_1,
+          0, 0,
+          n_cols - 1,
+          n_rows - 1);
+
+  if ( roc->print_se )
+    {
+      tab_text (tbl, n_cols - 4, 1, TAT_TITLE, _("Std. Error"));
+      tab_text (tbl, n_cols - 3, 1, TAT_TITLE, _("Asymptotic Sig."));
+
+      tab_text (tbl, n_cols - 2, 1, TAT_TITLE, _("Lower Bound"));
+      tab_text (tbl, n_cols - 1, 1, TAT_TITLE, _("Upper Bound"));
+
+      tab_joint_text_format (tbl, n_cols - 2, 0, 4, 0,
+                            TAT_TITLE | TAB_CENTER,
+                            _("Asymp. %g%% Confidence Interval"), roc->ci);
+      tab_vline (tbl, 0, n_cols - 1, 0, 0);
+      tab_hline (tbl, TAL_1, n_cols - 2, n_cols - 1, 1);
+    }
+
+  if ( roc->n_vars > 1)
+    tab_text (tbl, 0, 1, TAT_TITLE, _("Variable under test"));
+
+  if ( roc->n_vars > 1)
+    tab_vline (tbl, TAL_2, 1, 0, n_rows - 1);
+
+
+  for ( i = 0 ; i < roc->n_vars ; ++i )
+    {
+      tab_text (tbl, 0, 2 + i, TAT_TITLE, var_to_string (roc->vars[i]));
+
+      tab_double (tbl, n_cols - n_fields, 2 + i, 0, rs[i].auc, NULL);
+
+      if ( roc->print_se )
+       {
+         double se ;
+         const double sd_0_5 = sqrt ((rs[i].n1 + rs[i].n2 + 1) /
+                                     (12 * rs[i].n1 * rs[i].n2));
+         double ci ;
+         double yy ;
+
+         se = rs[i].auc * (1 - rs[i].auc) + (rs[i].n1 - 1) * (rs[i].q1hat - pow2 (rs[i].auc)) +
+           (rs[i].n2 - 1) * (rs[i].q2hat - pow2 (rs[i].auc));
+
+         se /= rs[i].n1 * rs[i].n2;
+
+         se = sqrt (se);
+
+         tab_double (tbl, n_cols - 4, 2 + i, 0,
+                     se,
+                     NULL);
+
+         ci = 1 - roc->ci / 100.0;
+         yy = gsl_cdf_gaussian_Qinv (ci, se) ;
+
+         tab_double (tbl, n_cols - 2, 2 + i, 0,
+                     rs[i].auc - yy,
+                     NULL);
+
+         tab_double (tbl, n_cols - 1, 2 + i, 0,
+                     rs[i].auc + yy,
+                     NULL);
+
+         tab_double (tbl, n_cols - 3, 2 + i, 0,
+                     2.0 * gsl_cdf_ugaussian_Q (fabs ((rs[i].auc - 0.5 ) / sd_0_5)),
+                     NULL);
+       }
+    }
+
+  tab_submit (tbl);
+}
+
+
+static void
+show_summary (const struct cmd_roc *roc)
+{
+  const int n_cols = 3;
+  const int n_rows = 4;
+  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+
+  tab_title (tbl, _("Case Summary"));
+
+  tab_headers (tbl, 1, 0, 2, 0);
+
+  tab_dim (tbl, tab_natural_dimensions, NULL);
+
+  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, 2);
+  tab_vline (tbl, TAL_2, 1, 0, n_rows - 1);
+
+
+  tab_hline (tbl, TAL_2, 1, n_cols - 1, 1);
+  tab_vline (tbl, TAL_1, 2, 1, n_rows - 1);
+
+
+  tab_text (tbl, 0, 1, TAT_TITLE | TAB_LEFT, var_to_string (roc->state_var));
+  tab_text (tbl, 1, 1, TAT_TITLE, _("Unweighted"));
+  tab_text (tbl, 2, 1, TAT_TITLE, _("Weighted"));
+
+  tab_joint_text (tbl, 1, 0, 2, 0,
+                 TAT_TITLE | TAB_CENTER,
+                 _("Valid N (listwise)"));
+
+
+  tab_text (tbl, 0, 2, TAB_LEFT, _("Positive"));
+  tab_text (tbl, 0, 3, TAB_LEFT, _("Negative"));
+
+
+  tab_double (tbl, 1, 2, 0, roc->pos, &F_8_0);
+  tab_double (tbl, 1, 3, 0, roc->neg, &F_8_0);
+
+  tab_double (tbl, 2, 2, 0, roc->pos_weighted, 0);
+  tab_double (tbl, 2, 3, 0, roc->neg_weighted, 0);
+
+  tab_submit (tbl);
+}
+
+
+static void
+show_coords (struct roc_state *rs, const struct cmd_roc *roc)
+{
+  int x = 1;
+  int i;
+  const int n_cols = roc->n_vars > 1 ? 4 : 3;
+  int n_rows = 1;
+  struct tab_table *tbl ;
+
+  for (i = 0; i < roc->n_vars; ++i)
+    n_rows += casereader_count_cases (rs[i].cutpoint_rdr);
+
+  tbl = tab_create (n_cols, n_rows, 0);
+
+  if ( roc->n_vars > 1)
+    tab_title (tbl, _("Coordinates of the Curve"));
+  else
+    tab_title (tbl, _("Coordinates of the Curve (%s)"), var_to_string (roc->vars[0]));
+
+
+  tab_headers (tbl, 1, 0, 1, 0);
+
+  tab_dim (tbl, tab_natural_dimensions, NULL);
+
+  tab_hline (tbl, TAL_2, 0, n_cols - 1, 1);
+
+  if ( roc->n_vars > 1)
+    tab_text (tbl, 0, 0, TAT_TITLE, _("Test variable"));
+
+  tab_text (tbl, n_cols - 3, 0, TAT_TITLE, _("Positive if greater than or equal to"));
+  tab_text (tbl, n_cols - 2, 0, TAT_TITLE, _("Sensitivity"));
+  tab_text (tbl, n_cols - 1, 0, TAT_TITLE, _("1 - Specificity"));
+
+  tab_box (tbl,
+          TAL_2, TAL_2,
+          -1, TAL_1,
+          0, 0,
+          n_cols - 1,
+          n_rows - 1);
+
+  if ( roc->n_vars > 1)
+    tab_vline (tbl, TAL_2, 1, 0, n_rows - 1);
+
+  for (i = 0; i < roc->n_vars; ++i)
+    {
+      struct ccase *cc;
+      struct casereader *r = casereader_clone (rs[i].cutpoint_rdr);
+
+      if ( roc->n_vars > 1)
+       tab_text (tbl, 0, x, TAT_TITLE, var_to_string (roc->vars[i]));
+
+      if ( i > 0)
+       tab_hline (tbl, TAL_1, 0, n_cols - 1, x);
+
+
+      for (; (cc = casereader_read (r)) != NULL;
+          case_unref (cc), x++)
+       {
+         const double se = case_data_idx (cc, TP)->f /
+           (
+            case_data_idx (cc, TP)->f
+            +
+            case_data_idx (cc, FN)->f
+            );
+
+         const double sp = case_data_idx (cc, TN)->f /
+           (
+            case_data_idx (cc, TN)->f
+            +
+            case_data_idx (cc, FP)->f
+            );
+
+         tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, CUTPOINT)->f,
+                     var_get_print_format (roc->vars[i]));
+
+         tab_double (tbl, n_cols - 2, x, 0, se, NULL);
+         tab_double (tbl, n_cols - 1, x, 0, 1 - sp, NULL);
+       }
+
+      casereader_destroy (r);
+    }
+
+  tab_submit (tbl);
+}
+
+
+static void
+draw_roc (struct roc_state *rs, const struct cmd_roc *roc)
+{
+  int i;
+
+  struct chart *roc_chart = chart_create ();
+
+  chart_write_title (roc_chart, _("ROC Curve"));
+  chart_write_xlabel (roc_chart, _("1 - Specificity"));
+  chart_write_ylabel (roc_chart, _("Sensitivity"));
+
+  chart_write_xscale (roc_chart, 0, 1, 5);
+  chart_write_yscale (roc_chart, 0, 1, 5);
+
+  if ( roc->reference )
+    {
+      chart_line (roc_chart, 1.0, 0,
+                 0.0, 1.0,
+                 CHART_DIM_X);
+    }
+
+  for (i = 0; i < roc->n_vars; ++i)
+    {
+      struct ccase *cc;
+      struct casereader *r = casereader_clone (rs[i].cutpoint_rdr);
+
+      chart_vector_start (roc_chart, var_get_name (roc->vars[i]));
+      for (; (cc = casereader_read (r)) != NULL;
+          case_unref (cc))
+       {
+         double se = case_data_idx (cc, TP)->f;
+         double sp = case_data_idx (cc, TN)->f;
+
+         se /= case_data_idx (cc, FN)->f +
+           case_data_idx (cc, TP)->f ;
+
+         sp /= case_data_idx (cc, TN)->f +
+           case_data_idx (cc, FP)->f ;
+
+         chart_vector (roc_chart, 1 - sp, se);
+       }
+      chart_vector_end (roc_chart);
+      casereader_destroy (r);
+    }
+
+  chart_write_legend (roc_chart);
+
+  chart_submit (roc_chart);
+}
+
+
+static void
+output_roc (struct roc_state *rs, const struct cmd_roc *roc)
+{
+  show_summary (roc);
+
+  if ( roc->curve )
+    draw_roc (rs, roc);
+
+  show_auc (rs, roc);
+
+
+  if ( roc->print_coords )
+    show_coords (rs, roc);
+}
+
diff --git a/src/language/stats/sign.c b/src/language/stats/sign.c
new file mode 100644 (file)
index 0000000..a5a2721
--- /dev/null
@@ -0,0 +1,226 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "sign.h"
+
+#include <data/variable.h>
+#include <libpspp/str.h>
+#include <output/table.h>
+#include <gsl/gsl_cdf.h>
+#include <gsl/gsl_randist.h>
+#include "npar.h"
+#include <data/procedure.h>
+#include <data/missing-values.h>
+#include <data/dictionary.h>
+#include <data/casereader.h>
+#include <data/format.h>
+
+#include "minmax.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+struct sign_test_params
+{
+  double pos;
+  double ties;
+  double neg;
+
+  double one_tailed_sig;
+  double point_prob;
+};
+
+
+static void
+output_frequency_table (const struct two_sample_test *t2s,
+                       const struct sign_test_params *param,
+                       const struct dictionary *dict)
+{
+  int i;
+  struct tab_table *table = tab_create (3, 1 + 4 * t2s->n_pairs, 0);
+
+  const struct variable *wv = dict_get_weight (dict);
+  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
+
+  tab_dim (table, tab_natural_dimensions, NULL);
+
+  tab_title (table, _("Frequencies"));
+
+  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"));
+
+  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, 0, 1 + i * 4, TAB_LEFT, ds_cstr (&pair_name));
+
+      ds_destroy (&pair_name);
+
+      tab_hline (table, TAL_1, 0, table->nc - 1, 1 + i * 4);
+
+      tab_text (table,  1, 1 + i * 4,  TAB_LEFT, _("Negative Differences"));
+      tab_text (table,  1, 2 + i * 4,  TAB_LEFT, _("Positive Differences"));
+      tab_text (table,  1, 3 + i * 4,  TAB_LEFT, _("Ties"));
+      tab_text (table,  1, 4 + i * 4,  TAB_LEFT, _("Total"));
+
+      tab_double (table, 2, 1 + i * 4, TAB_RIGHT, param[i].neg, wfmt);
+      tab_double (table, 2, 2 + i * 4, TAB_RIGHT, param[i].pos, wfmt);
+      tab_double (table, 2, 3 + i * 4, TAB_RIGHT, param[i].ties, wfmt);
+      tab_double (table, 2, 4 + i * 4, TAB_RIGHT,
+                param[i].ties + param[i].neg + param[i].pos, wfmt);
+    }
+
+  tab_submit (table);
+}
+
+static void
+output_statistics_table (const struct two_sample_test *t2s,
+                        const struct sign_test_params *param)
+{
+  int i;
+  struct tab_table *table = tab_create (1 + t2s->n_pairs, 4, 0);
+
+  tab_dim (table, tab_natural_dimensions, NULL);
+
+  tab_title (table, _("Test Statistics"));
+
+  tab_headers (table, 0, 1,  0, 1);
+
+  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
+  tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+
+
+  /* Vertical lines inside the box */
+  tab_box (table, -1, -1, -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, TAT_TITLE | TAB_LEFT,
+           _("Exact Sig. (2-tailed)"));
+
+  tab_text (table,  0, 2, TAT_TITLE | TAB_LEFT,
+           _("Exact Sig. (1-tailed)"));
+
+  tab_text (table,  0, 3, TAT_TITLE | TAB_LEFT,
+           _("Point Probability"));
+
+  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 + i, 0, TAB_LEFT, ds_cstr (&pair_name));
+      ds_destroy (&pair_name);
+
+      tab_double (table, 1 + i, 1, TAB_RIGHT,
+                 param[i].one_tailed_sig * 2, NULL);
+
+      tab_double (table, 1 + i, 2, TAB_RIGHT, param[i].one_tailed_sig, NULL);
+      tab_double (table, 1 + i, 3, TAB_RIGHT, param[i].point_prob, NULL);
+    }
+
+  tab_submit (table);
+}
+
+void
+sign_execute (const struct dataset *ds,
+                 struct casereader *input,
+                 enum mv_class exclude,
+                 const struct npar_test *test,
+                 bool exact UNUSED,
+                 double timer UNUSED)
+{
+  int i;
+  bool warn = true;
+  const struct dictionary *dict = dataset_dict (ds);
+  const struct two_sample_test *t2s = (const struct two_sample_test *) test;
+  struct ccase *c;
+
+  struct sign_test_params *stp = xcalloc (sizeof *stp, t2s->n_pairs);
+
+  struct casereader *r = input;
+
+  for (; (c = casereader_read (r)) != NULL; case_unref (c))
+    {
+      const double weight = dict_get_case_weight (dict, c, &warn);
+
+      for (i = 0 ; i < t2s->n_pairs; ++i )
+       {
+         variable_pair *vp = &t2s->pairs[i];
+         const union value *value0 = case_data (c, (*vp)[0]);
+         const union value *value1 = case_data (c, (*vp)[1]);
+         const double diff = value0->f - value1->f;
+
+         if (var_is_value_missing ((*vp)[0], value0, exclude))
+           continue;
+
+         if (var_is_value_missing ((*vp)[1], value1, exclude))
+           continue;
+
+         if ( diff > 0)
+           stp[i].pos += weight;
+         else if (diff < 0)
+           stp[i].neg += weight;
+         else
+           stp[i].ties += weight;
+       }
+    }
+
+  casereader_destroy (r);
+
+  for (i = 0 ; i < t2s->n_pairs; ++i )
+    {
+      int r = MIN (stp[i].pos, stp[i].neg);
+      stp[i].one_tailed_sig = gsl_cdf_binomial_P (r,
+                                                 0.5,
+                                                 stp[i].pos + stp[i].neg);
+
+      stp[i].point_prob = gsl_ran_binomial_pdf (r, 0.5,
+                                               stp[i].pos + stp[i].neg);
+    }
+
+  output_frequency_table (t2s, stp, dict);
+
+  output_statistics_table (t2s, stp);
+
+  free (stp);
+}
diff --git a/src/language/stats/sign.h b/src/language/stats/sign.h
new file mode 100644 (file)
index 0000000..1404e00
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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/>. */
+
+#if !sign_h
+#define sign_h 1
+
+
+#include <stdbool.h>
+#include <data/missing-values.h>
+
+struct casereader;
+struct dataset;
+struct npar_test;
+
+void sign_execute (const struct dataset *ds,
+                  struct casereader *input,
+                  enum mv_class exclude,
+                  const struct npar_test *test,
+                  bool exact,
+                  double timer);
+
+#endif
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 c69a6cdb5bc5adb552afedbc209030d6ada3b12b..d02cdb28d21597a087f51640762de88573c33a3b 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
@@ -32,6 +32,8 @@
 #include <language/command.h>
 #include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/value-parser.h>
+#include <libpspp/array.h>
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
@@ -46,6 +48,7 @@
 #include <data/format.h>
 
 #include "xalloc.h"
+#include "xmemdup0.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
 /* (specification)
    "T-TEST" (tts_):
-     +groups=custom;
-     testval=double;
-     +variables=varlist("PV_NO_SCRATCH | PV_NUMERIC");
-     +pairs=custom;
-     missing=miss:!analysis/listwise,
-            incl:include/!exclude;
-     +format=fmt:!labels/nolabels;
-     criteria=:cin(d:criteria,"%s > 0. && %s < 1.").
+   +groups=custom;
+   testval=double;
+   +variables=varlist("PV_NO_SCRATCH | PV_NUMERIC");
+   +pairs=custom;
+   missing=miss:!analysis/listwise,
+   incl:include/!exclude;
+   +format=fmt:!labels/nolabels;
+   criteria=:cin(d:criteria,"%s > 0. && %s < 1.").
 */
 /* (declarations) */
 /* (functions) */
 
-
-/* Variable for the GROUPS subcommand, if given. */
-static struct variable *indep_var;
-
 enum comparison
   {
-    CMP_LE = -2,
-    CMP_EQ = 0,
+    CMP_LE,
+    CMP_EQ,
   };
 
-struct group_properties
-{
-  /* The comparison criterion */
-  enum comparison criterion;
-
-  /* The width of the independent variable */
-  int indep_width ;
-
-  union {
-    /* The value of the independent variable at which groups are determined to
-       belong to one group or the other */
-    double critical_value;
-
-
-    /* The values of the independent variable for each group */
-    union value g_value[2];
-  } v ;
-
-};
-
-
-static struct group_properties gp ;
-
-
-
-/* PAIRS: Number of pairs to be compared ; each pair. */
-static int n_pairs = 0 ;
+/* A pair of variables to be compared. */
 struct pair
-{
-  /* The variables comprising the pair */
-  const struct variable *v[2];
-
-  /* The number of valid variable pairs */
-  double n;
-
-  /* The sum of the members */
-  double sum[2];
-
-  /* sum of squares of the members */
-  double ssq[2];
-
-  /* Std deviation of the members */
-  double std_dev[2];
-
-
-  /* Sample Std deviation of the members */
-  double s_std_dev[2];
-
-  /* The means of the members */
-  double mean[2];
-
-  /* The correlation coefficient between the variables */
-  double correlation;
-
-  /* The sum of the differences */
-  double sum_of_diffs;
-
-  /* The sum of the products */
-  double sum_of_prod;
-
-  /* The mean of the differences */
-  double mean_diff;
-
-  /* The sum of the squares of the differences */
-  double ssq_diffs;
+  {
+    const struct variable *v[2]; /* The paired variables. */
+    double n;             /* The number of valid variable pairs */
+    double sum[2];        /* The sum of the members */
+    double ssq[2];        /* sum of squares of the members */
+    double std_dev[2];    /* Std deviation of the members */
+    double s_std_dev[2];  /* Sample Std deviation of the members */
+    double mean[2];       /* The means of the members */
+    double correlation;   /* Correlation coefficient between the variables. */
+    double sum_of_diffs;  /* The sum of the differences */
+    double sum_of_prod;   /* The sum of the products */
+    double mean_diff;     /* The mean of the differences */
+    double ssq_diffs;     /* The sum of the squares of the differences */
+    double std_dev_diff;  /* The std deviation of the differences */
+  };
 
-  /* The std deviation of the differences */
-  double std_dev_diff;
+/* Which mode was T-TEST invoked */
+enum t_test_mode {
+  T_1_SAMPLE,                   /* One-sample tests. */
+  T_IND_SAMPLES,                /* Independent-sample tests. */
+  T_PAIRED                      /* Paired-sample tests. */
 };
 
-static struct pair *pairs=0;
-
-static int parse_value (struct lexer *lexer, union value * v, enum val_type);
-
-/* Structures and Functions for the Statistics Summary Box */
-struct ssbox;
-typedef void populate_ssbox_func (struct ssbox *ssb,
-                                 const struct dictionary *,
-                                 struct cmd_t_test *cmd);
-typedef void finalize_ssbox_func (struct ssbox *ssb);
+/* Total state of a T-TEST procedure. */
+struct t_test_proc
+  {
+    enum t_test_mode mode;      /* Mode that T-TEST was invoked in. */
+    double criteria;            /* Confidence interval in (0, 1). */
+    enum mv_class exclude;      /* Classes of missing values to exclude. */
+    bool listwise_missing;      /* Drop whole case if one missing var? */
+    struct fmt_spec weight_format; /* Format of weight variable. */
+
+    /* Dependent variables. */
+    const struct variable **vars;
+    size_t n_vars;
+
+    /* For mode == T_1_SAMPLE. */
+    double testval;
+
+    /* For mode == T_PAIRED only. */
+    struct pair *pairs;
+    size_t n_pairs;
+
+    /* For mode == T_IND_SAMPLES only. */
+    struct variable *indep_var; /* Independent variable. */
+    enum comparison criterion;  /* Type of comparison. */
+    double critical_value;      /* CMP_LE only: Grouping threshold value. */
+    union value g_value[2];     /* CMP_EQ only: Per-group indep var values. */
+  };
 
+/* Statistics Summary Box */
 struct ssbox
-{
-  struct tab_table *t;
-
-  populate_ssbox_func *populate;
-  finalize_ssbox_func *finalize;
-
-};
-
-/* Create a ssbox */
-void ssbox_create (struct ssbox *ssb,   struct cmd_t_test *cmd, int mode);
-
-/* Populate a ssbox according to cmd */
-void ssbox_populate (struct ssbox *ssb, const struct dictionary *dict,
-                    struct cmd_t_test *cmd);
-
-/* Submit and destroy a ssbox */
-void ssbox_finalize (struct ssbox *ssb);
-
-/* A function to create, populate and submit the Paired Samples Correlation
-   box */
-static void pscbox (const struct dictionary *);
+  {
+    struct tab_table *t;
+    void (*populate) (struct ssbox *, struct t_test_proc *);
+    void (*finalize) (struct ssbox *);
+  };
 
+static void ssbox_create (struct ssbox *, struct t_test_proc *);
+static void ssbox_populate (struct ssbox *, struct t_test_proc *);
+static void ssbox_finalize (struct ssbox *);
 
-/* Structures and Functions for the Test Results Box */
-struct trbox;
+/* Paired Samples Correlation box */
+static void pscbox (struct t_test_proc *);
 
-typedef void populate_trbox_func (struct trbox *trb,
-                                 const struct dictionary *dict,
-                                 struct cmd_t_test *cmd);
-typedef void finalize_trbox_func (struct trbox *trb);
 
+/* Test Results Box. */
 struct trbox {
   struct tab_table *t;
-  populate_trbox_func *populate;
-  finalize_trbox_func *finalize;
-};
-
-/* Create a trbox */
-void trbox_create (struct trbox *trb,   struct cmd_t_test *cmd, int mode);
-
-/* Populate a ssbox according to cmd */
-static void trbox_populate (struct trbox *trb, const struct dictionary *dict,
-                    struct cmd_t_test *cmd);
-
-/* Submit and destroy a ssbox */
-void trbox_finalize (struct trbox *trb);
-
-/* Which mode was T-TEST invoked */
-enum {
-  T_1_SAMPLE = 0 ,
-  T_IND_SAMPLES,
-  T_PAIRED
-};
-
-
-static int common_calc (const struct dictionary *dict,
-                       const struct ccase *, void *,
-                       enum mv_class);
-static void common_precalc (struct cmd_t_test *);
-static void common_postcalc (struct cmd_t_test *);
-
-static int one_sample_calc (const struct dictionary *dict, const struct ccase *, void *, enum mv_class);
-static void one_sample_precalc (struct cmd_t_test *);
-static void one_sample_postcalc (struct cmd_t_test *);
-
-static int  paired_calc (const struct dictionary *dict, const struct ccase *,
-                        struct cmd_t_test*, enum mv_class);
-static void paired_precalc (struct cmd_t_test *);
-static void paired_postcalc (struct cmd_t_test *);
-
-static void group_precalc (struct cmd_t_test *);
-static int  group_calc (const struct dictionary *dict, const struct ccase *,
-                       struct cmd_t_test *, enum mv_class);
-static void group_postcalc (struct cmd_t_test *);
-
-
-static void calculate (struct cmd_t_test *,
-                      struct casereader *,
-                     const struct dataset *);
-
-static  int mode;
-
-static struct cmd_t_test cmd;
+  void (*populate) (struct trbox *, struct t_test_proc *);
+  void (*finalize) (struct trbox *);
+  };
 
-static bool bad_weight_warn = false;
+static void trbox_create (struct trbox *, struct t_test_proc *);
+static void trbox_populate (struct trbox *, struct t_test_proc *);
+static void trbox_finalize (struct trbox *);
 
+static void calculate (struct t_test_proc *, struct casereader *,
+                       const struct dataset *);
 
 static int compare_group_binary (const struct group_statistics *a,
-                               const struct group_statistics *b,
-                               const struct group_properties *p);
-
-
-static unsigned  hash_group_binary (const struct group_statistics *g,
-                                  const struct group_properties *p);
-
-
+                                 const struct group_statistics *b,
+                                 const struct t_test_proc *);
+static unsigned hash_group_binary (const struct group_statistics *g,
+                                  const struct t_test_proc *p);
 
 int
 cmd_t_test (struct lexer *lexer, struct dataset *ds)
 {
+  struct cmd_t_test cmd;
+  struct t_test_proc proc;
   struct casegrouper *grouper;
   struct casereader *group;
-  bool ok;
+  struct variable *wv;
+  bool ok = false;
 
-  if ( !parse_t_test (lexer, ds, &cmd, NULL) )
-    return CMD_FAILURE;
+  proc.pairs = NULL;
+  proc.n_pairs = 0;
+  proc.vars = NULL;
+  proc.indep_var = NULL;
+  if (!parse_t_test (lexer, ds, &cmd, &proc))
+    goto parse_failed;
 
-  if (! cmd.sbc_criteria)
-    cmd.criteria=0.95;
+  wv = dict_get_weight (dataset_dict (ds));
+  proc.weight_format = wv ? *var_get_print_format (wv) : F_8_0;
 
-  {
-    int m=0;
-    if (cmd.sbc_testval) ++m;
-    if (cmd.sbc_groups) ++m;
-    if (cmd.sbc_pairs) ++m;
-
-    if ( m != 1)
-      {
-       msg (SE,
-           _ ("TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive.")
-           );
-        free_t_test (&cmd);
-       return CMD_FAILURE;
-      }
-  }
-
-  if (cmd.sbc_testval)
-    mode=T_1_SAMPLE;
-  else if (cmd.sbc_groups)
-    mode=T_IND_SAMPLES;
-  else
-    mode=T_PAIRED;
-
-  if ( mode == T_PAIRED)
+  if ((cmd.sbc_testval != 0) + (cmd.sbc_groups != 0) + (cmd.sbc_pairs != 0)
+      != 1)
     {
-      if (cmd.sbc_variables)
-       {
-         msg (SE, _ ("VARIABLES subcommand is not appropriate with PAIRS"));
-          free_t_test (&cmd);
-         return CMD_FAILURE;
-       }
-      else
-       {
-         /* Iterate through the pairs and put each variable that is a
-            member of a pair into cmd.v_variables */
+      msg (SE, _("Exactly one of TESTVAL, GROUPS and PAIRS subcommands "
+                 "must be specified."));
+      goto done;
+    }
 
-         int i;
-         struct hsh_iterator hi;
-         struct const_hsh_table *hash;
-         const struct variable *v;
+  proc.mode = (cmd.sbc_testval ? T_1_SAMPLE
+               : cmd.sbc_groups ? T_IND_SAMPLES
+               : T_PAIRED);
+  proc.criteria = cmd.sbc_criteria ? cmd.criteria : 0.95;
+  proc.exclude = cmd.incl != TTS_INCLUDE ? MV_ANY : MV_SYSTEM;
+  proc.listwise_missing = cmd.miss == TTS_LISTWISE;
 
-         hash = const_hsh_create (n_pairs, compare_vars_by_name, hash_var_by_name,
-          0, 0);
+  if (proc.mode == T_1_SAMPLE)
+    proc.testval = cmd.n_testval[0];
 
-         for (i=0; i < n_pairs; ++i)
-           {
-             const_hsh_insert (hash, pairs[i].v[0]);
-             const_hsh_insert (hash, pairs[i].v[1]);
-           }
+  if (proc.mode == T_PAIRED)
+    {
+      size_t i, j;
 
-         assert (cmd.n_variables == 0);
-         cmd.n_variables = const_hsh_count (hash);
-
-         cmd.v_variables = xnrealloc (cmd.v_variables, cmd.n_variables,
-                                       sizeof *cmd.v_variables);
-         /* Iterate through the hash */
-         for (i=0,v = const_hsh_first (hash, &hi);
-              v != 0;
-              v = const_hsh_next (hash, &hi) )
-           cmd.v_variables[i++]=v;
-         const_hsh_destroy (hash);
+      if (cmd.sbc_variables)
+       {
+         msg (SE, _("VARIABLES subcommand may not be used with PAIRS."));
+          goto done;
        }
+
+      /* Fill proc.vars with the unique variables from pairs. */
+      proc.n_vars = proc.n_pairs * 2;
+      proc.vars = xmalloc (sizeof *proc.vars * proc.n_vars);
+      for (i = j = 0; i < proc.n_pairs; i++)
+        {
+          proc.vars[j++] = proc.pairs[i].v[0];
+          proc.vars[j++] = proc.pairs[i].v[1];
+        }
+      proc.n_vars = sort_unique (proc.vars, proc.n_vars, sizeof *proc.vars,
+                                 compare_var_ptrs_by_name, NULL);
     }
-  else if ( !cmd.sbc_variables)
+  else
     {
-      msg (SE, _ ("One or more VARIABLES must be specified."));
-      free_t_test (&cmd);
-      return CMD_FAILURE;
+      if (!cmd.n_variables)
+        {
+          msg (SE, _("One or more VARIABLES must be specified."));
+          goto done;
+        }
+      proc.n_vars = cmd.n_variables;
+      proc.vars = cmd.v_variables;
+      cmd.v_variables = NULL;
     }
 
-  bad_weight_warn = true;
-
   /* Data pass. */
   grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
   while (casegrouper_get_next_group (grouper, &group))
-    calculate (&cmd, group, ds);
+    calculate (&proc, group, ds);
   ok = casegrouper_destroy (grouper);
   ok = proc_commit (ds) && ok;
 
-  n_pairs=0;
-  free (pairs);
-  pairs=0;
-
-  if ( mode == T_IND_SAMPLES)
+  if (proc.mode == T_IND_SAMPLES)
     {
       int v;
       /* Destroy any group statistics we created */
-      for (v = 0 ; v < cmd.n_variables ; ++v )
+      for (v = 0; v < proc.n_vars; v++)
        {
-         struct group_proc *grpp = group_proc_get (cmd.v_variables[v]);
+         struct group_proc *grpp = group_proc_get (proc.vars[v]);
          hsh_destroy (grpp->group_hash);
        }
     }
 
+done:
   free_t_test (&cmd);
-  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+parse_failed:
+  if (proc.indep_var != NULL)
+    {
+      int width = var_get_width (proc.indep_var);
+      value_destroy (&proc.g_value[0], width);
+      value_destroy (&proc.g_value[1], width);
+    }
+  free (proc.vars);
+  free (proc.pairs);
+  return ok ? CMD_SUCCESS : CMD_FAILURE;
 }
 
 static int
-tts_custom_groups (struct lexer *lexer, struct dataset *ds, struct cmd_t_test *cmd UNUSED, 
-       void *aux UNUSED)
+tts_custom_groups (struct lexer *lexer, struct dataset *ds,
+                   struct cmd_t_test *cmd UNUSED, void *proc_)
 {
-  int n_group_values=0;
+  struct t_test_proc *proc = proc_;
+  int n_values;
+  int width;
 
   lex_match (lexer, '=');
 
-  indep_var = parse_variable (lexer, dataset_dict (ds));
-  if (!indep_var)
+  proc->indep_var = parse_variable (lexer, dataset_dict (ds));
+  if (proc->indep_var == NULL)
     {
       lex_error (lexer, "expecting variable name in GROUPS subcommand");
       return 0;
     }
-
-  if (var_is_long_string (indep_var))
-    {
-      msg (SE, _ ("Long string variable %s is not valid here."),
-          var_get_name (indep_var));
-      return 0;
-    }
+  width = var_get_width (proc->indep_var);
+  value_init (&proc->g_value[0], width);
+  value_init (&proc->g_value[1], width);
 
   if (!lex_match (lexer, '('))
+    n_values = 0;
+  else
     {
-      if (var_is_numeric (indep_var))
-       {
-         gp.v.g_value[0].f = 1;
-         gp.v.g_value[1].f = 2;
-
-         gp.criterion = CMP_EQ;
-
-         n_group_values = 2;
-
-         return 1;
-       }
+      if (!parse_value (lexer, &proc->g_value[0], width))
+        return 0;
+      lex_match (lexer, ',');
+      if (lex_match (lexer, ')'))
+        n_values = 1;
       else
-       {
-         msg (SE, _ ("When applying GROUPS to a string variable, two "
-                    "values must be specified."));
-         return 0;
-       }
+        {
+          if (!parse_value (lexer, &proc->g_value[1], width)
+              || !lex_force_match (lexer, ')'))
+            return 0;
+          n_values = 2;
+        }
     }
 
-  if (!parse_value (lexer, &gp.v.g_value[0], var_get_type (indep_var)))
-      return 0;
-
-  lex_match (lexer, ',');
-  if (lex_match (lexer, ')'))
+  if (var_is_numeric (proc->indep_var))
     {
-      if (var_is_alpha (indep_var))
+      proc->criterion = n_values == 1 ? CMP_LE : CMP_EQ;
+      if (n_values == 1)
+        proc->critical_value = proc->g_value[0].f;
+      else if (n_values == 0)
        {
-         msg (SE, _ ("When applying GROUPS to a string variable, two "
-                    "values must be specified."));
-         return 0;
+         proc->g_value[0].f = 1;
+         proc->g_value[1].f = 2;
        }
-      gp.criterion = CMP_LE;
-      gp.v.critical_value = gp.v.g_value[0].f;
-
-      n_group_values = 1;
-      return 1;
     }
-
-  if (!parse_value (lexer, &gp.v.g_value[1], var_get_type (indep_var)))
-    return 0;
-
-  n_group_values = 2;
-  if (!lex_force_match (lexer, ')'))
-    return 0;
-
-  if ( n_group_values == 2 )
-    gp.criterion = CMP_EQ ;
   else
-    gp.criterion = CMP_LE ;
-
-
-  if ( var_is_alpha (indep_var))
     {
-      buf_copy_rpad (gp.v.g_value [0].s, var_get_width (indep_var),
-                    gp.v.g_value [0].s, strlen (gp.v.g_value[0].s));
-
-      buf_copy_rpad (gp.v.g_value [1].s, var_get_width (indep_var),
-                    gp.v.g_value [1].s, strlen (gp.v.g_value[1].s));
+      proc->criterion = CMP_EQ;
+      if (n_values != 2)
+       {
+         msg (SE, _("When applying GROUPS to a string variable, two "
+                     "values must be specified."));
+         return 0;
+       }
     }
-
   return 1;
 }
 
+static void
+add_pair (struct t_test_proc *proc,
+          const struct variable *v0, const struct variable *v1)
+{
+  struct pair *p = &proc->pairs[proc->n_pairs++];
+  p->v[0] = v0;
+  p->v[1] = v1;
+}
 
 static int
-tts_custom_pairs (struct lexer *lexer, struct dataset *ds, struct cmd_t_test *cmd UNUSED, void *aux UNUSED)
+tts_custom_pairs (struct lexer *lexer, struct dataset *ds,
+                  struct cmd_t_test *cmd UNUSED, void *proc_)
 {
-  const struct variable **vars;
-  size_t n_vars;
-  size_t n_pairs_local;
-
-  size_t n_before_WITH;
-  size_t n_after_WITH = SIZE_MAX;
-  int paired ; /* Was the PAIRED keyword given ? */
-
-  lex_match (lexer, '=');
+  struct t_test_proc *proc = proc_;
 
-  n_vars=0;
-  if (!parse_variables_const (lexer, dataset_dict (ds), &vars, &n_vars,
-                       PV_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH))
-    {
-      free (vars);
-      return 0;
-    }
-  assert (n_vars);
-
-  n_before_WITH = 0;
-  if (lex_match (lexer, T_WITH))
-    {
-      n_before_WITH = n_vars;
-      if (!parse_variables_const (lexer, dataset_dict (ds), &vars, &n_vars,
-                           PV_DUPLICATE | PV_APPEND
-                           | PV_NUMERIC | PV_NO_SCRATCH))
-       {
-         free (vars);
-         return 0;
-       }
-      n_after_WITH = n_vars - n_before_WITH;
-    }
+  const struct variable **vars1 = NULL;
+  size_t n_vars1 = 0;
 
-  paired = (lex_match (lexer, '(') && lex_match_id (lexer, "PAIRED") && lex_match (lexer, ')'));
+  const struct variable **vars2 = NULL;
+  size_t n_vars2 = 0;
 
-  /* Determine the number of pairs needed */
-  if (paired)
-    {
-      if (n_before_WITH != n_after_WITH)
-       {
-         free (vars);
-         msg (SE, _ ("PAIRED was specified but the number of variables "
-                    "preceding WITH (%zu) did not match the number "
-                    "following (%zu)."),
-               n_before_WITH, n_after_WITH);
-         return 0;
-       }
-      n_pairs_local = n_before_WITH;
-    }
-  else if (n_before_WITH > 0) /* WITH keyword given, but not PAIRED keyword */
-    {
-      n_pairs_local = n_before_WITH * n_after_WITH ;
-    }
-  else /* Neither WITH nor PAIRED keyword given */
-    {
-      if (n_vars < 2)
-       {
-         free (vars);
-         msg (SE, _ ("At least two variables must be specified "
-                    "on PAIRS."));
-         return 0;
-       }
-
-      /* how many ways can you pick 2 from n_vars ? */
-      n_pairs_local = n_vars * (n_vars - 1) / 2;
-    }
+  bool paired = false;
 
+  size_t n_total_pairs;
+  size_t i, j;
 
-  /* Allocate storage for the pairs */
-  pairs = xnrealloc (pairs, n_pairs + n_pairs_local, sizeof *pairs);
+  lex_match (lexer, '=');
 
-  /* Populate the pairs with the appropriate variables */
-  if ( paired )
-    {
-      int i;
+  if (!parse_variables_const (lexer, dataset_dict (ds), &vars1, &n_vars1,
+                              PV_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH))
+    return 0;
 
-      assert (n_pairs_local == n_vars / 2);
-      for (i = 0; i < n_pairs_local; ++i)
-       {
-         pairs[i].v[n_pairs] = vars[i];
-         pairs[i].v[n_pairs + 1] = vars[i + n_pairs_local];
-       }
-    }
-  else if (n_before_WITH > 0) /* WITH keyword given, but not PAIRED keyword */
+  if (lex_match (lexer, T_WITH))
     {
-      int i,j;
-      size_t p = n_pairs;
-
-      for (i=0 ; i < n_before_WITH ; ++i )
-       {
-         for (j=0 ; j < n_after_WITH ; ++j)
-           {
-             pairs[p].v[0] = vars[i];
-             pairs[p].v[1] = vars[j+n_before_WITH];
-             ++p;
-           }
-       }
+      if (!parse_variables_const (lexer, dataset_dict (ds), &vars2, &n_vars2,
+                                  PV_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH))
+        {
+          free (vars1);
+          return 0;
+        }
+
+      if (lex_match (lexer, '(')
+          && lex_match_id (lexer, "PAIRED")
+          && lex_match (lexer, ')'))
+        {
+          paired = true;
+          if (n_vars1 != n_vars2)
+            {
+              msg (SE, _("PAIRED was specified but the number of variables "
+                         "preceding WITH (%zu) did not match the number "
+                         "following (%zu)."),
+                   n_vars1, n_vars2);
+              free (vars1);
+              free (vars2);
+              return 0;
+            }
+        }
     }
-  else /* Neither WITH nor PAIRED given */
+  else
     {
-      size_t i,j;
-      size_t p=n_pairs;
-
-      for (i=0 ; i < n_vars ; ++i )
+      if (n_vars1 < 2)
        {
-         for (j=i+1 ; j < n_vars ; ++j)
-           {
-             pairs[p].v[0] = vars[i];
-             pairs[p].v[1] = vars[j];
-             ++p;
-           }
+         free (vars1);
+         msg (SE, _("At least two variables must be specified on PAIRS."));
+         return 0;
        }
     }
 
-  n_pairs+=n_pairs_local;
+  /* Allocate storage for the new pairs. */
+  n_total_pairs = proc->n_pairs + (paired ? n_vars1
+                                   : n_vars2 > 0 ? n_vars1 * n_vars2
+                                   : n_vars1 * (n_vars1 - 1) / 2);
+  proc->pairs = xnrealloc (proc->pairs, n_total_pairs, sizeof *proc->pairs);
 
-  free (vars);
-  return 1;
-}
-
-/* Parses the current token (numeric or string, depending on type)
-    value v and returns success. */
-static int
-parse_value (struct lexer *lexer, union value * v, enum val_type type)
-{
-  if (type == VAL_NUMERIC)
-    {
-      if (!lex_force_num (lexer))
-       return 0;
-      v->f = lex_tokval (lexer);
-    }
+  /* Populate the pairs with the appropriate variables. */
+  if (paired)
+    for (i = 0; i < n_vars1; i++)
+      add_pair (proc, vars1[i], vars2[i]);
+  else if (n_vars2 > 0)
+    for (i = 0; i < n_vars1; i++)
+      for (j = 0; j < n_vars2; j++)
+        add_pair (proc, vars1[i], vars2[j]);
   else
-    {
-      if (!lex_force_string (lexer))
-       return 0;
-      memset  (v->s, ' ', MAX_SHORT_STRING);
-      strncpy (v->s, ds_cstr (lex_tokstr (lexer)), ds_length (lex_tokstr (lexer)));
-    }
-
-  lex_get (lexer);
+    for (i = 0; i < n_vars1; i++)
+      for (j = i + 1; j < n_vars1; j++)
+        add_pair (proc, vars1[i], vars1[j]);
+  assert (proc->n_pairs == n_total_pairs);
 
+  free (vars1);
+  free (vars2);
   return 1;
 }
+\f
+/* Implementation of the SSBOX object. */
 
+static void ssbox_base_init (struct ssbox *, int cols, int rows);
+static void ssbox_base_finalize (struct ssbox *);
+static void ssbox_one_sample_init (struct ssbox *, struct t_test_proc *);
+static void ssbox_independent_samples_init (struct ssbox *, struct t_test_proc *);
+static void ssbox_paired_init (struct ssbox *, struct t_test_proc *);
 
-/* Implementation of the SSBOX object */
-
-void ssbox_base_init (struct ssbox *this, int cols,int rows);
-
-void ssbox_base_finalize (struct ssbox *ssb);
-
-void ssbox_one_sample_init (struct ssbox *this,
-                          struct cmd_t_test *cmd );
-
-void ssbox_independent_samples_init (struct ssbox *this,
-                                   struct cmd_t_test *cmd);
-
-void ssbox_paired_init (struct ssbox *this,
-                          struct cmd_t_test *cmd);
-
-
-/* Factory to create an ssbox */
-void
-ssbox_create (struct ssbox *ssb, struct cmd_t_test *cmd, int mode)
+/* Factory to create an ssbox. */
+static void
+ssbox_create (struct ssbox *ssb, struct t_test_proc *proc)
 {
-    switch (mode)
-      {
-      case T_1_SAMPLE:
-       ssbox_one_sample_init (ssb,cmd);
-       break;
-      case T_IND_SAMPLES:
-       ssbox_independent_samples_init (ssb,cmd);
-       break;
-      case T_PAIRED:
-       ssbox_paired_init (ssb,cmd);
-       break;
-      default:
-       NOT_REACHED ();
-      }
+  switch (proc->mode)
+    {
+    case T_1_SAMPLE:
+      ssbox_one_sample_init (ssb, proc);
+      break;
+    case T_IND_SAMPLES:
+      ssbox_independent_samples_init (ssb, proc);
+      break;
+    case T_PAIRED:
+      ssbox_paired_init (ssb, proc);
+      break;
+    default:
+      NOT_REACHED ();
+    }
 }
 
-
-
 /* Despatcher for the populate method */
-void
-ssbox_populate (struct ssbox *ssb, const struct dictionary *dict,
-               struct cmd_t_test *cmd)
+static void
+ssbox_populate (struct ssbox *ssb, struct t_test_proc *proc)
 {
-  ssb->populate (ssb, dict, cmd);
+  ssb->populate (ssb, proc);
 }
 
-
 /* Despatcher for finalize */
-void
+static void
 ssbox_finalize (struct ssbox *ssb)
 {
   ssb->finalize (ssb);
 }
 
-
 /* Submit the box and clear up */
-void
+static void
 ssbox_base_finalize (struct ssbox *ssb)
 {
   tab_submit (ssb->t);
 }
 
-
-
 /* Initialize a ssbox struct */
-void
-ssbox_base_init (struct ssbox *this, int cols,int rows)
+static void
+ssbox_base_init (struct ssbox *this, int cols, int rows)
 {
   this->finalize = ssbox_base_finalize;
   this->t = tab_create (cols, rows, 0);
 
   tab_columns (this->t, SOM_COL_DOWN, 1);
-  tab_headers (this->t,0,0,1,0);
-  tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols -1, rows -1 );
-  tab_hline (this->t, TAL_2,0,cols-1,1);
-  tab_dim (this->t, tab_natural_dimensions);
+  tab_headers (this->t, 0, 0, 1, 0);
+  tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
+  tab_hline (this->t, TAL_2, 0, cols- 1, 1);
+  tab_dim (this->t, tab_natural_dimensions, NULL);
 }
+\f
+/* ssbox implementations. */
 
-void  ssbox_one_sample_populate (struct ssbox *ssb,
-                                const struct dictionary *,
-                                struct cmd_t_test *cmd);
+static void ssbox_one_sample_populate (struct ssbox *, struct t_test_proc *);
+static void ssbox_independent_samples_populate (struct ssbox *,
+                                                struct t_test_proc *);
+static void ssbox_paired_populate (struct ssbox *, struct t_test_proc *);
 
 /* Initialize the one_sample ssbox */
-void
-ssbox_one_sample_init (struct ssbox *this,
-                          struct cmd_t_test *cmd )
+static void
+ssbox_one_sample_init (struct ssbox *this, struct t_test_proc *proc)
 {
-  const int hsize=5;
-  const int vsize=cmd->n_variables+1;
+  const int hsize = 5;
+  const int vsize = proc->n_vars + 1;
 
   this->populate = ssbox_one_sample_populate;
 
-  ssbox_base_init (this, hsize,vsize);
-  tab_title (this->t, _ ("One-Sample Statistics"));
-  tab_vline (this->t, TAL_2, 1,0,vsize - 1);
-  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, _ ("N"));
-  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _ ("Mean"));
-  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
-  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _ ("SE. Mean"));
+  ssbox_base_init (this, hsize, vsize);
+  tab_title (this->t, _("One-Sample Statistics"));
+  tab_vline (this->t, TAL_2, 1, 0, vsize - 1);
+  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, _("N"));
+  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
+  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
+  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _("SE. Mean"));
 }
 
-static void ssbox_independent_samples_populate (struct ssbox *ssb,
-                                               const struct dictionary *,
-                                               struct cmd_t_test *cmd);
-
 /* Initialize the independent samples ssbox */
-void
-ssbox_independent_samples_init (struct ssbox *this,
-       struct cmd_t_test *cmd)
+static void
+ssbox_independent_samples_init (struct ssbox *this, struct t_test_proc *proc)
 {
   int hsize=6;
-  int vsize = cmd->n_variables*2 +1;
+  int vsize = proc->n_vars * 2 + 1;
 
   this->populate = ssbox_independent_samples_populate;
 
-  ssbox_base_init (this, hsize,vsize);
-  tab_vline (this->t, TAL_GAP, 1, 0,vsize - 1);
-  tab_title (this->t, _ ("Group Statistics"));
-  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE, var_get_name (indep_var));
-  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _ ("N"));
-  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _ ("Mean"));
-  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
-  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _ ("SE. Mean"));
+  ssbox_base_init (this, hsize, vsize);
+  tab_vline (this->t, TAL_GAP, 1, 0, vsize - 1);
+  tab_title (this->t, _("Group Statistics"));
+  tab_text (this->t, 1, 0, TAB_CENTER | TAT_TITLE,
+            var_get_name (proc->indep_var));
+  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("N"));
+  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
+  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
+  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _("SE. Mean"));
 }
 
-
 /* Populate the ssbox for independent samples */
 static void
 ssbox_independent_samples_populate (struct ssbox *ssb,
-                                   const struct dictionary *dict,
-                                   struct cmd_t_test *cmd)
+                                    struct t_test_proc *proc)
 {
   int i;
 
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
-
-  char *val_lab[2] = {NULL, NULL};
+  char *val_lab[2];
   double indep_value[2];
 
-  char prefix[2][3]={"",""};
+  char prefix[2][3];
 
-  if ( var_is_numeric (indep_var) )
+  for (i = 0; i < 2; i++)
     {
-      const char *s;
+      union value *value = &proc->g_value[i];
+      int width = var_get_width (proc->indep_var);
 
-      s = var_lookup_value_label (indep_var, &gp.v.g_value[0]);
-      val_lab[0] = s ? strdup (s) : NULL;
+      indep_value[i] = (proc->criterion == CMP_LE ? proc->critical_value
+                        : value->f);
 
-      s = var_lookup_value_label (indep_var, &gp.v.g_value[1]);
-      val_lab[1] = s ? strdup (s) : NULL;
-    }
-  else
-    {
-      val_lab[0] = calloc (sizeof (char), MAX_SHORT_STRING + 1);
-      val_lab[1] = calloc (sizeof (char), MAX_SHORT_STRING + 1);
-      memcpy (val_lab[0], gp.v.g_value[0].s, MAX_SHORT_STRING);
-      memcpy (val_lab[1], gp.v.g_value[1].s, MAX_SHORT_STRING);
+      if (val_type_from_width (width) == VAL_NUMERIC)
+        {
+          const char *s = var_lookup_value_label (proc->indep_var, value);
+          val_lab[i] = s ? xstrdup (s) : xasprintf ("%g", indep_value[i]);
+        }
+      else
+        val_lab[i] = xmemdup0 (value_str (value, width), width);
     }
 
-  if (gp.criterion == CMP_LE )
+  if (proc->criterion == CMP_LE)
     {
-      strcpy (prefix[0],">=");
-      strcpy (prefix[1],"<");
-      indep_value[0] = gp.v.critical_value;
-      indep_value[1] = gp.v.critical_value;
+      strcpy (prefix[0], ">=");
+      strcpy (prefix[1], "<");
     }
   else
     {
-      indep_value[0] = gp.v.g_value[0].f;
-      indep_value[1] = gp.v.g_value[1].f;
+      strcpy (prefix[0], "");
+      strcpy (prefix[1], "");
     }
 
-  assert (ssb->t);
-
-  for (i=0; i < cmd->n_variables; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      const struct variable *var = cmd->v_variables[i];
+      const struct variable *var = proc->vars[i];
       struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
       int count=0;
 
-      tab_text (ssb->t, 0, i*2+1, TAB_LEFT,
-                var_get_name (cmd->v_variables[i]));
-
-      if (val_lab[0])
-       tab_text (ssb->t, 1, i*2+1, TAB_LEFT | TAT_PRINTF,
-                 "%s%s", prefix[0], val_lab[0]);
-      else
-         tab_text (ssb->t, 1, i*2+1, TAB_LEFT | TAT_PRINTF,
-                   "%s%g", prefix[0], indep_value[0]);
-
-
-      if (val_lab[1])
-       tab_text (ssb->t, 1, i*2+1+1, TAB_LEFT | TAT_PRINTF,
-                 "%s%s", prefix[1], val_lab[1]);
-      else
-         tab_text (ssb->t, 1, i*2+1+1, TAB_LEFT | TAT_PRINTF,
-                   "%s%g", prefix[1], indep_value[1]);
-
+      tab_text (ssb->t, 0, i * 2 + 1, TAB_LEFT,
+                var_get_name (proc->vars[i]));
+      tab_text_format (ssb->t, 1, i * 2 + 1, TAB_LEFT,
+                       "%s%s", prefix[0], val_lab[0]);
+      tab_text_format (ssb->t, 1, i * 2 + 1+ 1, TAB_LEFT,
+                       "%s%s", prefix[1], val_lab[1]);
 
       /* Fill in the group statistics */
-      for ( count = 0 ; count < 2 ; ++count )
+      for (count = 0; count < 2; count++)
        {
          union value search_val;
-
          struct group_statistics *gs;
 
-         if ( gp.criterion == CMP_LE )
-           {
-             if ( count == 0 )
-               {
-                 /* >= case  */
-                 search_val.f = gp.v.critical_value + 1.0;
-               }
-             else
-               {
-                 /*  less than ( < )  case */
-                 search_val.f = gp.v.critical_value - 1.0;
-               }
-           }
+         if (proc->criterion == CMP_LE)
+            search_val.f = proc->critical_value + (count == 0 ? 1.0 : -1.0);
          else
-           {
-             search_val = gp.v.g_value[count];
-           }
+            search_val = proc->g_value[count];
 
-         gs = hsh_find (grp_hash, (void *) &search_val);
+         gs = hsh_find (grp_hash, &search_val);
          assert (gs);
 
-         tab_double (ssb->t, 2, i*2+count+1, TAB_RIGHT, gs->n, wfmt);
-         tab_double (ssb->t, 3, i*2+count+1, TAB_RIGHT, gs->mean, NULL);
-         tab_double (ssb->t, 4, i*2+count+1, TAB_RIGHT, gs->std_dev, NULL);
-         tab_double (ssb->t, 5, i*2+count+1, TAB_RIGHT, gs->se_mean, NULL);
+         tab_double (ssb->t, 2, i * 2 + count+ 1, TAB_RIGHT, gs->n,
+                      &proc->weight_format);
+         tab_double (ssb->t, 3, i * 2 + count+ 1, TAB_RIGHT, gs->mean, NULL);
+         tab_double (ssb->t, 4, i * 2 + count+ 1, TAB_RIGHT, gs->std_dev,
+                      NULL);
+         tab_double (ssb->t, 5, i * 2 + count+ 1, TAB_RIGHT, gs->se_mean,
+                      NULL);
        }
     }
   free (val_lab[0]);
   free (val_lab[1]);
 }
 
-
-static void ssbox_paired_populate (struct ssbox *ssb,
-                                  const struct dictionary *dict,
-                                  struct cmd_t_test *cmd);
-
 /* Initialize the paired values ssbox */
-void
-ssbox_paired_init (struct ssbox *this, struct cmd_t_test *cmd UNUSED)
+static void
+ssbox_paired_init (struct ssbox *this, struct t_test_proc *proc)
 {
-  int hsize=6;
-
-  int vsize = n_pairs*2+1;
+  int hsize = 6;
+  int vsize = proc->n_pairs * 2 + 1;
 
   this->populate = ssbox_paired_populate;
 
-  ssbox_base_init (this, hsize,vsize);
-  tab_title (this->t, _ ("Paired Sample Statistics"));
-  tab_vline (this->t,TAL_GAP,1,0,vsize-1);
-  tab_vline (this->t,TAL_2,2,0,vsize-1);
-  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _ ("Mean"));
-  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _ ("N"));
-  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
-  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _ ("SE. Mean"));
+  ssbox_base_init (this, hsize, vsize);
+  tab_title (this->t, _("Paired Sample Statistics"));
+  tab_vline (this->t, TAL_GAP, 1, 0, vsize - 1);
+  tab_vline (this->t, TAL_2, 2, 0, vsize - 1);
+  tab_text (this->t, 2, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
+  tab_text (this->t, 3, 0, TAB_CENTER | TAT_TITLE, _("N"));
+  tab_text (this->t, 4, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
+  tab_text (this->t, 5, 0, TAB_CENTER | TAT_TITLE, _("SE. Mean"));
 }
 
-
 /* Populate the ssbox for paired values */
-void
-ssbox_paired_populate (struct ssbox *ssb, const struct dictionary *dict,
-                      struct cmd_t_test *cmd UNUSED)
+static void
+ssbox_paired_populate (struct ssbox *ssb, struct t_test_proc *proc)
 {
   int i;
 
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
-
-  assert (ssb->t);
-
-  for (i=0; i < n_pairs; ++i)
+  for (i = 0; i < proc->n_pairs; i++)
     {
+      struct pair *p = &proc->pairs[i];
       int j;
 
-      tab_text (ssb->t, 0, i*2+1, TAB_LEFT | TAT_PRINTF , _ ("Pair %d"),i);
-
-      for (j=0 ; j < 2 ; ++j)
+      tab_text_format (ssb->t, 0, i * 2 + 1, TAB_LEFT, _("Pair %d"), i);
+      for (j=0; j < 2; j++)
        {
-         struct group_statistics *gs;
-
-         gs = &group_proc_get (pairs[i].v[j])->ugs;
-
          /* Titles */
-
-         tab_text (ssb->t, 1, i*2+j+1, TAB_LEFT,
-                    var_get_name (pairs[i].v[j]));
+         tab_text (ssb->t, 1, i * 2 + j + 1, TAB_LEFT,
+                    var_get_name (p->v[j]));
 
          /* Values */
-         tab_double (ssb->t,2, i*2+j+1, TAB_RIGHT, pairs[i].mean[j], NULL);
-         tab_double (ssb->t,3, i*2+j+1, TAB_RIGHT, pairs[i].n, wfmt);
-         tab_double (ssb->t,4, i*2+j+1, TAB_RIGHT, pairs[i].std_dev[j], NULL);
-         tab_double (ssb->t,5, i*2+j+1, TAB_RIGHT,
-                     pairs[i].std_dev[j]/sqrt (pairs[i].n), NULL);
-
+         tab_double (ssb->t, 2, i * 2 + j + 1, TAB_RIGHT, p->mean[j], NULL);
+         tab_double (ssb->t, 3, i * 2 + j + 1, TAB_RIGHT, p->n,
+                      &proc->weight_format);
+         tab_double (ssb->t, 4, i * 2 + j + 1, TAB_RIGHT, p->std_dev[j],
+                      NULL);
+         tab_double (ssb->t, 5, i * 2 + j + 1, TAB_RIGHT,
+                     p->std_dev[j] /sqrt (p->n), NULL);
        }
     }
 }
 
 /* Populate the one sample ssbox */
-void
-ssbox_one_sample_populate (struct ssbox *ssb, const struct dictionary *dict,
-                          struct cmd_t_test *cmd)
+static void
+ssbox_one_sample_populate (struct ssbox *ssb, struct t_test_proc *proc)
 {
   int i;
 
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
-
-  assert (ssb->t);
-
-  for (i=0; i < cmd->n_variables; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      struct group_statistics *gs = &group_proc_get (cmd->v_variables[i])->ugs;
+      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
 
-      tab_text (ssb->t, 0, i+1, TAB_LEFT, var_get_name (cmd->v_variables[i]));
-      tab_double (ssb->t,1, i+1, TAB_RIGHT, gs->n, wfmt);
-      tab_double (ssb->t,2, i+1, TAB_RIGHT, gs->mean, NULL);
-      tab_double (ssb->t,3, i+1, TAB_RIGHT, gs->std_dev, NULL);
-      tab_double (ssb->t,4, i+1, TAB_RIGHT, gs->se_mean, NULL);
+      tab_text (ssb->t, 0, i + 1, TAB_LEFT, var_get_name (proc->vars[i]));
+      tab_double (ssb->t, 1, i + 1, TAB_RIGHT, gs->n, &proc->weight_format);
+      tab_double (ssb->t, 2, i + 1, TAB_RIGHT, gs->mean, NULL);
+      tab_double (ssb->t, 3, i + 1, TAB_RIGHT, gs->std_dev, NULL);
+      tab_double (ssb->t, 4, i + 1, TAB_RIGHT, gs->se_mean, NULL);
     }
 }
-
-
-
+\f
 /* Implementation of the Test Results box struct */
 
-void trbox_base_init (struct trbox *self,size_t n_vars, int cols);
-void trbox_base_finalize (struct trbox *trb);
-
-void trbox_independent_samples_init (struct trbox *trb,
-                                   struct cmd_t_test *cmd );
-
-static void trbox_independent_samples_populate (struct trbox *trb,
-                                        const struct dictionary *dict,
-                                        struct cmd_t_test *cmd);
-
-void trbox_one_sample_init (struct trbox *self,
-                     struct cmd_t_test *cmd );
-
-static void trbox_one_sample_populate (struct trbox *trb,
-                               const struct dictionary *,
-                               struct cmd_t_test *cmd);
-
-void trbox_paired_init (struct trbox *self,
-                      struct cmd_t_test *cmd );
-
-static void trbox_paired_populate (struct trbox *trb,
-                                  const struct dictionary *,
-                                  struct cmd_t_test *cmd);
-
-
+static void trbox_base_init (struct trbox *, size_t n_vars, int cols);
+static void trbox_base_finalize (struct trbox *);
+static void trbox_independent_samples_init (struct trbox *,
+                                            struct t_test_proc *);
+static void trbox_independent_samples_populate (struct trbox *,
+                                                struct t_test_proc *);
+static void trbox_one_sample_init (struct trbox *, struct t_test_proc *);
+static void trbox_one_sample_populate (struct trbox *, struct t_test_proc *);
+static void trbox_paired_init (struct trbox *, struct t_test_proc *);
+static void trbox_paired_populate (struct trbox *, struct t_test_proc *);
 
 /* Create a trbox according to mode*/
-void
-trbox_create (struct trbox *trb,
-            struct cmd_t_test *cmd, int mode)
+static void
+trbox_create (struct trbox *trb, struct t_test_proc *proc)
 {
-    switch (mode)
-      {
-      case T_1_SAMPLE:
-       trbox_one_sample_init (trb,cmd);
-       break;
-      case T_IND_SAMPLES:
-       trbox_independent_samples_init (trb,cmd);
-       break;
-      case T_PAIRED:
-       trbox_paired_init (trb,cmd);
-       break;
-      default:
-        NOT_REACHED ();
-      }
+  switch (proc->mode)
+    {
+    case T_1_SAMPLE:
+      trbox_one_sample_init (trb, proc);
+      break;
+    case T_IND_SAMPLES:
+      trbox_independent_samples_init (trb, proc);
+      break;
+    case T_PAIRED:
+      trbox_paired_init (trb, proc);
+      break;
+    default:
+      NOT_REACHED ();
+    }
 }
 
-/* Populate a trbox according to cmd */
+/* Populate a trbox according to proc */
 static void
-trbox_populate (struct trbox *trb, const struct dictionary *dict,
-               struct cmd_t_test *cmd)
+trbox_populate (struct trbox *trb, struct t_test_proc *proc)
 {
-  trb->populate (trb, dict, cmd);
+  trb->populate (trb, proc);
 }
 
 /* Submit and destroy a trbox */
-void
+static void
 trbox_finalize (struct trbox *trb)
 {
   trb->finalize (trb);
 }
 
 /* Initialize the independent samples trbox */
-void
+static void
 trbox_independent_samples_init (struct trbox *self,
-                          struct cmd_t_test *cmd UNUSED)
+                                struct t_test_proc *proc)
 {
-  const int hsize=11;
-  const int vsize=cmd->n_variables*2+3;
+  const int hsize = 11;
+  const int vsize = proc->n_vars * 2 + 3;
 
   assert (self);
   self->populate = trbox_independent_samples_populate;
 
-  trbox_base_init (self,cmd->n_variables*2,hsize);
-  tab_title (self->t,("Independent Samples Test"));
-  tab_hline (self->t,TAL_1,2,hsize-1,1);
-  tab_vline (self->t,TAL_2,2,0,vsize-1);
-  tab_vline (self->t,TAL_1,4,0,vsize-1);
-  tab_box (self->t,-1,-1,-1,TAL_1, 2,1,hsize-2,vsize-1);
-  tab_hline (self->t,TAL_1, hsize-2,hsize-1,2);
-  tab_box (self->t,-1,-1,-1,TAL_1, hsize-2,2,hsize-1,vsize-1);
+  trbox_base_init (self, proc->n_vars * 2, hsize);
+  tab_title (self->t, _("Independent Samples Test"));
+  tab_hline (self->t, TAL_1, 2, hsize - 1, 1);
+  tab_vline (self->t, TAL_2, 2, 0, vsize - 1);
+  tab_vline (self->t, TAL_1, 4, 0, vsize - 1);
+  tab_box (self->t, -1, -1, -1, TAL_1, 2, 1, hsize - 2, vsize - 1);
+  tab_hline (self->t, TAL_1, hsize - 2, hsize - 1, 2);
+  tab_box (self->t, -1, -1, -1, TAL_1, hsize - 2, 2, hsize - 1, vsize - 1);
   tab_joint_text (self->t, 2, 0, 3, 0,
-                TAB_CENTER,_ ("Levene's Test for Equality of Variances"));
-  tab_joint_text (self->t, 4,0,hsize-1,0,
-                TAB_CENTER,_ ("t-test for Equality of Means"));
-
-  tab_text (self->t,2,2, TAB_CENTER | TAT_TITLE,_ ("F"));
-  tab_text (self->t,3,2, TAB_CENTER | TAT_TITLE,_ ("Sig."));
-  tab_text (self->t,4,2, TAB_CENTER | TAT_TITLE,_ ("t"));
-  tab_text (self->t,5,2, TAB_CENTER | TAT_TITLE,_ ("df"));
-  tab_text (self->t,6,2, TAB_CENTER | TAT_TITLE,_ ("Sig. (2-tailed)"));
-  tab_text (self->t,7,2, TAB_CENTER | TAT_TITLE,_ ("Mean Difference"));
-  tab_text (self->t,8,2, TAB_CENTER | TAT_TITLE,_ ("Std. Error Difference"));
-  tab_text (self->t,9,2, TAB_CENTER | TAT_TITLE,_ ("Lower"));
-  tab_text (self->t,10,2, TAB_CENTER | TAT_TITLE,_ ("Upper"));
-
-  tab_joint_text (self->t, 9, 1, 10, 1, TAB_CENTER | TAT_PRINTF,
-                _ ("%g%% Confidence Interval of the Difference"),
-                cmd->criteria*100.0);
-
+                  TAB_CENTER, _("Levene's Test for Equality of Variances"));
+  tab_joint_text (self->t, 4, 0, hsize- 1, 0,
+                  TAB_CENTER, _("t-test for Equality of Means"));
+
+  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("F"));
+  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _("Sig."));
+  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _("t"));
+  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _("df"));
+  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
+  tab_text (self->t, 7, 2, TAB_CENTER | TAT_TITLE, _("Mean Difference"));
+  tab_text (self->t, 8, 2, TAB_CENTER | TAT_TITLE, _("Std. Error Difference"));
+  tab_text (self->t, 9, 2, TAB_CENTER | TAT_TITLE, _("Lower"));
+  tab_text (self->t, 10, 2, TAB_CENTER | TAT_TITLE, _("Upper"));
+
+  tab_joint_text_format (self->t, 9, 1, 10, 1, TAB_CENTER,
+                         _("%g%% Confidence Interval of the Difference"),
+                         proc->criteria * 100.0);
 }
 
 /* Populate the independent samples trbox */
 static void
 trbox_independent_samples_populate (struct trbox *self,
-                                   const struct dictionary *dict UNUSED,
-                                   struct cmd_t_test *cmd)
+                                    struct t_test_proc *proc)
 {
   int i;
 
-  assert (self);
-  for (i=0; i < cmd->n_variables; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      double p,q;
+      double p, q;
 
       double t;
       double df;
@@ -1058,889 +782,738 @@ trbox_independent_samples_populate (struct trbox *self,
       double std_err_diff;
       double mean_diff;
 
-      const struct variable *var = cmd->v_variables[i];
+      double se2;
+
+      const struct variable *var = proc->vars[i];
       struct group_proc *grp_data = group_proc_get (var);
 
       struct hsh_table *grp_hash = grp_data->group_hash;
 
-      struct group_statistics *gs0 ;
-      struct group_statistics *gs1 ;
+      struct group_statistics *gs0;
+      struct group_statistics *gs1;
 
       union value search_val;
 
-      if ( gp.criterion == CMP_LE )
-       search_val.f = gp.v.critical_value - 1.0;
+      if (proc->criterion == CMP_LE)
+       search_val.f = proc->critical_value - 1.0;
       else
-       search_val = gp.v.g_value[0];
+       search_val = proc->g_value[0];
 
-      gs0 = hsh_find (grp_hash, (void *) &search_val);
+      gs0 = hsh_find (grp_hash, &search_val);
       assert (gs0);
 
-      if ( gp.criterion == CMP_LE )
-       search_val.f = gp.v.critical_value + 1.0;
+      if (proc->criterion == CMP_LE)
+       search_val.f = proc->critical_value + 1.0;
       else
-       search_val = gp.v.g_value[1];
+       search_val = proc->g_value[1];
 
-      gs1 = hsh_find (grp_hash, (void *) &search_val);
+      gs1 = hsh_find (grp_hash, &search_val);
       assert (gs1);
 
 
-      tab_text (self->t, 0, i*2+3, TAB_LEFT, var_get_name (cmd->v_variables[i]));
-
-      tab_text (self->t, 1, i*2+3, TAB_LEFT, _ ("Equal variances assumed"));
-
-
-      tab_double (self->t, 2, i*2+3, TAB_CENTER, grp_data->levene, NULL);
+      tab_text (self->t, 0, i * 2 + 3, TAB_LEFT, var_get_name (proc->vars[i]));
+      tab_text (self->t, 1, i * 2 + 3, TAB_LEFT, _("Equal variances assumed"));
+      tab_double (self->t, 2, i * 2 + 3, TAB_CENTER, grp_data->levene, NULL);
 
       /* Now work out the significance of the Levene test */
-      df1 = 1; df2 = grp_data->ugs.n - 2;
+      df1 = 1;
+      df2 = grp_data->ugs.n - 2;
       q = gsl_cdf_fdist_Q (grp_data->levene, df1, df2);
+      tab_double (self->t, 3, i * 2 + 3, TAB_CENTER, q, NULL);
 
-      tab_double (self->t, 3, i*2+3, TAB_CENTER, q, NULL);
+      df = gs0->n + gs1->n - 2.0;
+      tab_double (self->t, 5, i * 2 + 3, TAB_RIGHT, df, NULL);
 
-      df = gs0->n + gs1->n - 2.0 ;
-      tab_double (self->t, 5, i*2+3, TAB_RIGHT, df, NULL);
+      pooled_variance = (gs0->n * pow2 (gs0->s_std_dev)
+                         + gs1->n *pow2 (gs1->s_std_dev)) / df ;
 
-      pooled_variance = ( (gs0->n )*pow2 (gs0->s_std_dev)
-                         +
-                         (gs1->n )*pow2 (gs1->s_std_dev)
-                       ) / df  ;
+      t = (gs0->mean - gs1->mean) / sqrt (pooled_variance);
+      t /= sqrt ((gs0->n + gs1->n) / (gs0->n * gs1->n));
 
-      t = (gs0->mean - gs1->mean) / sqrt (pooled_variance) ;
-      t /= sqrt ((gs0->n + gs1->n)/ (gs0->n*gs1->n));
-
-      tab_double (self->t, 4, i*2+3, TAB_RIGHT, t, NULL);
+      tab_double (self->t, 4, i * 2 + 3, TAB_RIGHT, t, NULL);
 
       p = gsl_cdf_tdist_P (t, df);
       q = gsl_cdf_tdist_Q (t, df);
 
-      tab_double (self->t, 6, i*2+3, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
+      tab_double (self->t, 6, i * 2 + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p),
+                  NULL);
 
       mean_diff = gs0->mean - gs1->mean;
-      tab_double (self->t, 7, i*2+3, TAB_RIGHT, mean_diff, NULL);
-
+      tab_double (self->t, 7, i * 2 + 3, TAB_RIGHT, mean_diff, NULL);
 
-      std_err_diff = sqrt ( pow2 (gs0->se_mean) + pow2 (gs1->se_mean));
-      tab_double (self->t, 8, i*2+3, TAB_RIGHT, std_err_diff, NULL);
 
+      std_err_diff = sqrt (pow2 (gs0->se_mean) + pow2 (gs1->se_mean));
+      tab_double (self->t, 8, i * 2 + 3, TAB_RIGHT, std_err_diff, NULL);
 
       /* Now work out the confidence interval */
-      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
+      q = (1 - proc->criteria)/2.0;  /* 2-tailed test */
 
-      t = gsl_cdf_tdist_Qinv (q,df);
-      tab_double (self->t, 9, i*2+3, TAB_RIGHT,
-               mean_diff - t * std_err_diff, NULL);
+      t = gsl_cdf_tdist_Qinv (q, df);
+      tab_double (self->t, 9, i * 2 + 3, TAB_RIGHT,
+                 mean_diff - t * std_err_diff, NULL);
 
-      tab_double (self->t, 10, i*2+3, TAB_RIGHT,
-               mean_diff + t * std_err_diff, NULL);
+      tab_double (self->t, 10, i * 2 + 3, TAB_RIGHT,
+                 mean_diff + t * std_err_diff, NULL);
 
 
-      {
-       double se2;
       /* Now for the \sigma_1 != \sigma_2 case */
-      tab_text (self->t, 1, i*2+3+1,
-               TAB_LEFT, _ ("Equal variances not assumed"));
-
+      tab_text (self->t, 1, i * 2 + 3 + 1,
+                TAB_LEFT, _("Equal variances not assumed"));
 
-      se2 = (pow2 (gs0->s_std_dev)/ (gs0->n -1) ) +
-        (pow2 (gs1->s_std_dev)/ (gs1->n -1) );
+      se2 = ((pow2 (gs0->s_std_dev) / (gs0->n - 1)) +
+             (pow2 (gs1->s_std_dev) / (gs1->n - 1)));
 
-      t = mean_diff / sqrt (se2) ;
-      tab_double (self->t, 4, i*2+3+1, TAB_RIGHT, t, NULL);
+      t = mean_diff / sqrt (se2);
+      tab_double (self->t, 4, i * 2 + 3 + 1, TAB_RIGHT, t, NULL);
 
-      df = pow2 (se2) / (
-                      (pow2 (pow2 (gs0->s_std_dev)/ (gs0->n - 1 ))
-                       / (gs0->n -1 )
-                       )
-                      +
-                      (pow2 (pow2 (gs1->s_std_dev)/ (gs1->n - 1 ))
-                       / (gs1->n -1 )
-                       )
-                      ) ;
-
-      tab_double (self->t, 5, i*2+3+1, TAB_RIGHT, df, NULL);
+      df = pow2 (se2) / ((pow2 (pow2 (gs0->s_std_dev) / (gs0->n - 1))
+                          / (gs0->n - 1))
+                         + (pow2 (pow2 (gs1->s_std_dev) / (gs1->n - 1))
+                            / (gs1->n - 1)));
+      tab_double (self->t, 5, i * 2 + 3 + 1, TAB_RIGHT, df, NULL);
 
       p = gsl_cdf_tdist_P (t, df);
       q = gsl_cdf_tdist_Q (t, df);
 
-      tab_double (self->t, 6, i*2+3+1, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
+      tab_double (self->t, 6, i * 2 + 3 + 1, TAB_RIGHT, 2.0 * (t > 0 ? q : p),
+                  NULL);
 
       /* Now work out the confidence interval */
-      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
+      q = (1 - proc->criteria) / 2.0;  /* 2-tailed test */
 
       t = gsl_cdf_tdist_Qinv (q, df);
 
-      tab_double (self->t, 7, i*2+3+1, TAB_RIGHT, mean_diff, NULL);
-
-
-      tab_double (self->t, 8, i*2+3+1, TAB_RIGHT, std_err_diff, NULL);
-
-
-      tab_double (self->t, 9, i*2+3+1, TAB_RIGHT,
-               mean_diff - t * std_err_diff, NULL);
-
-      tab_double (self->t, 10, i*2+3+1, TAB_RIGHT,
-               mean_diff + t * std_err_diff, NULL);
-      }
+      tab_double (self->t, 7, i * 2 + 3 + 1, TAB_RIGHT, mean_diff, NULL);
+      tab_double (self->t, 8, i * 2 + 3 + 1, TAB_RIGHT, std_err_diff, NULL);
+      tab_double (self->t, 9, i * 2 + 3 + 1, TAB_RIGHT,
+                 mean_diff - t * std_err_diff, NULL);
+      tab_double (self->t, 10, i * 2 + 3 + 1, TAB_RIGHT,
+                 mean_diff + t * std_err_diff, NULL);
     }
 }
 
 /* Initialize the paired samples trbox */
-void
-trbox_paired_init (struct trbox *self,
-                          struct cmd_t_test *cmd UNUSED)
+static void
+trbox_paired_init (struct trbox *self, struct t_test_proc *proc)
 {
-
   const int hsize=10;
-  const int vsize=n_pairs+3;
+  const int vsize=proc->n_pairs+ 3;
 
   self->populate = trbox_paired_populate;
 
-  trbox_base_init (self,n_pairs,hsize);
-  tab_title (self->t, _ ("Paired Samples Test"));
-  tab_hline (self->t,TAL_1,2,6,1);
-  tab_vline (self->t,TAL_2,2,0,vsize - 1);
-  tab_joint_text (self->t,2,0,6,0,TAB_CENTER,_ ("Paired Differences"));
-  tab_box (self->t,-1,-1,-1,TAL_1, 2,1,6,vsize-1);
-  tab_box (self->t,-1,-1,-1,TAL_1, 6,0,hsize-1,vsize-1);
-  tab_hline (self->t,TAL_1,5,6, 2);
-  tab_vline (self->t,TAL_GAP,6,0,1);
-
-  tab_joint_text (self->t, 5, 1, 6, 1, TAB_CENTER | TAT_PRINTF,
-                ("%g%% Confidence Interval of the Difference"),
-                cmd->criteria*100.0);
-
-  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _ ("Mean"));
-  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _ ("Std. Deviation"));
-  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _ ("Std. Error Mean"));
-  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _ ("Lower"));
-  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _ ("Upper"));
-  tab_text (self->t, 7, 2, TAB_CENTER | TAT_TITLE, _ ("t"));
-  tab_text (self->t, 8, 2, TAB_CENTER | TAT_TITLE, _ ("df"));
-  tab_text (self->t, 9, 2, TAB_CENTER | TAT_TITLE, _ ("Sig. (2-tailed)"));
+  trbox_base_init (self, proc->n_pairs, hsize);
+  tab_title (self->t, _("Paired Samples Test"));
+  tab_hline (self->t, TAL_1, 2, 6, 1);
+  tab_vline (self->t, TAL_2, 2, 0, vsize - 1);
+  tab_joint_text (self->t, 2, 0, 6, 0, TAB_CENTER, _("Paired Differences"));
+  tab_box (self->t, -1, -1, -1, TAL_1, 2, 1, 6, vsize - 1);
+  tab_box (self->t, -1, -1, -1, TAL_1, 6, 0, hsize - 1, vsize - 1);
+  tab_hline (self->t, TAL_1, 5, 6, 2);
+  tab_vline (self->t, TAL_GAP, 6, 0, 1);
+
+  tab_joint_text_format (self->t, 5, 1, 6, 1, TAB_CENTER,
+                         _("%g%% Confidence Interval of the Difference"),
+                         proc->criteria*100.0);
+
+  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("Mean"));
+  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
+  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _("Std. Error Mean"));
+  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _("Lower"));
+  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _("Upper"));
+  tab_text (self->t, 7, 2, TAB_CENTER | TAT_TITLE, _("t"));
+  tab_text (self->t, 8, 2, TAB_CENTER | TAT_TITLE, _("df"));
+  tab_text (self->t, 9, 2, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
 }
 
 /* Populate the paired samples trbox */
 static void
 trbox_paired_populate (struct trbox *trb,
-                      const struct dictionary *dict,
-                      struct cmd_t_test *cmd UNUSED)
+                       struct t_test_proc *proc)
 {
   int i;
 
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
-
-  for (i=0; i < n_pairs; ++i)
+  for (i = 0; i < proc->n_pairs; i++)
     {
-      double p,q;
+      struct pair *pair = &proc->pairs[i];
+      double p, q;
       double se_mean;
 
-      double n = pairs[i].n;
+      double n = pair->n;
       double t;
       double df = n - 1;
 
-      tab_text (trb->t, 0, i+3, TAB_LEFT | TAT_PRINTF, _ ("Pair %d"),i);
-
-      tab_text (trb->t, 1, i+3, TAB_LEFT | TAT_PRINTF, "%s - %s",
-               var_get_name (pairs[i].v[0]),
-                var_get_name (pairs[i].v[1]));
-
-      tab_double (trb->t, 2, i+3, TAB_RIGHT, pairs[i].mean_diff, NULL);
-
-      tab_double (trb->t, 3, i+3, TAB_RIGHT, pairs[i].std_dev_diff, NULL);
+      tab_text_format (trb->t, 0, i + 3, TAB_LEFT, _("Pair %d"), i);
+      tab_text_format (trb->t, 1, i + 3, TAB_LEFT, "%s - %s",
+                       var_get_name (pair->v[0]),
+                       var_get_name (pair->v[1]));
+      tab_double (trb->t, 2, i + 3, TAB_RIGHT, pair->mean_diff, NULL);
+      tab_double (trb->t, 3, i + 3, TAB_RIGHT, pair->std_dev_diff, NULL);
 
       /* SE Mean */
-      se_mean = pairs[i].std_dev_diff / sqrt (n) ;
-      tab_double (trb->t, 4, i+3, TAB_RIGHT, se_mean, NULL);
+      se_mean = pair->std_dev_diff / sqrt (n);
+      tab_double (trb->t, 4, i + 3, TAB_RIGHT, se_mean, NULL);
 
       /* Now work out the confidence interval */
-      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
+      q = (1 - proc->criteria) / 2.0;  /* 2-tailed test */
 
       t = gsl_cdf_tdist_Qinv (q, df);
 
-      tab_double (trb->t, 5, i+3, TAB_RIGHT,
-               pairs[i].mean_diff - t * se_mean , NULL);
+      tab_double (trb->t, 5, i + 3, TAB_RIGHT,
+                 pair->mean_diff - t * se_mean, NULL);
+      tab_double (trb->t, 6, i + 3, TAB_RIGHT,
+                 pair->mean_diff + t * se_mean, NULL);
 
-      tab_double (trb->t, 6, i+3, TAB_RIGHT,
-               pairs[i].mean_diff + t * se_mean , NULL);
+      t = ((pair->mean[0] - pair->mean[1])
+           / sqrt ((pow2 (pair->s_std_dev[0]) + pow2 (pair->s_std_dev[1])
+                    - (2 * pair->correlation
+                       * pair->s_std_dev[0] * pair->s_std_dev[1]))
+                   / (n - 1)));
 
-      t = (pairs[i].mean[0] - pairs[i].mean[1])
-       / sqrt (
-               ( pow2 (pairs[i].s_std_dev[0]) + pow2 (pairs[i].s_std_dev[1]) -
-                 2 * pairs[i].correlation *
-                 pairs[i].s_std_dev[0] * pairs[i].s_std_dev[1] )
-               / (n - 1)
-               );
-
-      tab_double (trb->t, 7, i+3, TAB_RIGHT, t, NULL);
+      tab_double (trb->t, 7, i + 3, TAB_RIGHT, t, NULL);
 
       /* Degrees of freedom */
-      tab_double (trb->t, 8, i+3, TAB_RIGHT, df, wfmt);
+      tab_double (trb->t, 8, i + 3, TAB_RIGHT, df, &proc->weight_format);
 
       p = gsl_cdf_tdist_P (t,df);
       q = gsl_cdf_tdist_Q (t,df);
 
-      tab_double (trb->t, 9, i+3, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
-
+      tab_double (trb->t, 9, i + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p), NULL);
     }
 }
 
 /* Initialize the one sample trbox */
-void
-trbox_one_sample_init (struct trbox *self, struct cmd_t_test *cmd )
+static void
+trbox_one_sample_init (struct trbox *self, struct t_test_proc *proc)
 {
-  const int hsize=7;
-  const int vsize=cmd->n_variables+3;
+  const int hsize = 7;
+  const int vsize = proc->n_vars + 3;
 
   self->populate = trbox_one_sample_populate;
 
-  trbox_base_init (self, cmd->n_variables,hsize);
-  tab_title (self->t, _ ("One-Sample Test"));
+  trbox_base_init (self, proc->n_vars, hsize);
+  tab_title (self->t, _("One-Sample Test"));
   tab_hline (self->t, TAL_1, 1, hsize - 1, 1);
   tab_vline (self->t, TAL_2, 1, 0, vsize - 1);
 
-  tab_joint_text (self->t, 1, 0, hsize-1,0, TAB_CENTER | TAT_PRINTF,
-                _ ("Test Value = %f"), cmd->n_testval[0]);
-
-  tab_box (self->t, -1, -1, -1, TAL_1, 1,1,hsize-1,vsize-1);
+  tab_joint_text_format (self->t, 1, 0, hsize - 1, 0, TAB_CENTER,
+                         _("Test Value = %f"), proc->testval);
 
+  tab_box (self->t, -1, -1, -1, TAL_1, 1, 1, hsize - 1, vsize - 1);
 
-  tab_joint_text (self->t,5,1,6,1,TAB_CENTER  | TAT_PRINTF,
-                _ ("%g%% Confidence Interval of the Difference"),
-                cmd->criteria*100.0);
 
-  tab_vline (self->t,TAL_GAP,6,1,1);
-  tab_hline (self->t,TAL_1,5,6,2);
-  tab_text (self->t, 1, 2, TAB_CENTER | TAT_TITLE, _ ("t"));
-  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _ ("df"));
-  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _ ("Sig. (2-tailed)"));
-  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _ ("Mean Difference"));
-  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _ ("Lower"));
-  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _ ("Upper"));
+  tab_joint_text_format (self->t, 5, 1, 6, 1, TAB_CENTER,
+                         _("%g%% Confidence Interval of the Difference"),
+                         proc->criteria * 100.0);
 
+  tab_vline (self->t, TAL_GAP, 6, 1, 1);
+  tab_hline (self->t, TAL_1, 5, 6, 2);
+  tab_text (self->t, 1, 2, TAB_CENTER | TAT_TITLE, _("t"));
+  tab_text (self->t, 2, 2, TAB_CENTER | TAT_TITLE, _("df"));
+  tab_text (self->t, 3, 2, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
+  tab_text (self->t, 4, 2, TAB_CENTER | TAT_TITLE, _("Mean Difference"));
+  tab_text (self->t, 5, 2, TAB_CENTER | TAT_TITLE, _("Lower"));
+  tab_text (self->t, 6, 2, TAB_CENTER | TAT_TITLE, _("Upper"));
 }
 
-
 /* Populate the one sample trbox */
 static void
-trbox_one_sample_populate (struct trbox *trb,
-                          const struct dictionary *dict,
-                          struct cmd_t_test *cmd)
+trbox_one_sample_populate (struct trbox *trb, struct t_test_proc *proc)
 {
   int i;
 
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
-
   assert (trb->t);
 
-  for (i=0; i < cmd->n_variables; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
       double t;
-      double p,q;
+      double p, q;
       double df;
-      struct group_statistics *gs = &group_proc_get (cmd->v_variables[i])->ugs;
-
+      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
 
-      tab_text (trb->t, 0, i+3, TAB_LEFT, var_get_name (cmd->v_variables[i]));
+      tab_text (trb->t, 0, i + 3, TAB_LEFT, var_get_name (proc->vars[i]));
 
-      t = (gs->mean - cmd->n_testval[0] ) * sqrt (gs->n) / gs->std_dev ;
+      t = (gs->mean - proc->testval) * sqrt (gs->n) / gs->std_dev;
 
-      tab_double (trb->t, 1, i+3, TAB_RIGHT, t, NULL);
+      tab_double (trb->t, 1, i + 3, TAB_RIGHT, t, NULL);
 
       /* degrees of freedom */
       df = gs->n - 1;
 
-      tab_double (trb->t, 2, i+3, TAB_RIGHT, df, wfmt);
+      tab_double (trb->t, 2, i + 3, TAB_RIGHT, df, &proc->weight_format);
 
       p = gsl_cdf_tdist_P (t, df);
       q = gsl_cdf_tdist_Q (t, df);
 
       /* Multiply by 2 to get 2-tailed significance, makeing sure we've got
         the correct tail*/
-      tab_double (trb->t, 3, i+3, TAB_RIGHT, 2.0* (t>0?q:p), NULL);
+      tab_double (trb->t, 3, i + 3, TAB_RIGHT, 2.0 * (t > 0 ? q : p), NULL);
+      tab_double (trb->t, 4, i + 3, TAB_RIGHT, gs->mean_diff, NULL);
 
-      tab_double (trb->t, 4, i+3, TAB_RIGHT, gs->mean_diff, NULL);
 
-
-      q = (1 - cmd->criteria)/2.0;  /* 2-tailed test */
+      q = (1 - proc->criteria) / 2.0;  /* 2-tailed test */
       t = gsl_cdf_tdist_Qinv (q, df);
 
-      tab_double (trb->t, 5, i+3, TAB_RIGHT,
+      tab_double (trb->t, 5, i + 3, TAB_RIGHT,
                 gs->mean_diff - t * gs->se_mean, NULL);
-
-      tab_double (trb->t, 6, i+3, TAB_RIGHT,
+      tab_double (trb->t, 6, i + 3, TAB_RIGHT,
                 gs->mean_diff + t * gs->se_mean, NULL);
     }
 }
 
 /* Base initializer for the generalized trbox */
-void
+static void
 trbox_base_init (struct trbox *self, size_t data_rows, int cols)
 {
   const size_t rows = 3 + data_rows;
 
   self->finalize = trbox_base_finalize;
   self->t = tab_create (cols, rows, 0);
-  tab_headers (self->t,0,0,3,0);
-  tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols -1, rows -1);
-  tab_hline (self->t, TAL_2,0,cols-1,3);
-  tab_dim (self->t, tab_natural_dimensions);
+  tab_headers (self->t, 0, 0, 3, 0);
+  tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols - 1, rows - 1);
+  tab_hline (self->t, TAL_2, 0, cols- 1, 3);
+  tab_dim (self->t, tab_natural_dimensions, NULL);
 }
 
-
 /* Base finalizer for the trbox */
-void
+static void
 trbox_base_finalize (struct trbox *trb)
 {
   tab_submit (trb->t);
 }
 
-
-/* Create , populate and submit the Paired Samples Correlation box */
+/* Create, populate and submit the Paired Samples Correlation box */
 static void
-pscbox (const struct dictionary *dict)
+pscbox (struct t_test_proc *proc)
 {
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : &F_8_0;
-
-  const int rows = 1 + n_pairs;
-  const int cols = 5;
+  const int rows=1+proc->n_pairs;
+  const int cols=5;
   int i;
 
   struct tab_table *table;
 
-  table = tab_create (cols,rows,0);
+  table = tab_create (cols, rows, 0);
 
   tab_columns (table, SOM_COL_DOWN, 1);
-  tab_headers (table,0,0,1,0);
-  tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols -1, rows -1 );
+  tab_headers (table, 0, 0, 1, 0);
+  tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (table, TAL_2, 0, cols - 1, 1);
   tab_vline (table, TAL_2, 2, 0, rows - 1);
-  tab_dim (table, tab_natural_dimensions);
-  tab_title (table, _ ("Paired Samples Correlations"));
+  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_title (table, _("Paired Samples Correlations"));
 
   /* column headings */
-  tab_text (table, 2,0, TAB_CENTER | TAT_TITLE, _ ("N"));
-  tab_text (table, 3,0, TAB_CENTER | TAT_TITLE, _ ("Correlation"));
-  tab_text (table, 4,0, TAB_CENTER | TAT_TITLE, _ ("Sig."));
+  tab_text (table, 2, 0, TAB_CENTER | TAT_TITLE, _("N"));
+  tab_text (table, 3, 0, TAB_CENTER | TAT_TITLE, _("Correlation"));
+  tab_text (table, 4, 0, TAB_CENTER | TAT_TITLE, _("Sig."));
 
-  for (i=0; i < n_pairs; ++i)
+  for (i = 0; i < proc->n_pairs; i++)
     {
-      double p,q;
-
-      double df = pairs[i].n -2;
-
-      double correlation_t =
-       pairs[i].correlation * sqrt (df) /
-       sqrt (1 - pow2 (pairs[i].correlation));
-
+      struct pair *pair = &proc->pairs[i];
+      double p, q;
+      double df = pair->n -2;
+      double correlation_t = (pair->correlation * sqrt (df) /
+                              sqrt (1 - pow2 (pair->correlation)));
 
       /* row headings */
-      tab_text (table, 0,i+1, TAB_LEFT | TAT_TITLE | TAT_PRINTF,
-              _ ("Pair %d"), i);
-
-      tab_text (table, 1,i+1, TAB_LEFT | TAT_TITLE | TAT_PRINTF,
-              _ ("%s & %s"),
-               var_get_name (pairs[i].v[0]),
-               var_get_name (pairs[i].v[1]));
-
+      tab_text_format (table, 0, i + 1, TAB_LEFT | TAT_TITLE,
+                       _("Pair %d"), i);
+      tab_text_format (table, 1, i + 1, TAB_LEFT | TAT_TITLE,
+                       _("%s & %s"),
+                       var_get_name (pair->v[0]),
+                       var_get_name (pair->v[1]));
 
       /* row data */
-      tab_double (table, 2, i+1, TAB_RIGHT, pairs[i].n, wfmt);
-      tab_double (table, 3, i+1, TAB_RIGHT, pairs[i].correlation, NULL);
+      tab_double (table, 2, i + 1, TAB_RIGHT, pair->n, &proc->weight_format);
+      tab_double (table, 3, i + 1, TAB_RIGHT, pair->correlation, NULL);
 
       p = gsl_cdf_tdist_P (correlation_t, df);
       q = gsl_cdf_tdist_Q (correlation_t, df);
-
-      tab_double (table, 4, i+1, TAB_RIGHT, 2.0* (correlation_t>0?q:p), NULL);
+      tab_double (table, 4, i + 1, TAB_RIGHT,
+                 2.0 * (correlation_t > 0 ? q : p), NULL);
     }
 
   tab_submit (table);
 }
-
-
-
-
+\f
 /* Calculation Implementation */
 
-/* Per case calculations common to all variants of the T test */
-static int
+/* Calculations common to all variants of the T test. */
+static void
 common_calc (const struct dictionary *dict,
-            const struct ccase *c,
-            void *_cmd,
-            enum mv_class exclude)
+             struct t_test_proc *proc,
+             struct casereader *reader)
 {
+  struct ccase *c;
   int i;
-  struct cmd_t_test *cmd = (struct cmd_t_test *)_cmd;
-
-  double weight = dict_get_case_weight (dict, c, NULL);
-
-
-  /* Listwise has to be implicit if the independent variable is missing ?? */
-  if ( cmd->sbc_groups )
-    {
-      if (var_is_value_missing (indep_var, case_data (c, indep_var), exclude))
-       return 0;
-    }
 
-  for (i = 0; i < cmd->n_variables ; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      const struct variable *v = cmd->v_variables[i];
-      const union value *val = case_data (c, v);
-
-      if (!var_is_value_missing (v, val, exclude))
-       {
-         struct group_statistics *gs;
-         gs = &group_proc_get (v)->ugs;
-
-         gs->n += weight;
-         gs->sum += weight * val->f;
-         gs->ssq += weight * val->f * val->f;
-       }
+      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
+      gs->sum = 0;
+      gs->n = 0;
+      gs->ssq = 0;
+      gs->sum_diff = 0;
     }
-  return 0;
-}
-
-/* Pre calculations common to all variants of the T test */
-static void
-common_precalc ( struct cmd_t_test *cmd )
-{
-  int i=0;
 
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
     {
-      struct group_statistics *gs;
-      gs= &group_proc_get (cmd->v_variables[i])->ugs;
-
-      gs->sum=0;
-      gs->n=0;
-      gs->ssq=0;
-      gs->sum_diff=0;
+      double weight = dict_get_case_weight (dict, c, NULL);
+
+      /* Listwise has to be implicit if the independent variable
+         is missing ?? */
+      if (proc->mode == T_IND_SAMPLES)
+        {
+          if (var_is_value_missing (proc->indep_var,
+                                    case_data (c, proc->indep_var),
+                                    proc->exclude))
+            continue;
+        }
+
+      for (i = 0; i < proc->n_vars; i++)
+        {
+          const struct variable *v = proc->vars[i];
+          const union value *val = case_data (c, v);
+
+          if (!var_is_value_missing (v, val, proc->exclude))
+            {
+              struct group_statistics *gs;
+              gs = &group_proc_get (v)->ugs;
+
+              gs->n += weight;
+              gs->sum += weight * val->f;
+              gs->ssq += weight * pow2 (val->f);
+            }
+        }
     }
-}
+  casereader_destroy (reader);
 
-/* Post calculations common to all variants of the T test */
-void
-common_postcalc (struct cmd_t_test *cmd)
-{
-  int i=0;
-
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      struct group_statistics *gs;
-      gs= &group_proc_get (cmd->v_variables[i])->ugs;
-
-      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 )
-                        ) ;
+      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
 
+      gs->mean = gs->sum / gs->n;
+      gs->s_std_dev = sqrt (((gs->ssq / gs->n) - pow2 (gs->mean)));
+      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;
+      gs->mean_diff = gs->sum_diff / gs->n;
     }
 }
 
-/* Per case calculations for one sample t test  */
+/* Calculations for one sample T test. */
 static int
-one_sample_calc (const struct dictionary *dict,
-                const struct ccase *c, void *cmd_,
-                enum mv_class exclude)
+one_sample_calc (const struct dictionary *dict, struct t_test_proc *proc,
+                 struct casereader *reader)
 {
+  struct ccase *c;
   int i;
 
-  struct cmd_t_test *cmd = (struct cmd_t_test *)cmd_;
-
-  double weight = dict_get_case_weight (dict, c, NULL);
-
-
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      struct group_statistics *gs;
-      const struct variable *v = cmd->v_variables[i];
-      const union value *val = case_data (c, v);
-
-      gs= &group_proc_get (cmd->v_variables[i])->ugs;
-
-      if (!var_is_value_missing (v, val, exclude))
-       gs->sum_diff += weight * (val->f - cmd->n_testval[0]);
+      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
+      gs->sum_diff = 0;
     }
 
-  return 0;
-}
-
-/* Pre calculations for one sample t test */
-static void
-one_sample_precalc ( struct cmd_t_test *cmd )
-{
-  int i=0;
-
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
     {
-      struct group_statistics *gs;
-      gs= &group_proc_get (cmd->v_variables[i])->ugs;
-
-      gs->sum_diff=0;
+      double weight = dict_get_case_weight (dict, c, NULL);
+      for (i = 0; i < proc->n_vars; i++)
+        {
+          const struct variable *v = proc->vars[i];
+          struct group_statistics *gs = &group_proc_get (v)->ugs;
+          const union value *val = case_data (c, v);
+          if (!var_is_value_missing (v, val, proc->exclude))
+            gs->sum_diff += weight * (val->f - proc->testval);
+        }
     }
-}
 
-/* Post calculations for one sample t test */
-static void
-one_sample_postcalc (struct cmd_t_test *cmd)
-{
-  int i=0;
-
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      struct group_statistics *gs;
-      gs= &group_proc_get (cmd->v_variables[i])->ugs;
-
-      gs->mean_diff = gs->sum_diff / gs->n ;
+      struct group_statistics *gs = &group_proc_get (proc->vars[i])->ugs;
+      gs->mean_diff = gs->sum_diff / gs->n;
     }
-}
-
 
+  casereader_destroy (reader);
 
-static void
-paired_precalc (struct cmd_t_test *cmd UNUSED)
-{
-  int i;
-
-  for (i=0; i < n_pairs ; ++i )
-    {
-      pairs[i].n = 0;
-      pairs[i].sum[0] = 0;      pairs[i].sum[1] = 0;
-      pairs[i].ssq[0] = 0;      pairs[i].ssq[1] = 0;
-      pairs[i].sum_of_prod = 0;
-      pairs[i].correlation = 0;
-      pairs[i].sum_of_diffs = 0;
-      pairs[i].ssq_diffs = 0;
-    }
-
+  return 0;
 }
 
-
 static int
-paired_calc (const struct dictionary *dict, const struct ccase *c,
-            struct cmd_t_test *cmd UNUSED, enum mv_class exclude)
+paired_calc (const struct dictionary *dict, struct t_test_proc *proc,
+             struct casereader *reader)
 {
+  struct ccase *c;
   int i;
 
-  double weight = dict_get_case_weight (dict, c, NULL);
-
-  for (i=0; i < n_pairs ; ++i )
+  for (i = 0; i < proc->n_pairs; i++)
     {
-      const struct variable *v0 = pairs[i].v[0];
-      const struct variable *v1 = pairs[i].v[1];
-
-      const union value *val0 = case_data (c, v0);
-      const union value *val1 = case_data (c, v1);
-
-      if (!var_is_value_missing (v0, val0, exclude) &&
-          !var_is_value_missing (v1, val1, exclude))
-       {
-         pairs[i].n += weight;
-         pairs[i].sum[0] += weight * val0->f;
-         pairs[i].sum[1] += weight * val1->f;
-
-         pairs[i].ssq[0] += weight * pow2 (val0->f);
-         pairs[i].ssq[1] += weight * pow2 (val1->f);
-
-         pairs[i].sum_of_prod += weight * val0->f * val1->f ;
-
-         pairs[i].sum_of_diffs += weight * ( val0->f - val1->f ) ;
-         pairs[i].ssq_diffs += weight * pow2 (val0->f - val1->f);
-       }
+      struct pair *pair = &proc->pairs[i];
+      pair->n = 0;
+      pair->sum[0] = pair->sum[1] = 0;
+      pair->ssq[0] = pair->ssq[1] = 0;
+      pair->sum_of_prod = 0;
+      pair->correlation = 0;
+      pair->sum_of_diffs = 0;
+      pair->ssq_diffs = 0;
     }
 
-  return 0;
-}
-
-static void
-paired_postcalc (struct cmd_t_test *cmd UNUSED)
-{
-  int i;
+  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
+    {
+      double weight = dict_get_case_weight (dict, c, NULL);
+      for (i = 0; i < proc->n_pairs; i++)
+        {
+          struct pair *pair = &proc->pairs[i];
+          const struct variable *v0 = pair->v[0];
+          const struct variable *v1 = pair->v[1];
+
+          const union value *val0 = case_data (c, v0);
+          const union value *val1 = case_data (c, v1);
+
+          if (!var_is_value_missing (v0, val0, proc->exclude)
+              && !var_is_value_missing (v1, val1, proc->exclude))
+            {
+              pair->n += weight;
+              pair->sum[0] += weight * val0->f;
+              pair->sum[1] += weight * val1->f;
+              pair->ssq[0] += weight * pow2 (val0->f);
+              pair->ssq[1] += weight * pow2 (val1->f);
+              pair->sum_of_prod += weight * val0->f * val1->f;
+              pair->sum_of_diffs += weight * (val0->f - val1->f);
+              pair->ssq_diffs += weight * pow2 (val0->f - val1->f);
+            }
+        }
+    }
 
-  for (i=0; i < n_pairs ; ++i )
+  for (i = 0; i < proc->n_pairs; i++)
     {
+      struct pair *pair = &proc->pairs[i];
+      const double n = pair->n;
       int j;
-      const double n = pairs[i].n;
 
-      for (j=0; j < 2 ; ++j)
+      for (j=0; j < 2; j++)
        {
-         pairs[i].mean[j] = pairs[i].sum[j] / n ;
-         pairs[i].s_std_dev[j] = sqrt ((pairs[i].ssq[j] / n -
-                                             pow2 (pairs[i].mean[j]))
-                                    );
-
-         pairs[i].std_dev[j] = sqrt (n/ (n-1)* (pairs[i].ssq[j] / n -
-                                             pow2 (pairs[i].mean[j]))
-                                    );
+         pair->mean[j] = pair->sum[j] / n;
+         pair->s_std_dev[j] = sqrt ((pair->ssq[j] / n
+                                      - pow2 (pair->mean[j])));
+         pair->std_dev[j] = sqrt (n / (n- 1) * (pair->ssq[j] / n
+                                                - pow2 (pair->mean[j])));
        }
 
-      pairs[i].correlation = pairs[i].sum_of_prod / pairs[i].n -
-       pairs[i].mean[0] * pairs[i].mean[1] ;
+      pair->correlation = (pair->sum_of_prod / pair->n
+                           - pair->mean[0] * pair->mean[1]);
       /* correlation now actually contains the covariance */
+      pair->correlation /= pair->std_dev[0] * pair->std_dev[1];
+      pair->correlation *= pair->n / (pair->n - 1);
 
-      pairs[i].correlation /= pairs[i].std_dev[0] * pairs[i].std_dev[1];
-      pairs[i].correlation *= pairs[i].n / ( pairs[i].n - 1 );
-
-      pairs[i].mean_diff = pairs[i].sum_of_diffs / n ;
-
-      pairs[i].std_dev_diff = sqrt (  n / (n - 1) * (
-                                   ( pairs[i].ssq_diffs / n )
-                                   -
-                                   pow2 (pairs[i].mean_diff )
-                                   ) );
+      pair->mean_diff = pair->sum_of_diffs / n;
+      pair->std_dev_diff = sqrt (n / (n - 1) * ((pair->ssq_diffs / n)
+                                                - pow2 (pair->mean_diff)));
     }
+
+  casereader_destroy (reader);
+  return 0;
 }
 
-static void
-group_precalc (struct cmd_t_test *cmd )
+static int
+group_calc (const struct dictionary *dict, struct t_test_proc *proc,
+            struct casereader *reader)
 {
+  struct ccase *c;
   int i;
-  int j;
 
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      struct group_proc *ttpr = group_proc_get (cmd->v_variables[i]);
+      struct group_proc *ttpr = group_proc_get (proc->vars[i]);
+      int j;
 
       /* There's always 2 groups for a T - TEST */
       ttpr->n_groups = 2;
-
-      gp.indep_width = var_get_width (indep_var);
-
       ttpr->group_hash = hsh_create (2,
-                                   (hsh_compare_func *) compare_group_binary,
-                                   (hsh_hash_func *) hash_group_binary,
-                                   (hsh_free_func *) free_group,
-                                   (void *) &gp );
+                                     (hsh_compare_func *) compare_group_binary,
+                                     (hsh_hash_func *) hash_group_binary,
+                                     (hsh_free_func *) free_group,
+                                     proc);
 
-      for (j=0 ; j < 2 ; ++j)
+      for (j = 0; j < 2; j++)
        {
          struct group_statistics *gs = xmalloc (sizeof *gs);
-
          gs->sum = 0;
          gs->n = 0;
          gs->ssq = 0;
-
-         if ( gp.criterion == CMP_EQ )
-           {
-             gs->id = gp.v.g_value[j];
-           }
+         if (proc->criterion == CMP_EQ)
+            gs->id = proc->g_value[j];
          else
            {
-             if ( j == 0 )
-               gs->id.f = gp.v.critical_value - 1.0 ;
+             if (j == 0)
+               gs->id.f = proc->critical_value - 1.0;
              else
-               gs->id.f = gp.v.critical_value + 1.0 ;
+               gs->id.f = proc->critical_value + 1.0;
            }
 
-         hsh_insert ( ttpr->group_hash, (void *) gs );
+         hsh_insert (ttpr->group_hash, gs);
        }
     }
 
-}
-
-static int
-group_calc (const struct dictionary *dict,
-           const struct ccase *c, struct cmd_t_test *cmd,
-           enum mv_class exclude)
-{
-  int i;
-
-  const double weight = dict_get_case_weight (dict, c, NULL);
-
-  const union value *gv;
-
-  if (var_is_value_missing (indep_var, case_data (c, indep_var), exclude))
-    return 0;
-
-  gv = case_data (c, indep_var);
-
-  for (i=0; i< cmd->n_variables ; ++i)
+  for (; (c = casereader_read (reader)) != NULL; case_unref (c))
     {
-      const struct variable *var = cmd->v_variables[i];
-      const union value *val = case_data (c, var);
-      struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
-      struct group_statistics *gs;
-
-      gs = hsh_find (grp_hash, (void *) gv);
-
-      /* If the independent variable doesn't match either of the values
-         for this case then move on to the next case */
-      if ( ! gs )
-       return 0;
-
-      if (!var_is_value_missing (var, val, exclude))
-       {
-         gs->n += weight;
-         gs->sum += weight * val->f;
-         gs->ssq += weight * pow2 (val->f);
-       }
+      const double weight = dict_get_case_weight (dict, c, NULL);
+      const union value *gv;
+
+      if (var_is_value_missing (proc->indep_var,
+                                case_data (c, proc->indep_var), proc->exclude))
+        continue;
+
+      gv = case_data (c, proc->indep_var);
+      for (i = 0; i < proc->n_vars; i++)
+        {
+          const struct variable *var = proc->vars[i];
+          const union value *val = case_data (c, var);
+          struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
+          struct group_statistics *gs = hsh_find (grp_hash, gv);
+
+          /* If the independent variable doesn't match either of the values
+             for this case then move on to the next case. */
+          if (gs == NULL)
+            break;
+
+          if (!var_is_value_missing (var, val, proc->exclude))
+            {
+              gs->n += weight;
+              gs->sum += weight * val->f;
+              gs->ssq += weight * pow2 (val->f);
+            }
+        }
     }
 
-  return 0;
-}
-
-
-static void
-group_postcalc ( struct cmd_t_test *cmd )
-{
-  int i;
-
-  for (i = 0; i < cmd->n_variables ; ++i)
+  for (i = 0; i < proc->n_vars; i++)
     {
-      const struct variable *var = cmd->v_variables[i];
+      const struct variable *var = proc->vars[i];
       struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
       struct hsh_iterator g;
       struct group_statistics *gs;
-      int count=0;
+      int count = 0;
 
-      for (gs =  hsh_first (grp_hash,&g);
-          gs != 0;
-          gs = hsh_next (grp_hash,&g))
+      for (gs = hsh_first (grp_hash, &g); gs != NULL;
+          gs = hsh_next (grp_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->s_std_dev = sqrt (((gs->ssq / gs->n) - pow2 (gs->mean)));
+         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);
-         count ++;
+         count++;
        }
       assert (count == 2);
     }
-}
 
+  casereader_destroy (reader);
 
+  return 0;
+}
 
 static void
-calculate (struct cmd_t_test *cmd,
-          struct casereader *input, const struct dataset *ds)
+calculate (struct t_test_proc *proc,
+           struct casereader *input, const struct dataset *ds)
 {
   const struct dictionary *dict = dataset_dict (ds);
   struct ssbox stat_summary_box;
   struct trbox test_results_box;
-
-  struct casereader *pass1, *pass2, *pass3;
   struct taint *taint;
-  struct ccase c;
-
-  enum mv_class exclude = cmd->miss != TTS_INCLUDE ? MV_ANY : MV_SYSTEM;
+  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);
 
-  if ( cmd->miss == TTS_LISTWISE )
+  if (proc->listwise_missing)
     input = casereader_create_filter_missing (input,
-                                              cmd->v_variables,
-                                              cmd->n_variables,
-                                              exclude, NULL);
-
+                                              proc->vars,
+                                              proc->n_vars,
+                                              proc->exclude, NULL, NULL);
   input = casereader_create_filter_weight (input, dict, NULL, NULL);
-
   taint = taint_clone (casereader_get_taint (input));
-  casereader_split (input, &pass1, &pass2);
-
-  common_precalc (cmd);
-  for (; casereader_read (pass1, &c); case_destroy (&c))
-    common_calc (dict, &c, cmd, exclude);
-  casereader_destroy (pass1);
-  common_postcalc (cmd);
 
-  switch (mode)
+  common_calc (dict, proc, casereader_clone (input));
+  switch (proc->mode)
     {
     case T_1_SAMPLE:
-      one_sample_precalc (cmd);
-      for (; casereader_read (pass2, &c); case_destroy (&c))
-        one_sample_calc (dict, &c, cmd, exclude);
-      one_sample_postcalc (cmd);
+      one_sample_calc (dict, proc, input);
       break;
     case T_PAIRED:
-      paired_precalc (cmd);
-      for (; casereader_read (pass2, &c); case_destroy (&c))
-        paired_calc (dict, &c, cmd, exclude);
-      paired_postcalc (cmd);
+      paired_calc (dict, proc, input);
       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);
-      group_postcalc (cmd);
-
-      levene (dict, pass3, indep_var, cmd->n_variables, cmd->v_variables,
-              exclude);
+      group_calc (dict, proc, casereader_clone (input));
+      levene (dict, input, proc->indep_var, proc->n_vars, proc->vars,
+              proc->exclude);
       break;
+    default:
+      NOT_REACHED ();
     }
-  casereader_destroy (pass2);
 
   if (!taint_has_tainted_successor (taint))
     {
-      ssbox_create (&stat_summary_box,cmd,mode);
-      ssbox_populate (&stat_summary_box, dict, cmd);
+      ssbox_create (&stat_summary_box, proc);
+      ssbox_populate (&stat_summary_box, proc);
       ssbox_finalize (&stat_summary_box);
 
-      if ( mode == T_PAIRED )
-        pscbox (dict);
+      if (proc->mode == T_PAIRED)
+        pscbox (proc);
 
-      trbox_create (&test_results_box, cmd, mode);
-      trbox_populate (&test_results_box, dict, cmd);
+      trbox_create (&test_results_box, proc);
+      trbox_populate (&test_results_box, proc);
       trbox_finalize (&test_results_box);
     }
 
   taint_destroy (taint);
 }
 
-short which_group (const struct group_statistics *g,
-                 const struct group_properties *p);
+/* return 0 if G belongs to group 0,
+          1 if it belongs to group 1,
+          2 if it belongs to neither group */
+static int
+which_group (const struct group_statistics *g,
+             const struct t_test_proc *proc)
+{
+  int width = var_get_width (proc->indep_var);
+
+  if (0 == value_compare_3way (&g->id, &proc->g_value[0], width))
+    return 0;
+
+  if (0 == value_compare_3way (&g->id, &proc->g_value[1], width))
+    return 1;
+
+  return 2;
+}
 
 /* Return -1 if the id of a is less than b; +1 if greater than and
    0 if equal */
 static int
 compare_group_binary (const struct group_statistics *a,
-                    const struct group_statistics *b,
-                    const struct group_properties *p)
+                      const struct group_statistics *b,
+                      const struct t_test_proc *proc)
 {
-  short flag_a;
-  short flag_b;
+  int flag_a;
+  int flag_b;
 
-  if ( p->criterion == CMP_LE )
+  if (proc->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 ) ;
+      flag_a = (a->id.f < proc->critical_value);
+      flag_b = (b->id.f < proc->critical_value);
     }
   else
     {
-      flag_a = which_group (a, p);
-      flag_b = which_group (b, p);
+      flag_a = which_group (a, proc);
+      flag_b = which_group (b, proc);
     }
 
-  if (flag_a < flag_b )
-    return -1;
+  if (flag_a < flag_b)
+    return - 1;
 
   return (flag_a > flag_b);
 }
@@ -1950,40 +1523,11 @@ compare_group_binary (const struct group_statistics *a,
 
 static unsigned
 hash_group_binary (const struct group_statistics *g,
-                 const struct group_properties *p)
-{
-  short flag = -1;
-
-  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)
-    {
-      flag = which_group (g,p);
-    }
-  else
-    NOT_REACHED ();
-
-  return flag;
-}
-
-/* return 0 if G belongs to group 0,
-          1 if it belongs to group 1,
-         2 if it belongs to neither group */
-short
-which_group (const struct group_statistics *g,
-           const struct group_properties *p)
+                   const struct t_test_proc *proc)
 {
-  if ( 0 == compare_values (&g->id, &p->v.g_value[0], p->indep_width))
-    return 0;
-
-  if ( 0 == compare_values (&g->id, &p->v.g_value[1], p->indep_width))
-    return 1;
-
-  return 2;
+  return (proc->criterion == CMP_LE
+          ? g->id.f < proc->critical_value
+          : which_group (g, proc));
 }
 
 /*
diff --git a/src/language/stats/wilcoxon.c b/src/language/stats/wilcoxon.c
new file mode 100644 (file)
index 0000000..310206c
--- /dev/null
@@ -0,0 +1,385 @@
+/* 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 <gsl/gsl_cdf.h>
+#include <math.h>
+#include <signal.h>
+#include <unistd.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 <gsl/gsl_cdf.h>
+#include <libpspp/assertion.h>
+#include <libpspp/message.h>
+#include <libpspp/misc.h>
+#include <math/sort.h>
+#include <math/wilcoxon-sig.h>
+#include <output/table.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "minmax.h"
+#include "xalloc.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, 0);
+  struct caseproto *proto;
+
+  input =
+    casereader_create_filter_weight (input, dict, &warn, NULL);
+
+  proto = caseproto_create ();
+  proto = caseproto_add_width (proto, 0);
+  proto = caseproto_add_width (proto, 0);
+  if (weight != NULL)
+    proto = caseproto_add_width (proto, 0);
+
+  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];
+
+      ws[i].sign = var_create_internal (0, 0);
+      ws[i].absdiff = var_create_internal (1, 0);
+
+      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, proto);
+      subcase_destroy (&ordering);
+
+      for (; (c = casereader_read (r)) != NULL; case_unref (c))
+       {
+         struct ccase *output = case_create (proto);
+         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);
+    }
+  caseproto_unref (proto);
+
+  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, NULL);
+
+  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, NULL);
+
+  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
index 678d2f51fa96b9217f6b172d190d7abf4dfbc01f..0771ade3a9e92dccc0c3063a7c14d13014c010e3 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
@@ -100,6 +100,7 @@ read_syntax_file (struct getl_interface *s,
   do
     {
       sfs->ln++;
+      ds_clear (line);
       if (!ds_read_line (line, sfs->syntax_file, SIZE_MAX))
         {
           if (ferror (sfs->syntax_file))
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..5f025cd5ef7ff4775c09ccba9f752fa46c932249 100644 (file)
@@ -1,19 +1,10 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-language_tests_built_sources = \
-       src/language/tests/check-model.c
-
 language_tests_sources = \
-       src/language/tests/check-model.h \
-       src/language/tests/datasheet-test.c \
        src/language/tests/format-guesser-test.c \
        src/language/tests/float-format.c \
        src/language/tests/moments-test.c \
        src/language/tests/paper-size.c \
        src/language/tests/pool-test.c 
 
-all_q_sources += $(language_tests_built_sources:.c=.q)
-EXTRA_DIST += $(language_tests_built_sources:.c=.q)
-CLEANFILES += $(language_tests_built_sources)
-
 EXTRA_DIST += src/language/tests/OChangeLog
diff --git a/src/language/tests/check-model.h b/src/language/tests/check-model.h
deleted file mode 100644 (file)
index 2c93355..0000000
+++ /dev/null
@@ -1,37 +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/>. */
-
-/* PSPP syntax interface to model checker.
-
-   A model checker is a software testing tool.  PSPP includes a
-   generic model checker in libpspp/model-checker.[ch].  This
-   module layers a PSPP syntax interface on top of the model
-   checker's options. */
-
-#ifndef LANGUAGE_TESTS_CHECK_MODEL
-#define LANGUAGE_TESTS_CHECK_MODEL 1
-
-#include <stdbool.h>
-
-struct lexer;
-struct mc_options;
-struct mc_results;
-
-bool check_model (struct lexer *lexer,
-                  struct mc_results *(*checker) (struct mc_options *, void *),
-                  void *aux);
-
-#endif /* check-model.h */
diff --git a/src/language/tests/check-model.q b/src/language/tests/check-model.q
deleted file mode 100644 (file)
index a265471..0000000
+++ /dev/null
@@ -1,273 +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 <limits.h>
-
-#include <language/tests/check-model.h>
-
-#include <errno.h>
-
-#include <libpspp/model-checker.h>
-#include <language/lexer/lexer.h>
-
-#include "error.h"
-#include "fwriteerror.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-/* (headers) */
-
-/* (specification)
-   "CHECK MODEL" (chm_):
-    search=strategy:broad/deep/random,
-           :mxd(n:max_depth),
-           :hash(n:hash_bits);
-    path=integer list;
-    queue=:limit(n:queue_limit,"%s>0"),
-          drop:newest/oldest/random;
-    seed=integer;
-    stop=:states(n:max_unique_states,"%s>0"),
-         :errors(n:max_errors),
-         :timeout(d:time_limit,"%s>0");
-    progress=progress:none/dots/fancy;
-    output=:verbosity(n:verbosity),
-           :errverbosity(n:err_verbosity),
-           :file(s:output_file).
-*/
-/* (declarations) */
-/* (functions) */
-
-static struct mc_options *parse_options (struct lexer *);
-static void print_results (const struct mc_results *, FILE *);
-
-/* Parses a syntax description of model checker options from
-   LEXER and passes them, along with AUX, to the CHECKER
-   function, which must wrap a call to mc_run and return the
-   mc_results that it returned.  This function then prints a
-   description of the mc_results to the output file.  Returns
-   true if the model checker run found no errors, false
-   otherwise. */
-bool
-check_model (struct lexer *lexer,
-             struct mc_results *(*checker) (struct mc_options *, void *aux),
-             void *aux)
-{
-  struct mc_options *options;
-  struct mc_results *results;
-  FILE *output_file;
-  bool ok;
-
-  options = parse_options (lexer);
-  if (options == NULL)
-    return false;
-  output_file = mc_options_get_output_file (options);
-
-  results = checker (options, aux);
-
-  print_results (results, output_file);
-
-  if (output_file != stdout && output_file != stderr)
-    {
-      if (fwriteerror (output_file) < 0)
-        {
-          /* We've already discarded the name of the output file.
-             Oh well. */
-          error (0, errno, "error closing output file");
-        }
-    }
-
-  ok = mc_results_get_error_count (results) == 0;
-  mc_results_destroy (results);
-
-  return ok;
-}
-
-/* Fancy progress function for mc_options_set_progress_func. */
-static bool
-fancy_progress (struct mc *mc)
-{
-  const struct mc_results *results = mc_get_results (mc);
-  if (mc_results_get_stop_reason (results) == MC_CONTINUING)
-    fprintf (stderr, "Processed %d unique states, max depth %d, "
-             "dropped %d duplicates...\r",
-             mc_results_get_unique_state_count (results),
-             mc_results_get_max_depth_reached (results),
-             mc_results_get_duplicate_dropped_states (results));
-  else
-    putc ('\n', stderr);
-  return true;
-}
-
-/* Parses options from LEXER and returns a corresponding
-   mc_options, or a null pointer if parsing fails. */
-static struct mc_options *
-parse_options (struct lexer *lexer)
-{
-  struct cmd_check_model cmd;
-  struct mc_options *options;
-
-  if (!parse_check_model (lexer, NULL, &cmd, NULL))
-    return NULL;
-
-  options = mc_options_create ();
-  if (cmd.strategy != -1)
-    mc_options_set_strategy (options,
-                             cmd.strategy == CHM_BROAD ? MC_BROAD
-                             : cmd.strategy == CHM_DEEP ? MC_DEEP
-                             : cmd.strategy == CHM_RANDOM ? MC_RANDOM
-                             : -1);
-  if (cmd.sbc_path > 0)
-    {
-      if (cmd.sbc_search > 0)
-        msg (SW, _("PATH and SEARCH subcommands are mutually exclusive.  "
-                   "Ignoring PATH."));
-      else
-        {
-          struct subc_list_int *list = &cmd.il_path[0];
-          int count = subc_list_int_count (list);
-          if (count > 0)
-            {
-              struct mc_path path;
-              int i;
-
-              mc_path_init (&path);
-              for (i = 0; i < count; i++)
-                mc_path_push (&path, subc_list_int_at (list, i));
-              mc_options_set_follow_path (options, &path);
-              mc_path_destroy (&path);
-            }
-          else
-            msg (SW, _("At least one value must be specified on PATH."));
-        }
-    }
-  if (cmd.max_depth != LONG_MIN)
-    mc_options_set_max_depth (options, cmd.max_depth);
-  if (cmd.hash_bits != LONG_MIN)
-    {
-      int hash_bits;
-      mc_options_set_hash_bits (options, cmd.hash_bits);
-      hash_bits = mc_options_get_hash_bits (options);
-      if (hash_bits != cmd.hash_bits)
-        msg (SW, _("Hash bits adjusted to %d."), hash_bits);
-    }
-  if (cmd.queue_limit != LONG_MIN)
-    mc_options_set_queue_limit (options, cmd.queue_limit);
-  if (cmd.drop != -1)
-    {
-      enum mc_queue_limit_strategy drop
-        = (cmd.drop == CHM_NEWEST ? MC_DROP_NEWEST
-           : cmd.drop == CHM_OLDEST ? MC_DROP_OLDEST
-           : cmd.drop == CHM_RANDOM ? MC_DROP_RANDOM
-           : -1);
-      mc_options_set_queue_limit_strategy (options, drop);
-    }
-  if (cmd.sbc_search > 0)
-    mc_options_set_seed (options, cmd.n_seed[0]);
-  if (cmd.max_unique_states != LONG_MIN)
-    mc_options_set_max_unique_states (options, cmd.max_unique_states);
-  if (cmd.max_errors != LONG_MIN)
-    mc_options_set_max_errors (options, cmd.max_errors);
-  if (cmd.time_limit != SYSMIS)
-    mc_options_set_time_limit (options, cmd.time_limit);
-  if (cmd.verbosity != LONG_MIN)
-    mc_options_set_verbosity (options, cmd.verbosity);
-  if (cmd.err_verbosity != LONG_MIN)
-    mc_options_set_failure_verbosity (options, cmd.err_verbosity);
-  if (cmd.progress != -1)
-    {
-      if (cmd.progress == CHM_NONE)
-        mc_options_set_progress_usec (options, 0);
-      else if (cmd.progress == CHM_DOTS)
-        {
-          /* Nothing to do: that's the default anyway. */
-        }
-      else if (cmd.progress == CHM_FANCY)
-        mc_options_set_progress_func (options, fancy_progress);
-    }
-  if (cmd.output_file != NULL)
-    {
-      FILE *output_file = fopen (cmd.output_file, "w");
-      if (output_file == NULL)
-        {
-          error (0, errno, _("error opening \"%s\" for writing"),
-                 cmd.output_file);
-          free_check_model (&cmd);
-          mc_options_destroy (options);
-          return NULL;
-        }
-      mc_options_set_output_file (options, output_file);
-    }
-
-
-  free_check_model (&cmd);
-
-  return options;
-}
-
-/* Prints a description of RESULTS to stream F. */
-static void
-print_results (const struct mc_results *results, FILE *f)
-{
-  enum mc_stop_reason reason = mc_results_get_stop_reason (results);
-
-  fputs ("\n"
-         "MODEL CHECKING SUMMARY\n"
-         "----------------------\n\n", f);
-
-  fprintf (f, "Stopped by: %s\n",
-           reason == MC_SUCCESS ? "state space exhaustion"
-           : reason == MC_MAX_UNIQUE_STATES ? "reaching max unique states"
-           : reason == MC_MAX_ERROR_COUNT ? "reaching max error count"
-           : reason == MC_END_OF_PATH ? "reached end of specified path"
-           : reason == MC_TIMEOUT ? "reaching time limit"
-           : reason == MC_INTERRUPTED ? "user interruption"
-           : "unknown reason");
-  fprintf (f, "Errors found: %d\n\n", mc_results_get_error_count (results));
-
-  fprintf (f, "Unique states checked: %d\n",
-           mc_results_get_unique_state_count (results));
-  fprintf (f, "Maximum depth reached: %d\n",
-           mc_results_get_max_depth_reached (results));
-  fprintf (f, "Mean depth reached: %.2f\n\n",
-           mc_results_get_mean_depth_reached (results));
-
-  fprintf (f, "Dropped duplicate states: %d\n",
-           mc_results_get_duplicate_dropped_states (results));
-  fprintf (f, "Dropped off-path states: %d\n",
-           mc_results_get_off_path_dropped_states (results));
-  fprintf (f, "Dropped too-deep states: %d\n",
-           mc_results_get_depth_dropped_states (results));
-  fprintf (f, "Dropped queue-overflow states: %d\n",
-           mc_results_get_queue_dropped_states (results));
-  fprintf (f, "Checked states still queued when stopped: %d\n",
-           mc_results_get_queued_unprocessed_states (results));
-  fprintf (f, "Maximum queue length reached: %d\n\n",
-           mc_results_get_max_queue_length (results));
-
-  fprintf (f, "Runtime: %.2f seconds\n",
-           mc_results_get_duration (results));
-
-  putc ('\n', f);
-}
-
-/*
-  Local Variables:
-  mode: c
-  End:
-*/
diff --git a/src/language/tests/datasheet-test.c b/src/language/tests/datasheet-test.c
deleted file mode 100644 (file)
index 67f6837..0000000
+++ /dev/null
@@ -1,102 +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/datasheet.h>
-
-#include <language/command.h>
-#include <language/lexer/lexer.h>
-#include <language/tests/check-model.h>
-#include <libpspp/array.h>
-#include <libpspp/assertion.h>
-
-#include "error.h"
-#include "xalloc.h"
-
-static bool parse_coordinates (struct lexer *, int *rows, int *cols);
-
-/* Parses and executes the DEBUG DATASHEET command, which runs
-   the model checker on the datasheet data structure.  The
-   command may include a specification of the form
-   MAX=(ROWS,COLS) to specify the maximum size of the data sheet
-   during the model checker run (default: 4x4) or
-   BACKING=(ROWS,COLS) to specify the size of the casereader
-   backing the datasheet (default: no backing).  These may be
-   optionally followed by any of the common model checker option
-   specifications (see check-model.q). */
-int
-cmd_debug_datasheet (struct lexer *lexer, struct dataset *dataset UNUSED)
-{
-  struct datasheet_test_params params;
-  bool ok;
-
-  params.max_rows = 4;
-  params.max_cols = 4;
-  params.backing_rows = 0;
-  params.backing_cols = 0;
-
-  for (;;)
-    {
-      if (lex_match_id (lexer, "MAX"))
-        {
-          if (!parse_coordinates (lexer, &params.max_rows, &params.max_cols))
-            return CMD_FAILURE;
-        }
-      else if (lex_match_id (lexer, "BACKING"))
-        {
-          if (!parse_coordinates (lexer,
-                                  &params.backing_rows, &params.backing_cols))
-            return CMD_FAILURE;
-        }
-      else
-        break;
-      lex_match (lexer, '/');
-    }
-
-  ok = check_model (lexer, datasheet_test, &params);
-  printf ("Datasheet test max(%d,%d) backing(%d,%d) %s.\n",
-          params.max_rows, params.max_cols,
-          params.backing_rows, params.backing_cols,
-          ok ? "successful" : "failed");
-  return ok ? lex_end_of_command (lexer) : CMD_FAILURE;
-}
-
-/* Parses a pair of coordinates with the syntax =(ROWS,COLS),
-   where all of the delimiters are optional, into *ROWS and
-   *COLS.  Returns true if successful, false on parse failure. */
-static bool
-parse_coordinates (struct lexer *lexer, int *rows, int *cols)
-{
-  lex_match (lexer, '=');
-  lex_match (lexer, '(');
-
-  if (!lex_force_int (lexer))
-    return false;
-  *rows = lex_integer (lexer);
-  lex_get (lexer);
-
-  lex_match (lexer, ',');
-
-  if (!lex_force_int (lexer))
-    return false;
-  *cols = lex_integer (lexer);
-  lex_get (lexer);
-
-  lex_match (lexer, ')');
-  return true;
-}
-
index 74136da303299e975baa9ba9176719516f92edd2..19e9f9e54bf36b860b46f8d4681ce61660931b10 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
@@ -35,7 +35,7 @@ cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED)
 
   tab = tab_create(1, 1, 0);
 
-  tab_dim (tab, tab_natural_dimensions);
+  tab_dim (tab, tab_natural_dimensions, NULL);
   tab_flags (tab, SOMF_NO_TITLE );
 
   tab_text(tab, 0, 0, 0, ds_cstr (lex_tokstr (lexer)));
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 37388f9264162e2c346c4f7b7a3edc637aa8bcce..fd4e1a93839e27b453abdc9298a559f3b1b8ce6d 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
@@ -38,6 +38,7 @@
 #include <libpspp/float-format.h>
 #include <libpspp/integer-format.h>
 #include <libpspp/message.h>
+#include <libpspp/i18n.h>
 #include <math/random.h>
 #include <output/journal.h>
 #include <output/output.h>
@@ -86,6 +87,7 @@ int tgetnum (const char *);
      journal=custom;
      log=custom;
      length=custom;
+     locale=custom;
      listing=custom;
      lowres=lores:auto/on/off;
      lpi=integer "x>0" "%s must be greater than 0";
@@ -361,6 +363,41 @@ stc_custom_length (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_se
   return 1;
 }
 
+static int
+stc_custom_locale (struct lexer *lexer, struct dataset *ds UNUSED,
+                  struct cmd_set *cmd UNUSED, void *aux UNUSED)
+{
+  const struct string *s;
+
+  lex_match (lexer, '=');
+
+  if ( !lex_force_string (lexer))
+    return 0;
+
+  s = lex_tokstr (lexer);
+
+  /* First try this string as an encoding name */
+  if ( valid_encoding (ds_cstr (s)))
+    set_default_encoding (ds_cstr (s));
+
+  /* Now try as a locale name (or alias) */
+  else if (set_encoding_from_locale (ds_cstr (s)))
+    {
+    }
+  else
+    {
+      msg (ME, _("%s is not a recognised encoding or locale name"),
+          ds_cstr (s));
+      return 0;
+    }
+
+  lex_get (lexer);
+
+  return 1;
+}
+
+
+
 static int
 stc_custom_seed (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
 {
@@ -478,183 +515,183 @@ stc_custom_disk (struct lexer *lexer, struct dataset *ds, struct cmd_set *cmd UN
   return stc_custom_listing (lexer, ds, cmd, aux);
 }
 \f
-static void
+static char *
 show_blanks (const struct dataset *ds UNUSED)
 {
-  if (settings_get_blanks () == SYSMIS)
-    msg (SN, _("BLANKS is SYSMIS."));
-  else
-    msg (SN, _("BLANKS is %g."), settings_get_blanks ());
-
+  return (settings_get_blanks () == SYSMIS
+          ? xstrdup ("SYSMIS")
+          : xasprintf ("%g", settings_get_blanks ()));
 }
 
-static char *
-format_cc (struct substring in, char grouping, char *out)
+static void
+format_cc (struct string *out, struct substring in, char grouping)
 {
   while (!ss_is_empty (in))
     {
       char c = ss_get_char (&in);
       if (c == grouping || c == '\'')
-        *out++ = '\'';
+        ds_put_char (out, '\'');
       else if (c == '"')
-        *out++ = '"';
-      *out++ = c;
+        ds_put_char (out, '"');
+      ds_put_char (out, c);
     }
-  return out;
 }
 
-static void
+static char *
 show_cc (enum fmt_type type)
 {
   const struct fmt_number_style *cc = settings_get_style (type);
-  char cc_string[FMT_STYLE_AFFIX_MAX * 4 * 2 + 3 + 1];
-  char *out;
+  struct string out;
 
-  out = format_cc (cc->neg_prefix, cc->grouping, cc_string);
-  *out++ = cc->grouping;
-  out = format_cc (cc->prefix, cc->grouping, out);
-  *out++ = cc->grouping;
-  out = format_cc (cc->suffix, cc->grouping, out);
-  *out++ = cc->grouping;
-  out = format_cc (cc->neg_suffix, cc->grouping, out);
-  *out = '\0';
+  ds_init_empty (&out);
+  format_cc (&out, cc->neg_prefix, cc->grouping);
+  ds_put_char (&out, cc->grouping);
+  format_cc (&out, cc->prefix, cc->grouping);
+  ds_put_char (&out, cc->grouping);
+  format_cc (&out, cc->suffix, cc->grouping);
+  ds_put_char (&out, cc->grouping);
+  format_cc (&out, cc->neg_suffix, cc->grouping);
 
-  msg (SN, _("%s is \"%s\"."), fmt_name (type), cc_string);
+  return ds_cstr (&out);
 }
 
-static void
+static char *
 show_cca (const struct dataset *ds UNUSED)
 {
-  show_cc (FMT_CCA);
+  return show_cc (FMT_CCA);
 }
 
-static void
+static char *
 show_ccb (const struct dataset *ds UNUSED)
 {
-  show_cc (FMT_CCB);
+  return show_cc (FMT_CCB);
 }
 
-static void
+static char *
 show_ccc (const struct dataset *ds UNUSED)
 {
-  show_cc (FMT_CCC);
+  return show_cc (FMT_CCC);
 }
 
-static void
+static char *
 show_ccd (const struct dataset *ds UNUSED)
 {
-  show_cc (FMT_CCD);
+  return show_cc (FMT_CCD);
 }
 
-static void
+static char *
 show_cce (const struct dataset *ds UNUSED)
 {
-  show_cc (FMT_CCE);
+  return show_cc (FMT_CCE);
 }
 
-static void
+static char *
 show_decimals (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("DECIMAL is \"%c\"."), settings_get_decimal_char (FMT_F));
+  return xasprintf ("\"%c\"", settings_get_decimal_char (FMT_F));
 }
 
-static void
+static char *
 show_endcmd (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("ENDCMD is \"%c\"."), settings_get_endcmd ());
+  return xasprintf ("\"%c\"", settings_get_endcmd ());
 }
 
-static void
+static char *
 show_errors (const struct dataset *ds UNUSED)
 {
   bool terminal = settings_get_error_routing_to_terminal ();
   bool listing = settings_get_error_routing_to_listing ();
-  msg (SN, _("ERRORS is \"%s\"."),
-       terminal && listing ? "BOTH"
-       : terminal ? "TERMINAL"
-       : listing ? "LISTING"
-       : "NONE");
+  return xstrdup (terminal && listing ? "BOTH"
+                  : terminal ? "TERMINAL"
+                  : listing ? "LISTING"
+                  : "NONE");
 }
 
-static void
+static char *
 show_format (const struct dataset *ds UNUSED)
 {
   char str[FMT_STRING_LEN_MAX + 1];
-  msg (SN, _("FORMAT is %s."), fmt_to_string (settings_get_format (), str));
+  return xstrdup (fmt_to_string (settings_get_format (), str));
 }
 
-static void
+static char *
 show_length (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("LENGTH is %d."), settings_get_viewlength ());
+  return xasprintf ("%d", settings_get_viewlength ());
 }
 
-static void
+static char *
+show_locale (const struct dataset *ds UNUSED)
+{
+  return xstrdup (get_default_encoding ());
+}
+
+static char *
 show_mxerrs (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("MXERRS is %d."), settings_get_mxerrs ());
+  return xasprintf ("%d", settings_get_mxerrs ());
 }
 
-static void
+static char *
 show_mxloops (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("MXLOOPS is %d."), settings_get_mxloops ());
+  return xasprintf ("%d", settings_get_mxloops ());
 }
 
-static void
+static char *
 show_mxwarns (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("MXWARNS is %d."), settings_get_mxwarns ());
+  return xasprintf ("%d", settings_get_mxwarns ());
 }
 
-/* Outputs that SETTING has the given INTEGER_FORMAT value. */
-static void
-show_integer_format (const char *setting, enum integer_format integer_format)
+/* Returns a name for the given INTEGER_FORMAT value. */
+static char *
+show_integer_format (enum integer_format integer_format)
 {
-  msg (SN, _("%s is %s (%s)."),
-       setting,
-       (integer_format == INTEGER_MSB_FIRST ? "MSBFIRST"
-        : integer_format == INTEGER_LSB_FIRST ? "LSBFIRST"
-        : "VAX"),
-       integer_format == INTEGER_NATIVE ? "NATIVE" : "nonnative");
+  return xasprintf ("%s (%s)",
+                    (integer_format == INTEGER_MSB_FIRST ? "MSBFIRST"
+                     : integer_format == INTEGER_LSB_FIRST ? "LSBFIRST"
+                     : "VAX"),
+                    integer_format == INTEGER_NATIVE ? "NATIVE" : "nonnative");
 }
 
-/* Outputs that SETTING has the given FLOAT_FORMAT value. */
-static void
-show_float_format (const char *setting, enum float_format float_format)
+/* Returns a name for the given FLOAT_FORMAT value. */
+static char *
+show_float_format (enum float_format float_format)
 {
   const char *format_name = "";
 
   switch (float_format)
     {
     case FLOAT_IEEE_SINGLE_LE:
-      format_name = "ISL (32-bit IEEE 754 single, little-endian)";
+      format_name = _("ISL (32-bit IEEE 754 single, little-endian)");
       break;
     case FLOAT_IEEE_SINGLE_BE:
-      format_name = "ISB (32-bit IEEE 754 single, big-endian)";
+      format_name = _("ISB (32-bit IEEE 754 single, big-endian)");
       break;
     case FLOAT_IEEE_DOUBLE_LE:
-      format_name = "IDL (64-bit IEEE 754 double, little-endian)";
+      format_name = _("IDL (64-bit IEEE 754 double, little-endian)");
       break;
     case FLOAT_IEEE_DOUBLE_BE:
-      format_name = "IDB (64-bit IEEE 754 double, big-endian)";
+      format_name = _("IDB (64-bit IEEE 754 double, big-endian)");
       break;
 
     case FLOAT_VAX_F:
-      format_name = "VF (32-bit VAX F, VAX-endian)";
+      format_name = _("VF (32-bit VAX F, VAX-endian)");
       break;
     case FLOAT_VAX_D:
-      format_name = "VD (64-bit VAX D, VAX-endian)";
+      format_name = _("VD (64-bit VAX D, VAX-endian)");
       break;
     case FLOAT_VAX_G:
-      format_name = "VG (64-bit VAX G, VAX-endian)";
+      format_name = _("VG (64-bit VAX G, VAX-endian)");
       break;
 
     case FLOAT_Z_SHORT:
-      format_name = "ZS (32-bit IBM Z hexadecimal short, big-endian)";
+      format_name = _("ZS (32-bit IBM Z hexadecimal short, big-endian)");
       break;
     case FLOAT_Z_LONG:
-      format_name = "ZL (64-bit IBM Z hexadecimal long, big-endian)";
+      format_name = _("ZL (64-bit IBM Z hexadecimal long, big-endian)");
       break;
 
     case FLOAT_FP:
@@ -662,73 +699,64 @@ show_float_format (const char *setting, enum float_format float_format)
       NOT_REACHED ();
     }
 
-  msg (SN, _("%s is %s (%s)."),
-       setting, format_name,
-       float_format == FLOAT_NATIVE_DOUBLE ? "NATIVE" : "nonnative");
+  return xasprintf ("%s (%s)", format_name,
+                    (float_format == FLOAT_NATIVE_DOUBLE
+                     ? "NATIVE" : "nonnative"));
 }
 
-static void
+static char *
 show_rib (const struct dataset *ds UNUSED)
 {
-  show_integer_format ("RIB", settings_get_input_integer_format ());
+  return show_integer_format (settings_get_input_integer_format ());
 }
 
-static void
+static char *
 show_rrb (const struct dataset *ds UNUSED)
 {
-  show_float_format ("RRB", settings_get_input_float_format ());
+  return show_float_format (settings_get_input_float_format ());
 }
 
-static void
+static char *
 show_scompression (const struct dataset *ds UNUSED)
 {
-  if (settings_get_scompression ())
-    msg (SN, _("SCOMPRESSION is ON."));
-  else
-    msg (SN, _("SCOMPRESSION is OFF."));
+  return xstrdup (settings_get_scompression () ? "ON" : "OFF");
 }
 
-static void
+static char *
 show_undefined (const struct dataset *ds UNUSED)
 {
-  if (settings_get_undefined ())
-    msg (SN, _("UNDEFINED is WARN."));
-  else
-    msg (SN, _("UNDEFINED is NOWARN."));
+  return xstrdup (settings_get_undefined () ? "WARN" : "NOWARN");
 }
 
-static void
+static char *
 show_weight (const struct dataset *ds)
 {
   const struct variable *var = dict_get_weight (dataset_dict (ds));
-  if (var == NULL)
-    msg (SN, _("WEIGHT is off."));
-  else
-    msg (SN, _("WEIGHT is variable %s."), var_get_name (var));
+  return xstrdup (var != NULL ? var_get_name (var) : "OFF");
 }
 
-static void
+static char *
 show_wib (const struct dataset *ds UNUSED)
 {
-  show_integer_format ("WIB", settings_get_output_integer_format ());
+  return show_integer_format (settings_get_output_integer_format ());
 }
 
-static void
+static char *
 show_wrb (const struct dataset *ds UNUSED)
 {
-  show_float_format ("WRB", settings_get_output_float_format ());
+  return show_float_format (settings_get_output_float_format ());
 }
 
-static void
+static char *
 show_width (const struct dataset *ds UNUSED)
 {
-  msg (SN, _("WIDTH is %d."), settings_get_viewwidth ());
+  return xasprintf ("%d", settings_get_viewwidth ());
 }
 
 struct show_sbc
   {
     const char *name;
-    void (*function) (const struct dataset *);
+    char *(*function) (const struct dataset *);
   };
 
 const struct show_sbc show_table[] =
@@ -744,6 +772,7 @@ const struct show_sbc show_table[] =
     {"ERRORS", show_errors},
     {"FORMAT", show_format},
     {"LENGTH", show_length},
+    {"LOCALE", show_locale},
     {"MXERRS", show_mxerrs},
     {"MXLOOPS", show_mxloops},
     {"MXWARNS", show_mxwarns},
@@ -757,22 +786,34 @@ const struct show_sbc show_table[] =
     {"WIDTH", show_width},
   };
 
+static void
+do_show (const struct dataset *ds, const struct show_sbc *sbc)
+{
+  char *value = sbc->function (ds);
+  msg (SN, _("%s is %s."), sbc->name, value);
+  free (value);
+}
+
 static void
 show_all (const struct dataset *ds)
 {
   size_t i;
 
   for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
-    show_table[i].function (ds);
+    do_show (ds, &show_table[i]);
 }
 
 static void
-show_all_cc (void)
+show_all_cc (const struct dataset *ds)
 {
   int i;
 
-  for (i = 0; i < 5; i++)
-    show_cc (i);
+  for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
+    {
+      const struct show_sbc *sbc = &show_table[i];
+      if (!strncmp (sbc->name, "CC", 2))
+        do_show (ds, sbc);
+    }
 }
 
 static void
@@ -801,7 +842,7 @@ cmd_show (struct lexer *lexer, struct dataset *ds)
       if (lex_match (lexer, T_ALL))
         show_all (ds);
       else if (lex_match_id (lexer, "CC"))
-        show_all_cc ();
+        show_all_cc (ds);
       else if (lex_match_id (lexer, "WARRANTY"))
         show_warranty (ds);
       else if (lex_match_id (lexer, "COPYING"))
@@ -811,10 +852,13 @@ cmd_show (struct lexer *lexer, struct dataset *ds)
           int i;
 
           for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
-            if (lex_match_id (lexer, show_table[i].name))
-              {
-                show_table[i].function (ds);
-                goto found;
+            {
+              const struct show_sbc *sbc = &show_table[i];
+              if (lex_match_id (lexer, sbc->name))
+                {
+                  do_show (ds, sbc);
+                  goto found;
+                }
               }
           lex_error (lexer, NULL);
           return CMD_FAILURE;
index f17798f77722a35cb2597a82f5ecaea4ccfcced7..09fc2155abc82c0a678364301b1ea0de6b3fa9c9 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_str_rw (*c, compute->variable),
+                         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,9 +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,
-                         var_get_width (vr));
+      *c = case_unshare (*c);
+      expr_evaluate_str (compute->rvalue, *c, case_num,
+                         case_str_rw (*c, vr), var_get_width (vr));
     }
 
   return TRNS_CONTINUE;
index 1c9b4d65735f9487b147da120585465b7501ea0d..8ce2d12576697beb5562fc65fc4a03522ce2a9a0 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
@@ -25,7 +25,7 @@
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/lexer/lexer.h>
-#include <language/lexer/range-parser.h>
+#include <language/lexer/value-parser.h>
 #include <language/lexer/variable-parser.h>
 #include <libpspp/compiler.h>
 #include <libpspp/message.h>
@@ -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..62b03ba073b9a5f6f6a1c5b282d9c26b0ce286b9 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
@@ -29,8 +29,8 @@
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/value-parser.h>
 #include <language/lexer/variable-parser.h>
-#include <language/lexer/range-parser.h>
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/message.h>
@@ -55,25 +55,18 @@ enum map_in_type
     MAP_CONVERT                        /* "123" => 123. */
   };
 
-/* A value involved in a RECODE mapping. */
-union recode_value
-  {
-    double f;                   /* Numeric. */
-    char *c;                    /* Short or long string. */
-  };
-
 /* Describes input values to be mapped. */
 struct map_in
   {
     enum map_in_type type;      /* One of MAP_*. */
-    union recode_value x, y;    /* Source values. */
+    union value x, y;           /* Source values. */
   };
 
 /* Describes the value used as output from a mapping. */
 struct map_out
   {
     bool copy_input;            /* If true, copy input to output. */
-    union recode_value value;   /* If copy_input false, recoded value. */
+    union value value;          /* If copy_input false, recoded value. */
     int width;                  /* If copy_input false, output value width. */
   };
 
@@ -90,6 +83,8 @@ struct recode_trns
   {
     struct pool *pool;
 
+
+
     /* Variable types, for convenience. */
     enum val_type src_type;     /* src_vars[*] type. */
     enum val_type dst_type;     /* dst_vars[*] type. */
@@ -97,12 +92,15 @@ struct recode_trns
     /* Variables. */
     const struct variable **src_vars;  /* Source variables. */
     const struct variable **dst_vars;  /* Destination variables. */
+    const struct dictionary *dst_dict;  /* Dictionary of dst_vars */
     char **dst_names;          /* Name of dest variables, if they're new. */
     size_t var_cnt;             /* Number of variables. */
 
     /* Mappings. */
     struct mapping *mappings;   /* Value mappings. */
     size_t map_cnt;             /* Number of mappings. */
+    int max_src_width;          /* Maximum width of src_vars[*]. */
+    int max_dst_width;          /* Maximum width of any map_out in mappings. */
   };
 
 static bool parse_src_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict);
@@ -161,7 +159,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,
@@ -193,18 +191,17 @@ parse_src_vars (struct lexer *lexer,
 static bool
 parse_mappings (struct lexer *lexer, struct recode_trns *trns)
 {
-  size_t max_src_width;
   size_t map_allocated;
   bool have_dst_type;
   size_t i;
 
   /* Find length of longest source variable. */
-  max_src_width = var_get_width (trns->src_vars[0]);
+  trns->max_src_width = var_get_width (trns->src_vars[0]);
   for (i = 1; i < trns->var_cnt; i++)
     {
       size_t var_width = var_get_width (trns->src_vars[i]);
-      if (var_width > max_src_width)
-        max_src_width = var_width;
+      if (var_width > trns->max_src_width)
+        trns->max_src_width = var_width;
     }
 
   /* Parse the mappings in parentheses. */
@@ -230,8 +227,9 @@ 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))
+                                 trns->src_type, trns->max_src_width))
                 return false;
               add_mapping (trns, &map_allocated, &in);
               lex_match (lexer, ',');
@@ -240,7 +238,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,6 +291,7 @@ 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)
@@ -307,16 +310,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;
@@ -363,8 +371,9 @@ set_map_in_str (struct map_in *in, struct pool *pool,
                 const struct string *string, size_t width)
 {
   in->type = MAP_SINGLE;
-  in->x.c = pool_alloc_unaligned (pool, width);
-  buf_copy_rpad (in->x.c, width, ds_data (string), ds_length (string));
+  value_init_pool (pool, &in->x, width);
+  value_copy_buf_rpad (&in->x, width,
+                       ds_data (string), ds_length (string), ' ');
 }
 
 /* Parses a mapping output value into OUT, allocating memory from
@@ -384,8 +393,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"));
@@ -411,9 +423,17 @@ set_map_out_str (struct map_out *out, struct pool *pool,
   const char *string = ds_data (value);
   size_t length = ds_length (value);
 
+  if (length == 0)
+    {
+      /* A length of 0 will yield a numeric value, which is not
+         what we want. */
+      string = " ";
+      length = 1;
+    }
+
   out->copy_input = false;
-  out->value.c = pool_alloc_unaligned (pool, length);
-  memcpy (out->value.c, string, length);
+  value_init_pool (pool, &out->value, length);
+  memcpy (value_str_rw (&out->value, length), string, length);
   out->width = length;
 }
 
@@ -461,6 +481,7 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns,
               return false;
             }
         }
+
     }
   else
     {
@@ -497,26 +518,22 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns,
 static void
 enlarge_dst_widths (struct recode_trns *trns)
 {
-  size_t max_dst_width;
   size_t i;
 
-  max_dst_width = 0;
+  trns->max_dst_width = 0;
   for (i = 0; i < trns->var_cnt; i++)
     {
       const struct variable *v = trns->dst_vars[i];
-      if (var_get_width (v) > max_dst_width)
-        max_dst_width = var_get_width (v);
+      if (var_get_width (v) > trns->max_dst_width)
+        trns->max_dst_width = var_get_width (v);
     }
 
   for (i = 0; i < trns->map_cnt; i++)
     {
       struct map_out *out = &trns->mappings[i].out;
-      if (!out->copy_input && out->width < max_dst_width)
-        {
-          char *s = pool_alloc_unaligned (trns->pool, max_dst_width + 1);
-          buf_copy_rpad (s, max_dst_width + 1, out->value.c, out->width);
-          out->value.c = s;
-        }
+      if (!out->copy_input)
+        value_resize_pool (trns->pool, &out->value,
+                           out->width, trns->max_dst_width);
     }
 }
 
@@ -526,6 +543,8 @@ create_dst_vars (struct recode_trns *trns, struct dictionary *dict)
 {
   size_t i;
 
+  trns->dst_dict = dict;
+
   for (i = 0; i < trns->var_cnt; i++)
     {
       const struct variable **var = &trns->dst_vars[i];
@@ -584,9 +603,11 @@ 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 uint8_t *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++)
     {
@@ -597,7 +618,8 @@ find_src_string (struct recode_trns *trns, const char *value, int width)
       switch (in->type)
         {
         case MAP_SINGLE:
-          match = !memcmp (value, in->x.c, width);
+          match = !memcmp (value, value_str (&in->x, trns->max_src_width),
+                           width);
           break;
         case MAP_ELSE:
           match = true;
@@ -608,11 +630,14 @@ find_src_string (struct recode_trns *trns, const char *value, int width)
 
             msg_disable ();
             match = data_in (ss_buffer (value, width), LEGACY_NATIVE,
-                             FMT_F, 0, 0, 0, &uv, 0);
+                             FMT_F, 0, 0, 0, trns->dst_dict,  &uv, 0);
             msg_enable ();
             out->value.f = uv.f;
             break;
           }
+       case MAP_MISSING:
+         match = var_is_str_missing (src_var, value, MV_ANY);
+         break;
         default:
           NOT_REACHED ();
         }
@@ -626,45 +651,49 @@ 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 struct map_out *out;
 
       if (trns->src_type == VAL_NUMERIC)
-        out = find_src_numeric (trns, src_data->f, src_var);
+        out = find_src_numeric (trns, case_num (*c, src_var), src_var);
       else
-        out = find_src_string (trns, src_data->s, var_get_width (src_var));
+        out = find_src_string (trns, case_str (*c, src_var), src_var);
 
       if (trns->dst_type == VAL_NUMERIC)
         {
+          double *dst = &case_data_rw (*c, dst_var)->f;
           if (out != NULL)
-            dst_data->f = !out->copy_input ? out->value.f : src_data->f;
+            *dst = !out->copy_input ? out->value.f : case_num (*c, src_var);
           else if (trns->src_vars != trns->dst_vars)
-            dst_data->f = SYSMIS;
+            *dst = SYSMIS;
         }
       else
         {
+          char *dst = case_str_rw (*c, dst_var);
           if (out != NULL)
             {
               if (!out->copy_input)
-                memcpy (dst_data->s, out->value.c, var_get_width (dst_var));
+                memcpy (dst, value_str (&out->value, trns->max_dst_width),
+                        var_get_width (dst_var));
               else if (trns->src_vars != trns->dst_vars)
-                buf_copy_rpad (dst_data->s, var_get_width (dst_var),
-                               src_data->s, var_get_width (src_var));
+                {
+                  union value *dst_data = case_data_rw (*c, dst_var);
+                  const union value *src_data = case_data (*c, src_var);
+                  value_copy_rpad (dst_data, var_get_width (dst_var),
+                                   src_data, var_get_width (src_var), ' ');
+                }
             }
           else if (trns->src_vars != trns->dst_vars)
-            memset (dst_data->s, ' ', var_get_width (dst_var));
+            memset (dst, ' ', var_get_width (dst_var));
         }
     }
 
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);
 }
 
diff --git a/src/libpspp/argv-parser.c b/src/libpspp/argv-parser.c
new file mode 100644 (file)
index 0000000..ecf1ab2
--- /dev/null
@@ -0,0 +1,183 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <libpspp/argv-parser.h>
+
+#include <limits.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/str.h>
+
+#include "xalloc.h"
+
+struct argv_option_plus
+  {
+    struct argv_option base;
+    void (*cb) (int id, void *aux);
+    void *aux;
+  };
+
+struct argv_parser
+  {
+    struct argv_option_plus *options;
+    size_t n_options, allocated_options;
+  };
+
+/* Creates and returns a new argv_parser that initially is not
+   configured to parse any command-line options. */
+struct argv_parser *
+argv_parser_create (void)
+{
+  struct argv_parser *ap = xzalloc (sizeof *ap);
+  return ap;
+}
+
+/* Destroys AP. */
+void
+argv_parser_destroy (struct argv_parser *ap)
+{
+  if (ap != NULL)
+    {
+      free (ap->options);
+      free (ap);
+    }
+}
+
+/* Adds the N options in OPTIONS to AP.  When argv_parser_run is
+   later called for AP, each of the options in OPTIONS will be
+   handled by passing the option's 'id' member to CB along with
+   AUX.  For an option that has an argument, the 'optarg' global
+   variable will be set to point to it before calling CB;
+   otherwise 'optarg' will be set to NULL. */
+void
+argv_parser_add_options (struct argv_parser *ap,
+                         const struct argv_option *options, size_t n,
+                         void (*cb) (int id, void *aux), void *aux)
+{
+  const struct argv_option *src;
+  for (src = options; src < &options[n]; src++)
+    {
+      struct argv_option_plus *dst;
+
+      if (ap->n_options >= ap->allocated_options)
+        ap->options = x2nrealloc (ap->options, &ap->allocated_options,
+                                  sizeof *ap->options);
+
+      assert (src->long_name != NULL || src->short_name != 0);
+      dst = &ap->options[ap->n_options++];
+      dst->base = *src;
+      dst->cb = cb;
+      dst->aux = aux;
+    }
+}
+
+/* Parses all ARGC command-line arguments in ARGV according to
+   the options configured in AP with argv_parser_add_options.
+   Returns true if all the command-line arguments were parsed
+   successfully, false if there was an error.  Upon failure
+   return, if the external variable 'opterr' is nonzero (which is
+   the default), an error message will also be printed.  Upon
+   successful return, external variable 'optind' will be set to
+   the index of the first non-option argument. */
+bool
+argv_parser_run (struct argv_parser *ap, int argc, char **argv)
+{
+  enum { LONGOPT_VAL_BASE = UCHAR_MAX + 1 };
+  const struct argv_option_plus *shortopt_ptrs[UCHAR_MAX + 1];
+  struct string shortopts;
+  struct option *longopts;
+  size_t n_longopts;
+  bool retval;
+  size_t i;
+
+  memset (shortopt_ptrs, 0, sizeof shortopt_ptrs);
+  ds_init_empty (&shortopts);
+  longopts = xmalloc ((ap->n_options + 1) * sizeof *longopts);
+  n_longopts = 0;
+  for (i = 0; i < ap->n_options; i++)
+    {
+      const struct argv_option_plus *aop = &ap->options[i];
+
+      if (aop->base.long_name != NULL)
+        {
+          struct option *o = &longopts[n_longopts++];
+          o->name = aop->base.long_name;
+          o->has_arg = aop->base.has_arg;
+          o->flag = NULL;
+          o->val = i + LONGOPT_VAL_BASE;
+        }
+
+      if (aop->base.short_name != 0)
+        {
+          unsigned char c = aop->base.short_name;
+          if (shortopt_ptrs[c] == NULL)
+            {
+              shortopt_ptrs[c] = aop;
+              ds_put_char (&shortopts, aop->base.short_name);
+              if (aop->base.has_arg != no_argument)
+                ds_put_char (&shortopts, ':');
+              if (aop->base.has_arg == optional_argument)
+                ds_put_char (&shortopts, ':');
+            }
+          else
+            {
+              if (opterr)
+                fprintf (stderr, "option -%c multiply defined",
+                         aop->base.short_name);
+              retval = false;
+              goto exit;
+            }
+        }
+    }
+  memset (&longopts[n_longopts], 0, sizeof *longopts);
+
+  for (;;)
+    {
+      int indexptr;
+      int c = getopt_long (argc, argv, ds_cstr (&shortopts),
+                           longopts, &indexptr);
+
+      if (c == -1)
+        {
+          retval = true;
+          break;
+        }
+      else if (c == '?')
+        {
+          retval = false;
+          break;
+        }
+      else if (c >= LONGOPT_VAL_BASE && c < LONGOPT_VAL_BASE + n_longopts)
+        {
+          struct argv_option_plus *aop = &ap->options[c - LONGOPT_VAL_BASE];
+          aop->cb (aop->base.id, aop->aux);
+        }
+      else if (c >= SCHAR_MIN && c <= UCHAR_MAX)
+        {
+          const struct argv_option_plus *aop = shortopt_ptrs[(unsigned char) c];
+          aop->cb (aop->base.id, aop->aux);
+        }
+      else
+        NOT_REACHED ();
+    }
+
+exit:
+  ds_destroy (&shortopts);
+  free (longopts);
+  return retval;
+}
diff --git a/src/libpspp/argv-parser.h b/src/libpspp/argv-parser.h
new file mode 100644 (file)
index 0000000..3406ba2
--- /dev/null
@@ -0,0 +1,58 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_ARGV_PARSER_H
+#define LIBPSPP_ARGV_PARSER_H 1
+
+/* Simple, modular command-line argument parser.
+
+   glibc has two option parsers, but neither one of them feels
+   quite right:
+
+     - getopt_long is simple, but not modular, in that there is
+       no easy way to make it accept multiple collections of
+       options supported by different modules.
+
+     - argp is more sophisticated and more complete, and hence
+       more complex.  It still lacks one important feature for
+       modularity: there is no straightforward way for option
+       groups that are implemented independently to have separate
+       auxiliary data.
+
+   The parser implemented in this file is meant to be simple and
+   modular.  It is based internally on getopt_long. */
+
+#include <getopt.h>
+#include <stdbool.h>
+
+struct argv_option
+  {
+    const char *long_name;  /* Long option name, NULL if none. */
+    int short_name;         /* Short option character, 0 if none. */
+    int has_arg;            /* no_argument, required_argument, or
+                               optional_argument. */
+    int id;                 /* Value passed to callback. */
+  };
+
+struct argv_parser *argv_parser_create (void);
+void argv_parser_destroy (struct argv_parser *);
+
+void argv_parser_add_options (struct argv_parser *,
+                              const struct argv_option *options, size_t n,
+                              void (*cb) (int id, void *aux), void *aux);
+bool argv_parser_run (struct argv_parser *, int argc, char **argv);
+
+#endif /* libpspp/argv-parser.h */
index b4fa926edd8cf3969c48070b93a1e95488102f42..5e434206970825c2773d9d045d7c469b4ab94fc5 100644 (file)
@@ -1,11 +1,13 @@
 ## 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/argv-parser.c \
+       src/libpspp/argv-parser.h \
        src/libpspp/array.c \
        src/libpspp/array.h \
        src/libpspp/assertion.h \
@@ -23,10 +25,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 \
@@ -53,12 +61,16 @@ src_libpspp_libpspp_a_SOURCES = \
        src/libpspp/range-set.h \
        src/libpspp/sparse-array.c \
        src/libpspp/sparse-array.h \
+       src/libpspp/sparse-xarray.c \
+       src/libpspp/sparse-xarray.h \
        src/libpspp/start-date.c \
        src/libpspp/start-date.h \
        src/libpspp/str.c \
        src/libpspp/str.h \
        src/libpspp/taint.c \
        src/libpspp/taint.h \
+       src/libpspp/tmpfile.c \
+       src/libpspp/tmpfile.h \
        src/libpspp/tower.c \
        src/libpspp/tower.h \
        src/libpspp/verbose-msg.c \
@@ -67,14 +79,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..f9f1f0e
--- /dev/null
@@ -0,0 +1,174 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 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 <libpspp/hash-functions.h>
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+/* Based on http://burtleburtle.net/bob/c/lookup3.c, by Bob
+   Jenkins <bob_jenkins@burtleburtle.net>, as retrieved on April
+   8, 2009.  The license information there says the following:
+   "You can use this free for any purpose.  It's in the public
+   domain.  It has no warranty." and "You may use this code any
+   way you wish, private, educational, or commercial.  It's
+   free." */
+
+#define HASH_ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+
+#define HASH_MIX(a, b, c)                               \
+        do                                              \
+          {                                             \
+            a -= c;  a ^= HASH_ROT (c,  4);  c += b;    \
+            b -= a;  b ^= HASH_ROT (a,  6);  a += c;    \
+            c -= b;  c ^= HASH_ROT (b,  8);  b += a;    \
+            a -= c;  a ^= HASH_ROT (c, 16);  c += b;    \
+            b -= a;  b ^= HASH_ROT (a, 19);  a += c;    \
+            c -= b;  c ^= HASH_ROT (b,  4);  b += a;    \
+          }                                             \
+        while (0)
+
+#define HASH_FINAL(a, b, c)                     \
+        do                                      \
+          {                                     \
+            c ^= b; c -= HASH_ROT (b, 14);      \
+            a ^= c; a -= HASH_ROT (c, 11);      \
+            b ^= a; b -= HASH_ROT (a, 25);      \
+            c ^= b; c -= HASH_ROT (b, 16);      \
+            a ^= c; a -= HASH_ROT (c, 4);       \
+            b ^= a; b -= HASH_ROT (a, 14);      \
+            c ^= b; c -= HASH_ROT (b, 24);      \
+          }                                     \
+        while (0)
+
+/* Returns a hash value for the N bytes starting at P, starting
+   from BASIS. */
+unsigned int
+hash_bytes (const void *p_, size_t n, unsigned int basis)
+{
+  const uint8_t *p = p_;
+  uint32_t a, b, c;
+  uint32_t tmp[3];
+
+  a = b = c = 0xdeadbeef + n + basis;
+
+  while (n >= 12)
+    {
+      memcpy (tmp, p, 12);
+      a += tmp[0];
+      b += tmp[1];
+      c += tmp[2];
+      HASH_MIX (a, b, c);
+      n -= 12;
+      p += 12;
+    }
+
+  if (n > 0)
+    {
+      memset (tmp, 0, 12);
+      memcpy (tmp, p, n);
+      a += tmp[0];
+      b += tmp[1];
+      c += tmp[2];
+    }
+
+  HASH_FINAL (a, b, c);
+  return c;
+}
+
+/* Returns a hash value for null-terminated string S, starting
+   from BASIS. */
+unsigned int
+hash_string (const char *s, unsigned int basis)
+{
+  return hash_bytes (s, strlen (s), basis);
+}
+
+/* Returns a hash value for null-terminated string S, with
+   lowercase and uppercase letters treated as equal, starting
+   from BASIS. */
+unsigned int
+hash_case_string (const char *s, unsigned int basis)
+{
+  size_t n = strlen (s);
+  uint32_t a, b, c;
+  uint32_t tmp[3];
+  int i;
+
+  a = b = c = 0xdeadbeef + n + basis;
+
+  while (n >= 12)
+    {
+      for (i = 0; i < 12; i++)
+        ((unsigned char *)tmp)[i] = toupper ((unsigned char) s[i]);
+      a += tmp[0];
+      b += tmp[1];
+      c += tmp[2];
+      HASH_MIX (a, b, c);
+      n -= 12;
+      s += 12;
+    }
+
+  if (n > 0)
+    {
+      memset (tmp, 0, 12);
+      for (i = 0; i < n; i++)
+        ((unsigned char *)tmp)[i] = toupper ((unsigned char) s[i]);
+      a += tmp[0];
+      b += tmp[1];
+      c += tmp[2];
+    }
+
+  HASH_FINAL (a, b, c);
+  return c;
+}
+
+/* Returns a hash value for integer X, starting from BASIS. */
+unsigned int
+hash_int (int x, unsigned int basis)
+{
+  x -= x << 6;
+  x ^= x >> 17;
+  x -= x << 9;
+  x ^= x << 4;
+  x -= x << 3;
+  x ^= x << 10;
+  x ^= x >> 15;
+  return x + basis;
+}
+
+/* Returns a hash value for double D, starting from BASIS. */
+unsigned int
+hash_double (double d, unsigned int basis)
+{
+#if SIZEOF_DOUBLE == 8
+  uint32_t tmp[2];
+  uint32_t a, b, c;
+
+  a = b = c = 0xdeadbeef + 8 + basis;
+
+  memcpy (tmp, &d, 8);
+  a += tmp[0];
+  b += tmp[1];
+  HASH_FINAL (a, b, c);
+  return c;
+#else /* SIZEOF_DOUBLE != 8 */
+  return hash_bytes (&d, sizeof d, basis);
+#endif /* SIZEOF_DOUBLE != 8 */
+}
diff --git a/src/libpspp/hash-functions.h b/src/libpspp/hash-functions.h
new file mode 100644 (file)
index 0000000..089134b
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 int hash_bytes (const void *, size_t, unsigned int basis);
+unsigned int hash_string (const char *, unsigned int basis);
+unsigned int hash_case_string (const char *, unsigned int basis);
+unsigned int hash_int (int, unsigned int basis);
+unsigned int hash_double (double, unsigned int basis);
+
+#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..4c97e23
--- /dev/null
@@ -0,0 +1,188 @@
+/* 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/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/hmap.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "xalloc.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 = xcalloc (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..e08ba2804bbf4d6b0dbe7c6cf81b820c7a239462 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
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <libintl.h>
 #include <iconv.h>
 #include <errno.h>
+#include <relocatable.h>
 #include "assertion.h"
+#include "hmapx.h"
+#include "hash-functions.h"
+#include "pool.h"
 
 #include "i18n.h"
 
+#include "version.h"
+
 #include <localcharset.h>
 #include "xstrndup.h"
 
+#if HAVE_NL_LANGINFO
+#include <langinfo.h>
+#endif
 
-static char *locale = 0;
-static const char *charset;
-
-
-static iconv_t convertor[n_CONV];
+struct converter
+  {
+    const char *tocode;
+    const char *fromcode;
+    iconv_t conv;
+  };
 
+static char *default_encoding;
+static struct hmapx map;
 
 /* A wrapper around iconv_open */
 static iconv_t
 create_iconv (const char* tocode, const char* fromcode)
 {
-  iconv_t conv = iconv_open (tocode, fromcode);
+  size_t hash;
+  struct hmapx_node *node;
+  struct converter *converter;
+  assert (fromcode);
+
+  hash = hash_string (tocode, hash_string (fromcode, 0));
+  HMAPX_FOR_EACH_WITH_HASH (converter, node, hash, &map)
+    if (!strcmp (tocode, converter->tocode)
+        && !strcmp (fromcode, converter->fromcode))
+      return converter->conv;
+
+  converter = xmalloc (sizeof *converter);
+  converter->tocode = xstrdup (tocode);
+  converter->fromcode = xstrdup (fromcode);
+  converter->conv = iconv_open (tocode, fromcode);
+  hmapx_insert (&map, converter, hash);
 
   /* I don't think it's safe to translate this string or to use messaging
      as the convertors have not yet been set up */
-  if ( (iconv_t) -1 == conv && 0 != strcmp (tocode, fromcode))
+  if ( (iconv_t) -1 == converter->conv && 0 != strcmp (tocode, fromcode))
     {
       const int err = errno;
       fprintf (stderr,
-       "Warning: cannot create a convertor for \"%s\" to \"%s\": %s\n",
-       fromcode, tocode, strerror (err));
+               "Warning: "
+               "cannot create a convertor for \"%s\" to \"%s\": %s\n",
+               fromcode, tocode, strerror (err));
     }
 
-  return conv;
+  return converter->conv;
+}
+
+char *
+recode_string (const char *to, const char *from,
+              const char *text, int length)
+{
+  return recode_string_pool (to, from, text, length, NULL);
 }
 
-/* Return a string based on TEXT converted according to HOW.
+
+/* Return a string based on TEXT which must be encoded using FROM.
+   The returned string will be encoded in TO.
    If length is not -1, then it must be the number of bytes in TEXT.
    The returned string must be freed when no longer required.
 */
 char *
-recode_string (enum conv_id how,  const char *text, int length)
+recode_string_pool (const char *to, const char *from,
+              const char *text, int length, struct pool *pool)
 {
   char *outbuf = 0;
   size_t outbufferlength;
@@ -70,6 +109,7 @@ recode_string (enum conv_id how,  const char *text, int length)
   char *op ;
   size_t inbytes = 0;
   size_t outbytes ;
+  iconv_t conv ;
 
   /* FIXME: Need to ensure that this char is valid in the target encoding */
   const char fallbackchar = '?';
@@ -80,24 +120,31 @@ recode_string (enum conv_id how,  const char *text, int length)
   if ( length == -1 )
      length = strlen(text);
 
-  assert (how < n_CONV);
+  if (to == NULL)
+    to = default_encoding;
 
-  if (convertor[how] == (iconv_t) -1)
-    return xstrndup (text, length);
+  if (from == NULL)
+    from = default_encoding;
 
   for ( outbufferlength = 1 ; outbufferlength != 0; outbufferlength <<= 1 )
     if ( outbufferlength > length)
       break;
 
-  outbuf = xmalloc(outbufferlength);
+  outbuf = pool_malloc (pool, outbufferlength);
   op = outbuf;
 
   outbytes = outbufferlength;
   inbytes = length;
 
+
+  conv = create_iconv (to, from);
+
+  if ( (iconv_t) -1 == conv )
+       return xstrdup (text);
+
   do {
     const char *ip = text;
-    result = iconv (convertor[how], (ICONV_CONST char **) &text, &inbytes,
+    result = iconv (conv, (ICONV_CONST char **) &text, &inbytes,
                   &op, &outbytes);
 
     if ( -1 == result )
@@ -120,7 +167,7 @@ recode_string (enum conv_id how,  const char *text, int length)
          case E2BIG:
            free (outbuf);
            outbufferlength <<= 1;
-           outbuf = xmalloc (outbufferlength);
+           outbuf = pool_malloc (pool, outbufferlength);
            op = outbuf;
            outbytes = outbufferlength;
            inbytes = length;
@@ -128,6 +175,7 @@ recode_string (enum conv_id how,  const char *text, int length)
            break;
          default:
            /* should never happen */
+            fprintf (stderr, "Character conversion error: %s\n", strerror (the_error));
            NOT_REACHED ();
            break;
          }
@@ -137,7 +185,7 @@ recode_string (enum conv_id how,  const char *text, int length)
   if (outbytes == 0 )
     {
       char *const oldaddr = outbuf;
-      outbuf = xrealloc (outbuf, outbufferlength + 1);
+      outbuf = pool_realloc (pool, outbuf, outbufferlength + 1);
 
       op += (outbuf - oldaddr) ;
     }
@@ -148,68 +196,141 @@ recode_string (enum conv_id how,  const char *text, int length)
 }
 
 
-/* Returns the current PSPP locale */
+void
+i18n_init (void)
+{
+#if ENABLE_NLS
+  setlocale (LC_CTYPE, "");
+#ifdef LC_MESSAGES
+  setlocale (LC_MESSAGES, "");
+#endif
+#if HAVE_LC_PAPER
+  setlocale (LC_PAPER, "");
+#endif
+  bindtextdomain (PACKAGE, relocate(locale_dir));
+  textdomain (PACKAGE);
+#endif /* ENABLE_NLS */
+
+  assert (default_encoding == NULL);
+  default_encoding = xstrdup (locale_charset ());
+
+  hmapx_init (&map);
+}
+
+
 const char *
-get_pspp_locale (void)
+get_default_encoding (void)
 {
-  assert (locale);
-  return locale;
+  return default_encoding;
 }
 
-/* Set the PSPP locale */
 void
-set_pspp_locale (const char *l)
+set_default_encoding (const char *enc)
 {
-  char *current_locale;
-  const char *current_charset;
+  free (default_encoding);
+  default_encoding = xstrdup (enc);
+}
 
-  free(locale);
-  locale = strdup(l);
 
-  current_locale = setlocale (LC_CTYPE, 0);
-  current_charset = locale_charset ();
-  setlocale (LC_CTYPE, locale);
+/* Attempts to set the encoding from a locale name
+   returns true if successfull.
+   This function does not (should not!) alter the current locale.
+*/
+bool
+set_encoding_from_locale (const char *loc)
+{
+  bool ok = true;
+  char *c_encoding;
+  char *loc_encoding;
+  char *tmp = xstrdup (setlocale (LC_CTYPE, NULL));
+
+  setlocale (LC_CTYPE, "C");
+  c_encoding = xstrdup (locale_charset ());
 
-  charset = locale_charset ();
-  setlocale (LC_CTYPE, current_locale);
+  setlocale (LC_CTYPE, loc);
+  loc_encoding = xstrdup (locale_charset ());
 
-  iconv_close (convertor[CONV_PSPP_TO_UTF8]);
-  convertor[CONV_PSPP_TO_UTF8] = create_iconv ("UTF-8", charset);
 
-  iconv_close (convertor[CONV_SYSTEM_TO_PSPP]);
-  convertor[CONV_SYSTEM_TO_PSPP] = create_iconv (charset, current_charset);
+  if ( 0 == strcmp (loc_encoding, c_encoding))
+    {
+      ok = false;
+    }
 
-  iconv_close (convertor[CONV_UTF8_TO_PSPP]);
-  convertor[CONV_UTF8_TO_PSPP] = create_iconv (charset, "UTF-8");
+
+  setlocale (LC_CTYPE, tmp);
+
+  free (tmp);
+
+  if (ok)
+    {
+      free (default_encoding);
+      default_encoding = loc_encoding;
+    }
+  else
+    free (loc_encoding);
+
+  free (c_encoding);
+
+  return ok;
 }
 
 void
-i18n_init (void)
+i18n_done (void)
 {
-  assert (!locale) ;
-  locale = strdup (setlocale (LC_CTYPE, NULL));
+  struct hmapx_node *node;
+  struct converter *cvtr;
+  HMAPX_FOR_EACH (cvtr, node, &map)
+    {
+      iconv_close (cvtr->conv);
+      free (cvtr);
+    }
 
-  setlocale (LC_CTYPE, locale);
-  charset = locale_charset ();
+  hmapx_destroy (&map);
 
-  convertor[CONV_PSPP_TO_UTF8]   = create_iconv ("UTF-8", charset);
-  convertor[CONV_SYSTEM_TO_PSPP] = create_iconv (charset, charset);
-  convertor[CONV_UTF8_TO_PSPP]   = create_iconv (charset, "UTF-8");
+  free (default_encoding);
+  default_encoding = NULL;
 }
 
 
-void
-i18n_done (void)
+
+bool
+valid_encoding (const char *enc)
 {
-  int i;
-  free (locale);
-  locale = 0;
+  iconv_t conv = iconv_open ("UTF8", enc);
 
-  for(i = 0 ; i < n_CONV; ++i )
-    {
-      if ( (iconv_t) -1 == convertor[i] )
-       continue;
-      iconv_close (convertor[i]);
-    }
+  if ( conv == (iconv_t) -1)
+    return false;
+
+  iconv_close (conv);
+
+  return true;
+}
+
+
+/* Return the system local's idea of the
+   decimal seperator character */
+char
+get_system_decimal (void)
+{
+  char radix_char;
+
+  char *ol = xstrdup (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..9c8f7c14014bdfca443b4d8d01012b1eb40539e9 100644 (file)
 #ifndef I18N_H
 #define I18N_H
 
-const char * get_pspp_locale (void);
-void set_pspp_locale (const char *locale);
-const char * get_pspp_charset (void);
+#include <stdbool.h>
 
 void  i18n_done (void);
 void  i18n_init (void);
 
-enum conv_id
-  {
-    CONV_PSPP_TO_UTF8,
-    CONV_SYSTEM_TO_PSPP,
-    CONV_UTF8_TO_PSPP,
-    n_CONV
-  };
+#define UTF8 "UTF-8"
 
+struct pool;
 
-char * recode_string (enum conv_id how,  const char *text, int len);
+char *recode_string_pool (const char *to, const char *from,
+                         const char *text, int length, struct pool *pool);
 
+char *recode_string (const char *to, const char *from,
+                     const char *text, int len);
+
+
+bool valid_encoding (const char *enc);
+
+/* Return the decimal separator according to the
+   system locale */
+char get_system_decimal (void);
+
+const char * get_default_encoding (void);
+void set_default_encoding (const char *enc);
+
+bool set_encoding_from_locale (const char *loc);
 
 
 #endif /* i18n.h */
index 45f0195f37f3fc18b035401fcd1313a85f76b259..18a621970c85da745917dcf2b179c9945862e13e 100644 (file)
 #include <config.h>
 
 #include <libpspp/legacy-encoding.h>
-
-#include "str.h"
-
-static const char ascii_to_ebcdic[256];
-static const char ebcdic_to_ascii[256];
-
-void
-legacy_recode (enum legacy_encoding from, const char *src,
-             enum legacy_encoding to, char *dst,
-             size_t size)
-{
-  if (from != to)
-    {
-      const char *table;
-      size_t i;
-
-      table = from == LEGACY_ASCII ? ascii_to_ebcdic : ebcdic_to_ascii;
-      for (i = 0; i < size; i++)
-        dst[i] = table[(unsigned char) src[i]];
-    }
-  else
-    {
-      if (src != dst)
-        memcpy (dst, src, size);
-    }
-}
+#include <libpspp/i18n.h>
+#include <stdlib.h>
 
 char
-legacy_to_native (enum legacy_encoding from, char c)
+legacy_to_native (const char *from, char c)
 {
-  legacy_recode (from, &c, LEGACY_NATIVE, &c, 1);
-  return c;
+  char x;
+  char *s = recode_string (LEGACY_NATIVE, from, &c, 1);
+  x = s[0];
+  free (s);
+  return x;
 }
 
 char
-legacy_from_native (enum legacy_encoding to, char c)
+legacy_from_native (const char *to, char c)
 {
-  legacy_recode (LEGACY_NATIVE, &c, to, &c, 1);
-  return c;
+  char x;
+  char *s = recode_string (to, LEGACY_NATIVE, &c, 1);
+  x = s[0];
+  free (s);
+  return x;
 }
-
-static const char ascii_to_ebcdic[256] =
-  {
-    0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f,
-    0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26,
-    0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,
-    0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d,
-    0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,
-    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
-    0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,
-    0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
-    0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
-    0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
-    0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d,
-    0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
-    0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
-    0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
-    0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07,
-    0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17,
-    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b,
-    0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08,
-    0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1,
-    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
-    0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
-    0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-    0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
-    0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d,
-    0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e,
-    0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf,
-    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
-    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf,
-    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb,
-    0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed,
-    0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
-  };
-
-static const char ebcdic_to_ascii[256] =
-  {
-    0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f,
-    0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87,
-    0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f,
-    0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b,
-    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07,
-    0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
-    0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a,
-    0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
-    0xa7, 0xa8, 0xd5, 0x2e, 0x3c, 0x28, 0x2b, 0x7c,
-    0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
-    0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x7e,
-    0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
-    0xb8, 0xb9, 0xcb, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
-    0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1,
-    0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22,
-    0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-    0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
-    0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
-    0x71, 0x72, 0x5e, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
-    0xd1, 0xe5, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
-    0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7,
-    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
-    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7,
-    0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
-    0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,
-    0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
-    0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
-    0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
-    0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
-    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
-    0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
-  };
-
index 12afe42b7f22d97a190659fad70579f7ee4e9043..c6ae0ab4aa2bb25faf9f074436aa3bd9637bb137 100644 (file)
 #ifndef LIBPSPP_LEGACY_ENCODING
 #define LIBPSPP_LEGACY_ENCODING 1
 
-#include <stddef.h>
 #include <libpspp/compiler.h>
 
-/* A legacy character encoding.
-   This exists only to handle the specific legacy EBCDIC-to-ASCII
-   recoding that MODE=360 file handles perform. */
-enum legacy_encoding
-  {
-    LEGACY_ASCII,         /* ASCII or similar character set. */
-    LEGACY_EBCDIC,        /* IBM EBCDIC character set. */
-
-    /* Native character set. */
 #if 'A' == 0x41
-    LEGACY_NATIVE = LEGACY_ASCII
+#define  LEGACY_NATIVE "ASCII"
 #elif 'A' == 0xc1
-    LEGACY_NATIVE = LEGACY_EBCDIC
+#define  LEGACY_NATIVE "EBCDIC-US"
 #else
 #error Cannot detect native character set.
 #endif
-  };
 
-void legacy_recode (enum legacy_encoding, const char *src,
-                    enum legacy_encoding, char *dst, size_t);
-char legacy_to_native (enum legacy_encoding from, char) PURE_FUNCTION;
-char legacy_from_native (enum legacy_encoding to, char) PURE_FUNCTION;
+char legacy_to_native (const char *from, char) PURE_FUNCTION;
+char legacy_from_native (const char *to, char) PURE_FUNCTION;
+
 
 #endif /* libpspp/legacy-encoding.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..5faee1f1d6c8dc33279e36acc9b3188b56b1f218 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
@@ -82,7 +82,7 @@ msg_dup(const struct msg *m)
   struct msg *new_msg = xmalloc (sizeof *m);
 
   *new_msg = *m;
-  new_msg->text = strdup(m->text);
+  new_msg->text = xstrdup(m->text);
 
   return new_msg;
 }
@@ -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 */
index cb51c485df27ae983104b7540888c1b6492bb16d..80198f82bc76262b60b2841be92a25e8ce1cc648 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 <string.h>
 #include <sys/time.h>
 
-#include <data/val-type.h>
+#include <libpspp/argv-parser.h>
 #include <libpspp/bit-vector.h>
 #include <libpspp/compiler.h>
 #include <libpspp/deque.h>
+#include <libpspp/misc.h>
 #include <libpspp/str.h>
-#include <math/moments.h>
 
 #include "error.h"
 #include "minmax.h"
@@ -162,8 +162,8 @@ struct mc_options
   };
 
 /* Default progress function. */
-static bool
-default_progress (struct mc *mc)
+bool
+mc_progress_dots (struct mc *mc)
 {
   if (mc_results_get_stop_reason (mc_get_results (mc)) == MC_CONTINUING)
     putc ('.', stderr);
@@ -172,6 +172,39 @@ default_progress (struct mc *mc)
   return true;
 }
 
+/* Progress function that prints a one-line summary of the
+   current state on stderr. */
+bool
+mc_progress_fancy (struct mc *mc)
+{
+  const struct mc_results *results = mc_get_results (mc);
+  if (mc_results_get_stop_reason (results) == MC_CONTINUING)
+    fprintf (stderr, "Processed %d unique states, max depth %d, "
+             "dropped %d duplicates...\r",
+             mc_results_get_unique_state_count (results),
+             mc_results_get_max_depth_reached (results),
+             mc_results_get_duplicate_dropped_states (results));
+  else
+    putc ('\n', stderr);
+  return true;
+}
+
+/* Progress function that displays a detailed summary of the
+   current state on stderr. */
+bool
+mc_progress_verbose (struct mc *mc)
+{
+  const struct mc_results *results = mc_get_results (mc);
+
+  /* VT100 clear screen and home cursor. */
+  fprintf (stderr, "\033[H\033[2J");
+
+  if (mc_results_get_stop_reason (results) == MC_CONTINUING)
+    mc_results_print (results, stderr);
+
+  return true;
+}
+
 /* Do-nothing progress function. */
 static bool
 null_progress (struct mc *mc UNUSED)
@@ -203,7 +236,7 @@ mc_options_create (void)
   options->failure_verbosity = 2;
   options->output_file = stdout;
   options->progress_usec = 250000;
-  options->progress_func = default_progress;
+  options->progress_func = mc_progress_dots;
 
   options->aux = NULL;
 
@@ -610,6 +643,214 @@ mc_options_set_aux (struct mc_options *options, void *aux)
   options->aux = aux;
 }
 \f
+/* Options command-line parser. */
+
+enum
+  {
+    /* Search strategies. */
+    OPT_STRATEGY,
+    OPT_PATH,
+    OPT_MAX_DEPTH,
+    OPT_HASH_BITS,
+    OPT_SEED,
+
+    /* Queuing. */
+    OPT_QUEUE_LIMIT,
+    OPT_QUEUE_DROP,
+
+    /* Stop conditions. */
+    OPT_MAX_STATES,
+    OPT_MAX_ERRORS,
+    OPT_TIME_LIMIT,
+
+    /* User interface. */
+    OPT_PROGRESS,
+    OPT_VERBOSITY,
+    OPT_FAILURE_VERBOSITY,
+  };
+
+static const struct argv_option mc_argv_options[] =
+  {
+    {"strategy", 0, required_argument, OPT_STRATEGY},
+    {"max-depth", 0, required_argument, OPT_MAX_DEPTH},
+    {"hash-bits", 0, required_argument, OPT_HASH_BITS},
+    {"path", 0, required_argument, OPT_PATH},
+    {"queue-limit", 0, required_argument, OPT_QUEUE_LIMIT},
+    {"queue-drop", 0, required_argument, OPT_QUEUE_DROP},
+    {"seed", 0, required_argument, OPT_SEED},
+    {"max-states", 0, required_argument, OPT_MAX_STATES},
+    {"max-errors", 0, required_argument, OPT_MAX_ERRORS},
+    {"time-limit", 0, required_argument, OPT_TIME_LIMIT},
+    {"progress", 0, required_argument, OPT_PROGRESS},
+    {"verbosity", 0, required_argument, OPT_VERBOSITY},
+    {"failure-verbosity", 0, required_argument, OPT_FAILURE_VERBOSITY},
+  };
+enum { N_MC_ARGV_OPTIONS = sizeof mc_argv_options / sizeof *mc_argv_options };
+
+/* Called by argv_parser_run to indicate that option ID has been
+   parsed. */
+static void
+mc_parser_option_callback (int id, void *mc_options_)
+{
+  struct mc_options *options = mc_options_;
+  switch (id)
+    {
+    case OPT_STRATEGY:
+      if (mc_options_get_strategy (options) == MC_PATH)
+        error (1, 0, "--strategy and --path are mutually exclusive");
+
+      if (!strcmp (optarg, "broad"))
+        mc_options_set_strategy (options, MC_BROAD);
+      else if (!strcmp (optarg, "deep"))
+        mc_options_set_strategy (options, MC_DEEP);
+      else if (!strcmp (optarg, "random"))
+        mc_options_set_strategy (options, MC_RANDOM);
+      else
+        error (1, 0,
+               "strategy must be \"broad\", \"deep\", or \"random\"");
+      break;
+
+    case OPT_MAX_DEPTH:
+      mc_options_set_max_depth (options, atoi (optarg));
+      break;
+
+    case OPT_HASH_BITS:
+      {
+        int requested_hash_bits = atoi (optarg);
+        int hash_bits;
+        mc_options_set_hash_bits (options, requested_hash_bits);
+        hash_bits = mc_options_get_hash_bits (options);
+        if (hash_bits != requested_hash_bits)
+          error (0, 0, "hash bits adjusted to %d.", hash_bits);
+      }
+      break;
+
+    case OPT_PATH:
+      {
+        struct mc_path path;
+        char *op;
+
+        if (mc_options_get_strategy (options) != MC_BROAD)
+          error (1, 0, "--strategy and --path are mutually exclusive");
+
+        mc_path_init (&path);
+        for (op = strtok (optarg, ":, \t"); op != NULL;
+             op = strtok (NULL, ":, \t"))
+          mc_path_push (&path, atoi (op));
+        if (mc_path_get_length (&path) == 0)
+          error (1, 0, "at least one value must be specified on --path");
+        mc_options_set_follow_path (options, &path);
+        mc_path_destroy (&path);
+      }
+      break;
+
+    case OPT_QUEUE_LIMIT:
+      mc_options_set_queue_limit (options, atoi (optarg));
+      break;
+
+    case OPT_QUEUE_DROP:
+      if (!strcmp (optarg, "newest"))
+        mc_options_set_queue_limit_strategy (options, MC_DROP_NEWEST);
+      else if (!strcmp (optarg, "oldest"))
+        mc_options_set_queue_limit_strategy (options, MC_DROP_OLDEST);
+      else if (!strcmp (optarg, "random"))
+        mc_options_set_queue_limit_strategy (options, MC_DROP_RANDOM);
+      else
+        error (1, 0, "--queue-drop argument must be \"newest\", "
+               "\"oldest\", or \"random\"");
+      break;
+
+    case OPT_SEED:
+      mc_options_set_seed (options, atoi (optarg));
+      break;
+
+    case OPT_MAX_STATES:
+      mc_options_set_max_unique_states (options, atoi (optarg));
+      break;
+
+    case OPT_MAX_ERRORS:
+      mc_options_set_max_errors (options, atoi (optarg));
+      break;
+
+    case OPT_TIME_LIMIT:
+      mc_options_set_time_limit (options, atof (optarg));
+      break;
+
+    case OPT_PROGRESS:
+      if (!strcmp (optarg, "none"))
+        mc_options_set_progress_usec (options, 0);
+      else if (!strcmp (optarg, "dots"))
+        mc_options_set_progress_func (options, mc_progress_dots);
+      else if (!strcmp (optarg, "fancy"))
+        mc_options_set_progress_func (options, mc_progress_fancy);
+      else if (!strcmp (optarg, "verbose"))
+        mc_options_set_progress_func (options, mc_progress_verbose);
+      break;
+
+    case OPT_VERBOSITY:
+      mc_options_set_verbosity (options, atoi (optarg));
+      break;
+
+    case OPT_FAILURE_VERBOSITY:
+      mc_options_set_failure_verbosity (options, atoi (optarg));
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+}
+
+/* Adds the model checker command line options to the specified
+   command line PARSER.  Model checker options parsed from the
+   command line will be applied to OPTIONS. */
+void
+mc_options_register_argv_parser (struct mc_options *options,
+                                 struct argv_parser *parser)
+{
+  argv_parser_add_options (parser, mc_argv_options, N_MC_ARGV_OPTIONS,
+                           mc_parser_option_callback, options);
+}
+
+/* Prints a reference for the model checker command line options
+   to stdout. */
+void
+mc_options_usage (void)
+{
+  fputs (
+    "\nModel checker search algorithm options:\n"
+    "  --strategy=STRATEGY  Basic search strategy.  One of:\n"
+    "                         broad: breadth-first search (default)\n"
+    "                         deep: depth-first search\n"
+    "                         random: randomly ordered search\n"
+    "  --path=#[,#]...      Fixes the exact search path to follow;\n"
+    "                       mutually exclusive with --strategy\n"
+    "  --max-depth=MAX      Limits search depth to MAX.  The initial\n"
+    "                       states are at depth 1.\n"
+    "  --hash-bits=BITS     Use 2**BITS size hash table to avoid\n"
+    "                       duplicate states (0 will disable hashing)\n"
+    "  --seed=SEED          Sets the random number seed\n"
+    "\nModel checker queuing options:\n"
+    "  --queue-limit=N      Limit queue to N states (default: 10000)\n"
+    "  --queue-drop=TYPE    How to drop states when queue overflows:\n"
+    "                         newest: drop most recently added state\n"
+    "                         oldest: drop least recently added state\n"
+    "                         random (default): drop a random state\n"
+    "\nModel checker stop condition options:\n"
+    "  --max-states=N       Stop after visiting N unique states\n"
+    "  --max-errors=N       Stop after N errors (default: 1)\n"
+    "  --time-limit=SECS    Stop after SECS seconds\n"
+    "\nModel checker user interface options:\n"
+    "  --progress=TYPE      Show progress according to TYPE.  One of:\n"
+    "                         none: Do not output progress message\n"
+    "                         dots (default): Output lines of dots\n"
+    "                         fancy: Show a few stats\n"
+    "                         verbose: Show all available stats\n"
+    "  --verbosity=LEVEL    Verbosity level before an error (default: 1)\n"
+    "  --failure-verbosity=LEVEL  Verbosity level for replaying failure\n"
+    "                       cases (default: 2)\n",
+    stdout);
+}
+\f
 /* Results of a model checking run. */
 struct mc_results
   {
@@ -620,7 +861,8 @@ struct mc_results
 
     /* Depth statistics. */
     int max_depth_reached;              /* Max depth state examined. */
-    struct moments1 *depth_moments;     /* Enables reporting mean depth. */
+    unsigned long int depth_sum;        /* Sum of depths. */
+    int n_depths;                       /* Number of depths in depth_sum. */
 
     /* If error_count > 0, path to the last error reported. */
     struct mc_path error_path;
@@ -646,7 +888,6 @@ 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;
 }
@@ -657,7 +898,6 @@ mc_results_destroy (struct mc_results *results)
 {
   if (results != NULL)
     {
-      moments1_destroy (results->depth_moments);
       mc_path_destroy (&results->error_path);
       free (results);
     }
@@ -726,9 +966,9 @@ mc_results_get_max_depth_reached (const struct mc_results *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;
+  return (results->n_depths == 0
+          ? 0
+          : (double) results->depth_sum / results->n_depths);
 }
 
 /* Returns the path traversed to obtain the last error
@@ -857,6 +1097,48 @@ mc_results_get_duration (const struct mc_results *results)
   assert (results->stop_reason != MC_CONTINUING);
   return timeval_subtract (results->end, results->start);
 }
+
+/* Prints a description of RESULTS to stream F. */
+void
+mc_results_print (const struct mc_results *results, FILE *f)
+{
+  enum mc_stop_reason reason = mc_results_get_stop_reason (results);
+
+  if (reason != MC_CONTINUING)
+    fprintf (f, "Stopped by: %s\n",
+             reason == MC_SUCCESS ? "state space exhaustion"
+             : reason == MC_MAX_UNIQUE_STATES ? "reaching max unique states"
+             : reason == MC_MAX_ERROR_COUNT ? "reaching max error count"
+             : reason == MC_END_OF_PATH ? "reached end of specified path"
+             : reason == MC_TIMEOUT ? "reaching time limit"
+             : reason == MC_INTERRUPTED ? "user interruption"
+             : "unknown reason");
+  fprintf (f, "Errors found: %d\n\n", mc_results_get_error_count (results));
+
+  fprintf (f, "Unique states checked: %d\n",
+           mc_results_get_unique_state_count (results));
+  fprintf (f, "Maximum depth reached: %d\n",
+           mc_results_get_max_depth_reached (results));
+  fprintf (f, "Mean depth reached: %.2f\n\n",
+           mc_results_get_mean_depth_reached (results));
+
+  fprintf (f, "Dropped duplicate states: %d\n",
+           mc_results_get_duplicate_dropped_states (results));
+  fprintf (f, "Dropped off-path states: %d\n",
+           mc_results_get_off_path_dropped_states (results));
+  fprintf (f, "Dropped too-deep states: %d\n",
+           mc_results_get_depth_dropped_states (results));
+  fprintf (f, "Dropped queue-overflow states: %d\n",
+           mc_results_get_queue_dropped_states (results));
+  fprintf (f, "Checked states still queued when stopped: %d\n",
+           mc_results_get_queued_unprocessed_states (results));
+  fprintf (f, "Maximum queue length reached: %d\n",
+           mc_results_get_max_queue_length (results));
+
+  if (reason != MC_CONTINUING)
+    fprintf (f, "\nRuntime: %.2f seconds\n",
+             mc_results_get_duration (results));
+}
 \f
 /* An active model checking run. */
 struct mc
@@ -984,7 +1266,7 @@ mc_include_state (struct mc *mc)
 bool
 mc_discard_dup_state (struct mc *mc, unsigned int hash)
 {
-  if (mc->options->hash_bits > 0)
+  if (!mc->state_error && mc->options->hash_bits > 0)
     {
       hash &= (1u << mc->options->hash_bits) - 1;
       if (TEST_BIT (mc->hash, hash))
@@ -1200,7 +1482,8 @@ enqueue_state (struct mc *mc, struct mc_state *new)
 
   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);
+  mc->results->depth_sum += new->path.length;
+  mc->results->n_depths++;
 
   if (deque_count (&mc->queue_deque) < mc->options->queue_limit)
     {
index 8c86fae61d3eb99f1a3956fbaebad6f0a15be0f5..a7372f8d3faf6066435064372c3279eedc970031 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
                   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
+                  if (!state_is_consistent (clone))
+                    mc_error (mc, "inconsistent state");
+                  if (mc_discard_dup_state (mc, hash_foo (clone)))
                     destroy_foo (clone);
+                  else
+                    mc_add_state (mc, clone);
                 }
           }
 
             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.
+            checking for consistency.  Calling it after checking
+            may make checking a given number of unique states
+            take longer, but it also ensures that all paths to a
+            given state produce correct results.
 
           - The mc_error function reports errors.  It may be
             called as many times as desired to report each kind
@@ -418,6 +416,10 @@ 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 *);
+mc_progress_func mc_progress_dots;
+mc_progress_func mc_progress_fancy;
+mc_progress_func mc_progress_verbose;
+
 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 *);
@@ -425,6 +427,11 @@ 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);
+
+struct argv_parser;
+void mc_options_register_argv_parser (struct mc_options *,
+                                      struct argv_parser *);
+void mc_options_usage (void);
 \f
 /* Reason that a model checking run terminated. */
 enum mc_stop_reason
@@ -460,4 +467,6 @@ 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 *);
 
+void mc_results_print (const struct mc_results *, FILE *);
+
 #endif /* libpspp/model-checker.h */
index 88d3535df140e6648f025de4076be66bc466ce06..dbdd71b6cbbf250fbb91248e6c8cc820da13e4f0 100644 (file)
@@ -364,6 +364,19 @@ pool_strdup (struct pool *pool, const char *string)
   return pool_clone_unaligned (pool, string, strlen (string) + 1);
 }
 
+/* Duplicates the SIZE bytes of STRING, plus a trailing 0 byte,
+   and returns a pointer to the duplicate.  For use only with
+   strings, because the returned pointere may not be aligned
+   properly for other types. */
+char *
+pool_strdup0 (struct pool *pool, const char *string, size_t size)
+{
+  char *new_string = pool_alloc_unaligned (pool, size + 1);
+  memcpy (new_string, string, size);
+  new_string[size] = '\0';
+  return new_string;
+}
+
 /* Formats FORMAT with the given ARGS in memory allocated from
    POOL and returns the formatted string. */
 char *
index e6c885e48b3686583d76ecd1413b810ed5fa48ae..fc24b2f9e6ff0ae38221a2ac7f9d3bba60aa8fbf 100644 (file)
@@ -62,6 +62,7 @@ void *pool_clone (struct pool *, const void *, size_t) MALLOC_LIKE;
 void *pool_alloc_unaligned (struct pool *, size_t) MALLOC_LIKE;
 void *pool_clone_unaligned (struct pool *, const void *, size_t) MALLOC_LIKE;
 char *pool_strdup (struct pool *, const char *) MALLOC_LIKE;
+char *pool_strdup0 (struct pool *, const char *, size_t) MALLOC_LIKE;
 char *pool_vasprintf (struct pool *, const char *, va_list)
      MALLOC_LIKE PRINTF_FORMAT (2, 0);
 char *pool_asprintf (struct pool *, const char *, ...)
index c13fc33f618453aa759c01364fc8e67e5f3edb47..efa3b4d670e7e06c3c560ebc64ade1bf4d45ae21 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 <libpspp/assertion.h>
-#include <libpspp/bt.h>
 #include <libpspp/compiler.h>
 #include <libpspp/pool.h>
 
+#include "minmax.h"
 #include "xalloc.h"
 
-/* A set of ranges. */
-struct range_set
-  {
-    struct pool *pool;                  /* Pool for freeing range_set. */
-    struct bt bt;                       /* Tree of range_set_nodes. */
-
-    /* Cache. */
-    unsigned long int cache_start;      /* Start of region. */
-    unsigned long int cache_end;        /* One past end of region. */
-    bool cache_value;                   /* Is the region in the set? */
-  };
-
-/* A node in the range set. */
-struct range_set_node
-  {
-    struct bt_node bt_node;             /* Binary tree node. */
-    unsigned long int start;            /* Start of region. */
-    unsigned long int end;              /* One past end of region. */
-  };
-
-static struct range_set_node *bt_to_rs_node (const struct bt_node *);
 static int compare_range_set_nodes (const struct bt_node *,
                                     const struct bt_node *,
                                     const void *aux);
-static struct range_set_node *first_node (const struct range_set *);
-static struct range_set_node *next_node (const struct range_set *,
-                                         const struct range_set_node *);
 static void delete_node (struct range_set *, struct range_set_node *);
 static struct range_set_node *delete_node_get_next (struct range_set *,
                                                     struct range_set_node *);
@@ -107,7 +83,8 @@ range_set_clone (const struct range_set *old, struct pool *pool)
   struct range_set_node *node;
 
   new = range_set_create_pool (pool);
-  for (node = first_node (old); node != NULL; node = next_node (old, node))
+  for (node = range_set_first__ (old); node != NULL;
+       node = range_set_next__ (old, node))
     insert_node (new, node->start, node->end);
   return new;
 }
@@ -121,7 +98,7 @@ range_set_destroy (struct range_set *rs)
       if (rs->pool != NULL)
         pool_unregister (rs->pool, rs);
       while (!range_set_is_empty (rs))
-        delete_node (rs, first_node (rs));
+        delete_node (rs, range_set_first__ (rs));
       free (rs);
     }
 }
@@ -150,7 +127,7 @@ range_set_insert (struct range_set *rs,
         {
           /* New region does not overlap NODE, but it might
              overlap the next node. */
-          insert_just_before (rs, start, end, next_node (rs, node));
+          insert_just_before (rs, start, end, range_set_next__ (rs, node));
         }
       else if (end > node->end)
         {
@@ -171,7 +148,7 @@ range_set_insert (struct range_set *rs,
     {
       /* New region starts before any existing region, but it
          might overlap the first region. */
-      insert_just_before (rs, start, end, first_node (rs));
+      insert_just_before (rs, start, end, range_set_first__ (rs));
     }
 }
 
@@ -194,7 +171,7 @@ range_set_delete (struct range_set *rs,
 
   node = find_node_le (rs, start);
   if (node == NULL)
-    node = first_node (rs);
+    node = range_set_first__ (rs);
 
   while (node != NULL && end > node->start)
     {
@@ -228,11 +205,11 @@ range_set_delete (struct range_set *rs,
               /* Region to delete covers only right part of
                  node. */
               node->end = start;
-              node = next_node (rs, node);
+              node = range_set_next__ (rs, node);
             }
         }
       else
-        node = next_node (rs, node);
+        node = range_set_next__ (rs, node);
     }
 }
 
@@ -252,7 +229,7 @@ range_set_allocate (struct range_set *rs, unsigned long int request,
 
   assert (request > 0);
 
-  node = first_node (rs);
+  node = range_set_first__ (rs);
   if (node == NULL)
     return false;
   node_width = node->end - node->start;
@@ -274,6 +251,37 @@ range_set_allocate (struct range_set *rs, unsigned long int request,
   return true;
 }
 
+/* Scans RS for and deletes the first contiguous run of REQUEST
+   1-bits.  If successful, sets *START to the position of the
+   first 1-bit deleted and returns true If RS does not contain a
+   run of REQUEST or more contiguous 1-bits, returns false and
+   does not modify RS. */
+bool
+range_set_allocate_fully (struct range_set *rs, unsigned long int request,
+                          unsigned long int *start)
+{
+  struct range_set_node *node;
+
+  assert (request > 0);
+
+  for (node = range_set_first__ (rs); node != NULL;
+       node = range_set_next__ (rs, node))
+    {
+      unsigned long int node_width = node->end - node->start;
+      if (node_width >= request)
+        {
+          *start = node->start;
+          if (node_width > request)
+            node->start += request;
+          else
+            delete_node (rs, node);
+          rs->cache_end = 0;
+          return true;
+        }
+    }
+  return false;
+}
+
 /* Returns true if there is a 1-bit at the given POSITION in RS,
    false otherwise. */
 bool
@@ -296,7 +304,7 @@ range_set_contains (const struct range_set *rs_, unsigned long int position)
             }
           else
             {
-              struct range_set_node *next = next_node (rs, node);
+              struct range_set_node *next = range_set_next__ (rs, node);
               rs->cache_start = node->end;
               rs->cache_end = next != NULL ? next->start : ULONG_MAX;
               rs->cache_value = false;
@@ -305,7 +313,7 @@ range_set_contains (const struct range_set *rs_, unsigned long int position)
         }
       else
         {
-          node = first_node (rs);
+          node = range_set_first__ (rs);
           rs->cache_start = 0;
           rs->cache_end = node != NULL ? node->start : ULONG_MAX;
           rs->cache_value = false;
@@ -314,67 +322,40 @@ range_set_contains (const struct range_set *rs_, unsigned long int position)
     }
 }
 
-/* Returns true if RS contains no 1-bits, false otherwise. */
-bool
-range_set_is_empty (const struct range_set *rs)
-{
-  return bt_count (&rs->bt) == 0;
-}
-
-/* Returns the node representing the first contiguous region of
-   1-bits in RS, or a null pointer if RS is empty.
-   Any call to range_set_insert, range_set_delete, or
-   range_set_allocate invalidates the returned node. */
-const struct range_set_node *
-range_set_first (const struct range_set *rs)
-{
-  return first_node (rs);
-}
-
-/* If NODE is nonnull, returns the node representing the next
-   contiguous region of 1-bits in RS following NODE, or a null
-   pointer if NODE is the last region in RS.
-   If NODE is null, returns the first region in RS, as for
-   range_set_first.
-   Any call to range_set_insert, range_set_delete, or
-   range_set_allocate invalidates the returned node. */
-const struct range_set_node *
-range_set_next (const struct range_set *rs, const struct range_set_node *node)
-{
-  return (node != NULL
-          ? next_node (rs, (struct range_set_node *) node)
-          : first_node (rs));
-}
-
-/* Returns the position of the first 1-bit in NODE. */
-unsigned long int
-range_set_node_get_start (const struct range_set_node *node)
-{
-  return node->start;
-}
-
-/* Returns one past the position of the last 1-bit in NODE. */
+/* Returns the smallest position of a 1-bit greater than or
+   equal to START.  Returns ULONG_MAX if there is no 1-bit with
+   position greater than or equal to START. */
 unsigned long int
-range_set_node_get_end (const struct range_set_node *node)
+range_set_scan (const struct range_set *rs_, unsigned long int start)
 {
-  return node->end;
-}
+  struct range_set *rs = (struct range_set *) rs_;
+  unsigned long int retval = ULONG_MAX;
+  struct bt_node *bt_node;
 
-/* Returns the number of contiguous 1-bits in NODE. */
-unsigned long int
-range_set_node_get_width (const struct range_set_node *node)
-{
-  return node->end - node->start;
+  if (start < rs->cache_end && start >= rs->cache_start && rs->cache_value)
+    return start;
+  bt_node = rs->bt.root;
+  while (bt_node != NULL)
+    {
+      struct range_set_node *node = range_set_node_from_bt__ (bt_node);
+      if (start < node->start)
+        {
+          retval = node->start;
+          bt_node = node->bt_node.down[0];
+        }
+      else if (start >= node->end)
+        bt_node = node->bt_node.down[1];
+      else
+        {
+          rs->cache_start = node->start;
+          rs->cache_end = node->end;
+          rs->cache_value = true;
+          return start;
+        }
+    }
+  return retval;
 }
 \f
-/* Returns the range_set_node corresponding to the given
-   BT_NODE.  Returns a null pointer if BT_NODE is null. */
-static struct range_set_node *
-bt_to_rs_node (const struct bt_node *bt_node)
-{
-  return bt_node ? bt_data (bt_node, struct range_set_node, bt_node) : NULL;
-}
-
 /* Compares `range_set_node's A and B and returns a strcmp-like
    return value. */
 static int
@@ -382,28 +363,12 @@ compare_range_set_nodes (const struct bt_node *a_,
                          const struct bt_node *b_,
                          const void *aux UNUSED)
 {
-  const struct range_set_node *a = bt_to_rs_node (a_);
-  const struct range_set_node *b = bt_to_rs_node (b_);
+  const struct range_set_node *a = range_set_node_from_bt__ (a_);
+  const struct range_set_node *b = range_set_node_from_bt__ (b_);
 
   return a->start < b->start ? -1 : a->start > b->start;
 }
 
-/* Returns the first range_set_node in RS,
-   or a null pointer if RS is empty. */
-static struct range_set_node *
-first_node (const struct range_set *rs)
-{
-  return bt_to_rs_node (bt_first (&rs->bt));
-}
-
-/* Returns the next range_set_node in RS after NODE,
-   or a null pointer if NODE is the last node in RS. */
-static struct range_set_node *
-next_node (const struct range_set *rs, const struct range_set_node *node)
-{
-  return bt_to_rs_node (bt_next (&rs->bt, &node->bt_node));
-}
-
 /* Deletes NODE from RS and frees it. */
 static void
 delete_node (struct range_set *rs, struct range_set_node *node)
@@ -417,7 +382,7 @@ delete_node (struct range_set *rs, struct range_set_node *node)
 static struct range_set_node *
 delete_node_get_next (struct range_set *rs, struct range_set_node *node)
 {
-  struct range_set_node *next = next_node (rs, node);
+  struct range_set_node *next = range_set_next__ (rs, node);
   delete_node (rs, node);
   return next;
 }
@@ -432,7 +397,7 @@ find_node_le (const struct range_set *rs, unsigned long int position)
 {
   struct range_set_node tmp;
   tmp.start = position;
-  return bt_to_rs_node (bt_find_le (&rs->bt, &tmp.bt_node));
+  return range_set_node_from_bt__ (bt_find_le (&rs->bt, &tmp.bt_node));
 }
 
 /* Creates a new region with the given START and END, inserts the
@@ -485,7 +450,8 @@ merge_node_with_successors (struct range_set *rs, struct range_set_node *node)
 {
   struct range_set_node *next;
 
-  while ((next = next_node (rs, node)) != NULL && node->end >= next->start)
+  while ((next = range_set_next__ (rs, node)) != NULL
+         && node->end >= next->start)
     {
       if (next->end > node->end)
         node->end = next->end;
index 83e5db6f8181d2097f14f60b88d3f1ed7b5800bc..941692b40029098ea8224ea4d0a4f0e1b9026385 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
 #define LIBPSPP_RANGE_SET_H
 
 #include <stdbool.h>
+#include <libpspp/bt.h>
 
-struct pool;
+/* A set of ranges. */
+struct range_set
+  {
+    struct pool *pool;                  /* Pool for freeing range_set. */
+    struct bt bt;                       /* Tree of range_set_nodes. */
+
+    /* Cache. */
+    unsigned long int cache_start;      /* Start of region. */
+    unsigned long int cache_end;        /* One past end of region. */
+    bool cache_value;                   /* Is the region in the set? */
+  };
+
+/* A node in the range set. */
+struct range_set_node
+  {
+    struct bt_node bt_node;             /* Binary tree node. */
+    unsigned long int start;            /* Start of region. */
+    unsigned long int end;              /* One past end of region. */
+  };
 
 struct range_set *range_set_create (void);
 struct range_set *range_set_create_pool (struct pool *);
@@ -39,15 +58,162 @@ void range_set_delete (struct range_set *,
                        unsigned long int start, unsigned long int width);
 bool range_set_allocate (struct range_set *, unsigned long int request,
                          unsigned long int *start, unsigned long int *width);
+bool range_set_allocate_fully (struct range_set *, unsigned long int request,
+                               unsigned long int *start);
 bool range_set_contains (const struct range_set *, unsigned long int position);
+unsigned long int range_set_scan (const struct range_set *,
+                                  unsigned long int start);
+
+static inline bool range_set_is_empty (const struct range_set *);
+
+static inline const struct range_set_node *range_set_first (
+  const struct range_set *);
+static inline const struct range_set_node *range_set_next (
+  const struct range_set *, const struct range_set_node *);
+static inline const struct range_set_node *range_set_last (
+  const struct range_set *);
+static inline const struct range_set_node *range_set_prev (
+  const struct range_set *, const struct range_set_node *);
+static inline unsigned long int range_set_node_get_start (
+  const struct range_set_node *);
+static inline unsigned long int range_set_node_get_end (
+  const struct range_set_node *);
+static inline unsigned long int range_set_node_get_width (
+  const struct range_set_node *);
+\f
+/* Inline functions. */
+
+static inline struct range_set_node *range_set_node_from_bt__ (
+  const struct bt_node *);
+static inline struct range_set_node *range_set_next__ (
+  const struct range_set *, const struct range_set_node *);
+static inline struct range_set_node *range_set_first__ (
+  const struct range_set *);
+static inline struct range_set_node *range_set_prev__ (
+  const struct range_set *, const struct range_set_node *);
+static inline struct range_set_node *range_set_last__ (
+  const struct range_set *);
+
+/* Returns true if RS contains no 1-bits, false otherwise. */
+static inline bool
+range_set_is_empty (const struct range_set *rs)
+{
+  return bt_count (&rs->bt) == 0;
+}
+
+/* Returns the node representing the first contiguous region of
+   1-bits in RS, or a null pointer if RS is empty.
+   Any call to range_set_insert, range_set_delete, or
+   range_set_allocate invalidates the returned node. */
+static inline const struct range_set_node *
+range_set_first (const struct range_set *rs)
+{
+  return range_set_first__ (rs);
+}
+
+/* If NODE is nonnull, returns the node representing the next
+   contiguous region of 1-bits in RS following NODE, or a null
+   pointer if NODE is the last region in RS.
+   If NODE is null, returns the first region in RS, as for
+   range_set_first.
+   Any call to range_set_insert, range_set_delete, or
+   range_set_allocate invalidates the returned node. */
+static inline const struct range_set_node *
+range_set_next (const struct range_set *rs, const struct range_set_node *node)
+{
+  return (node != NULL
+          ? range_set_next__ (rs, (struct range_set_node *) node)
+          : range_set_first__ (rs));
+}
+
+/* Returns the node representing the last contiguous region of
+   1-bits in RS, or a null pointer if RS is empty.
+   Any call to range_set_insert, range_set_delete, or
+   range_set_allocate invalidates the returned node. */
+static inline const struct range_set_node *
+range_set_last (const struct range_set *rs)
+{
+  return range_set_last__ (rs);
+}
+
+/* If NODE is nonnull, returns the node representing the previous
+   contiguous region of 1-bits in RS following NODE, or a null
+   pointer if NODE is the first region in RS.
+   If NODE is null, returns the last region in RS, as for
+   range_set_last.
+   Any call to range_set_insert, range_set_delete, or
+   range_set_allocate invalidates the returned node. */
+static inline const struct range_set_node *
+range_set_prev (const struct range_set *rs, const struct range_set_node *node)
+{
+  return (node != NULL
+          ? range_set_prev__ (rs, (struct range_set_node *) node)
+          : range_set_last__ (rs));
+}
+
+/* Returns the position of the first 1-bit in NODE. */
+static inline unsigned long int
+range_set_node_get_start (const struct range_set_node *node)
+{
+  return node->start;
+}
+
+/* Returns one past the position of the last 1-bit in NODE. */
+static inline unsigned long int
+range_set_node_get_end (const struct range_set_node *node)
+{
+  return node->end;
+}
+
+/* Returns the number of contiguous 1-bits in NODE. */
+static inline unsigned long int
+range_set_node_get_width (const struct range_set_node *node)
+{
+  return node->end - node->start;
+}
+\f
+/* Internal helper functions. */
+
+/* Returns the range_set_node corresponding to the given
+   BT_NODE.  Returns a null pointer if BT_NODE is null. */
+static inline struct range_set_node *
+range_set_node_from_bt__ (const struct bt_node *bt_node)
+{
+  return bt_node ? bt_data (bt_node, struct range_set_node, bt_node) : NULL;
+}
+
+/* Returns the next range_set_node in RS after NODE,
+   or a null pointer if NODE is the last node in RS. */
+static inline struct range_set_node *
+range_set_next__ (const struct range_set *rs,
+                  const struct range_set_node *node)
+{
+  return range_set_node_from_bt__ (bt_next (&rs->bt, &node->bt_node));
+}
+
+/* Returns the first range_set_node in RS,
+   or a null pointer if RS is empty. */
+static inline struct range_set_node *
+range_set_first__ (const struct range_set *rs)
+{
+  return range_set_node_from_bt__ (bt_first (&rs->bt));
+}
 
-bool range_set_is_empty (const struct range_set *);
+/* Returns the previous range_set_node in RS after NODE,
+   or a null pointer if NODE is the first node in RS. */
+static inline struct range_set_node *
+range_set_prev__ (const struct range_set *rs,
+                  const struct range_set_node *node)
+{
+  return range_set_node_from_bt__ (bt_prev (&rs->bt, &node->bt_node));
+}
 
-const struct range_set_node *range_set_first (const struct range_set *);
-const struct range_set_node *range_set_next (const struct range_set *,
-                                             const struct range_set_node *);
-unsigned long int range_set_node_get_start (const struct range_set_node *);
-unsigned long int range_set_node_get_end (const struct range_set_node *);
-unsigned long int range_set_node_get_width (const struct range_set_node *);
+/* Returns the last range_set_node in RS,
+   or a null pointer if RS is empty. */
+static inline struct range_set_node *
+range_set_last__ (const struct range_set *rs)
+{
+  return range_set_node_from_bt__ (bt_last (&rs->bt));
+}
 
 #endif /* libpspp/range-set.h */
index eba5d8327a2f0b8e9202c8e3244d8b90ac17cd89..28398d5cc86321e9e648a9c0ac30c052c1d26641 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
@@ -25,6 +25,9 @@
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 
+#include "count-one-bits.h"
+#include "minmax.h"
+
 /* Sparse array data structure.
 
    The sparse array is implemented in terms of a "radix tree", a
@@ -48,6 +51,7 @@
 
 /* Maximum height. */
 #define LONG_BITS (sizeof (unsigned long int) * CHAR_BIT)
+#define LONG_MASK ((unsigned long int) LONG_BITS - 1)
 #define MAX_HEIGHT DIV_RND_UP (LONG_BITS, BITS_PER_LEVEL)
 
 /* Bit-mask for index. */
@@ -88,7 +92,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. */
   };
 
@@ -113,7 +117,6 @@ static inline bool is_in_use (const struct leaf_node *, unsigned int);
 static inline bool any_in_use (const struct leaf_node *);
 static inline void set_in_use (struct leaf_node *, unsigned int);
 static inline void unset_in_use (struct leaf_node *, unsigned int);
-static inline int scan_in_use (struct leaf_node *, unsigned int);
 static inline void *leaf_element (const struct sparse_array *,
                                   struct leaf_node *, unsigned int);
 static inline size_t leaf_size (const struct sparse_array *);
@@ -121,10 +124,20 @@ static inline size_t leaf_size (const struct sparse_array *);
 static struct leaf_node *find_leaf_node (const struct sparse_array *,
                                          unsigned long int);
 static void decrease_height (struct sparse_array *);
-static void *scan_leaf (struct sparse_array *, struct leaf_node *,
-                        unsigned long int, unsigned long int *);
-static void *do_scan (struct sparse_array *, union pointer *, int,
-                      unsigned long int, unsigned long int *);
+
+static int scan_in_use_forward (struct leaf_node *, unsigned int);
+static void *do_scan_forward (struct sparse_array *, union pointer *, int,
+                              unsigned long int, unsigned long int *);
+static void *scan_forward (const struct sparse_array *,
+                           unsigned long int start,
+                           unsigned long int *found);
+
+static int scan_in_use_reverse (struct leaf_node *, unsigned int);
+static void *do_scan_reverse (struct sparse_array *, union pointer *, int,
+                              unsigned long int, unsigned long int *);
+static void *scan_reverse (const struct sparse_array *,
+                           unsigned long int start,
+                           unsigned long int *found);
 \f
 /* Creates and returns a new sparse array that will contain
    elements that are ELEM_SIZE bytes in size. */
@@ -281,12 +294,9 @@ sparse_array_insert (struct sparse_array *spar, unsigned long int key)
 void *
 sparse_array_get (const struct sparse_array *spar, unsigned long int key)
 {
-  if (index_in_range (spar, key))
-    {
-      struct leaf_node *leaf = find_leaf_node (spar, key);
-      if (leaf != NULL && is_in_use (leaf, key))
-        return leaf_element (spar, leaf, key);
-    }
+  struct leaf_node *leaf = find_leaf_node (spar, key);
+  if (leaf != NULL && is_in_use (leaf, key))
+    return leaf_element (spar, leaf, key);
   return NULL;
 }
 
@@ -306,9 +316,6 @@ sparse_array_remove (struct sparse_array *spar, unsigned long int key)
   union pointer *p;
   int level;
 
-  if (!index_in_range (spar, key))
-    return false;
-
   /* Find and free element in leaf. */
   leaf = find_leaf_node (spar, key);
   if (leaf == NULL || !is_in_use (leaf, key))
@@ -355,46 +362,46 @@ sparse_array_remove (struct sparse_array *spar, unsigned long int key)
   return true;
 }
 
-/* Scans SPAR in increasing order of keys for in-use elements.
-   If SKIP is NULL, the scan starts from key 0;
-   otherwise, it starts just after key *SKIP.
-   If an element is found, returns it and stores the element's
-   key into *FOUND; otherwise, returns a null pointer and does
-   not modify *FOUND. */
+/* Returns a pointer to the in-use element with the smallest
+   index and sets *IDXP to its index or, if SPAR has no in-use
+   elements, returns NULL without modifying *IDXP. */
 void *
-sparse_array_scan (const struct sparse_array *spar_, unsigned long int *skip,
-                   unsigned long int *found)
+sparse_array_first (const struct sparse_array *spar, unsigned long int *idxp)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
-  unsigned long int start;
+  return scan_forward (spar, 0, idxp);
+}
 
-  /* Find our starting point. */
-  if (skip != NULL)
-    {
-      start = *skip + 1;
-      if (start == 0)
-        return NULL;
-    }
-  else
-    start = 0;
+/* Returns a pointer to the in-use element with the smallest
+   index greater than SKIP and sets *IDXP to its index or, if
+   SPAR has no in-use elements with index greater than SKIP,
+   returns NULL without modifying *IDXP. */
+void *
+sparse_array_next (const struct sparse_array *spar, unsigned long int skip,
+                   unsigned long int *idxp)
+{
+  return skip < ULONG_MAX ? scan_forward (spar, skip + 1, idxp) : NULL;
+}
 
-  /* Check the cache. */
-  if (start >> BITS_PER_LEVEL == spar->cache_ofs)
-    {
-      void *p = scan_leaf (spar, spar->cache, start, found);
-      if (p)
-        return p;
-      start &= ~LEVEL_MASK;
-      start += PTRS_PER_LEVEL;
-      if (start == 0)
-        return NULL;
-    }
+/* Returns a pointer to the in-use element with the greatest
+   index and sets *IDXP to its index or, if SPAR has no in-use
+   elements, returns NULL without modifying *IDXP. */
+void *
+sparse_array_last (const struct sparse_array *spar, unsigned long int *idxp)
+{
+  return scan_reverse (spar, ULONG_MAX, idxp);
+}
 
-  /* Do the scan. */
-  if (!index_in_range (spar, start))
-    return NULL;
-  return do_scan (spar, &spar->root, spar->height - 1, start, found);
+/* Returns a pointer to the in-use element with the greatest
+   index less than SKIP and sets *IDXP to its index or, if SPAR
+   has no in-use elements with index less than SKIP, returns NULL
+   without modifying *IDXP. */
+void *
+sparse_array_prev (const struct sparse_array *spar, unsigned long int skip,
+                   unsigned long int *idxp)
+{
+  return skip > 0 ? scan_reverse (spar, skip - 1, idxp) : NULL;
 }
+
 \f
 /* Returns true iff KEY is in the range of keys currently
    represented by SPAR. */
@@ -447,6 +454,9 @@ unset_in_use (struct leaf_node *leaf, unsigned int key)
 static inline int
 count_trailing_zeros (unsigned long int x)
 {
+#if __GNUC__ >= 4
+  return __builtin_ctzl (x);
+#else /* not GCC 4+ */
   /* This algorithm is from _Hacker's Delight_ section 5.4. */
   int n = 1;
 
@@ -469,24 +479,69 @@ count_trailing_zeros (unsigned long int x)
   COUNT_STEP (2);
 
   return n - (x & 1);
+#endif /* not GCC 4+ */
 }
 
 /* Returns the least index of the in-use element in LEAF greater
-   than or equal to IDX. */
-static inline int
-scan_in_use (struct leaf_node *leaf, unsigned int idx)
+   than or equal to IDX.  Bits in IDX not in LEVEL_MASK are
+   considered to have value 0. */
+static int
+scan_in_use_forward (struct leaf_node *leaf, unsigned int idx)
 {
-  for (; idx < PTRS_PER_LEVEL; idx = (idx & ~(LONG_BITS - 1)) + LONG_BITS)
+  idx &= LEVEL_MASK;
+  for (; idx < PTRS_PER_LEVEL; idx = (idx & ~LONG_MASK) + LONG_BITS)
     {
       int ofs = idx % LONG_BITS;
       unsigned long int in_use = leaf->in_use[idx / LONG_BITS] >> ofs;
       if (!in_use)
         continue;
-      return count_trailing_zeros (in_use) + idx;
+      return idx + count_trailing_zeros (in_use);
     }
   return -1;
 }
 
+/* Returns the number of leading 0-bits in X.
+   Undefined if X is zero. */
+static inline int
+count_leading_zeros (unsigned long int x)
+{
+#if __GNUC__ >= 4
+  return __builtin_clzl (x);
+#else
+  x |= x >> 1;
+  x |= x >> 2;
+  x |= x >> 4;
+  x |= x >> 8;
+  x |= x >> 16;
+#if ULONG_MAX >> 31 >> 1
+  x |= x >> 32;
+#endif
+#if ULONG_MAX >> 31 >> 31 >> 2
+  x |= x >> 64;
+#endif
+  return LONG_BITS - count_one_bits_l (x);
+#endif
+}
+
+/* Returns the greatest index of the in-use element in LEAF less
+   than or equal to IDX.  Bits in IDX not in LEVEL_MASK are
+   considered to have value 0. */
+static int
+scan_in_use_reverse (struct leaf_node *leaf, unsigned int idx)
+{
+  idx &= LEVEL_MASK;
+  for (;;)
+    {
+      int ofs = idx % LONG_BITS;
+      unsigned long int in_use = leaf->in_use[idx / LONG_BITS] << (31 - ofs);
+      if (in_use)
+        return idx - count_leading_zeros (in_use);
+      if (idx < LONG_BITS)
+        return -1;
+      idx = (idx | LONG_MASK) - LONG_BITS;
+    }
+}
+
 /* Returns the address of element with the given KEY in LEAF,
    which is a node in SPAR. */
 static inline void *
@@ -497,6 +552,18 @@ leaf_element (const struct sparse_array *spar, struct leaf_node *leaf,
   return (char *) leaf + SIZEOF_ALIGNED (*leaf) + (spar->elem_size * key);
 }
 
+/* As leaf_element, returns the address of element with the given
+   KEY in LEAF, which is a node in SPAR.  Also, updates SPAR's
+   cache to contain LEAF. */
+static void *
+cache_leaf_element (struct sparse_array *spar, struct leaf_node *leaf,
+                    unsigned long int key)
+{
+  spar->cache = leaf;
+  spar->cache_ofs = key >> BITS_PER_LEVEL;
+  return leaf_element (spar, leaf, key);
+}
+
 /* Returns the size of a leaf node in SPAR. */
 static inline size_t
 leaf_size (const struct sparse_array *spar)
@@ -514,12 +581,13 @@ find_leaf_node (const struct sparse_array *spar_, unsigned long int key)
   const union pointer *p;
   int level;
 
-  assert (index_in_range (spar, key));
-
   /* Check the cache first. */
   if (key >> BITS_PER_LEVEL == spar->cache_ofs)
     return spar->cache;
 
+  if (!index_in_range (spar, key))
+    return NULL;
+
   /* Descend through internal nodes. */
   p = &spar->root;
   for (level = spar->height - 1; level > 0; level--)
@@ -553,24 +621,36 @@ decrease_height (struct sparse_array *spar)
     }
 }
 
-/* Scans leaf node LEAF, which is in SPAR, for the in-use element
-   with the least key greater than or equal to START.  If such an
-   element is found, returns a pointer to it and stores its key
-   in *FOUND; otherwise, returns a null pointer and does not
-   modify *FOUND. */
-static void *
-scan_leaf (struct sparse_array *spar, struct leaf_node *leaf,
-           unsigned long int start, unsigned long int *found)
+/* Scans P, which is at LEVEL and within SPAR, and its subnodes,
+   for the in-use element with the least key greater than or
+   equal to START.  If such an element is found, returns a
+   pointer to it and stores its key in *FOUND; otherwise, returns
+   a null pointer and does not modify *FOUND. */
+static inline void *
+scan_internal_node_forward (struct sparse_array *spar,
+                            struct internal_node *node,
+                            int level, unsigned long int start,
+                            unsigned long int *found)
 {
-  int idx = scan_in_use (leaf, start & LEVEL_MASK);
-  if (idx >= 0)
+  int shift = level * BITS_PER_LEVEL;
+  int count = node->count;
+  int i;
+
+  for (i = (start >> shift) & LEVEL_MASK; i < PTRS_PER_LEVEL; i++)
     {
-      *found = (start & ~LEVEL_MASK) | idx;
-      spar->cache = leaf;
-      spar->cache_ofs = *found >> BITS_PER_LEVEL;
-      return leaf_element (spar, leaf, idx);
-    }
+      union pointer *q = &node->down[i];
+      if (level > 1 ? q->internal != NULL : q->leaf != NULL)
+        {
+          void *element = do_scan_forward (spar, q, level - 1, start, found);
+          if (element)
+            return element;
+          if (--count == 0)
+            return NULL;
+        }
 
+      start &= ~((1ul << shift) - 1);
+      start += 1ul << shift;
+    }
   return NULL;
 }
 
@@ -579,44 +659,134 @@ scan_leaf (struct sparse_array *spar, struct leaf_node *leaf,
    equal to START.  If such an element is found, returns a
    pointer to it and stores its key in *FOUND; otherwise, returns
    a null pointer and does not modify *FOUND. */
+static void *
+do_scan_forward (struct sparse_array *spar, union pointer *p, int level,
+                 unsigned long int start, unsigned long int *found)
+{
+  if (level == 0)
+    {
+      int idx = scan_in_use_forward (p->leaf, start);
+      if (idx >= 0)
+        {
+          unsigned long int key = *found = (start & ~LEVEL_MASK) | idx;
+          return cache_leaf_element (spar, p->leaf, key);
+        }
+    }
+  return scan_internal_node_forward (spar, p->internal, level, start, found);
+}
+
+static void *
+scan_forward (const struct sparse_array *spar_, unsigned long int start,
+              unsigned long int *found)
+{
+  struct sparse_array *spar = (struct sparse_array *) spar_;
+
+  /* Check the cache. */
+  if (start >> BITS_PER_LEVEL == spar->cache_ofs)
+    {
+      int idx = scan_in_use_forward (spar->cache, start);
+      if (idx >= 0)
+        {
+          *found = (start & ~LEVEL_MASK) | idx;
+          return leaf_element (spar, spar->cache, idx);
+        }
+      start = (start & ~LEVEL_MASK) + PTRS_PER_LEVEL;
+      if (start == 0)
+        return NULL;
+    }
+
+  /* Do the scan. */
+  if (!index_in_range (spar, start))
+    return NULL;
+  return do_scan_forward (spar, &spar->root, spar->height - 1, start, found);
+}
+
+/* Scans P, which is at LEVEL and within SPAR, and its subnodes,
+   for the in-use element with the greatest key less than or
+   equal to START.  If such an element is found, returns a
+   pointer to it and stores its key in *FOUND; otherwise, returns
+   a null pointer and does not modify *FOUND. */
 static inline void *
-scan_internal_node (struct sparse_array *spar, struct internal_node *node,
-                    int level, unsigned long int start,
-                    unsigned long int *found)
+scan_internal_node_reverse (struct sparse_array *spar,
+                            struct internal_node *node,
+                            int level, unsigned long int start,
+                            unsigned long int *found)
 {
   int shift = level * BITS_PER_LEVEL;
   int count = node->count;
   int i;
 
-  for (i = (start >> shift) & LEVEL_MASK; i < PTRS_PER_LEVEL; i++)
+  for (i = (start >> shift) & LEVEL_MASK; i >= 0; i--)
     {
       union pointer *q = &node->down[i];
       if (level > 1 ? q->internal != NULL : q->leaf != NULL)
         {
-          void *element = do_scan (spar, q, level - 1, start, found);
+          void *element = do_scan_reverse (spar, q, level - 1, start, found);
           if (element)
             return element;
           if (--count == 0)
             return NULL;
         }
 
-      start &= ~((1ul << shift) - 1);
-      start += 1ul << shift;
+      start |= (1ul << shift) - 1;
+      start -= 1ul << shift;
     }
   return NULL;
 }
 
 /* Scans P, which is at LEVEL and within SPAR, and its subnodes,
-   for the in-use element with the least key greater than or
+   for the in-use element with the greatest key less than or
    equal to START.  If such an element is found, returns a
    pointer to it and stores its key in *FOUND; otherwise, returns
    a null pointer and does not modify *FOUND. */
 static void *
-do_scan (struct sparse_array *spar, union pointer *p, int level,
-         unsigned long int start, unsigned long int *found)
+do_scan_reverse (struct sparse_array *spar, union pointer *p, int level,
+                 unsigned long int start, unsigned long int *found)
 {
-  return (level == 0
-          ? scan_leaf (spar, p->leaf, start, found)
-          : scan_internal_node (spar, p->internal, level, start, found));
+  if (level == 0)
+    {
+      int idx = scan_in_use_reverse (p->leaf, start);
+      if (idx >= 0)
+        {
+          unsigned long int key = *found = (start & ~LEVEL_MASK) | idx;
+          return cache_leaf_element (spar, p->leaf, key);
+        }
+      else
+        return NULL;
+    }
+  return scan_internal_node_reverse (spar, p->internal, level, start, found);
 }
 
+static void *
+scan_reverse (const struct sparse_array *spar_, unsigned long int start,
+              unsigned long int *found)
+{
+  struct sparse_array *spar = (struct sparse_array *) spar_;
+
+  /* Check the cache. */
+  if (start >> BITS_PER_LEVEL == spar->cache_ofs)
+    {
+      int idx = scan_in_use_reverse (spar->cache, start);
+      if (idx >= 0)
+        {
+          *found = (start & ~LEVEL_MASK) | idx;
+          return leaf_element (spar, spar->cache, idx);
+        }
+      start |= LEVEL_MASK;
+      if (start < PTRS_PER_LEVEL)
+        return NULL;
+      start -= PTRS_PER_LEVEL;
+    }
+  else
+    {
+      if (spar->height == 0)
+        return NULL;
+      else if (spar->height < MAX_HEIGHT)
+        {
+          unsigned long int max_key;
+          max_key = (1ul << (spar->height * BITS_PER_LEVEL)) - 1;
+          start = MIN (start, max_key);
+        }
+    }
+  return do_scan_reverse (spar, &spar->root, spar->height - 1, start, found);
+}
index e8be2e182bc37fa48fd72964ef52c9d4be58db4b..56360cabc0c47c4f11eb52f17af8fe91b5f088a1 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
@@ -49,8 +49,13 @@ void *sparse_array_insert (struct sparse_array *, unsigned long int key);
 void *sparse_array_get (const struct sparse_array *, unsigned long int key);
 bool sparse_array_remove (struct sparse_array *, unsigned long int key);
 
-void *sparse_array_scan (const struct sparse_array *,
-                         unsigned long int *skip,
-                         unsigned long int *key);
+void *sparse_array_first (const struct sparse_array *,
+                          unsigned long int *idxp);
+void *sparse_array_next (const struct sparse_array *,
+                         unsigned long int skip, unsigned long int *idxp);
+void *sparse_array_last (const struct sparse_array *,
+                          unsigned long int *idxp);
+void *sparse_array_prev (const struct sparse_array *,
+                         unsigned long int skip, unsigned long int *idxp);
 
 #endif /* libpspp/sparse-array.h */
diff --git a/src/libpspp/sparse-xarray.c b/src/libpspp/sparse-xarray.c
new file mode 100644 (file)
index 0000000..893fe39
--- /dev/null
@@ -0,0 +1,609 @@
+/* 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 <libpspp/sparse-xarray.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/misc.h>
+#include <libpspp/range-set.h>
+#include <libpspp/sparse-array.h>
+#include <libpspp/tmpfile.h>
+
+#include "md4.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+/* A sparse array of arrays of bytes. */
+struct sparse_xarray
+  {
+    size_t n_bytes;                     /* Number of bytes per row. */
+    uint8_t *default_row;               /* Defaults for unwritten rows. */
+    unsigned long int max_memory_rows;  /* Max rows before dumping to disk. */
+    struct sparse_array *memory;        /* Backing, if stored in memory. */
+    struct tmpfile *disk;               /* Backing, if stored on disk. */
+    struct range_set *disk_rows;        /* Allocated rows, if on disk. */
+  };
+
+static bool UNUSED
+range_is_valid (const struct sparse_xarray *sx, size_t ofs, size_t n)
+{
+  return n <= sx->n_bytes && ofs <= sx->n_bytes && ofs + n <= sx->n_bytes;
+}
+
+/* Creates and returns a new sparse array of arrays of bytes.
+   Each row in the sparse array will consist of N_BYTES bytes.
+   If fewer than MAX_MEMORY_ROWS rows are added to the array,
+   then it will be kept in memory; if more than that are added,
+   then it will be stored on disk. */
+struct sparse_xarray *
+sparse_xarray_create (size_t n_bytes, size_t max_memory_rows)
+{
+  struct sparse_xarray *sx = xmalloc (sizeof *sx);
+  sx->n_bytes = n_bytes;
+  sx->default_row = xzalloc (n_bytes);
+  sx->max_memory_rows = max_memory_rows;
+  sx->memory = sparse_array_create (sizeof (uint8_t *));
+  sx->disk = NULL;
+  sx->disk_rows = NULL;
+  return sx;
+}
+
+/* Creates and returns a new sparse array of rows that contains
+   the same data as OLD.  Returns a null pointer if cloning
+   fails. */
+struct sparse_xarray *
+sparse_xarray_clone (const struct sparse_xarray *old)
+{
+  struct sparse_xarray *new = xmalloc (sizeof *new);
+
+  new->n_bytes = old->n_bytes;
+
+  new->default_row = xmemdup (old->default_row, old->n_bytes);
+
+  new->max_memory_rows = old->max_memory_rows;
+
+  if (old->memory != NULL)
+    {
+      unsigned long int idx;
+      uint8_t **old_row;
+
+      new->memory = sparse_array_create (sizeof (uint8_t *));
+      for (old_row = sparse_array_first (old->memory, &idx); old_row != NULL;
+           old_row = sparse_array_next (old->memory, idx, &idx))
+        {
+          uint8_t **new_row = sparse_array_insert (new->memory, idx);
+          *new_row = xmemdup (*old_row, new->n_bytes);
+        }
+    }
+  else
+    new->memory = NULL;
+
+  if (old->disk != NULL)
+    {
+      const struct range_set_node *node;
+      void *tmp = xmalloc (old->n_bytes);
+
+      new->disk = tmpfile_create ();
+      new->disk_rows = range_set_clone (old->disk_rows, NULL);
+      for (node = range_set_first (old->disk_rows); node != NULL;
+           node = range_set_next (old->disk_rows, node))
+        {
+          unsigned long int start = range_set_node_get_start (node);
+          unsigned long int end = range_set_node_get_end (node);
+          unsigned long int idx;
+
+          for (idx = start; idx < end; idx++)
+            {
+              off_t offset = (off_t) idx * old->n_bytes;
+              if (!tmpfile_read (old->disk, offset, old->n_bytes, tmp)
+                  || !tmpfile_write (new->disk, offset, old->n_bytes, tmp))
+                {
+                  free (tmp);
+                  sparse_xarray_destroy (new);
+                  return NULL;
+                }
+            }
+        }
+      free (tmp);
+    }
+  else
+    {
+      new->disk = NULL;
+      new->disk_rows = NULL;
+    }
+
+  return new;
+}
+
+/* Destroys sparse array of rows SX. */
+void
+sparse_xarray_destroy (struct sparse_xarray *sx)
+{
+  if (sx != NULL)
+    {
+      free (sx->default_row);
+      if (sx->memory != NULL)
+        {
+          unsigned long int idx;
+          uint8_t **row;
+          for (row = sparse_array_first (sx->memory, &idx); row != NULL;
+               row = sparse_array_next (sx->memory, idx, &idx))
+            free (*row);
+          sparse_array_destroy (sx->memory);
+        }
+      tmpfile_destroy (sx->disk);
+      range_set_destroy (sx->disk_rows);
+      free (sx);
+    }
+}
+
+/* Returns the number of bytes in each row in SX. */
+size_t
+sparse_xarray_get_n_columns (const struct sparse_xarray *sx)
+{
+  return sx->n_bytes;
+}
+
+/* Returns the number of rows in SX. */
+size_t
+sparse_xarray_get_n_rows (const struct sparse_xarray *sx)
+{
+  if (sx->memory)
+    {
+      unsigned long int idx;
+      return sparse_array_last (sx->memory, &idx) != NULL ? idx + 1 : 0;
+    }
+  else
+    {
+      const struct range_set_node *last = range_set_last (sx->disk_rows);
+      return last != NULL ? range_set_node_get_end (last) : 0;
+    }
+}
+
+/* Dumps the rows in SX, which must currently be stored in
+   memory, to disk.  Returns true if successful, false on I/O
+   error. */
+static bool
+dump_sparse_xarray_to_disk (struct sparse_xarray *sx)
+{
+  unsigned long int idx;
+  uint8_t **row;
+
+  assert (sx->memory != NULL);
+  assert (sx->disk == NULL);
+
+  sx->disk = tmpfile_create ();
+  sx->disk_rows = range_set_create ();
+
+  for (row = sparse_array_first (sx->memory, &idx); row != NULL;
+       row = sparse_array_next (sx->memory, idx, &idx))
+    {
+      if (!tmpfile_write (sx->disk, (off_t) idx * sx->n_bytes, sx->n_bytes,
+                          *row))
+        {
+          tmpfile_destroy (sx->disk);
+          sx->disk = NULL;
+          range_set_destroy (sx->disk_rows);
+          sx->disk_rows = NULL;
+          return false;
+        }
+      range_set_insert (sx->disk_rows, idx, 1);
+    }
+  sparse_array_destroy (sx->memory);
+  sx->memory = NULL;
+  return true;
+}
+
+/* Returns true if any data has ever been written to ROW in SX,
+   false otherwise. */
+bool
+sparse_xarray_contains_row (const struct sparse_xarray *sx,
+                            unsigned long int row)
+{
+  return (sx->memory != NULL
+          ? sparse_array_get (sx->memory, row) != NULL
+          : range_set_contains (sx->disk_rows, row));
+}
+
+/* Reads columns COLUMNS...(COLUMNS + VALUE_CNT), exclusive, in
+   the given ROW in SX, into the VALUE_CNT values in VALUES.
+   Returns true if successful, false on I/O error. */
+bool
+sparse_xarray_read (const struct sparse_xarray *sx, unsigned long int row,
+                    size_t start, size_t n, void *data)
+{
+  assert (range_is_valid (sx, start, n));
+
+  if (sx->memory != NULL)
+    {
+      uint8_t **p = sparse_array_get (sx->memory, row);
+      if (p != NULL)
+        {
+          memcpy (data, *p + start, n);
+          return true;
+        }
+    }
+  else
+    {
+      if (range_set_contains (sx->disk_rows, row))
+        return tmpfile_read (sx->disk, (off_t) row * sx->n_bytes + start,
+                             n, data);
+    }
+
+  memcpy (data, sx->default_row + start, n);
+  return true;
+}
+
+/* Implements sparse_xarray_write for an on-disk sparse_xarray. */
+static bool
+write_disk_row (struct sparse_xarray *sx, unsigned long int row,
+                size_t start, size_t n, const void *data)
+{
+  off_t ofs = (off_t) row * sx->n_bytes;
+  if (range_set_contains (sx->disk_rows, row))
+    return tmpfile_write (sx->disk, ofs + start, n, data);
+  else
+    {
+      range_set_insert (sx->disk_rows, row, 1);
+      return (tmpfile_write (sx->disk, ofs, start, sx->default_row)
+              && tmpfile_write (sx->disk, ofs + start, n, data)
+              && tmpfile_write (sx->disk, ofs + start + n,
+                                sx->n_bytes - start - n,
+                                sx->default_row + start + n));
+    }
+}
+
+/* Writes the VALUE_CNT values in VALUES into columns
+   COLUMNS...(COLUMNS + VALUE_CNT), exclusive, in the given ROW
+   in SX.
+   Returns true if successful, false on I/O error. */
+bool
+sparse_xarray_write (struct sparse_xarray *sx, unsigned long int row,
+                     size_t start, size_t n, const void *data)
+{
+  assert (range_is_valid (sx, start, n));
+  if (sx->memory != NULL)
+    {
+      uint8_t **p = sparse_array_get (sx->memory, row);
+      if (p == NULL)
+        {
+          if (sparse_array_count (sx->memory) < sx->max_memory_rows)
+            {
+              p = sparse_array_insert (sx->memory, row);
+              *p = xmemdup (sx->default_row, sx->n_bytes);
+            }
+          else
+            {
+              if (!dump_sparse_xarray_to_disk (sx))
+                return false;
+              return write_disk_row (sx, row, start, n, data);
+            }
+        }
+      memcpy (*p + start, data, n);
+      return true;
+    }
+  else
+    return write_disk_row (sx, row, start, n, data);
+}
+
+/* Writes the VALUE_CNT values in VALUES to columns
+   START_COLUMN...(START_COLUMN + VALUE_CNT), exclusive, in every
+   row in SX, even those rows that have not yet been written.
+   Returns true if successful, false on I/O error.
+
+   The runtime of this function is linear in the number of rows
+   in SX that have already been written. */
+bool
+sparse_xarray_write_columns (struct sparse_xarray *sx, size_t start,
+                             size_t n, const void *data)
+{
+  assert (range_is_valid (sx, start, n));
+
+  /* Set defaults. */
+  memcpy (sx->default_row + start, data, n);
+
+  /* Set individual rows. */
+  if (sx->memory != NULL)
+    {
+      unsigned long int idx;
+      uint8_t **p;
+
+      for (p = sparse_array_first (sx->memory, &idx); p != NULL;
+           p = sparse_array_next (sx->memory, idx, &idx))
+        memcpy (*p + start, data, n);
+    }
+  else
+    {
+      const struct range_set_node *node;
+
+      for (node = range_set_first (sx->disk_rows); node != NULL;
+           node = range_set_next (sx->disk_rows, node))
+        {
+          unsigned long int start_row = range_set_node_get_start (node);
+          unsigned long int end_row = range_set_node_get_end (node);
+          unsigned long int row;
+
+          for (row = start_row; row < end_row; row++)
+            {
+              off_t offset = (off_t) row * sx->n_bytes;
+              if (!tmpfile_write (sx->disk, offset + start, n, data))
+                break;
+            }
+        }
+
+      if (tmpfile_error (sx->disk))
+        return false;
+    }
+  return true;
+}
+
+static unsigned long int
+scan_first (const struct sparse_xarray *sx)
+{
+  if (sx->memory)
+    {
+      unsigned long int idx;
+      return sparse_array_first (sx->memory, &idx) ? idx : ULONG_MAX;
+    }
+  else
+    return range_set_scan (sx->disk_rows, 0);
+}
+
+static unsigned long int
+scan_next (const struct sparse_xarray *sx, unsigned long int start)
+{
+  if (sx->memory)
+    {
+      unsigned long int idx;
+      return sparse_array_next (sx->memory, start, &idx) ? idx : ULONG_MAX;
+    }
+  else
+    return range_set_scan (sx->disk_rows, start + 1);
+}
+
+/* Only works for rows for which sparse_xarray_contains_row()
+   would return true. */
+static uint8_t *
+get_row (const struct sparse_xarray *sx, unsigned long int idx,
+         uint8_t *buffer)
+{
+  if (sx->memory)
+    {
+      uint8_t **p = sparse_array_get (sx->memory, idx);
+      return *p;
+    }
+  else if (tmpfile_read (sx->disk, (off_t) idx * sx->n_bytes,
+                         sx->n_bytes, buffer))
+    return buffer;
+  else
+    return NULL;
+}
+
+/* Iterates over all the rows in SX and DX, passing each pair of
+   rows with equal indexes to CB.  CB's modifications, if any, to
+   destination rows are written back to DX.
+
+   All rows that are actually in use in SX or in DX or both are
+   passed to CB.  If a row is in use in SX but not in DX, or vice
+   versa, then the "default" row (as set by
+   sparse_xarray_write_columns) is passed as the contents of the
+   other row.
+
+   CB is also called once with the default row from SX and the
+   default row from DX.  Modifying the data passed as the default
+   row from DX will change DX's default row.
+
+   Returns true if successful, false if I/O on SX or DX fails or
+   if CB returns false.  On failure, the contents of DX are
+   undefined. */
+bool
+sparse_xarray_copy (const struct sparse_xarray *sx, struct sparse_xarray *dx,
+                    bool (*cb) (const void *src, void *dst, void *aux),
+                    void *aux)
+{
+  bool success = true;
+
+  if (!cb (sx->default_row, dx->default_row, aux))
+    return false;
+
+  if (sx == dx)
+    {
+      if (sx->memory)
+        {
+          unsigned long int idx;
+          uint8_t **row;
+
+          for (row = sparse_array_first (sx->memory, &idx); row != NULL;
+               row = sparse_array_next (sx->memory, idx, &idx))
+            {
+              success = cb (*row, *row, aux);
+              if (!success)
+                break;
+            }
+        }
+      else if (sx->disk)
+        {
+          const struct range_set_node *node;
+          void *tmp = xmalloc (sx->n_bytes);
+
+          for (node = range_set_first (sx->disk_rows); node != NULL;
+               node = range_set_next (sx->disk_rows, node))
+            {
+              unsigned long int start = range_set_node_get_start (node);
+              unsigned long int end = range_set_node_get_end (node);
+              unsigned long int row;
+
+              for (row = start; row < end; row++)
+                {
+                  off_t offset = (off_t) row * sx->n_bytes;
+                  success = (tmpfile_read (sx->disk, offset, sx->n_bytes, tmp)
+                             && cb (tmp, tmp, aux)
+                             && tmpfile_write (dx->disk, offset,
+                                               dx->n_bytes, tmp));
+                  if (!success)
+                    break;
+                }
+            }
+
+          free (tmp);
+        }
+    }
+  else
+    {
+      unsigned long int src_idx = scan_first (sx);
+      unsigned long int dst_idx = scan_first (dx);
+
+      uint8_t *tmp_src_row = xmalloc (sx->n_bytes);
+      uint8_t *tmp_dst_row = xmalloc (dx->n_bytes);
+
+      for (;;)
+        {
+          unsigned long int idx;
+          const uint8_t *src_row;
+          uint8_t *dst_row;
+
+          /* Determine the index of the row to process.  If
+             src_idx == dst_idx, then the row has been written in
+             both SX and DX.  Otherwise, it has been written in
+             only the sparse_xarray corresponding to the smaller
+             index, and has the default contents in the other. */
+          idx = MIN (src_idx, dst_idx);
+          if (idx == ULONG_MAX)
+            break;
+
+          /* Obtain a copy of the source row as src_row. */
+          if (idx == src_idx)
+            src_row = get_row (sx, idx, tmp_src_row);
+          else
+            src_row = sx->default_row;
+
+          /* Obtain the destination row as dst_row. */
+          if (idx == dst_idx)
+            dst_row = get_row (dx, idx, tmp_dst_row);
+          else if (dx->memory
+                   && sparse_array_count (dx->memory) < dx->max_memory_rows)
+            {
+              uint8_t **p = sparse_array_insert (dx->memory, idx);
+              dst_row = *p = xmemdup (dx->default_row, dx->n_bytes);
+            }
+          else
+            {
+              memcpy (tmp_dst_row, dx->default_row, dx->n_bytes);
+              dst_row = tmp_dst_row;
+            }
+
+          /* Run the callback. */
+          success = cb (src_row, dst_row, aux);
+          if (!success)
+            break;
+
+          /* Write back the destination row, if necessary. */
+          if (dst_row == tmp_dst_row)
+            {
+              success = sparse_xarray_write (dx, idx, 0, dx->n_bytes, dst_row);
+              if (!success)
+                break;
+            }
+          else
+            {
+              /* Nothing to do: we modified the destination row in-place. */
+            }
+
+          /* Advance to the next row. */
+          if (src_idx == idx)
+            src_idx = scan_next (sx, src_idx);
+          if (dst_idx == idx)
+            dst_idx = scan_next (dx, dst_idx);
+        }
+
+      free (tmp_src_row);
+      free (tmp_dst_row);
+    }
+
+  return success;
+}
+
+/* Returns a hash value for SX suitable for use with the model
+   checker.  The value in BASIS is folded into the hash.
+
+   The returned hash value is *not* suitable for storage and
+   retrieval of sparse_xarrays that have identical contents,
+   because it will return different hash values for
+   sparse_xarrays that have the same contents (and it's slow).
+
+   We use MD4 because it is much faster than MD5 or SHA-1 but its
+   collision resistance is just as good. */
+unsigned int
+sparse_xarray_model_checker_hash (const struct sparse_xarray *sx,
+                                  unsigned int basis)
+{
+  unsigned int hash[DIV_RND_UP (20, sizeof (unsigned int))];
+  struct md4_ctx ctx;
+
+  md4_init_ctx (&ctx);
+  md4_process_bytes (&basis, sizeof basis, &ctx);
+  md4_process_bytes (&sx->n_bytes, sizeof sx->n_bytes, &ctx);
+  md4_process_bytes (sx->default_row, sx->n_bytes, &ctx);
+
+  if (sx->memory)
+    {
+      unsigned long int idx;
+      uint8_t **row;
+
+      md4_process_bytes ("m", 1, &ctx);
+      md4_process_bytes (&sx->max_memory_rows, sizeof sx->max_memory_rows,
+                         &ctx);
+      for (row = sparse_array_first (sx->memory, &idx); row != NULL;
+           row = sparse_array_next (sx->memory, idx, &idx))
+        {
+          md4_process_bytes (&idx, sizeof idx, &ctx);
+          md4_process_bytes (*row, sx->n_bytes, &ctx);
+        }
+    }
+  else
+    {
+      const struct range_set_node *node;
+      void *tmp = xmalloc (sx->n_bytes);
+
+      md4_process_bytes ("d", 1, &ctx);
+      for (node = range_set_first (sx->disk_rows); node != NULL;
+           node = range_set_next (sx->disk_rows, node))
+        {
+          unsigned long int start = range_set_node_get_start (node);
+          unsigned long int end = range_set_node_get_end (node);
+          unsigned long int idx;
+
+          for (idx = start; idx < end; idx++)
+            {
+              off_t offset = (off_t) idx * sx->n_bytes;
+              if (!tmpfile_read (sx->disk, offset, sx->n_bytes, tmp))
+                NOT_REACHED ();
+              md4_process_bytes (&idx, sizeof idx, &ctx);
+              md4_process_bytes (tmp, sx->n_bytes, &ctx);
+            }
+        }
+      free (tmp);
+    }
+  md4_finish_ctx (&ctx, hash);
+  return hash[0];
+}
diff --git a/src/libpspp/sparse-xarray.h b/src/libpspp/sparse-xarray.h
new file mode 100644 (file)
index 0000000..15ba49f
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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/>. */
+
+/* Sparse 2-d array.
+
+   Implements a sparse array, in particular a sparse array of
+   byte arrays.  Each row is either present or absent, and each
+   row that is present consists of a fixed number of bytes
+   (columns).  Data in the array may be accessed randomly by
+   column and row.  When the number of rows stored in the array
+   is small, the data is stored in memory; when it is large, the
+   data is stored in a temporary file.
+
+   The sparse_xarray_write_columns function provides a somewhat
+   unusual ability: to write a given value to every row in a
+   column or set of columns.  This overwrites any values
+   previously written into those columns.  For rows that have
+   never been written, this function sets "default" values that
+   later writes can override.  The default values are initially
+   all zero bytes.
+
+   The array keeps track of which row have been written.  Reading
+   from a row that has never been written yields the default
+   values.  It is permissible to write to only some columns in a
+   row and leave the rest of the row's data at the default
+   values. */
+
+#ifndef LIBPSPP_SPARSE_XARRAY_H
+#define LIBPSPP_SPARSE_XARRAY_H 1
+
+#include <stddef.h>
+#include <stdbool.h>
+
+struct sparse_xarray *sparse_xarray_create (size_t n_columns,
+                                            size_t max_memory_rows);
+struct sparse_xarray *sparse_xarray_clone (const struct sparse_xarray *);
+void sparse_xarray_destroy (struct sparse_xarray *);
+
+size_t sparse_xarray_get_n_columns (const struct sparse_xarray *);
+size_t sparse_xarray_get_n_rows (const struct sparse_xarray *);
+
+bool sparse_xarray_contains_row (const struct sparse_xarray *,
+                                 unsigned long int row);
+bool sparse_xarray_read (const struct sparse_xarray *, unsigned long int row,
+                         size_t start, size_t n, void *);
+bool sparse_xarray_write (struct sparse_xarray *, unsigned long int row,
+                          size_t start, size_t n, const void *);
+bool sparse_xarray_write_columns (struct sparse_xarray *, size_t start,
+                                  size_t n, const void *);
+bool sparse_xarray_copy (const struct sparse_xarray *src,
+                         struct sparse_xarray *dst,
+                         bool (*cb) (const void *src, void *dst, void *aux),
+                         void *aux);
+
+/* For testing purposes only. */
+unsigned int sparse_xarray_model_checker_hash (const struct sparse_xarray *,
+                                               unsigned int basis);
+
+#endif /* libpspp/sparse-libpspp.h */
index 552968b5c837d02411e4e101a7ba419b1b0d7a32..afe32de9f2049bfcc5a80ac7a95b36b348be4cb7 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
@@ -125,9 +125,9 @@ str_compare_rpad (const char *a, const char *b)
 
 /* Copies string SRC to buffer DST, of size DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the right with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
-buf_copy_str_rpad (char *dst, size_t dst_size, const char *src)
+buf_copy_str_rpad (char *dst, size_t dst_size, const char *src, char pad)
 {
   size_t src_len = strlen (src);
   if (src_len >= dst_size)
@@ -135,15 +135,15 @@ buf_copy_str_rpad (char *dst, size_t dst_size, const char *src)
   else
     {
       memcpy (dst, src, src_len);
-      memset (&dst[src_len], ' ', dst_size - src_len);
+      memset (&dst[src_len], pad, dst_size - src_len);
     }
 }
 
 /* Copies string SRC to buffer DST, of size DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the left with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
-buf_copy_str_lpad (char *dst, size_t dst_size, const char *src)
+buf_copy_str_lpad (char *dst, size_t dst_size, const char *src, char pad)
 {
   size_t src_len = strlen (src);
   if (src_len >= dst_size)
@@ -151,40 +151,42 @@ buf_copy_str_lpad (char *dst, size_t dst_size, const char *src)
   else
     {
       size_t pad_cnt = dst_size - src_len;
-      memset (&dst[0], ' ', pad_cnt);
+      memset (&dst[0], pad, pad_cnt);
       memcpy (dst + pad_cnt, src, src_len);
     }
 }
 
 /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the left with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
 buf_copy_lpad (char *dst, size_t dst_size,
-               const char *src, size_t src_size)
+               const char *src, size_t src_size,
+               char pad)
 {
   if (src_size >= dst_size)
     memmove (dst, src, dst_size);
   else
     {
-      memset (dst, ' ', dst_size - src_size);
+      memset (dst, pad, dst_size - src_size);
       memmove (&dst[dst_size - src_size], src, src_size);
     }
 }
 
 /* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
    DST is truncated to DST_SIZE bytes or padded on the right with
-   spaces as needed. */
+   copies of PAD as needed. */
 void
 buf_copy_rpad (char *dst, size_t dst_size,
-               const char *src, size_t src_size)
+               const char *src, size_t src_size,
+               char pad)
 {
   if (src_size >= dst_size)
     memmove (dst, src, dst_size);
   else
     {
       memmove (dst, src, src_size);
-      memset (&dst[src_size], ' ', dst_size - src_size);
+      memset (&dst[src_size], pad, dst_size - src_size);
     }
 }
 
@@ -195,15 +197,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.
@@ -1066,6 +1071,34 @@ ds_set_length (struct string *st, size_t new_length, char pad)
     st->ss.length = new_length;
 }
 
+/* Removes N characters from ST starting at offset START. */
+void
+ds_remove (struct string *st, size_t start, size_t n)
+{
+  if (n > 0 && start < st->ss.length)
+    {
+      if (st->ss.length - start <= n)
+        {
+          /* All characters at or beyond START are deleted. */
+          st->ss.length = start;
+        }
+      else
+        {
+          /* Some characters remain and must be shifted into
+             position. */
+          memmove (st->ss.string + st->ss.length,
+                   st->ss.string + st->ss.length + n,
+                   st->ss.length - start - n);
+          st->ss.length -= n;
+        }
+    }
+  else
+    {
+      /* There are no characters to delete or no characters at or
+         beyond START, hence deletion is a no-op. */
+    }
+}
+
 /* Returns true if ST is empty, false otherwise. */
 bool
 ds_is_empty (const struct string *st)
@@ -1188,48 +1221,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,
@@ -1414,3 +1441,25 @@ ds_relocate (struct string *st)
       free ((char *) rel);
     }
 }
+
+
+\f
+
+/* Operations on uint8_t "strings" */
+
+/* Copies buffer SRC, of SRC_SIZE bytes, to DST, of DST_SIZE bytes.
+   DST is truncated to DST_SIZE bytes or padded on the right with
+   copies of PAD as needed. */
+void
+u8_buf_copy_rpad (uint8_t *dst, size_t dst_size,
+                 const uint8_t *src, size_t src_size,
+                 char pad)
+{
+  if (src_size >= dst_size)
+    memmove (dst, src, dst_size);
+  else
+    {
+      memmove (dst, src, src_size);
+      memset (&dst[src_size], pad, dst_size - src_size);
+    }
+}
index ca990dbc3632f8b4d4f0870cf8874f595be44163..a134079f90dd52f7c359d7ad4fba3049c5c85203 100644 (file)
@@ -20,6 +20,7 @@
 #include <assert.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -34,10 +35,10 @@ void buf_reverse (char *, size_t);
 char *buf_find_reverse (const char *, size_t, const char *, size_t);
 int buf_compare_case (const char *, const char *, size_t);
 int buf_compare_rpad (const char *, size_t, const char *, size_t);
-void buf_copy_lpad (char *, size_t, const char *, size_t);
-void buf_copy_rpad (char *, size_t, const char *, size_t);
-void buf_copy_str_lpad (char *, size_t, const char *);
-void buf_copy_str_rpad (char *, size_t, const char *);
+void buf_copy_lpad (char *, size_t, const char *, size_t, char pad);
+void buf_copy_rpad (char *, size_t, const char *, size_t, char pad);
+void buf_copy_str_lpad (char *, size_t, const char *, char pad);
+void buf_copy_str_rpad (char *, size_t, const char *, char pad);
 
 int str_compare_rpad (const char *, const char *);
 void str_copy_rpad (char *, size_t, const char *);
@@ -177,6 +178,7 @@ bool ds_tokenize (const struct string *src, struct substring delimiters,
                   size_t *save_idx, struct substring *token);
 void ds_rpad (struct string *, size_t length, char pad);
 void ds_set_length (struct string *, size_t new_length, char pad);
+void ds_remove (struct string *, size_t start, size_t n);
 
 /* Extracting substrings. */
 struct substring ds_ss (const struct string *);
@@ -222,4 +224,10 @@ char *ds_put_uninit (struct string *st, size_t incr);
 /* calls relocate from gnulib on ST */
 void ds_relocate (struct string *st);
 
+
+void u8_buf_copy_rpad (uint8_t *dst, size_t dst_size,
+                      const uint8_t *src, size_t src_size,
+                      char pad);
+
+
 #endif /* str_h */
diff --git a/src/libpspp/tmpfile.c b/src/libpspp/tmpfile.c
new file mode 100644 (file)
index 0000000..0d30936
--- /dev/null
@@ -0,0 +1,165 @@
+/* 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/>. */
+
+/* A interface to allow a temporary file to be treated as an
+   array of data. */
+
+#include <config.h>
+
+#include <libpspp/tmpfile.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libpspp/assertion.h>
+
+#include "error.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+struct tmpfile
+  {
+    FILE *file;                 /* Underlying file. */
+
+    /* Current byte offset in file.  We track this manually,
+       instead of using ftello, because in glibc ftello flushes
+       the stream buffer, making the common case of sequential
+       access to cases unreasonably slow. */
+    off_t position;
+  };
+
+/* Creates and returns a new temporary file.  The temporary file
+   will be automatically deleted when the process exits. */
+struct tmpfile *
+tmpfile_create (void)
+{
+  struct tmpfile *tf = xmalloc (sizeof *tf);
+  tf->file = tmpfile ();
+  if (tf->file == NULL)
+    error (0, errno, _("failed to create temporary file"));
+  tf->position = 0;
+  return tf;
+}
+
+/* Closes and destroys temporary file TF.  Returns true if I/O on
+   TF always succeeded, false if an I/O error occurred at some
+   point. */
+bool
+tmpfile_destroy (struct tmpfile *tf)
+{
+  bool ok = true;
+  if (tf != NULL)
+    {
+      ok = !tmpfile_error (tf);
+      if (tf->file != NULL)
+        fclose (tf->file);
+      free (tf);
+    }
+  return ok;
+}
+
+/* Seeks TF's underlying file to the start of `union value'
+   VALUE_IDX within case CASE_IDX.
+   Returns true if the seek is successful and TF is not
+   otherwise tainted, false otherwise. */
+static bool
+do_seek (const struct tmpfile *tf_, off_t offset)
+{
+  struct tmpfile *tf = (struct tmpfile *) tf_;
+
+  if (!tmpfile_error (tf))
+    {
+      if (tf->position == offset)
+        return true;
+      else if (fseeko (tf->file, offset, SEEK_SET) == 0)
+        {
+          tf->position = offset;
+          return true;
+        }
+      else
+        error (0, errno, _("seeking in temporary file"));
+    }
+
+  return false;
+}
+
+/* Reads BYTES bytes from TF's underlying file into BUFFER.
+   TF must not be tainted upon entry into this function.
+   Returns true if successful, false upon an I/O error (in which
+   case TF is marked tainted). */
+static bool
+do_read (const struct tmpfile *tf_, void *buffer, size_t bytes)
+{
+  struct tmpfile *tf = (struct tmpfile *) tf_;
+
+  assert (!tmpfile_error (tf));
+  if (bytes > 0 && fread (buffer, bytes, 1, tf->file) != 1)
+    {
+      if (ferror (tf->file))
+        error (0, errno, _("reading temporary file"));
+      else if (feof (tf->file))
+        error (0, 0, _("unexpected end of file reading temporary file"));
+      else
+        NOT_REACHED ();
+      return false;
+    }
+  tf->position += bytes;
+  return true;
+}
+
+/* Writes BYTES bytes from BUFFER into TF's underlying file.
+   TF must not be tainted upon entry into this function.
+   Returns true if successful, false upon an I/O error (in which
+   case TF is marked tainted). */
+static bool
+do_write (struct tmpfile *tf, const void *buffer, size_t bytes)
+{
+  assert (!tmpfile_error (tf));
+  if (bytes > 0 && fwrite (buffer, bytes, 1, tf->file) != 1)
+    {
+      error (0, errno, _("writing to temporary file"));
+      return false;
+    }
+  tf->position += bytes;
+  return true;
+}
+
+/* Reads N bytes from TF at byte offset OFFSET into DATA.
+   Returns true if successful, false on failure.  */
+bool
+tmpfile_read (const struct tmpfile *tf, off_t offset, size_t n, void *data)
+{
+  return do_seek (tf, offset) && do_read (tf, data, n);
+}
+
+/* Writes the N bytes in DATA to TF at byte offset OFFSET.
+   Returns true if successful, false on failure.  */
+bool
+tmpfile_write (struct tmpfile *tf, off_t offset, size_t n, const void *data)
+{
+  return do_seek (tf, offset) && do_write (tf, data, n);
+}
+
+/* Returns true if an error has occurred in I/O on TF,
+   false if no error has been detected. */
+bool
+tmpfile_error (const struct tmpfile *tf)
+{
+  return tf->file == NULL || ferror (tf->file) || feof (tf->file);
+}
diff --git a/src/libpspp/tmpfile.h b/src/libpspp/tmpfile.h
new file mode 100644 (file)
index 0000000..e679a2f
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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/>. */
+
+/* A interface to allow a temporary file to be treated as an
+   array of data. */
+
+#ifndef LIBPSPP_TMPFILE_H
+#define LIBPSPP_TMPFILE_H 1
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct tmpfile *tmpfile_create (void);
+bool tmpfile_destroy (struct tmpfile *);
+bool tmpfile_read (const struct tmpfile *, off_t offset, size_t n, void *);
+bool tmpfile_write (struct tmpfile *, off_t offset, size_t n, const void *);
+bool tmpfile_error (const struct tmpfile *);
+
+#endif /* libpspp/tmpfile.h */
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..f78895f8214e4b70b89668477ac1dfe1abc98fe2 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
@@ -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];
@@ -177,9 +185,12 @@ pspp_coeff_var_to_coeff (const struct variable *v, struct pspp_coeff **coefs,
           */
          if (val != NULL)
            {
+              int width = var_get_width (v);
+
              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
+                     && value_compare_3way (pspp_coeff_get_value (coefs[j], v),
+                                            val, width) != 0)
                {
                  j++;
                }
index 5414379119d1cecba94bf342c877209f58fc9b2f..89660ba9c1e3f5fbc1f4168af2ef8af549a43c7d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 */
 #include <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 hsh_table *ca;
+  struct moments1 **m1;
+  struct moments **m;
+  const struct variable **v_variables;
+  const struct interaction_variable **interactions;
+  size_t n_variables;
+  size_t n_intr;
+  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->n_intr = 0;
+  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;
+}
+void
+covariance_interaction_set (struct covariance_matrix *cov, 
+                           const struct interaction_variable **intr, size_t n_intr)
+{
+  cov->interactions = intr;
+  cov->n_intr = n_intr;
+}
+
+static size_t 
+get_n_rows (size_t n_variables, const struct variable *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[])
+{
+  size_t n_rows = get_n_rows (n_variables, v_variables);
+  return design_matrix_create (n_variables, v_variables, n_rows);
+}
+
+static size_t 
+get_n_rows_s (const struct variable *var)
+{
+  size_t result = 0;
+  if (var_is_numeric (var))
+    {
+      result++;
+    }
+  else
+    {
+      result += cat_get_n_categories (var) - 1;
+    }
+  return result;
+}
+static struct design_matrix *
+covariance_matrix_create_s (struct covariance_matrix *cov)
+{
+  struct variable **v_variables;
+  size_t n_variables;
+  size_t n_rows = 0;
+  size_t i;
+  size_t j;
+
+  n_variables = cov->n_variables + cov->n_intr;
+  v_variables = xnmalloc (n_variables, sizeof (*v_variables));
+  for (i = 0; i < cov->n_variables; i++)
+    {
+      v_variables[i] = cov->v_variables[i];
+      n_rows += get_n_rows_s (v_variables[i]);
+    }
+  for (j = 0; j < cov->n_intr; j++)
+    {
+      v_variables[i + j] = interaction_get_variable (cov->interactions[j]);
+      n_rows += get_n_rows_s (v_variables[i]);
+    }
+  return design_matrix_create (n_variables, v_variables, n_rows);
+}
+
+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)
 {
-  return design_matrix_create (n_variables, v_variables, (size_t) n_variables);
+  assert (cov->m != NULL);
+  moments_pass_one (cov->m[i], x, 1.0);
 }
 
-void covariance_matrix_destroy (struct design_matrix *x)
+void
+covariance_matrix_destroy (struct covariance_matrix *cov)
 {
-  design_matrix_destroy (x);
+  size_t i;
+
+  assert (cov != NULL);
+  design_matrix_destroy (cov->cov);
+  design_matrix_destroy (cov->ssize);
+  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,53 +273,58 @@ 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,
                double ssize, double x, const union value *val1, size_t row)
 {
+  int width = var_get_width (v);
   size_t col;
   size_t i;
   double y;
   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 (!value_equal (tmp_val, val1, width))
        {
          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 +340,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 (!value_equal (tmp_val, val1, var_get_width (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 +359,671 @@ 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 hash = value_hash (val_max, var_get_width (v_max), 0);
+      hash = value_hash (val_min, var_get_width (v_min), hash);
+      return hash_int (*n_vars * (*n_vars + 1 + idx_max) + idx_min, hash);
+    }
+  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);
+}
+
+static int 
+ordered_match_nodes (const struct covariance_accumulator *c, const struct variable *v1,
+                    const struct variable *v2, const union value *val1, const union value *val2)
+{
+  size_t result;
+  size_t m;
+
+  result = var_get_dict_index (v1) ^ var_get_dict_index (c->v1);
+  m = var_get_dict_index (v2) ^ var_get_dict_index (c->v2);
+  result = result|m;
+  if (var_is_alpha (v1))
+    {
+      result |= value_compare_3way (val1, c->val1, var_get_width (v1));
+      if (var_is_alpha (v2))
+       {
+         result |= value_compare_3way (val2, c->val2, var_get_width (v2));
+       }
+    }
+  else if (var_is_alpha (v2))
+    {
+      result |= value_compare_3way (val2, c->val2, var_get_width (v2));
+    }
+  return result;
+}
+  
+/*
+  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)
+{
+  size_t n;
+  size_t m;
+
+  n = ordered_match_nodes (c, v1, v2, val1, val2);
+  m = ordered_match_nodes (c, v2, v1, val2, val1);
+  return (n & m);
+}
+
+/*
+  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) + value_hash (val, var_get_width (v2), 0);
+    }
+  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))
+    {
+      return val2->f;
+    }
+  else
+    {
+      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_intr (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);
+    }
+}
+
+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)
+{
+  struct covariance_accumulator *ca;
+  struct covariance_accumulator *new_entry;
+
+  ca = get_new_covariance_accumulator (v1, v2, val1, val2);
+  ca->dot_product = update_product (ca->v1, ca->v2, ca->val1, ca->val2);
+  ca->sum1 = update_sum (ca->v1, ca->val1, 1.0);
+  ca->sum2 = update_sum (ca->v2, ca->val2, 1.0);
+  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);
+    }
+}
+
+static void
+inner_intr_loop (struct covariance_matrix *cov, const struct ccase  *ccase, const struct variable *var1,
+                const union value *val1, const struct interaction_variable **i_var, 
+                const struct interaction_value *i_val1, size_t j)
+{
+  struct variable *var2;
+  union value *val2;
+  struct interaction_value *i_val2;
+
+  var2 = interaction_get_variable (i_var[j]);
+  i_val2 = interaction_case_data (ccase, i_var[j]);
+  val2 = interaction_value_get (i_val2);
+  
+  if (!var_is_value_missing (var2, val2, cov->missing_value))
+    {
+      update_hash_entry_intr (cov->ca, var1, var2, val1, val2, i_val1, i_val2);
+    }
+}       
+/*
+  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;
+  const struct variable *var1;
+  const struct variable *var2;
+  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)
+    {
+      var1 = v_variables[i];
+      val1 = case_data (ccase, var1);
+      if (!var_is_value_missing (var1, val1, cov->missing_value))
+       {
+         cat_value_update (var1, val1);
+         if (var_is_numeric (var1))
+           cov->update_moments (cov, i, val1->f);
+
+         for (j = i; j < cov->n_variables; j++)
+           {
+             var2 = v_variables[j];
+             val2 = case_data (ccase, var2);
+             if (!var_is_value_missing
+                 (var2, val2, cov->missing_value))
+               {
+                 update_hash_entry (cov->ca, var1, var2, val1, val2);
+               }
+           }
+         for (j = 0; j < cov->n_intr; j++)
+           {
+             inner_intr_loop (cov, ccase, var1, val1, i_var, i_val1, j);
+           }
+       }
+    }
+  for (i = 0; i < cov->n_intr; i++)
+    {
+      var1 = interaction_get_variable (i_var[i]);
+      i_val1 = interaction_case_data (ccase, i_var[i]);
+      val1 = interaction_value_get (i_val1);
+      cat_value_update (var1, val1);
+      if (!var_is_value_missing (var1, val1, cov->missing_value))
+       {
+         for (j = i; j < cov->n_intr; j++)
+           {
+             inner_intr_loop (cov, ccase, var1, val1, i_var, i_val1, j);
+           }
+       }
+    }
+}
+
+/*
+  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)
+    {
+      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++)
+       {
+         update_hash_entry (cov->ca, v_variables[i], v_variables[j],
+                            val1, val2);
+       }
+    }
+}
+
+/*
+  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);
+}
+
+/*
+  Return the value corresponding to subscript TARGET. If that value corresponds
+  to the origin, return NULL.
+ */
+static const union value *
+get_value_from_subscript (const struct design_matrix *dm, size_t target)
+{
+  const union value *result = NULL;
+  const struct variable *var;
+  size_t i;
+  
+  var = design_matrix_col_to_var (dm, target);
+  if (var_is_numeric (var))
+    {
+      return NULL;
+    }
+  for (i = 0; i < cat_get_n_categories (var); i++)
+    {
+      result = cat_subscript_to_value (i, var);
+      if (dm_get_exact_subscript (dm, var, result) == target)
+       {
+         return result;
+       }
+    }
+  return NULL;
+}
+
+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);
+  v2 = design_matrix_col_to_var (dm, j);
+  if (var_get_dict_index (v1) == var_get_dict_index(ca->v1))
+    {
+      if (var_get_dict_index (v2) == var_get_dict_index (ca->v2))
+       {
+         k = dm_get_exact_subscript (dm, v1, ca->val1);
+         if (k == i)
+           {
+             k = dm_get_exact_subscript (dm, v2, ca->val2);
+             if (k == j)
+               {
+                 return true;
+               }
+           }
+       }
+    }
+  else if (var_get_dict_index (v1) == var_get_dict_index (ca->v2))
+    {
+      if (var_get_dict_index (v2) == var_get_dict_index (ca->v1))
+       {
+         k = dm_get_exact_subscript (dm, v1, ca->val2);
+         if (k == i)
+           {
+             k = dm_get_exact_subscript (dm, v2, ca->val1);
+             if (k == j)
+               {
+                 return true;
+               }
+           }
+       }
+    }
+  
+  return false;
+}
+static double
+get_sum (const struct covariance_matrix *cov, size_t i)
+{
+  size_t k;
+  double mean;
+  double n;
+  const struct variable *var;
+  const union value *val = NULL;
+
+  assert ( cov != NULL);
+  var = design_matrix_col_to_var (cov->cov, i);
+  if (var != NULL)
+    {
+      if (var_is_alpha (var))
+       {
+         val = get_value_from_subscript (cov->cov, i);
+         k = cat_value_find (var, val);
+         return cat_get_category_count (k, var);
+       }
+      else
+       {
+         k = 0;
+         while (cov->v_variables[k] != var && k  < cov->n_variables)
+           {
+             k++;
+           }
+         if (k < cov->n_variables)
+           {
+             moments1_calculate (cov->m1[k], &n, &mean, NULL, NULL, NULL);
+             return mean * n;
+           }
+       }
+    }
+      
+  return 0.0;
+}
+static void
+update_ssize (struct design_matrix *dm, size_t i, size_t j, struct covariance_accumulator *ca)
+{
+  const 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_s (cov);
+  cov->ssize = covariance_matrix_create_s (cov);
+  entry = hsh_first (cov->ca, &iter);
+  while (entry != NULL)
+    {
+      entry = hsh_next (cov->ca, &iter);
+    }
+
+  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);
+         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))
+               {
+                 design_matrix_set_element (cov->cov, i, j, 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);
+         design_matrix_set_element (cov->cov, j, i, 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;
+}
+size_t
+covariance_matrix_get_n_rows (const struct covariance_matrix *c)
+{
+  return design_matrix_get_n_rows (c->cov);
+}
+
+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..c16e5cbc4dfde7d3056b05a32363769e43175251 100644 (file)
 #ifndef COVARIANCE_MATRIX_H
 #define COVARIANCE_MATRIX_H
 
-#include "design-matrix.h"
+#include <math/design-matrix.h>
+#include <math/interaction.h>
+
+struct moments1;
+struct ccase;
+struct hsh_table;
+struct covariance_matrix;
+enum
+{ ONE_PASS,
+  TWO_PASS
+};
 
-struct design_matrix *
-covariance_matrix_create (int, const struct variable *[]);
-
-void covariance_matrix_destroy (struct design_matrix *);
+/*
+  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 *);
 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);
+void covariance_interaction_set (struct covariance_matrix *, 
+                                const struct interaction_variable **, size_t);
 #endif
index 298d03357b7208e66392c0b15bec0eb9e0f0146b..8f125c58b1e734d3b5d38ce1262e1e4477280edd 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,113 @@ 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);
+}
+
+/*
+  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.
+ */
+size_t
+dm_get_exact_subscript (const struct design_matrix *dm, const struct variable *var,
+                    const union value *val)
+{
+  size_t result;
+
+  result = design_matrix_var_to_column (dm, var);
+  if (var_is_alpha (var))
+    {
+      if (cat_is_origin (var, val))
+       {
+         return -1u;
+       }
+      result += cat_value_find (var, val) - 1;
+    }
+  return result;
+}
index ad2b82585229c3d9c52d119865f7dfe76e2d9bd3..b1cda5a9068d38be49ed45dd53a5016f78368056 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,23 @@ 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);
+size_t dm_get_exact_subscript (const struct design_matrix *, const struct variable *,
+                                  const union value *);
 
 #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..3a483671fbb6bca1561943554f9a52517469b56b 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
 /* 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 value_compare_3way (&a->id, &b->id, var_get_width (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 = value_hash (&g->id, var_get_width (var), 0);
 
   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..7ed36f6627ed469ce1c6775efd504a191c23f338 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 <unistr.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;
+  int width = 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->n_vars = n_vars;
+      for (i = 0; i < n_vars; i++)
        {
-         length *= cat_get_n_categories (v[i]);
-       }
-      else
-       {
-         length = (length > 0) ? length : 1;
+         result->members[i] = vars[i];
+         if (var_is_alpha (vars[i]))
+           {
+             result->n_alpha++;
+             width = 1;
+           }
        }
     }
-  if (length > 0)
-    {
-      length--;
-    }
+  result->intr = var_create_internal (0, width);
+
+  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_get_member (const struct interaction_variable *iv, size_t i)
+{
+  return iv->members[i];
+}
 
-  result = gsl_vector_calloc (length);
-  subs = xnmalloc (n_vars, sizeof (*subs));
-  for (j = 0; j < n_vars; j++)
+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 variable itself.
+ */
+const struct variable *
+interaction_get_variable (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)
     {
-      if (var_is_alpha (v[j]))
+      uint8_t *val;
+      int val_width = 1;
+
+      result = xmalloc (sizeof (*result));
+      result->intr = var;
+      n_vars = interaction_get_n_vars (var);
+      value_init (&result->val, val_width);
+      val = value_str_rw (&result->val, val_width);
+      val[0] = '\0';
+      result->f = 1.0;
+      for (i = 0; i < n_vars; i++)
        {
-         subs[j] = cat_value_find (v[j], vals[j]);
+         member = interaction_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]))
+               {
+                  int w = var_get_width (var->members[i]);
+                 value_resize (result, val_width, val_width + w);
+                 u8_strncat (val, value_str (vals[i], w), w);
+                 val = value_str_rw (&result->val, val_width);
+               }
+             else if (var_is_numeric (var->members[i]))
+               {
+                 result->f *= vals[i]->f;
+               }
+           }
+       }
+      if (interaction_get_n_alpha (var) == 0)
+       {
+         /*
+           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;
        }
     }
-  j = subs[0];
-  for (i = 1; i < n_vars; i++)
+  return result;
+}
+
+const 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)
     {
-      j = j * cat_get_n_categories (v[i]) + subs[i];
+      size_t n_vars = interaction_get_n_vars (val->intr);
+      int val_width = n_vars * MAX_SHORT_STRING + 1;
+
+      value_destroy (&val->val, val_width);
+      free (val);
     }
-  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++)
-    {
-      if (var_is_numeric (v[i]))
+}
+
+/*
+  Return a value from a variable that is an interaction. 
+ */
+struct interaction_value *
+interaction_case_data (const struct ccase *ccase, const struct interaction_variable *iv)
+{
+  size_t i;
+  size_t n_vars;
+  const struct variable *member;
+  const union value **vals = NULL;
+
+  n_vars = interaction_get_n_vars (iv);
+  vals = xnmalloc (n_vars, sizeof (*vals));
+
+  for (i = 0; i < n_vars; i++)
        {
-         tmp *= vals[i]->f;
+         member = interaction_get_member (iv, i);
+         vals[i] = case_data (ccase, member);
        }
-    }
-  if (fabs (tmp - 1.0) > GSL_DBL_EPSILON)
+
+  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++)
     {
-      gsl_vector_set (result, j, tmp);
+      intr = interaction_get_variable (iv[i]);
+      if (var_get_dict_index (intr) == var_get_dict_index (var))
+       {
+         return true;
+       }
     }
-  free (subs);
-
-  return result;
+  return false;
 }
+  
index 070060781635131ca91f05fb6ac7e787f421f48d..995d0684517d78a8207e040121126b1ae63f473c 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 *);
+const union value *interaction_value_get (const struct interaction_value *);
+const struct variable * interaction_get_variable (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_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 interaction_variable *);
+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..c03af4672955f4dd20514291faf8cf06bebb49e5 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])
@@ -368,8 +383,8 @@ pspp_linreg (const gsl_vector * Y, const struct design_matrix *dm,
              gsl_matrix_set (design, j, i, tmp);
            }
        }
-      sw = gsl_matrix_calloc (cache->n_indeps + 1, cache->n_indeps + 1);
-      xtx = gsl_matrix_submatrix (sw, 0, 0, cache->n_indeps, cache->n_indeps);
+      sw = gsl_matrix_calloc (cache->n_coeffs + 1, cache->n_coeffs + 1);
+      xtx = gsl_matrix_submatrix (sw, 0, 0, cache->n_coeffs, cache->n_coeffs);
 
       for (i = 0; i < xtx.matrix.size1; i++)
        {
@@ -384,8 +399,8 @@ pspp_linreg (const gsl_vector * Y, const struct design_matrix *dm,
            }
        }
 
-      gsl_matrix_set (sw, cache->n_indeps, cache->n_indeps, cache->sst);
-      xty = gsl_matrix_column (sw, cache->n_indeps);
+      gsl_matrix_set (sw, cache->n_coeffs, cache->n_coeffs, cache->sst);
+      xty = gsl_matrix_column (sw, cache->n_coeffs);
       /*
          This loop starts at 1, with i=0 outside the loop, so we can get
          the model sum of squares due to the first independent variable.
@@ -395,7 +410,7 @@ pspp_linreg (const gsl_vector * Y, const struct design_matrix *dm,
       gsl_vector_set (&(xty.vector), 0, tmp);
       tmp *= tmp / gsl_vector_get (cache->ssx, 0);
       gsl_vector_set (cache->ss_indeps, 0, tmp);
-      for (i = 1; i < cache->n_indeps; i++)
+      for (i = 1; i < cache->n_coeffs; i++)
        {
          xi = gsl_matrix_column (design, i);
          gsl_blas_ddot (&(xi.vector), Y, &tmp);
@@ -626,7 +641,7 @@ double pspp_linreg_get_indep_variable_mean (pspp_linreg_cache *c, const struct v
       coef = pspp_linreg_get_coeff (c, v, NULL);
       return pspp_coeff_get_mean (coef);
     }
-  return GSL_NAN;
+  return 0.0;
 }
 
 void pspp_linreg_set_indep_variable_mean (pspp_linreg_cache *c, const struct variable *v, 
@@ -645,100 +660,84 @@ 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;
+  size_t n_coeffs = 0;
 
+  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);
+  model_vars = xnmalloc (1 + c->n_indeps, sizeof (*model_vars));
+
   /*
     Put the model variables in the right order in MODEL_VARS.
+    Count the number of coefficients.
    */
-  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];
     }
+  model_vars[i] = c->depvar;
+  result = covariance_matrix_create (1 + c->n_indeps, model_vars);
+  permutation = xnmalloc (design_matrix_get_n_cols (result), sizeof (*permutation));
 
-  result = covariance_matrix_create (k, model_vars);
-  for (i = 0; i < result->m->size1; i++)
+  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..af44855ce1cbb7718dd18d48d459fc5cfe15fa8a 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;
+    struct caseproto *proto;
   };
 
 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, const struct caseproto *proto)
 {
   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;
+  m->proto = caseproto_ref (proto);
   return m;
 }
 
@@ -66,9 +66,10 @@ 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);
+      caseproto_unref (m->proto);
       free (m);
     }
 }
@@ -97,7 +98,7 @@ merge_make_reader (struct merge *m)
     }
   else if (m->input_cnt == 0)
     {
-      struct casewriter *writer = mem_writer_create (m->value_cnt);
+      struct casewriter *writer = mem_writer_create (m->proto);
       r = casewriter_make_reader (writer);
     }
   else
@@ -111,7 +112,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
     {
@@ -130,7 +132,7 @@ do_merge (struct merge *m)
 
   assert (m->input_cnt > 1);
 
-  w = tmpfile_writer_create (m->value_cnt);
+  w = tmpfile_writer_create (m->proto);
   for (i = 0; i < m->input_cnt; i++)
     taint_propagate (casereader_get_taint (m->inputs[i].reader),
                      casewriter_get_taint (w));
@@ -144,11 +146,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..5fdb6fc062d24d692b1d3b95552690d00e9fd793 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
 #ifndef MATH_MERGE_H
 #define MATH_MERGE_H 1
 
-#include <stdbool.h>
-#include <stddef.h>
-
-struct case_ordering;
+struct caseproto;
 struct casereader;
+struct subcase;
 
-struct merge *merge_create (const struct case_ordering *, size_t);
+struct merge *merge_create (const struct subcase *, const struct caseproto *);
 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..e61bf58
--- /dev/null
@@ -0,0 +1,102 @@
+/* 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 <gsl/gsl_cdf.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include <data/case.h>
+#include <data/casewriter.h>
+#include <libpspp/compiler.h>
+#include <libpspp/misc.h>
+#include <math/moments.h>
+
+#include "xalloc.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 (casewriter_get_proto (np->writer));
+  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;
+  struct caseproto *proto;
+  int i;
+
+  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;
+
+  proto = caseproto_create ();
+  for (i = 0; i < n_NP_IDX; i++)
+    proto = caseproto_add_width (proto, 0);
+  np->writer = autopaging_writer_create (proto);
+  caseproto_unref (proto);
+
+  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..8719d877e096fcc9fc0d0f911d462d7bd0566725 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>
@@ -41,44 +41,44 @@ int max_buffers = INT_MAX;
 
 struct sort_writer
   {
-    size_t value_cnt;
-    struct case_ordering *ordering;
+    struct caseproto *proto;
+    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 *,
+                                     const struct caseproto *);
 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,
+                    const struct caseproto *proto)
 {
   struct sort_writer *sort;
 
   sort = xmalloc (sizeof *sort);
-  sort->value_cnt = value_cnt;
-  sort->ordering = case_ordering_clone (ordering);
-  sort->merge = merge_create (ordering, value_cnt);
-  sort->pqueue = pqueue_create (ordering, value_cnt);
+  sort->proto = caseproto_ref (proto);
+  subcase_clone (&sort->ordering, ordering);
+  sort->merge = merge_create (ordering, proto);
+  sort->pqueue = pqueue_create (ordering, proto);
   sort->run = NULL;
   sort->run_id = 0;
-  case_nullify (&sort->run_end);
+  sort->run_end = NULL;
 
-  case_ordering_destroy (ordering);
-
-  return casewriter_create (value_cnt, &sort_casewriter_class, sort);
+  return casewriter_create (proto, &sort_casewriter_class, sort);
 }
 
 static void
@@ -91,9 +91,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 +102,12 @@ 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);
+  caseproto_unref (sort->proto);
   free (sort);
 }
 
@@ -119,7 +120,7 @@ sort_casewriter_convert_to_reader (struct casewriter *writer, void *sort_)
   if (sort->run == NULL && sort->run_id == 0)
     {
       /* In-core sort. */
-      sort->run = mem_writer_create (casewriter_get_value_cnt (writer));
+      sort->run = mem_writer_create (sort->proto);
       sort->run_id = 1;
     }
   while (!pqueue_is_empty (sort->pqueue))
@@ -136,12 +137,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)
@@ -151,14 +152,13 @@ output_record (struct sort_writer *sort)
     }
   if (sort->run == NULL)
     {
-      sort->run = tmpfile_writer_create (sort->value_cnt);
+      sort->run = tmpfile_writer_create (sort->proto);
       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 +169,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));
+    sort_create_writer (ordering, casereader_get_proto (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 +206,7 @@ struct pqueue
 struct pqueue_record
   {
     casenumber id;
-    struct ccase c;
+    struct ccase *c;
     casenumber idx;
   };
 
@@ -201,14 +214,13 @@ 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, const struct caseproto *proto)
 {
   struct pqueue *pq;
 
   pq = xmalloc (sizeof *pq);
-  pq->ordering = case_ordering_clone (ordering);
-  pq->record_cap
-    = settings_get_workspace_cases (value_cnt);
+  subcase_clone (&pq->ordering, ordering);
+  pq->record_cap = settings_get_workspace_cases (proto);
   if (pq->record_cap > max_buffers)
     pq->record_cap = max_buffers;
   else if (pq->record_cap < min_buffers)
@@ -227,12 +239,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 +270,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 +289,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 +303,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..96ac32cc0b7a54a85e8297822b80ccfddbd40451 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
 #ifndef MATH_SORT_H
 #define MATH_SORT_H 1
 
-#include <stddef.h>
-#include <stdbool.h>
-
-struct case_ordering;
+struct subcase;
+struct caseproto;
+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 *,
+                                       const struct caseproto *);
+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..3ab2f3edce165ed0a709688e711867986683eee3 100644 (file)
  */
 
 #include <config.h>
+
+#include <math.h>
+#include <stdlib.h>
+
 #include <gsl/gsl_matrix.h>
 #include <gsl/gsl_vector.h>
-#include <stdlib.h>
 #include <libpspp/compiler.h>
+#include <libpspp/misc.h>
 #include <math/coefficient.h>
 #include <math/ts/innovations.h>
 
@@ -160,7 +164,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
diff --git a/src/output/Makefile b/src/output/Makefile
deleted file mode 100644 (file)
index c1a052e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-all:
-       $(MAKE) -C /home/res/jmd/PSPP/pspp 
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 49819e9fe03cc4073242ae97d05b77365c7e78a2..e324901cc4df1d445f3fc8c7a4b64296bca55bca 100644 (file)
@@ -83,6 +83,9 @@ chart_create(void)
   chart->legend_left = 810;
   chart->legend_right = 1000;
   chart->font_size = 0;
+  chart->in_path = false;
+  chart->dataset = NULL;
+  chart->n_datasets = 0;
   strcpy(chart->fill_colour,"red");
 
   /* Get default font size */
@@ -100,6 +103,7 @@ chart_create(void)
 void
 chart_submit(struct chart *chart)
 {
+  int i;
   struct som_entity s;
   struct outp_driver *d;
 
@@ -124,6 +128,11 @@ chart_submit(struct chart *chart)
 
   d = outp_drivers (NULL);
   d->class->finalise_chart(d, chart);
+
+  for (i = 0 ; i < chart->n_datasets; ++i)
+    free (chart->dataset[i]);
+  free (chart->dataset);
+
   free(chart);
 }
 
index 9586eb1e068aba5f480b758a79db4463c8ae465a..4da12ecfe5b0afba3ea7035680fca5b1ea1d9af9 100644 (file)
@@ -64,6 +64,8 @@ struct chart {
 
   int legend_left ;
   int legend_right ;
+  const char **dataset;
+  int n_datasets;
 
 
   /* Default font size for the plot (if zero, then use plotter default) */
@@ -78,6 +80,7 @@ struct chart {
   double x_max;
   double y_min;
   double y_max;
+  bool in_path;
 };
 
 
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 75c49aa88c9d4fc84ec56d9439ae8560f3e2a09b..cb2f346b1b4134a0092cb322e3fe2a7a16177c55 100644 (file)
 #include <libpspp/compiler.h>
 
 
-
-struct dataset
+/* Start a new vector called NAME */
+void
+chart_vector_start (struct chart *ch, const char *name)
 {
-  int n_data;
-  const char *label;
-};
+  if ( ! ch )
+    return ;
 
+  pl_savestate_r (ch->lp);
 
-#define DATASETS 2
+  pl_colorname_r (ch->lp, data_colour [ch->n_datasets % N_CHART_COLOURS]);
 
-static const struct dataset dataset[DATASETS] =
-  {
-    { 13, "male"},
-    { 11, "female"},
-  };
+  ch->n_datasets++;
+  ch->dataset = xrealloc (ch->dataset, ch->n_datasets * sizeof (*ch->dataset));
 
+  ch->dataset[ch->n_datasets - 1] = strdup (name);
+}
 
 /* Plot a data point */
 void
-chart_datum(struct chart *ch, int dataset UNUSED, double x, double y)
+chart_datum (struct chart *ch, int dataset UNUSED, double x, double y)
 {
   if ( ! ch )
     return ;
@@ -66,13 +66,48 @@ chart_datum(struct chart *ch, int dataset UNUSED, double x, double y)
   }
 }
 
+void
+chart_vector_end (struct chart *ch)
+{
+  pl_endpath_r (ch->lp);
+  pl_colorname_r (ch->lp, "black");
+  ch->in_path = false;
+  pl_restorestate_r (ch->lp);
+}
+
+/* Plot a data point */
+void
+chart_vector (struct chart *ch, double x, double y)
+{
+  if ( ! ch )
+    return ;
+
+  {
+    const double x_pos =
+      (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
+
+    const double y_pos =
+      (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
+
+    if ( ch->in_path)
+      pl_fcont_r (ch->lp, x_pos, y_pos);
+    else
+      {
+       pl_fmove_r (ch->lp, x_pos, y_pos);
+       ch->in_path = true;
+      }
+  }
+}
+
+
+
 /* Draw a line with slope SLOPE and intercept INTERCEPT.
    between the points limit1 and limit2.
    If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
    y axis otherwise the x axis
 */
 void
-chart_line(struct chart *ch, double slope, double intercept,
+chart_line (struct chart *ch, double slope, double intercept,
           double limit1, double limit2, enum CHART_DIM lim_dim)
 {
   double x1, y1;
@@ -107,5 +142,4 @@ chart_line(struct chart *ch, double slope, double intercept,
   pl_fline_r(ch->lp, x1, y1, x2, y2);
 
   pl_restorestate_r(ch->lp);
-
 }
index 40e3f71455f5886813eee2d3e63e04d55b3fe095..0874b9cc61d4d8f507188a00f07c44ca614176cf 100644 (file)
@@ -27,16 +27,19 @@ enum CHART_DIM
   };
 
 
+void chart_vector_start (struct chart *ch, const char *name);
+void chart_vector (struct chart *ch, double x, double y);
+void chart_vector_end (struct chart *ch);
 
 /* Plot a data point */
-void chart_datum(struct chart *ch, int dataset UNUSED, double x, double y);
+void chart_datum (struct chart *ch, int dataset UNUSED, double x, double y);
 
 /* Draw a line with slope SLOPE and intercept INTERCEPT.
    between the points limit1 and limit2.
    If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
    y axis otherwise the x axis
 */
-void chart_line(struct chart *ch, double slope, double intercept,
+void chart_line (struct chart *ch, double slope, double intercept,
                double limit1, double limit2, enum CHART_DIM lim_dim);
 
 
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 3b4f1b377a12a624ceb929d00c617bb4bd581f30..5641db1213be2cd070d66cffb7daf479823076f6 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
@@ -210,3 +210,47 @@ chart_write_ylabel(struct chart *ch, const char *label)
 
   pl_restorestate_r(ch->lp);
 }
+
+
+void
+chart_write_legend (struct chart *ch)
+{
+  int i;
+  const int vstep = ch->font_size * 2;
+  const int xpad = 10;
+  const int ypad = 10;
+  const int swatch = 20;
+  const int legend_top = ch->data_top;
+  const int legend_bottom = legend_top -
+    (vstep * ch->n_datasets + 2 * ypad );
+
+  if ( ! ch )
+    return ;
+
+  pl_savestate_r (ch->lp);
+
+  pl_box_r (ch->lp, ch->legend_left, legend_top,
+           ch->legend_right - xpad, legend_bottom);
+
+  for (i = 0 ; i < ch->n_datasets ; ++i )
+    {
+      const int ypos = vstep * (i + 1);
+      const int xpos = ch->legend_left + xpad;
+      pl_move_r (ch->lp, xpos, legend_top - ypos);
+
+      pl_savestate_r(ch->lp);
+       pl_fillcolorname_r (ch->lp, data_colour [ i % N_CHART_COLOURS]);
+
+       pl_pentype_r (ch->lp, 1);
+       pl_filltype_r (ch->lp, 1);
+       pl_boxrel_r (ch->lp, 0, 0, swatch, swatch);
+
+
+      pl_moverel_r (ch->lp, swatch, 0);
+      pl_alabel_r (ch->lp, 0, 0, ch->dataset[i]);
+
+      pl_restorestate_r (ch->lp);
+    }
+
+  pl_restorestate_r (ch->lp);
+}
index 4a1dc10538d2567b630cca096886c8ca8e5011cd..f4cc5bbf06c3f91650c4988b15e76e36f8efc74a 100644 (file)
@@ -70,4 +70,6 @@ void chart_write_xlabel(struct chart *ch, const char *label) ;
 /* Write the ordinate label */
 void  chart_write_ylabel(struct chart *ch, const char *label);
 
+void chart_write_legend (struct chart *ch);
+
 #endif
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 5c8dc6f7ea8767feb23785617cba912e0529a0ed..72edf17f08b7f6e9e0dec91ad8b56f5b9e87b88b 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
@@ -29,6 +29,7 @@
 #include <data/data-out.h>
 #include <data/format.h>
 #include <data/value.h>
+#include <data/dictionary.h>
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/misc.h>
@@ -372,18 +373,6 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
     }
 }
 
-/* Formats text TEXT and arguments ARGS as indicated in OPT in
-   TABLE's pool and returns the resultant string. */
-static struct substring
-text_format (struct tab_table *table, int opt, const char *text, va_list args)
-{
-  assert (table != NULL && text != NULL);
-
-  return ss_cstr (opt & TAT_PRINTF
-                  ? pool_vasprintf (table->container, text, args)
-                  : pool_strdup (table->container, text));
-}
-
 /* Set the title of table T to TITLE, which is formatted as if
    passed to printf(). */
 void
@@ -399,10 +388,11 @@ tab_title (struct tab_table *t, const char *title, ...)
 
 /* Set DIM_FUNC as the dimension function for table T. */
 void
-tab_dim (struct tab_table *t, tab_dim_func *dim_func)
+tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
 {
   assert (t != NULL && t->dim == NULL);
   t->dim = dim_func;
+  t->dim_aux = aux;
 }
 
 /* Returns the natural width of column C in table T for driver D, that
@@ -496,7 +486,8 @@ tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
 /* Callback function to set all columns and rows to their natural
    dimensions.  Not really meant to be called directly.  */
 void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
+tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
+                        void *aux UNUSED)
 {
   int i;
 
@@ -516,7 +507,8 @@ tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
    from V, displayed with format spec F. */
 void
 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
-          const union value *v, const struct fmt_spec *f)
+          const union value *v, const struct dictionary *dict, 
+          const struct fmt_spec *f)
 {
   char *contents;
 
@@ -535,11 +527,10 @@ tab_value (struct tab_table *table, int c, int r, unsigned char opt,
     }
 #endif
 
-  contents = pool_alloc (table->container, f->w);
-  table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
-  table->ct[c + r * table->cf] = opt;
+  contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
 
-  data_out (v, f, contents);
+  table->cc[c + r * table->cf] = ss_cstr (contents);
+  table->ct[c + r * table->cf] = opt;
 }
 
 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
@@ -548,8 +539,7 @@ void
 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
           double val, int w, int d)
 {
-  char *contents;
-  char buf[40], *cp;
+  char *s, *cp;
 
   struct fmt_spec f;
   union value double_value;
@@ -578,17 +568,15 @@ tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
 #endif
 
   double_value.f = val;
-  data_out (&double_value, &f, buf);
+  s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
 
-  cp = buf;
-  while (isspace ((unsigned char) *cp) && cp < &buf[w])
+  cp = s;
+  while (isspace ((unsigned char) *cp) && cp < &s[w])
     cp++;
-  f.w = w - (cp - buf);
+  f.w = w - (cp - s);
 
-  contents = pool_alloc (table->container, f.w);
-  table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
+  table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
   table->ct[c + r * table->cf] = opt;
-  memcpy (contents, cp, f.w);
 }
 
 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
@@ -599,11 +587,8 @@ void
 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
           double val, const struct fmt_spec *fmt)
 {
-  int w;
-  char *contents;
-  char buf[40], *cp;
-
-  union value double_value;
+  struct substring ss;
+  union value double_value ;
 
   assert (table != NULL);
 
@@ -632,35 +617,23 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
 #endif
 
   double_value.f = val;
-  data_out (&double_value, fmt, buf);
+  ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container));
 
-  cp = buf;
-  while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
-    cp++;
-  w = fmt->w - (cp - buf);
+  ss_ltrim (&ss, ss_cstr (" "));
 
-  contents = pool_alloc (table->container, w);
-  table->cc[c + r * table->cf] = ss_buffer (contents, w);
+  table->cc[c + r * table->cf] = ss;
   table->ct[c + r * table->cf] = opt;
-  memcpy (contents, cp, w);
 }
 
 
-/* Sets cell (C,R) in TABLE, with options OPT, to have text value
-   TEXT. */
-void
-tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
+static void
+do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
 {
-  va_list args;
-
-  assert (table != NULL && text != NULL);
-
-  assert (c >= 0 );
-  assert (r >= 0 );
+  assert (c >= 0);
+  assert (r >= 0);
   assert (c < table->nc);
   assert (r < table->nr);
 
-
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
       || c + table->col_ofs >= table->nc
@@ -675,21 +648,38 @@ tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text,
     }
 #endif
 
-  va_start (args, text);
-  table->cc[c + r * table->cf] = text_format (table, opt, text, args);
+  table->cc[c + r * table->cf] = ss_cstr (text);
   table->ct[c + r * table->cf] = opt;
-  va_end (args);
 }
 
-/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
-   options OPT to have text value TEXT. */
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+   TEXT. */
 void
-tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
-               unsigned opt, const char *text, ...)
+tab_text (struct tab_table *table, int c, int r, unsigned opt,
+          const char *text)
 {
-  struct tab_joined_cell *j;
+  do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+   FORMAT, which is formatted as if passed to printf. */
+void
+tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
+                 const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  do_tab_text (table, c, r, opt,
+               pool_vasprintf (table->container, format, args));
+  va_end (args);
+}
 
-  assert (table != NULL && text != NULL);
+static void
+do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
+                   unsigned opt, char *text)
+{
+  struct tab_joined_cell *j;
 
   assert (x1 + table->col_ofs >= 0);
   assert (y1 + table->row_ofs >= 0);
@@ -723,14 +713,7 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   j->y1 = y1 + table->row_ofs;
   j->x2 = ++x2 + table->col_ofs;
   j->y2 = ++y2 + table->row_ofs;
-
-  {
-    va_list args;
-
-    va_start (args, text);
-    j->contents = text_format (table, opt, text, args);
-    va_end (args);
-  }
+  j->contents = ss_cstr (text);
 
   opt |= TAB_JOIN;
 
@@ -757,6 +740,31 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   }
 }
 
+/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
+   options OPT to have text value TEXT. */
+void
+tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
+                unsigned opt, const char *text)
+{
+  do_tab_joint_text (table, x1, y1, x2, y2, opt,
+                     pool_strdup (table->container, text));
+}
+
+/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
+   with options OPT to have text value FORMAT, which is formatted
+   as if passed to printf. */
+void
+tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
+                       unsigned opt, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  do_tab_joint_text (table, x1, y1, x2, y2, opt,
+                     pool_vasprintf (table->container, format, args));
+  va_end (args);
+}
+
 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
 void
 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
@@ -787,7 +795,7 @@ tab_raw (struct tab_table *table, int c, int r, unsigned opt,
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d)
+nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   t->w[0] = tab_natural_width (t, d, 0);
   t->h[0] = d->font_height;
@@ -796,36 +804,47 @@ nowrap_dim (struct tab_table *t, struct outp_driver *d)
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-wrap_dim (struct tab_table *t, struct outp_driver *d)
+wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
 {
   t->w[0] = tab_natural_width (t, d, 0);
   t->h[0] = tab_natural_height (t, d, 0);
 }
 
-/* Outputs text BUF as a table with a single cell having cell options
+static void
+do_tab_output_text (struct tab_table *t, int options, char *text)
+{
+  do_tab_text (t, 0, 0, options, text);
+  tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
+  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
+  tab_submit (t);
+}
+
+/* Outputs TEXT as a table with a single cell having cell options
    OPTIONS, which is a combination of the TAB_* and TAT_*
-   constants. */
+   constants.  */
 void
-tab_output_text (int options, const char *buf, ...)
+tab_output_text (int options, const char *text)
 {
-  struct tab_table *t = tab_create (1, 1, 0);
-  char *tmp_buf = NULL;
-
-  if (options & TAT_PRINTF)
-    {
-      va_list args;
+  struct tab_table *table = tab_create (1, 1, 0);
+  do_tab_output_text (table, options, pool_strdup (table->container, text));
+}
 
-      va_start (args, buf);
-      buf = tmp_buf = xvasprintf (buf, args);
-      va_end (args);
-    }
+/* Outputs FORMAT as a table with a single cell having cell
+   options OPTIONS, which is a combination of the TAB_* and TAT_*
+   constants.  FORMAT is formatted as if it was passed through
+   printf. */
+void
+tab_output_text_format (int options, const char *format, ...)
+{
+  struct tab_table *table;
+  va_list args;
 
-  tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
-  tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
-  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
-  tab_submit (t);
+  table = tab_create (1, 1, 0);
 
-  free (tmp_buf);
+  va_start (args, format);
+  do_tab_output_text (table, options,
+                      pool_vasprintf (table->container, format, args));
+  va_end (args);
 }
 
 /* Set table flags to FLAGS. */
@@ -982,7 +1001,7 @@ tabi_driver (struct outp_driver *driver)
 #endif
 
   assert (t->dim != NULL);
-  t->dim (t, d);
+  t->dim (t, d, t->dim_aux);
 
 #if DEBUGGING
   {
index 829410edfd3db644930a18233f9a3a939a8413c9..1748c24c855e71ec8c0212dac76d00662db76f67 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
@@ -67,7 +67,8 @@ struct tab_joined_cell
 
 struct outp_driver;
 struct tab_table;
-typedef void tab_dim_func (struct tab_table *, struct outp_driver *);
+typedef void tab_dim_func (struct tab_table *, struct outp_driver *,
+                           void *aux);
 
 /* A table. */
 struct tab_table
@@ -87,6 +88,7 @@ struct tab_table
     unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
     unsigned char *rv;         /* Vert rules; unsigned char[nr][nc+1]. */
     tab_dim_func *dim;         /* Calculates cell widths and heights. */
+    void *dim_aux;              /* Auxiliary data for dim function. */
 
     /* Calculated during output. */
     int *w;                    /* Column widths; [nc]. */
@@ -133,7 +135,7 @@ void tab_submit (struct tab_table *);
 tab_dim_func tab_natural_dimensions;
 int tab_natural_width (struct tab_table *t, struct outp_driver *d, int c);
 int tab_natural_height (struct tab_table *t, struct outp_driver *d, int r);
-void tab_dim (struct tab_table *, tab_dim_func *);
+void tab_dim (struct tab_table *, tab_dim_func *, void *aux);
 
 /* Rules. */
 void tab_hline (struct tab_table *, int style, int x1, int x2, int y);
@@ -145,16 +147,17 @@ void tab_box (struct tab_table *, int f_h, int f_v, int i_h, int i_v,
 enum
   {
     TAT_NONE = 0,              /* No options. */
-    TAT_PRINTF = 0x0100,       /* Format the text string with sprintf. */
     TAT_TITLE = 0x0200 | TAB_EMPH, /* Title attributes. */
     TAT_NOWRAP = 0x0800         /* No text wrap (tab_output_text() only). */
   };
 
 /* Cells. */
 struct fmt_spec;
+struct dictionary;
 union value;
 void tab_value (struct tab_table *, int c, int r, unsigned char opt,
-               const union value *, const struct fmt_spec *);
+               const union value *, const struct dictionary *dict,
+               const struct fmt_spec *);
 
 void tab_fixed (struct tab_table *, int c, int r, unsigned char opt,
                double v, int w, int d);
@@ -162,11 +165,15 @@ void tab_fixed (struct tab_table *, int c, int r, unsigned char opt,
 void tab_double (struct tab_table *, int c, int r, unsigned char opt,
                double v, const struct fmt_spec *);
 
-void tab_text (struct tab_table *, int c, int r, unsigned opt,
-              const char *, ...)
+void tab_text (struct tab_table *, int c, int r, unsigned opt, const char *);
+void tab_text_format (struct tab_table *, int c, int r, unsigned opt,
+                      const char *, ...)
      PRINTF_FORMAT (5, 6);
+
 void tab_joint_text (struct tab_table *, int x1, int y1, int x2, int y2,
-                    unsigned opt, const char *, ...)
+                    unsigned opt, const char *);
+void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
+                            unsigned opt, const char *, ...)
      PRINTF_FORMAT (7, 8);
 
 /* Cell low-level access. */
@@ -183,7 +190,8 @@ void tab_next_row (struct tab_table *);
 #define tab_col(TABLE) ((TABLE)->col_ofs)
 
 /* Simple output. */
-void tab_output_text (int options, const char *string, ...)
+void tab_output_text (int options, const char *string);
+void tab_output_text_format (int options, const char *, ...)
      PRINTF_FORMAT (2, 3);
 
 /* Embedding the command name in the output. */
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..d9bc2803321e0ad2efe5411dd728b840047b5b34 100644 (file)
 #include <config.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <libpspp/copyleft.h>
 #include <libpspp/version.h>
 #include "about.h"
 #include "helper.h"
 
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 
-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");
-
-  GtkWidget *about =  get_widget_assert (xml, "aboutdialog1");
+  GtkWidget *about =  gtk_about_dialog_new ();
 
   GdkPixbuf *pb =
     gdk_pixbuf_new_from_file_at_size (relocate (PKGDATADIR "/pspplogo.png"),
@@ -42,8 +43,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");
@@ -60,6 +60,17 @@ about_new (GtkMenuItem *m, GtkWindow *parent)
   gtk_about_dialog_set_license (GTK_ABOUT_DIALOG (about),
                                copyleft);
 
+  gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (about),
+                                _("A program for the analysis of sampled data"));
+
+  gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG (about),
+                                 "Free Software Foundation");
+
+
+  /* TRANSLATORS: Use this string to list the people who have helped with
+     translation to your language. */
+  gtk_about_dialog_set_translator_credits (GTK_ABOUT_DIALOG (about),
+                                          _("translator-credits"));
 
   gtk_window_set_transient_for (GTK_WINDOW (about), parent);
 
index 8dabffbf695e643da35d7e63c5dfcf390ea1b2ab..110ef43f04ac2d53551ba457153a75fd0208888b 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,63 @@ 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/find.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 +97,99 @@ 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/executor.c \
+       src/ui/gui/executor.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 +208,41 @@ 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/find.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 +253,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..923953a1514bd34e0d223144ed4d4aaec27107ed 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 "executor.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..e779fba6d7f7c847144d1d6254ad57354bb705e2 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 <libpspp/i18n.h>
 
 #include <language/expressions/public.h>
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "executor.h"
 
 static void function_list_populate (GtkTreeView *tv);
 
@@ -47,7 +48,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 +114,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 +138,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 +366,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 +395,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 +447,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 +458,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);
       }
@@ -606,7 +604,6 @@ insert_source_row_into_text_view (GtkTreeIter iter,
   gint *idx;
   struct variable *var;
   GtkTreeIter dict_iter;
-  gchar *name;
   GtkTextBuffer *buffer;
 
   g_return_if_fail (GTK_IS_TEXT_VIEW (dest));
@@ -634,13 +631,10 @@ insert_source_row_into_text_view (GtkTreeIter iter,
 
   gtk_tree_path_free (path);
 
-  name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
-
   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dest));
 
   erase_selection (buffer);
 
-  gtk_text_buffer_insert_at_cursor (buffer, name, -1);
+  gtk_text_buffer_insert_at_cursor (buffer, var_get_name (var), -1);
 
-  g_free (name);
 }
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..f5593586778cd3b3bf17bcdb6376564f44d08085 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 "executor.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..a867fe016d05131f0ff759e3536118c5ae60c855 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 "executor.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 0aab294e12ef0b2a8ca937851f60533525847619..5d52204ce566ad89cdb1807adda7be338bf846e5 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include <libpspp/i18n.h>
 #include "dialog-common.h"
 
 #include "psppire-var-ptr.h"
@@ -113,14 +114,9 @@ cell_var_name (GtkTreeViewColumn *tree_column,
               gpointer data)
 {
   PsppireDict *dict = data;
-  struct variable *var;
-  gchar *name;
-
-  var = get_selected_variable (tree_model, iter, dict);
+  const struct variable *var = get_selected_variable (tree_model, iter, dict);
 
-  name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
-  g_object_set (cell, "text", name, NULL);
-  g_free (name);
+  g_object_set (cell, "text", var_get_name (var), NULL);
 }
 
 
index 356758beeea4100c4f4f1fd3feeaae71ebeb2fb4..1665d7ff3f7a3c0a4bc8e1b4ebeb12b99cc8cfb9 100644 (file)
 #include <gettext.h>
 #include <gtk/gtk.h>
 
+#include "psppire-conf.h"
 #include "dict-display.h"
 
 #include "psppire-dict.h"
+#include <libpspp/i18n.h>
 #include "helper.h"
 #include <data/variable.h>
 #include <data/format.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 +53,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
@@ -290,7 +67,6 @@ insert_source_row_into_entry (GtkTreeIter iter,
   gint *idx;
   struct variable *var;
   GtkTreeIter dict_iter;
-  gchar *name;
 
   g_return_if_fail (GTK_IS_ENTRY(dest));
 
@@ -304,9 +80,7 @@ insert_source_row_into_entry (GtkTreeIter iter,
 
   gtk_tree_path_free (path);
 
-  name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
-  gtk_entry_set_text (GTK_ENTRY (dest),  name);
-  g_free (name);
+  gtk_entry_set_text (GTK_ENTRY (dest),  var_get_name (var));
 }
 
 
@@ -345,14 +119,13 @@ is_currently_in_entry (GtkTreeModel *model, GtkTreeIter *iter,
                       PsppireSelector *selector)
 {
   gboolean result;
-  gchar *name;
   GtkTreeIter dict_iter;
   GtkTreeModel *dict;
   struct variable *var;
   gint dict_index;
   gint *indeces;
   GtkTreePath *path;
-  const gchar *text =   gtk_entry_get_text (GTK_ENTRY (selector->dest));
+  const gchar *text =  gtk_entry_get_text (GTK_ENTRY (selector->dest));
 
   get_base_model (model, iter, &dict, &dict_iter);
 
@@ -366,11 +139,10 @@ is_currently_in_entry (GtkTreeModel *model, GtkTreeIter *iter,
 
   gtk_tree_path_free (path);
 
-  name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
-  result = ( 0 == strcmp (text, name));
-  g_free (name);
+  result = ( 0 == strcmp (text, var_get_name (var) ));
 
   return result;
 }
 
 
+
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..13ef847a36f436b49c8cd062eda617d7f0563624 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 "executor.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>
diff --git a/src/ui/gui/executor.c b/src/ui/gui/executor.c
new file mode 100644 (file)
index 0000000..9336f7c
--- /dev/null
@@ -0,0 +1,110 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2007, 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 "executor.h"
+#include "psppire-data-store.h"
+#include <data/lazy-casereader.h>
+#include <data/procedure.h>
+#include <libpspp/getl.h>
+#include <language/lexer/lexer.h>
+#include <language/command.h>
+#include <output/manager.h>
+#include "psppire-output-window.h"
+
+extern struct dataset *the_dataset;
+extern struct source_stream *the_source_stream;
+extern PsppireDataStore *the_data_store;
+
+/* Lazy casereader callback function used by execute_syntax. */
+static struct casereader *
+create_casereader_from_data_store (void *data_store_)
+{
+  PsppireDataStore *data_store = data_store_;
+  return psppire_data_store_get_reader (data_store);
+}
+
+gboolean
+execute_syntax (struct getl_interface *sss)
+{
+  struct lexer *lexer;
+  gboolean retval = TRUE;
+
+  struct casereader *reader;
+  const struct caseproto *proto;
+  casenumber case_cnt;
+  unsigned long int lazy_serial;
+
+  /* When the user executes a number of snippets of syntax in a
+     row, none of which read from the active file, the GUI becomes
+     progressively less responsive.  The reason is that each syntax
+     execution encapsulates the active file data in another
+     datasheet layer.  The cumulative effect of having a number of
+     layers of datasheets wastes time and space.
+
+     To solve the problem, we use a "lazy casereader", a wrapper
+     around the casereader obtained from the data store, that
+     only actually instantiates that casereader when it is
+     needed.  If the data store casereader is never needed, then
+     it is reused the next time syntax is run, without wrapping
+     it in another layer. */
+  proto = psppire_data_store_get_proto (the_data_store);
+  case_cnt = psppire_data_store_get_case_count (the_data_store);
+  reader = lazy_casereader_create (proto, case_cnt,
+                                   create_casereader_from_data_store,
+                                   the_data_store, &lazy_serial);
+  proc_set_active_file_data (the_dataset, reader);
+
+  g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
+
+  lexer = lex_create (the_source_stream);
+
+  getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
+
+  for (;;)
+    {
+      enum cmd_result result = cmd_parse (lexer, the_dataset);
+
+      if ( cmd_result_is_failure (result))
+       {
+         retval = FALSE;
+         if ( source_stream_current_error_mode (the_source_stream)
+              == ERRMODE_STOP )
+           break;
+       }
+
+      if ( result == CMD_EOF || result == CMD_FINISH)
+       break;
+    }
+
+  getl_abort_noninteractive (the_source_stream);
+
+  lex_destroy (lexer);
+
+  psppire_dict_replace_dictionary (the_data_store->dict,
+                                  dataset_dict (the_dataset));
+
+  reader = proc_extract_active_file_data (the_dataset);
+  if (!lazy_casereader_destroy (reader, lazy_serial))
+    psppire_data_store_set_reader (the_data_store, reader);
+
+  som_flush ();
+
+  psppire_output_window_reload ();
+
+  return retval;
+}
diff --git a/src/ui/gui/executor.h b/src/ui/gui/executor.h
new file mode 100644 (file)
index 0000000..40925be
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2007, 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 EXECUTOR_H
+#define EXECUTOR_H
+
+#include <glib.h>
+
+struct getl_interface;
+
+gboolean execute_syntax (struct getl_interface *sss);
+
+
+#endif
index 5d98b4ac91ccc0f22a783643825cc2a66bc444e4..faeb8f6490578ad703096f40cbce96cce44886a8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 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
@@ -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 ("find.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,
@@ -287,7 +285,7 @@ forward (casenumber *i, struct datasheet *data UNUSED)
 static void
 forward_wrap (casenumber *i, struct datasheet *data)
 {
-  if ( ++*i >=  datasheet_get_row_cnt (data) ) *i = 0;
+  if ( ++*i >=  datasheet_get_n_rows (data) ) *i = 0;
 }
 
 static void
@@ -301,7 +299,7 @@ static void
 backward_wrap (casenumber *i, struct datasheet *data)
 {
   if ( --*i < 0 )
-    *i = datasheet_get_row_cnt (data) - 1;
+    *i = datasheet_get_n_rows (data) - 1;
 }
 
 
@@ -346,7 +344,7 @@ cm1c (casenumber current, struct datasheet *data)
 static casenumber
 last (casenumber current, struct datasheet *data)
 {
-  return datasheet_get_row_cnt (data) ;
+  return datasheet_get_n_rows (data) ;
 }
 
 static casenumber
@@ -429,6 +427,7 @@ struct comparator
 {
   const struct variable *var;
   enum string_cmp_flags flags;
+  const PsppireDict *dict;
 
   bool (*compare) (const struct comparator *,
                   const union value *);
@@ -441,7 +440,7 @@ struct comparator
 struct value_comparator
 {
   struct comparator parent;
-  union value *pattern;
+  union value pattern;
 };
 
 /* A comparator which matches string values or parts thereof */
@@ -464,7 +463,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));
 }
 
 
@@ -494,20 +493,24 @@ static bool
 string_value_compare (const struct comparator *cmptr,
                      const union value *val)
 {
+  bool found;
+  char *text;
   const struct string_comparator *ssc =
     (const struct string_comparator *) cmptr;
 
-  const char *text = val->s;
   int width = var_get_width (cmptr->var);
-
+  g_return_val_if_fail (width > 0, false);
   assert ( ! (cmptr->flags & STR_CMP_LABELS));
 
-  g_return_val_if_fail (width > 0, false);
+  text = value_to_text (*val, cmptr->dict, *var_get_write_format (cmptr->var));
 
   if ( cmptr->flags & STR_CMP_SUBSTR)
-    return (NULL != g_strstr_len (text, width, ssc->pattern));
+    found =  (NULL != g_strstr_len (text, width, ssc->pattern));
   else
-    return (0 == strncmp (text, ssc->pattern, width));
+    found = (0 == strncmp (text, ssc->pattern, width));
+
+  free (text);
+  return found;
 }
 
 
@@ -528,9 +531,9 @@ regexp_value_compare (const struct comparator *cmptr,
 
   g_return_val_if_fail (width > 0, false);
 
+  text = value_to_text (*val, cmptr->dict, *var_get_write_format (cmptr->var));
   /* We must remove trailing whitespace, otherwise $ will not match where
      one would expect */
-  text = g_strndup (val->s, width);
   g_strchomp (text);
 
   retval = (0 == regexec (&rec->re, text, 0, 0, 0));
@@ -572,15 +575,15 @@ regexp_destroy (struct comparator *cmptr)
 }
 
 static void
-value_destroy (struct comparator *cmptr)
+cmptr_value_destroy (struct comparator *cmptr)
 {
   struct value_comparator *vc = (struct value_comparator *) cmptr;
-  free (vc->pattern);
+  value_destroy (&vc->pattern, var_get_width (cmptr->var));
 }
 
 
 static struct comparator *
-value_comparator_create (const struct variable *var, const char *target)
+value_comparator_create (const struct variable *var, const PsppireDict *dict, const char *target)
 {
   const struct fmt_spec *fmt;
   int width ;
@@ -590,28 +593,22 @@ value_comparator_create (const struct variable *var, const char *target)
   cmptr->flags = 0;
   cmptr->var = var;
   cmptr->compare  = value_compare ;
-  cmptr->destroy = value_destroy;
+  cmptr->destroy = cmptr_value_destroy;
+  cmptr->dict = dict;
 
   width = var_get_width (var);
   fmt = var_get_write_format (var);
 
-  vc->pattern = value_create (width);
+  value_init (&vc->pattern, width);
 
-  if ( ! data_in (ss_cstr (target),
-                  LEGACY_NATIVE,
-                 fmt->type,
-                 0, 0, 0,
-                 vc->pattern, width) )
-    {
-      free (vc);
-      return NULL;
-    }
+  text_to_value (target, &vc->pattern, dict, *var_get_write_format (var) );
 
   return cmptr;
 }
 
 static struct comparator *
-string_comparator_create (const struct variable *var, const char *target,
+string_comparator_create (const struct variable *var, const PsppireDict *dict, 
+                         const char *target,
                          enum string_cmp_flags flags)
 {
   struct string_comparator *ssc = xzalloc (sizeof (*ssc));
@@ -619,6 +616,7 @@ string_comparator_create (const struct variable *var, const char *target,
 
   cmptr->flags = flags;
   cmptr->var = var;
+  cmptr->dict = dict;
 
   if ( flags & STR_CMP_LABELS)
     cmptr->compare = string_label_compare;
@@ -632,7 +630,7 @@ string_comparator_create (const struct variable *var, const char *target,
 
 
 static struct comparator *
-regexp_comparator_create (const struct variable *var, const char *target,
+regexp_comparator_create (const struct variable *var, const PsppireDict *dict, const char *target,
                          enum string_cmp_flags flags)
 {
   int code;
@@ -641,6 +639,7 @@ regexp_comparator_create (const struct variable *var, const char *target,
 
   cmptr->flags = flags;
   cmptr->var = var;
+  cmptr->dict = dict;
   cmptr->compare  = (flags & STR_CMP_LABELS)
     ? regexp_label_compare : regexp_value_compare ;
 
@@ -690,16 +689,16 @@ comparator_destroy (struct comparator *cmptr)
 
 
 static struct comparator *
-comparator_factory (const struct variable *var, const char *str,
+comparator_factory (const struct variable *var, const PsppireDict *dict, const char *str,
                    enum string_cmp_flags flags)
 {
   if ( flags & STR_CMP_REGEXP )
-    return regexp_comparator_create (var, str, flags);
+    return regexp_comparator_create (var, dict, str, flags);
 
   if ( flags & (STR_CMP_SUBSTR | STR_CMP_LABELS) )
-    return string_comparator_create (var, str, flags);
+    return string_comparator_create (var, dict, str, flags);
 
-  return value_comparator_create (var, str);
+  return value_comparator_create (var, dict, str);
 }
 
 
@@ -741,12 +740,13 @@ find_value (const struct find_dialog *fd, casenumber current_row,
     flags |= STR_CMP_LABELS;
 
   {
-    union value *val = value_create (width);
+    union value val;
     casenumber i;
     const struct casenum_iterator *ip = get_iteration_params (fd);
     struct comparator *cmptr =
-      comparator_factory (var, target_string, flags);
+      comparator_factory (var, fd->dict, target_string, flags);
 
+    value_init (&val, width);
     if ( ! cmptr)
       goto finish;
 
@@ -754,10 +754,9 @@ find_value (const struct find_dialog *fd, casenumber current_row,
         i != ip->end (current_row, fd->data);
         ip->next (&i, fd->data))
       {
-       datasheet_get_value (fd->data, i, var_get_case_index (var),
-                            val, width);
+       datasheet_get_value (fd->data, i, var_get_case_index (var), &val);
 
-       if ( comparator_compare (cmptr, val))
+       if ( comparator_compare (cmptr, &val))
          {
            *row = i;
            break;
@@ -766,6 +765,6 @@ find_value (const struct find_dialog *fd, casenumber current_row,
 
   finish:
     comparator_destroy (cmptr);
-    free (val);
+    value_destroy (&val, width);
   }
 }
diff --git a/src/ui/gui/find.glade b/src/ui/gui/find.glade
new file mode 100644 (file)
index 0000000..3ab4f4e
--- /dev/null
@@ -0,0 +1,236 @@
+<?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="find-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">Find Case</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <widget class="GtkHBox" id="dialog-hbox14">
+        <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>
+        <child>
+          <widget class="GtkHBox" id="hbox10">
+            <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="spacing">5</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_ETCHED_IN</property>
+                <child>
+                  <widget class="PsppireDictView" id="find-variable-treeview">
+                    <property name="height_request">300</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                    <property name="fixed_height_mode">True</property>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox27">
+                <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="PsppireSelector" id="find-selector">
+                    <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="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox10">
+                <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="GtkVBox" id="vbox11">
+                    <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="label33">
+                        <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:</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="padding">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="find-variable-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>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkVBox" id="vbox15">
+                    <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="label34">
+                        <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">Value:</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="padding">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkEntry" id="find-value-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>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="find-value-labels-checkbutton">
+                        <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 value labels</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>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkVButtonBox" id="bb1">
+                    <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="find-match-regexp-checkbutton">
+                        <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="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="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="find-wrap">
+                        <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">Wrap around</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkCheckButton" id="find-backwards">
+                        <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">Search backward</property>
+                        <property name="response_id">0</property>
+                        <property name="draw_indicator">True</property>
+                      </widget>
+                      <packing>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </widget>
+        </child>
+        <child>
+          <widget class="PsppireVButtonBox" id="find-buttonbox">
+            <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="buttons">PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK | PSPPIRE_BUTTON_RESET_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>
+    </child>
+  </widget>
+</glade-interface>
index 802a4664e710b8a2352d9938af4dba1930e2c6d6..9d9814f1a64045ba96fe967c85588e796869d803 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 "executor.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..ff750b25f7aa6ffe3b6a99e55a2d014791bcf5d8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 2009  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -31,7 +31,8 @@
 #include <data/dictionary.h>
 #include <data/casereader-provider.h>
 #include <libpspp/message.h>
-
+#include "psppire-syntax-window.h"
+#include <gtk/gtkbuilder.h>
 #include <libpspp/i18n.h>
 
 #include <ctype.h>
 #include <stdlib.h>
 #include <data/settings.h>
 
-#include <language/command.h>
-#include <data/lazy-casereader.h>
-#include <data/procedure.h>
-#include <language/lexer/lexer.h>
 #include "psppire-data-store.h"
-#include <output/manager.h>
-#include "output-viewer.h"
 
 #include "xalloc.h"
 
 /* Formats a value according to FORMAT
    The returned string must be freed when no longer required */
 gchar *
-value_to_text (union value v, struct fmt_spec format)
+value_to_text (union value v, const PsppireDict *dict, struct fmt_spec format)
 {
   gchar *s = 0;
 
-  s = g_new (gchar, format.w + 1);
-  data_out (&v, &format, s);
-  s[format.w]='\0';
+  s = data_out (&v, dict_get_encoding (dict->dict),  &format);
   g_strchug (s);
 
   return s;
@@ -70,6 +63,7 @@ value_to_text (union value v, struct fmt_spec format)
 
 gboolean
 text_to_value (const gchar *text, union value *v,
+              const PsppireDict *dict,
              struct fmt_spec format)
 {
   bool ok;
@@ -92,7 +86,8 @@ text_to_value (const gchar *text, union value *v,
     }
 
   msg_disable ();
-  ok = data_in (ss_cstr (text), LEGACY_NATIVE, format.type, 0, 0, 0,
+  ok = data_in (ss_cstr (text), UTF8, format.type, 0, 0, 0,
+               dict->dict,
                 v, fmt_var_width (&format));
   msg_enable ();
 
@@ -100,29 +95,81 @@ 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.
-   The return value must be freed when no longer required*/
+/* 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 *
-pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err)
+convert_glib_filename_to_system_filename (const gchar *fname, GError **err)
 {
-  return recode_string (CONV_PSPP_TO_UTF8, text, len);
+  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 = xstrdup (fname);
+#endif
+
+  return output_name;
 }
 
+
+
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
@@ -143,163 +190,52 @@ 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);
-
-  g_list_free (helps);
-}
-
-
-
-void
-reference_manual (GtkMenuItem *menu, gpointer data)
-{
-  GError *err = NULL;
-  if ( ! g_spawn_command_line_async ("yelp info:pspp", &err) )
+  GSList *i;
+  for ( i = helps; i ; i = g_slist_next (i))
     {
-      msg (ME, _("Cannot open reference manual: %s"), err->message);
-    }
-  g_clear_error (&err);
-}
-
+      GObject *o = i->data;
+      if ( GTK_IS_WIDGET (o) )
+       {
+         gchar *name = NULL;
+         gchar s[12] = {0};
+         g_object_get (o, "name", &name, NULL);
 
-extern struct dataset *the_dataset;
-extern struct source_stream *the_source_stream;
-extern PsppireDataStore *the_data_store;
+         if ( name)
+           strncpy (s, name, 11);
+         s[11] = '\0';
 
-/* Lazy casereader callback function used by execute_syntax. */
-static struct casereader *
-create_casereader_from_data_store (void *data_store_)
-{
-  PsppireDataStore *data_store = data_store_;
-  return psppire_data_store_get_reader (data_store);
-}
 
-gboolean
-execute_syntax (struct getl_interface *sss)
-{
-  struct lexer *lexer;
-  gboolean retval = TRUE;
-
-  struct casereader *reader;
-  size_t value_cnt;
-  casenumber case_cnt;
-  unsigned long int lazy_serial;
-
-  /* When the user executes a number of snippets of syntax in a
-     row, none of which read from the active file, the GUI becomes
-     progressively less responsive.  The reason is that each syntax
-     execution encapsulates the active file data in another
-     datasheet layer.  The cumulative effect of having a number of
-     layers of datasheets wastes time and space.
-
-     To solve the problem, we use a "lazy casereader", a wrapper
-     around the casereader obtained from the data store, that
-     only actually instantiates that casereader when it is
-     needed.  If the data store casereader is never needed, then
-     it is reused the next time syntax is run, without wrapping
-     it in another layer. */
-  value_cnt = psppire_data_store_get_value_count (the_data_store);
-  case_cnt = psppire_data_store_get_case_count (the_data_store);
-  reader = lazy_casereader_create (value_cnt, case_cnt,
-                                   create_casereader_from_data_store,
-                                   the_data_store, &lazy_serial);
-  proc_set_active_file_data (the_dataset, reader);
-
-  g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
-
-  lexer = lex_create (the_source_stream);
-
-  getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
-
-  for (;;)
-    {
-      enum cmd_result result = cmd_parse (lexer, the_dataset);
-
-      if ( cmd_result_is_failure (result))
-       {
-         retval = FALSE;
-         if ( source_stream_current_error_mode (the_source_stream)
-              == ERRMODE_STOP )
-           break;
+         if ( 0 == strcmp ("help_button", s))
+           {
+           g_signal_connect (o, "clicked", give_help, 0);
+           }
        }
-
-      if ( result == CMD_EOF || result == CMD_FINISH)
-       break;
     }
 
-  getl_abort_noninteractive (the_source_stream);
-
-  lex_destroy (lexer);
-
-  psppire_dict_replace_dictionary (the_data_store->dict,
-                                  dataset_dict (the_dataset));
-
-  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));
-
-  som_flush ();
-
-  reload_the_viewer ();
-
-  return retval;
+  g_slist_free (helps);
 }
 
 
-
-#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)
+reference_manual (GtkMenuItem *menu, gpointer 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
+  GError *err = NULL;
+  gchar *cmd = g_strdup_printf ("yelp file://%s", relocate (DOCDIR "/pspp.xml"));
+
+  if ( ! g_spawn_command_line_async (cmd, &err) )
     {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
+      msg (ME, _("Cannot open reference manual: %s"), err->message);
     }
-  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);
+  g_free (cmd);
+  g_clear_error (&err);
 }
 
+
 /* Create a deep copy of SRC */
 GtkListStore *
 clone_list_store (const GtkListStore *src)
@@ -344,3 +280,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..f6c084d48e8373d12366b24455f45267daacd89b 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)
+#include "psppire-dict.h"
+
+void paste_syntax_in_new_window (const gchar *syntax);
 
 struct fmt_spec;
 
+
 /* Formats a value according to FORMAT
    The returned string must be freed when no longer required */
-gchar * value_to_text (union value v, struct fmt_spec format);
+gchar * value_to_text (union value v, const PsppireDict *dict, struct fmt_spec format);
 
 
 gboolean text_to_value (const gchar *text, union value *v,
+                       const PsppireDict *dict,
                       struct fmt_spec format);
 
-GtkWidget * get_widget_assert (GladeXML *xml, 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);
+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);
 
+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..05cc421b3ede67e4f08ef4ded3c10b0f065c5ec9 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
+#include <gl/xalloc.h>
 #include <gtk/gtk.h>
 #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 +106,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 ();
 
@@ -88,18 +131,32 @@ run_inner_loop (gpointer data)
 }
 
 
+static GMemVTable vtable =
+  {
+    xmalloc,
+    xrealloc,
+    free,
+    xcalloc,
+    malloc,
+    realloc
+  };
 
 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]);
 
+  g_mem_set_vtable (&vtable);
+
+  gtk_disable_setlocale ();
+
+
   if ( ! gtk_parse_args (&argc, &argv) )
     {
       perror ("Error parsing arguments");
@@ -110,78 +167,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..7513295323988677b38c26807cff73b96d10b04a 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,12 @@ 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");
+
+  GTK_WIDGET_SET_FLAGS (get_widget_assert (message_xml, "close-button"),
+                       GTK_CAN_DEFAULT);
+
 }
 
 void
@@ -72,7 +74,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 +185,7 @@ enqueue_msg (const struct msg *msg)
     }
 }
 
-gboolean
+static gboolean
 popup_messages (gpointer unused UNUSED)
 {
   GtkTextBuffer *text_buffer;
@@ -194,12 +196,23 @@ popup_messages (gpointer unused UNUSED)
   struct string msg = DS_EMPTY_INITIALIZER;
   int message_cnt;
 
+  gdk_threads_enter ();
+
+  /* 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);
+      gdk_threads_leave ();
+      return TRUE;
+    }
 
   /* Compose the lead-in. */
   message_cnt = error_cnt + warning_cnt + note_cnt;
@@ -239,10 +252,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,12 +266,15 @@ 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_default (get_widget_assert (message_xml, "close-button"));
+  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);
 
+  gdk_threads_leave ();
   return FALSE;
 
 use_fallback:
@@ -271,6 +283,7 @@ use_fallback:
   fputs (ds_cstr (&msg), stderr);
   ds_destroy (&lead);
   ds_destroy (&msg);
+  gdk_threads_leave ();
   return FALSE;
 }
 
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..16a7f6f34ba7bde4aea39394305def0327824a57 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2005, 2006  Free Software Foundation
+   Copyright (C) 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
@@ -32,7 +32,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <string.h>
 
@@ -101,7 +100,8 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
              continue;
            }
 
-         if ( text_to_value (text, &v, *write_spec))
+         if ( text_to_value (text, &v, 
+                             dialog->dict, *write_spec))
            {
              nvals++;
              mv_add_value (&dialog->mvl, &v);
@@ -127,9 +127,9 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
       const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
       const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
 
-      if ( text_to_value (low_text, &low_val, *write_spec)
+      if ( text_to_value (low_text, &low_val, dialog->dict, *write_spec)
           &&
-          text_to_value (high_text, &high_val, *write_spec) )
+          text_to_value (high_text, &high_val, dialog->dict, *write_spec) )
        {
          if ( low_val.f > high_val.f )
            {
@@ -155,6 +155,7 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
        {
          union value discrete_val;
          if ( !text_to_value (discrete_text, &discrete_val,
+                              dialog->dict,
                              *write_spec))
            {
              err_dialog (_("Incorrect value for variable type"),
@@ -219,20 +220,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 +261,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;
 }
 
@@ -310,8 +311,9 @@ missing_val_dialog_show (struct missing_val_dialog *dialog)
       gchar *high_text;
       mv_get_range (&dialog->mvl, &low.f, &high.f);
 
-      low_text = value_to_text (low, *write_spec);
-      high_text = value_to_text (high, *write_spec);
+
+      low_text = value_to_text (low, dialog->dict, *write_spec);
+      high_text = value_to_text (high, dialog->dict,  *write_spec);
 
       gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text);
       gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text);
@@ -321,9 +323,7 @@ missing_val_dialog_show (struct missing_val_dialog *dialog)
       if ( mv_has_value (&dialog->mvl))
        {
          gchar *text;
-         union value value;
-         mv_get_value (&dialog->mvl, &value, 0);
-         text = value_to_text (value, *write_spec);
+         text = value_to_text (*mv_get_value (&dialog->mvl, 0), dialog->dict, *write_spec);
          gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text);
          g_free (text);
        }
@@ -343,10 +343,9 @@ missing_val_dialog_show (struct missing_val_dialog *dialog)
          if ( i < n)
            {
              gchar *text ;
-             union value value;
 
-             mv_get_value (&dialog->mvl, &value, i);
-             text = value_to_text (value, *write_spec);
+             text = value_to_text (*mv_get_value (&dialog->mvl, i), dialog->dict,
+                                    *write_spec);
              gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text);
              g_free (text);
            }
index b7fab934aed838fe762bed1f3659bb4ced1690fa..82acf9757f0738eea85ec186a9a51047c18ebad3 100644 (file)
@@ -22,7 +22,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <data/missing-values.h>
 
@@ -33,6 +32,9 @@ struct missing_val_dialog
   /* The variable whose missing values are to be updated */
   struct variable *pv;
 
+  /* The dictionary to which that value belongs */
+  PsppireDict *dict;
+
   /* local copy */
   struct missing_values mvl;
 
@@ -48,7 +50,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..a17a93977e612db7fa99b9eb58226fd9b91f72c8 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 "executor.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 641944d..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);
-}
-
-
-
-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_warning ("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_warning ("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);
-}
-
-
-#define OUTPUT_FILE_NAME "psppire.txt"
-
-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..071751ad6fdeb6372cdd590c1ea8dcf47a347a5e 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 |
@@ -272,6 +272,12 @@ psppire_button_box_init (PsppireButtonBox *bb)
   bb->button[PSPPIRE_BUTTON_CONTINUE] =
     gtk_button_new_with_mnemonic (_("Continue"));
 
+  GTK_WIDGET_SET_FLAGS (bb->button[PSPPIRE_BUTTON_CONTINUE],
+                       GTK_CAN_DEFAULT);
+
+  g_signal_connect (bb->button[PSPPIRE_BUTTON_CONTINUE], "realize",
+        G_CALLBACK (gtk_widget_grab_default), NULL);
+
   gtk_box_pack_start_defaults (GTK_BOX (bb),
                               bb->button[PSPPIRE_BUTTON_CONTINUE]);
   g_signal_connect (bb->button[PSPPIRE_BUTTON_CONTINUE], "clicked",
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..38250f7ea6959a76ab18e6f1db1458ffb8ed2e0f 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 "helper.h"
+#include <libpspp/i18n.h>
+#include <ui/gui/sheet/psppire-axis.h>
+#include "executor.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 +109,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 +155,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 +419,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->data_sheet, "button-event-column",
-                         G_CALLBACK (popup_variable_menu), menu);
+       g_signal_connect (de->var_sheet, "button-event-row",
+                         G_CALLBACK (popup_variable_row_menu), menu);
       }
       break;
-    case PROP_ROW_MENU:
+    case PROP_DS_COLUMN_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-column",
+                         G_CALLBACK (popup_variable_column_menu), menu);
+      }
+      break;
+    case PROP_DS_ROW_MENU:
+      {
+       GObject *menu = g_value_get_object (value);
+
+       g_signal_connect (de->data_sheet[0], "button-event-row",
                          G_CALLBACK (popup_cases_menu), menu);
       }
       break;
@@ -228,14 +453,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 +472,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 +499,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 +511,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 +538,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 +555,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 +578,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 +664,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 +724,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)
     {
@@ -485,13 +744,10 @@ update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data)
          gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
                                         var_get_name (var));
 
-         gchar *s = pspp_locale_to_utf8 (text, -1, 0);
-
-         g_free (text);
 
-         gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), s);
+         gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), text);
 
-         g_free (s);
+         g_free (text);
        }
       else
        goto blank_entry;
@@ -533,7 +789,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 +797,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 +816,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 +939,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);
 
-  gtk_widget_show_all (vbox);
 
-  gtk_notebook_append_page (GTK_NOTEBOOK (de), vbox,
+  psppire_data_editor_remove_split (de);
+
+  gtk_widget_show_all (de->data_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 +962,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 +972,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 +995,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 +1012,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);
+}
 
-  widget =  g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
-                         "var-store",  var_store,
-                         "data-store",  data_store,
-                         NULL);
 
-  g_signal_connect (PSPPIRE_DATA_EDITOR(widget)->data_sheet, "traverse",
-                   G_CALLBACK (traverse_cell_callback), data_store);
+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);
 
-  return widget;
+  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 data_sheet_set_clip (GtkSheet *sheet);
+
+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);
+
+  g_object_unref (de->paned);
+
+  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);
+
+  gtk_widget_show_all (de->data_vbox);
+}
+
+static void data_sheet_set_clip (PsppireSheet *sheet);
 static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
                                                   GtkSelectionData *sd,
                                                   gpointer data);
@@ -698,13 +1088,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 +1111,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 +1164,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 +1173,20 @@ 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,
@@ -804,18 +1196,41 @@ popup_variable_menu (GtkSheet *sheet, gint column,
 
 
 static void
-popup_cases_menu (GtkSheet *sheet, gint row,
+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,
+                     event->button, event->time);
+    }
+}
+
+
+static void
+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 +1268,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 +1280,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 +1294,28 @@ 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 ( PSPPIRE_SHEET (de->data_sheet[0])->select_status
+          == 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 ( PSPPIRE_SHEET (de->var_sheet)->select_status
+          == 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,14 @@ 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 ( PSPPIRE_SHEET (de->data_sheet[0])->select_status == 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 +1344,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 +1362,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 +1376,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 +1423,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 +1443,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 +1463,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 +1488,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 +1503,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 +1517,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 +1547,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;
@@ -1169,19 +1621,12 @@ data_sheet_set_clip (GtkSheet *sheet)
 
   /* Construct clip data. */
   map = case_map_by_name (ds->dict->dict, clip_dict);
-  writer = autopaging_writer_create (dict_get_next_value_idx (clip_dict));
+  writer = autopaging_writer_create (dict_get_proto (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);
     }
@@ -1201,20 +1646,18 @@ enum {
 
 /* Perform data_out for case CC, variable V, appending to STRING */
 static void
-data_out_g_string (GString *string, const struct variable *v,
+data_out_g_string (GString *string, const struct dictionary *dict, 
+                  const struct variable *v,
                   const struct ccase *cc)
 {
-  char *buf ;
-
   const struct fmt_spec *fs = var_get_print_format (v);
   const union value *val = case_data (cc, v);
-  buf = xzalloc (fs->w);
 
-  data_out (val, fs, buf);
+  char *s = data_out (val, dict_get_encoding (dict), fs);
 
-  g_string_append_len (string, buf, fs->w);
+  g_string_append (string, s);
 
-  g_free (buf);
+  g_free (s);
 }
 
 static GString *
@@ -1223,7 +1666,7 @@ clip_to_text (void)
   casenumber r;
   GString *string;
 
-  const size_t val_cnt = casereader_get_value_cnt (clip_datasheet);
+  const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
   const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
   const size_t var_cnt = dict_get_var_cnt (clip_dict);
 
@@ -1232,8 +1675,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 +1687,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, clip_dict, v, cc);
          if ( c < val_cnt - 1 )
            g_string_append (string, "\t");
        }
@@ -1250,7 +1695,7 @@ clip_to_text (void)
       if ( r < case_cnt)
        g_string_append (string, "\n");
 
-      case_destroy (&cc);
+      case_unref (cc);
     }
 
   return string;
@@ -1263,7 +1708,7 @@ clip_to_html (void)
   casenumber r;
   GString *string;
 
-  const size_t val_cnt = casereader_get_value_cnt (clip_datasheet);
+  const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
   const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
   const size_t var_cnt = dict_get_var_cnt (clip_dict);
 
@@ -1275,8 +1720,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 +1732,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, clip_dict, 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 +1799,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 +1836,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..9833fb496f8a0be6dd3145fc7bbb334fe46b341f 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 <libpspp/i18n.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 +112,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 +138,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 +147,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_value (PsppireDataStore *ds,
+                                 gint width, gint where);
+
+static bool
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value);
+
+
+static gboolean
+psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
+                             gint idx, union value *v);
+
+
+
+
 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 +214,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_n_rows (store->datasheet);
 }
 
 size_t
@@ -174,8 +223,14 @@ 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)
+const struct caseproto *
+psppire_data_store_get_proto (const PsppireDataStore *store)
+{
+  return psppire_dict_get_proto (store->dict);
+}
+
+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 +240,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 +253,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 +263,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);
-
-  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);
+  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_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;
 }
 
 
@@ -296,35 +302,42 @@ changed_case_callback (GtkWidget *w, gint casenum, gpointer data)
  */
 static void
 delete_variable_callback (GObject *obj, gint dict_index,
-                         gint case_index, gint val_cnt,
+                         gint case_index, gint width,
                          gpointer data)
 {
   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);
+  datasheet_delete_columns (store->datasheet, case_index, 1);
+  datasheet_insert_column (store->datasheet, NULL, -1, case_index);
+#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
 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
 {
+  struct variable *variable;
   PsppireDataStore *store;
   gint posn;
 
@@ -332,43 +345,64 @@ insert_variable_callback (GObject *obj, gint var_num, gpointer data)
 
   store  = PSPPIRE_DATA_STORE (data);
 
-  if ( var_num > 0 )
-    {
-      struct variable *variable =
-       psppire_dict_get_variable (store->dict, var_num);
-
-      g_assert (variable != NULL);
-
-      posn = var_get_case_index (variable);
-    }
-  else
-    {
-      posn = 0;
-    }
+  variable = psppire_dict_get_variable (store->dict, var_num);
+  posn = var_get_case_index (variable);
+  psppire_data_store_insert_value (store, var_get_width (variable), posn);
 
-  psppire_case_file_insert_values (store->case_file, 1, posn);
+#if AXIS_TRANSITION
 
-  g_sheet_column_columns_changed (G_SHEET_COLUMN (store),
+  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);
 }
 
+struct resize_datum_aux
+  {
+    int old_width;
+    int new_width;
+  };
+
+
+void
+resize_datum (const union value *old, union value *new, void *aux_)
+{
+  struct resize_datum_aux *aux = aux_;
+
+  if (aux->new_width == 0)
+    {
+      /* FIXME: try to parse string as number. */
+      new->f = SYSMIS;
+    }
+  else if (aux->old_width == 0)
+    {
+      /* FIXME: format number as string. */
+      value_set_missing (new, aux->new_width);
+    }
+  else
+    value_copy_rpad (new, aux->new_width, old, aux->old_width, ' ');
+}
 
 static void
 dict_size_change_callback (GObject *obj,
-                         gint posn, gint adjustment, gpointer data)
+                         gint var_num, gint old_width, gpointer data)
 {
   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
+  struct variable *variable;
+  int posn;
 
-  const struct variable *v = psppire_dict_get_variable (store->dict, posn);
-
-  const gint new_val_width = value_cnt_from_width (var_get_width (v));
+  variable = psppire_dict_get_variable (store->dict, var_num);
+  posn = var_get_case_index (variable);
 
-  if ( adjustment > 0 )
-    psppire_case_file_insert_values (store->case_file, adjustment,
-                                    new_val_width - adjustment +
-                                    var_get_case_index(v));
+  if (old_width != var_get_width (variable))
+    {
+      struct resize_datum_aux aux;
+      aux.old_width = old_width;
+      aux.new_width = var_get_width (variable);
+      datasheet_resize_column (store->datasheet, posn, aux.new_width,
+                               resize_datum, &aux);
+    }
 }
 
 
@@ -392,28 +426,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 +450,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 +504,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 +538,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,48 +551,26 @@ 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
 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
 {
   gboolean result;
-  gint val_cnt, v;
-  struct ccase cc;
+  const struct caseproto *proto;
+  struct ccase *cc;
   g_return_val_if_fail (ds, FALSE);
 
-  val_cnt = datasheet_get_column_cnt (ds->case_file->datasheet) ;
-
-  g_return_val_if_fail (val_cnt > 0, FALSE);
-
+  proto = datasheet_get_proto (ds->datasheet);
+  g_return_val_if_fail (caseproto_get_n_widths (proto) > 0, FALSE);
   g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
 
-  case_create (&cc, val_cnt);
-
-  memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING);
+  cc = case_create (proto);
+  case_set_missing (cc);
 
-  for (v = 0 ; v < psppire_dict_get_var_cnt (ds->dict) ; ++v)
-    {
-      const struct variable *pv = psppire_dict_get_variable (ds->dict, v);
-      if ( var_is_alpha (pv))
-       continue;
-
-      case_data_rw (&cc, pv)->f = SYSMIS;
-    }
+  result = psppire_data_store_insert_case (ds, cc, posn);
 
-  result = psppire_case_file_insert_case (ds->case_file, &cc, posn);
-
-  case_destroy (&cc);
+  case_unref (cc);
 
   return result;
 }
@@ -587,16 +583,19 @@ psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
   char *text;
   const struct fmt_spec *fp ;
   const struct variable *pv ;
-  union value *v ;
-  GString *s;
+  const struct dictionary *dict;
+  union value v;
+  int width;
 
   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);
+
+  dict = store->dict->dict;
 
   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);
@@ -604,66 +603,54 @@ psppire_data_store_get_string (PsppireDataStore *store, glong row, glong column)
   g_assert (pv);
 
   idx = var_get_case_index (pv);
+  width = var_get_width (pv);
 
   g_assert (idx >= 0);
 
-  v = psppire_case_file_get_value (store->case_file, row, idx, NULL,
-                                   var_get_width (pv));
-
-  g_return_val_if_fail (v, NULL);
+  value_init (&v, width);
+  if (!psppire_data_store_get_value (store, row, idx, &v))
+    return NULL;
 
   if ( store->show_labels)
     {
-      const gchar *label = var_lookup_value_label (pv, v);
+      const gchar *label = var_lookup_value_label (pv, &v);
       if (label)
         {
-          free (v);
-         return pspp_locale_to_utf8 (label, -1, 0);
+          value_destroy (&v, width);
+         return g_strdup (label);
         }
     }
 
   fp = var_get_write_format (pv);
 
-  s = g_string_sized_new (fp->w + 1);
-  g_string_set_size (s, fp->w);
-
-  memset (s->str, 0, fp->w);
-
-  g_assert (fp->w == s->len);
-
-  /* Converts binary value V into printable form in the exactly
-     FP->W character in buffer S according to format specification
-     FP.  No null terminator is appended to the buffer.  */
-  data_out (v, fp, s->str);
-
-  text = pspp_locale_to_utf8 (s->str, fp->w, 0);
-  g_string_free (s, TRUE);
+  text = data_out (&v, dict_get_encoding (dict), fp);
 
   g_strchomp (text);
 
-  free (v);
+  value_destroy (&v, width);
   return text;
 }
 
 
 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);
 
   union value v;
   const struct variable *pv = psppire_dict_get_variable (store->dict, col);
+  int width = var_get_width (pv);
 
   const gint index = var_get_case_index (pv) ;
 
-  if ( var_is_numeric (pv))
-    v.f = SYSMIS;
-  else
-    memcpy (v.s, "", MAX_SHORT_STRING);
+  value_init (&v, width);
+  value_set_missing (&v, width);
+  psppire_data_store_set_value (store, row, index, &v);
+  value_destroy (&v, width);
+
+  psppire_sheet_model_range_changed (model, row, col, row, col);
 
-  psppire_case_file_set_value (store->case_file, row, index, &v,
-                             var_get_width (pv));
 
   return TRUE;
 }
@@ -679,7 +666,8 @@ psppire_data_store_set_string (PsppireDataStore *store,
 {
   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,32 +677,16 @@ 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));
+  psppire_data_store_data_in (store, row,
+                             var_get_case_index (pv), ss_cstr (text),
+                             var_get_write_format (pv));
+
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (store), row, col, row, col);
 
   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));
-
-  store->font_desc = fd;
-#if 0
-  store->width_of_m = calc_m_width (fd);
-#endif
-  g_signal_emit (store, signals [FONT_CHANGED], 0);
-
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (store),
-                                -1, -1, -1, -1);
-}
-
 
 void
 psppire_data_store_show_labels (PsppireDataStore *store, gboolean show_labels)
@@ -724,17 +696,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 +721,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 +728,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,56 +740,89 @@ psppire_data_store_get_reader (PsppireDataStore *ds)
 
 /* Column related funcs */
 
-static glong
-geometry_get_column_count (const GSheetColumn *geom)
+
+static const gchar null_var_name[]=N_("var");
+
+\f
+
+/* Row related funcs */
+
+static gchar *
+get_row_button_label (const PsppireSheetModel *model, gint unit)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  // PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-  return MAX (MIN_COLUMNS, psppire_dict_get_var_cnt (ds->dict));
+  return g_strdup_printf (_("%d"), unit + FIRST_CASE_NUMBER);
 }
 
 
+static gboolean
+get_row_sensitivity (const PsppireSheetModel *model, gint unit)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-static gint
-geometry_get_width (const GSheetColumn *geom, glong unit)
+  return (unit < psppire_data_store_get_case_count (ds));
+}
+
+
+\f
+
+/* Column related stuff */
+
+static gchar *
+get_column_subtitle (const PsppireSheetModel *model, gint col)
 {
-  const struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  const struct variable *v ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return ds->width_of_m * 8 ;
+  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);
 
-  if ( pv == NULL )
-    return ds->width_of_m * 8 ;
+  if ( ! var_has_label (v))
+    return NULL;
 
-  return ds->width_of_m * var_get_display_width (pv);
+  return xstrdup (var_get_label (v));
 }
 
-static void
-geometry_set_width (GSheetColumn *geom, glong unit, gint width)
+static gchar *
+get_column_button_label (const PsppireSheetModel *model, gint col)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  struct variable *pv ;
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
+
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
+    return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
 
-  struct variable *pv = psppire_dict_get_variable (ds->dict, unit);
+  pv = psppire_dict_get_variable (ds->dict, col);
 
-  var_set_display_width (pv, width / ds->width_of_m );
+  if (NULL == pv)
+    return NULL;
+
+  return xstrdup (var_get_name (pv));
+}
+
+static gboolean
+get_column_sensitivity (const PsppireSheetModel *model, gint col)
+{
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
+
+  return (col < psppire_dict_get_var_cnt (ds->dict));
 }
 
 
 
 static GtkJustification
-geometry_get_justification (const GSheetColumn *geom, glong unit)
+get_column_justification (const PsppireSheetModel *model, gint col)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  PsppireDataStore *ds = PSPPIRE_DATA_STORE (model);
   const struct variable *pv ;
 
-
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
+  if ( col >= psppire_dict_get_var_cnt (ds->dict) )
     return GTK_JUSTIFY_LEFT;
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
+  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
@@ -825,146 +830,198 @@ geometry_get_justification (const GSheetColumn *geom, glong unit)
 }
 
 
-static const gchar null_var_name[]=N_("var");
 
-static gchar *
-geometry_get_column_button_label (const GSheetColumn *geom, glong unit)
-{
-  gchar *text;
-  struct variable *pv ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
-
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return g_locale_to_utf8 (null_var_name, -1, 0, 0, 0);
+\f
 
-  pv = psppire_dict_get_variable (ds->dict, unit);
 
-  text =  pspp_locale_to_utf8 (var_get_name (pv), -1, 0);
+/* Returns the CASENUMth case, or a null pointer on failure.
+ */
+struct ccase *
+psppire_data_store_get_case (const PsppireDataStore *ds,
+                            casenumber casenum)
+{
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
-  return text;
+  return datasheet_get_row (ds->datasheet, casenum);
 }
 
 
-static gchar *
-geometry_get_column_subtitle (const GSheetColumn *geom, glong unit)
+gboolean
+psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
+                                casenumber n_cases)
 {
-  gchar *text;
-  const struct variable *v ;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
-  if ( unit >= psppire_dict_get_var_cnt (ds->dict) )
-    return NULL;
+  g_return_val_if_fail (first + n_cases <=
+                       psppire_data_store_get_case_count (ds), FALSE);
 
-  v = psppire_dict_get_variable (ds->dict, unit);
 
-  if ( ! var_has_label (v))
-    return NULL;
+  datasheet_delete_rows (ds->datasheet, first, n_cases);
 
-  text =  pspp_locale_to_utf8 (var_get_label (v), -1, 0);
+  g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
+  psppire_sheet_model_rows_deleted (PSPPIRE_SHEET_MODEL (ds), first, n_cases);
 
-  return text;
+  return TRUE;
 }
 
 
+
+/* Insert case CC into the case file before POSN */
 static gboolean
-geometry_get_sensitivity (const GSheetColumn *geom, glong unit)
+psppire_data_store_insert_case (PsppireDataStore *ds,
+                               struct ccase *cc,
+                               casenumber posn)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  bool result ;
 
-  return (unit < psppire_dict_get_var_cnt (ds->dict));
-}
+  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);
 
-static void
-psppire_data_store_sheet_column_init (GSheetColumnIface *iface)
-{
-  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;
-}
+  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;
+}
 
-/* Row related funcs */
 
-static glong
-geometry_get_row_count (const GSheetRow *geom)
+/* Copies the IDXth value from case CASENUM into VALUE, which
+   must be of the correct width for IDX.
+   Returns true if successful, false on failure. */
+static bool
+psppire_data_store_get_value (const PsppireDataStore *ds,
+                             casenumber casenum, size_t idx,
+                             union value *value)
 {
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  g_return_val_if_fail (ds, false);
+  g_return_val_if_fail (ds->datasheet, false);
+  g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), false);
 
-  return TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file);
+  return datasheet_get_value (ds->datasheet, casenum, idx, value);
 }
 
-#define ROW_HEIGHT 25
 
-static gint
-geometry_get_height (const GSheetRow *geom, glong unit)
-{
-  return ROW_HEIGHT;
-}
 
-static guint
-geometry_get_top_ypixel (const GSheetRow *geo, glong row)
+/* Set the IDXth value of case C to V.
+   V must be the correct width for IDX.
+   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)
 {
-  return row * ROW_HEIGHT;
-}
+  bool ok;
 
-static glong
-geometry_pixel_to_row (const GSheetRow *geo, guint pixel)
-{
-  glong row  = pixel / ROW_HEIGHT;
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
+
+  g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), FALSE);
 
-  if (row >= geometry_get_row_count (geo))
-    row = geometry_get_row_count (geo) - 1;
+  ok = datasheet_put_value (ds->datasheet, casenum, idx, v);
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
 
-  return row;
+  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;
+  int width;
+  bool ok;
 
+  PsppireDict *dict;
 
-  return (unit < psppire_case_file_get_case_count (ds->case_file));
-}
+  g_return_val_if_fail (ds, FALSE);
+  g_return_val_if_fail (ds->datasheet, FALSE);
 
+  g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), FALSE);
 
-static gchar *
-geometry_get_row_button_label (const GSheetRow *geom, glong unit)
+  dict = ds->dict;
+
+  width = fmt_var_width (fmt);
+  g_return_val_if_fail (caseproto_get_width (
+                          datasheet_get_proto (ds->datasheet), idx) == width,
+                        FALSE);
+  value_init (&value, width);
+  ok = (datasheet_get_value (ds->datasheet, casenum, idx, &value)
+        && data_in (input, UTF8, fmt->type, 0, 0, 0,
+                   dict->dict, &value, width)
+        && datasheet_put_value (ds->datasheet, casenum, idx, &value));
+  value_destroy (&value, width);
+
+  if (ok)
+    g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
+
+  return ok;
+}
+
+/* Resize the cases in the casefile, by inserting a value of the
+   given WIDTH into every one of them at the position immediately
+   preceding WHERE.
+*/
+static gboolean
+psppire_data_store_insert_value (PsppireDataStore *ds,
+                                 gint width, gint where)
 {
-  gchar *text;
-  gchar *s;
-  PsppireDataStore *ds = PSPPIRE_DATA_STORE (geom);
+  union value value;
 
-  if ( unit >
-       TRAILING_ROWS + psppire_case_file_get_case_count (ds->case_file))
-    return 0;
+  g_return_val_if_fail (ds, FALSE);
 
-  s = g_strdup_printf (_("%ld"), unit + FIRST_CASE_NUMBER);
+  g_assert (width >= 0);
 
-  text =  pspp_locale_to_utf8 (s, -1, 0);
+  if ( ! ds->datasheet )
+    ds->datasheet = datasheet_create (NULL);
 
-  g_free (s);
+  value_init (&value, width);
+  if (width == 0)
+    value.f = 0;
+  else
+    value_set_missing (&value, width);
 
-  return text;
+  datasheet_insert_column (ds->datasheet, &value, width, where);
+
+  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_n_rows (ds->datasheet))
+    return FALSE;
+
+  if ( ! filter)
+    return FALSE;
+
+  g_assert (var_is_numeric (filter));
+
+  value_init (&val, 0);
+  if ( ! datasheet_get_value (ds->datasheet, row,
+                             var_get_case_index (filter),
+                             &val) )
+    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..610b6b45071bfb48a457aa7d841d98a81f394353 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);
+const struct caseproto *psppire_data_store_get_proto (const PsppireDataStore *);
+
+\f
+
+struct ccase *psppire_data_store_get_case (const PsppireDataStore *ds,
+                                           casenumber casenum);
+
+
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
 
+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..185648b
--- /dev/null
@@ -0,0 +1,1899 @@
+/* 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 "executor.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);
+
+    if ( ! g_path_is_absolute (filename))
+      {
+       gchar *path =
+         g_build_filename (g_get_current_dir (), filename, NULL);
+       dir_name = g_path_get_dirname (path);
+       g_free (path);
+      }
+    else
+      {
+       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 *native_file_name = NULL;
+  gchar *file_name = NULL;
+  GString *fnx;
+  struct getl_interface *sss;
+  struct string filename ;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
+
+  g_object_get (w, "filename", &file_name, NULL);
+
+  fnx = g_string_new (file_name);
+
+  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 (&filename);
+
+  native_file_name =
+    convert_glib_filename_to_system_filename (fnx->str, NULL);
+
+  g_string_free (fnx, TRUE);
+
+  syntax_gen_string (&filename, ss_cstr (native_file_name));
+  g_free (native_file_name);
+
+  if ( de->save_as_portable )
+    {
+      sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
+                                        ds_cstr (&filename));
+    }
+  else
+    {
+      sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
+                                        ds_cstr (&filename));
+    }
+
+  ds_destroy (&filename);
+
+  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);
+  }
+
+  {
+    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);
+  }
+
+  {
+    GtkMenu *data_sheet_variable_popup_menu =
+      GTK_MENU (create_data_sheet_variable_popup_menu (de));
+
+    GtkMenu *var_sheet_variable_popup_menu =
+      GTK_MENU (create_var_sheet_variable_popup_menu (de));
+
+    GtkMenu *data_sheet_cases_popup_menu =
+      GTK_MENU (create_data_sheet_cases_popup_menu (de));
+
+    g_object_set (de->data_editor,
+                 "datasheet-column-menu", data_sheet_variable_popup_menu,
+                 "datasheet-row-menu", data_sheet_cases_popup_menu,
+                 "varsheet-row-menu", 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..c3bcb84
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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;
+
+
+  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..cea5134630c8483e095c365e95693b595fddb47e 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) ;
 }
@@ -426,7 +513,9 @@ psppire_dialog_run (PsppireDialog *dialog)
 
   g_signal_emit (dialog, signals [DIALOG_REFRESH], 0);
 
+  gdk_threads_leave ();
   g_main_loop_run (dialog->loop);
+  gdk_threads_enter ();
 
   g_main_loop_unref (dialog->loop);
 
@@ -475,3 +564,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..c82395f2ebdc047e1f8678b9e9d6873df2452207 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2004, 2006, 2007  Free Software Foundation
+   Copyright (C) 2004, 2006, 2007, 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 <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 <data/missing-values.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
+#include <libpspp/i18n.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 +39,7 @@ enum  {
   VARIABLE_RESIZED,
   VARIABLE_INSERTED,
   VARIABLE_DELETED,
+  VARIABLE_DISPLAY_WIDTH_CHANGED,
 
   WEIGHT_CHANGED,
   FILTER_CHANGED,
@@ -55,6 +47,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 +97,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 +154,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 +168,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,15 +236,17 @@ 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
-delcb (struct dictionary *d, int dict_idx, int case_idx, int value_cnt,
-       void *pd)
+delcb (struct dictionary *d, int dict_idx, int case_idx, int width, void *pd)
 {
   g_signal_emit (pd, signals [VARIABLE_DELETED], 0,
-                dict_idx, case_idx, value_cnt );
+                 dict_idx, case_idx, width );
 }
 
 static void
@@ -241,9 +256,9 @@ mutcb (struct dictionary *d, int idx, void *pd)
 }
 
 static void
-resize_cb (struct dictionary *d, int idx, int delta, void *pd)
+resize_cb (struct dictionary *d, int idx, int old_width, void *pd)
 {
-  g_signal_emit (pd, signals [VARIABLE_RESIZED], 0, idx, delta);
+  g_signal_emit (pd, signals [VARIABLE_RESIZED], 0, idx, old_width);
 }
 
 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)
 {
@@ -440,6 +472,17 @@ psppire_dict_get_value_cnt (const PsppireDict *d)
 }
 
 
+/* Returns the prototype for the cases that match the dictionary */
+const struct caseproto *
+psppire_dict_get_proto (const PsppireDict *d)
+{
+  g_return_val_if_fail (d, NULL);
+  g_return_val_if_fail (d->dict, NULL);
+
+  return dict_get_proto (d->dict);
+}
+
+
 /* Return a variable by name.
    Return NULL if it doesn't exist
 */
@@ -498,17 +541,14 @@ void
 psppire_dict_resize_variable (PsppireDict *d, const struct variable *pv,
                              gint old_size, gint new_size)
 {
-  gint fv;
   g_return_if_fail (d);
   g_return_if_fail (d->dict);
 
   if ( old_size == new_size )
     return ;
 
-  fv = var_get_case_index (pv);
-
   g_signal_emit (d, signals [VARIABLE_RESIZED], 0,
-                fv + old_size,
+                var_get_dict_index (pv),
                 new_size - old_size );
 }
 
@@ -720,10 +760,8 @@ tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
     {
     case DICT_TVM_COL_NAME:
       {
-      gchar *name = pspp_locale_to_utf8(var_get_name (var), -1, NULL);
-      g_value_init (value, G_TYPE_STRING);
-      g_value_set_string (value, name);
-      g_free (name);
+       g_value_init (value, G_TYPE_STRING);
+       g_value_set_string (value, var_get_name (var));
       }
       break;
     case DICT_TVM_COL_VAR:
@@ -818,12 +856,20 @@ psppire_dict_dump (const PsppireDict *dict)
     {
       const struct variable *v = psppire_dict_get_variable (dict, i);
       int di = var_get_dict_index (v);
-      g_print ("\"%s\" idx=%d, fv=%d, size=%d\n",
+      g_print ("\"%s\" idx=%d, fv=%d\n",
               var_get_name(v),
               di,
-              var_get_case_index(v),
-              value_cnt_from_width(var_get_width(v)));
+              var_get_case_index(v));
 
     }
 }
 #endif
+
+
+
+
+const gchar *
+psppire_dict_encoding (const PsppireDict *dict)
+{
+  return dict_get_encoding (dict->dict);
+}
index f645b355a6b0d5b90100fc60606e96d8240b1968..798749328101be104f8f704030716cb87814280c 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
@@ -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;
 };
@@ -72,6 +72,9 @@ gint psppire_dict_get_var_cnt (const PsppireDict *d);
 /* Return the number of `union value's in the dictionary */
 size_t psppire_dict_get_value_cnt (const PsppireDict *d);
 
+/* Returns the prototype for the cases that match the dictionary */
+const struct caseproto *psppire_dict_get_proto (const PsppireDict *d);
+
 /* Return a variable by name.
    Return NULL if it doesn't exist
 */
@@ -96,7 +99,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);
@@ -109,6 +112,8 @@ struct variable * psppire_dict_get_weight_variable (const PsppireDict *);
 void psppire_dict_dump (const PsppireDict *);
 #endif
 
+const gchar *psppire_dict_encoding (const PsppireDict *);
+
 G_END_DECLS
 
 #endif /* __PSPPIRE_DICT_H__ */
diff --git a/src/ui/gui/psppire-dictview.c b/src/ui/gui/psppire-dictview.c
new file mode 100644 (file)
index 0000000..243d907
--- /dev/null
@@ -0,0 +1,583 @@
+/* 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 <libpspp/i18n.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;
+
+  if ( iter)
+    *iter = *top_iter;
+
+  while ( ! PSPPIRE_IS_DICT (*model))
+    {
+      GtkTreeIter parent_iter;
+      if (iter)
+       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);
+
+         if (iter)
+           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);
+
+         if (iter)
+           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;
+  PsppireDict *dict;
+
+  dv_get_base_model (top_model, top_iter, &model, &iter);
+
+  dict = PSPPIRE_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));
+
+      g_object_set (cell, "markup", text, NULL);
+      g_free (text);
+    }
+  else
+    {
+      g_object_set (cell, "text", var_get_name (var), NULL);
+    }
+}
+
+
+
+/* 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;
+
+  {
+    const gchar *tip ;
+    GtkTreeModel *m;
+    PsppireDict *dict;
+
+    dv_get_base_model (tree_model, NULL, &m, NULL);
+    dict = PSPPIRE_DICT (m);
+
+    if ( PSPPIRE_DICT_VIEW (treeview)->prefer_labels )
+      tip = var_get_name (var);
+    else
+      tip = var_get_label (var);
+
+    gtk_tooltip_set_text (tooltip, 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..b0eff95
--- /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_critical ("Cannot open %s\n", output_file_name());
+       return;
+      }
+
+    /* Delete all the entire buffer */
+    gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
+    gtk_text_buffer_delete (ow->buffer, &start_iter, &end_iter);
+
+
+    gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
+    /* Read in the next lot of text */
+    while (fgets (line, viewer_width + 1, fp) != NULL)
+      {
+       chars_inserted = TRUE;
+       gtk_text_buffer_insert (ow->buffer, &start_iter, line, -1);
+      }
+
+    fclose (fp);
+  }
+#else
+  {
+    if ( ow->fp == NULL)
+      {
+       ow->fp = fopen (output_file_name(), "r");
+       if ( ow->fp == NULL)
+         {
+           g_critical ("Cannot open %s\n", output_file_name());
+           return;
+         }
+      }
+
+    /* Read in the next lot of text */
+    while (fgets (line, viewer_width + 1, ow->fp) != NULL)
+      {
+       chars_inserted = TRUE;
+       gtk_text_buffer_insert (ow->buffer, &end_iter, line, -1);
+      }
+  }
+#endif
+
+  /* Scroll to where the start of this lot of text begins */
+  gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ow->textview),
+                               mark,
+                               0.1, TRUE, 0.0, 0.0);
+
+
+  if ( chars_inserted )
+    gtk_window_set_urgency_hint ( GTK_WINDOW (ow), TRUE);
+}
+
+
+
diff --git a/src/ui/gui/psppire-output-window.h b/src/ui/gui/psppire-output-window.h
new file mode 100644 (file)
index 0000000..d11eca6
--- /dev/null
@@ -0,0 +1,79 @@
+/* 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 "psppire.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..48a77fc
--- /dev/null
@@ -0,0 +1,615 @@
+/* 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 "executor.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 "xalloc.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 xstrdup (filename);
+}
+
+/*
+  Save BUFFER to the file called FILENAME.
+  FILENAME must be encoded in Glib filename encoding.
+  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;
+  g_assert (filename);
+
+  suffixedname = append_suffix (filename);
+
+  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 (suffixedname, text, -1, err);
+
+  g_free (suffixedname);
+
+  if ( result )
+    {
+      char *fn = g_filename_display_name (filename);
+      gchar *msg = g_strdup_printf (_("Saved file \"%s\""), fn);
+      g_free (fn);
+      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..dfd62bb6737f1dbc78a605a6aaf62e2e7189a757 100644 (file)
@@ -1,6 +1,5 @@
-
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <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.
+   Callback whenever the active cell changes 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.
-*/
-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,15 +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));
-
-       if ( var_is_long_string (var))
-         g_object_set (customEntry,
-                       "editable", FALSE,
-                       NULL);
+         PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
 
        val_labs_dialog_set_target_variable (vs->val_labs_dialog, var);
 
@@ -407,20 +376,16 @@ 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));
-
-       if ( var_is_long_string (var))
-         g_object_set (customEntry,
-                       "editable", FALSE,
-                       NULL);
-
+         PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
 
        vs->missing_val_dialog->pv =
          psppire_var_store_get_var (var_store, row);
 
+       vs->missing_val_dialog->dict = var_store->dict;
+
        g_signal_connect_swapped (customEntry,
                                  "clicked",
                                  G_CALLBACK (missing_val_dialog_show),
@@ -432,10 +397,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 +417,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 +454,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 +467,58 @@ 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),
+                                               PSPPIRE_VAR_STORE (psppire_sheet_get_model (PSPPIRE_SHEET (vs))));
 
+  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 +526,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..26c1ee4d34e6cd0d5b13b4f2f89c78e1d5b865c8 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
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-
+#include <libpspp/i18n.h>
 
 #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 gchar *text_for_column (const struct variable *pv, gint c, GError **err);
-
-
-static void psppire_var_store_sheet_row_init (GSheetRowIface *iface);
+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 (PsppireVarStore *vs, const struct variable *pv,
+                              gint c, GError **err);
 
 
 static GObjectClass *parent_class = NULL;
@@ -128,24 +121,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 +141,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 +161,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 +185,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 +258,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 +277,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 +291,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 +325,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 +335,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 +345,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 +355,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 +386,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 +397,8 @@ 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);
 
@@ -456,7 +409,7 @@ psppire_var_store_get_string (const GSheetModel *model, glong row, glong column)
 
   pv = psppire_dict_get_variable (store->dict, row);
 
-  return text_for_column (pv, column, 0);
+  return text_for_column (store, pv, column, 0);
 }
 
 
@@ -465,7 +418,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 ;
 
@@ -482,7 +435,7 @@ psppire_var_store_clear (GSheetModel *model,  glong row, glong col)
   switch (col)
     {
     case PSPPIRE_VAR_STORE_COL_LABEL:
-      var_set_label (pv, 0);
+      var_set_label (pv, NULL);
       return TRUE;
       break;
     }
@@ -495,7 +448,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 +466,11 @@ 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;
+      {
+       gboolean ok;
+       ok =  psppire_dict_rename_var (var_store->dict, pv, text);
+       return ok;
+      }
     case PSPPIRE_VAR_STORE_COL_COLUMNS:
       if ( ! text) return FALSE;
       var_set_display_width (pv, atoi (text));
@@ -522,10 +478,19 @@ psppire_var_store_set_string (GSheetModel *model,
       break;
     case PSPPIRE_VAR_STORE_COL_WIDTH:
       {
-       int width = atoi (text);
-       if ( ! text) return FALSE;
+       const int width = atoi (text);
+       if ( ! text)
+         return FALSE;
+
+       if (width < 0)
+         return FALSE;
+
        if ( var_is_alpha (pv))
+         {
+           if ( width > MAX_STRING )
+             return FALSE;
            var_set_width (pv, width);
+         }
        else
          {
             bool for_input
@@ -568,8 +533,10 @@ psppire_var_store_set_string (GSheetModel *model,
       }
       break;
     case PSPPIRE_VAR_STORE_COL_LABEL:
-      var_set_label (pv, text);
-      return TRUE;
+      {
+       var_set_label (pv, text);
+       return TRUE;
+      }
       break;
     case PSPPIRE_VAR_STORE_COL_TYPE:
     case PSPPIRE_VAR_STORE_COL_VALUES:
@@ -588,11 +555,13 @@ 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)
+text_for_column (PsppireVarStore *vs,
+                const struct variable *pv, gint c, GError **err)
 {
+  PsppireDict *dict = vs->dict;
   static const gchar *const type_label[] =
     {
       N_("Numeric"),
@@ -604,6 +573,7 @@ text_for_column (const struct variable *pv, gint c, GError **err)
       N_("Custom"),
       N_("String")
     };
+
   enum {VT_NUMERIC, VT_COMMA, VT_DOT, VT_SCIENTIFIC, VT_DATE, VT_DOLLAR,
        VT_CUSTOM, VT_STRING};
 
@@ -612,7 +582,7 @@ text_for_column (const struct variable *pv, gint c, GError **err)
   switch (c)
     {
     case PSPPIRE_VAR_STORE_COL_NAME:
-      return pspp_locale_to_utf8 ( var_get_name (pv), -1, err);
+      return xstrdup (var_get_name (pv));
       break;
     case PSPPIRE_VAR_STORE_COL_TYPE:
       {
@@ -699,12 +669,17 @@ text_for_column (const struct variable *pv, gint c, GError **err)
       }
       break;
     case PSPPIRE_VAR_STORE_COL_LABEL:
-      return pspp_locale_to_utf8 (var_get_label (pv), -1, err);
+      {
+       const char *label = var_get_label (pv);
+       if (label)
+         return xstrdup (label);
+       return NULL;
+      }
       break;
 
     case PSPPIRE_VAR_STORE_COL_MISSING:
       {
-       return missing_values_to_string (pv, err);
+       return missing_values_to_string (dict, pv, err);
       }
       break;
     case PSPPIRE_VAR_STORE_COL_VALUES:
@@ -713,26 +688,18 @@ text_for_column (const struct variable *pv, gint c, GError **err)
          return g_locale_to_utf8 (gettext (none), -1, 0, 0, err);
        else
          {
-           gchar *ss;
-           GString *gstr = g_string_sized_new (10);
            const struct val_labs *vls = var_get_value_labels (pv);
-           struct val_labs_iterator *ip = 0;
-           struct val_lab *vl = val_labs_first_sorted (vls, &ip);
+            const struct val_lab **labels = val_labs_sorted (vls);
+           const struct val_lab *vl = labels[0];
+            free (labels);
 
            g_assert (vl);
 
            {
-             gchar *const vstr = value_to_text (vl->value, *write_spec);
+             gchar *const vstr = value_to_text (vl->value, dict, *write_spec);
 
-             g_string_printf (gstr, "{%s,\"%s\"}_", vstr, vl->label);
-             g_free (vstr);
+             return g_strdup_printf ( "{%s,\"%s\"}_", vstr, val_lab_get_label (vl));
            }
-
-           val_labs_done (&ip);
-
-           ss = pspp_locale_to_utf8 (gstr->str, gstr->len, err);
-           g_string_free (gstr, TRUE);
-           return ss;
          }
       }
       break;
@@ -763,20 +730,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 +743,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 +764,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..70500e6
--- /dev/null
@@ -0,0 +1,678 @@
+/* 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 <xalloc.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 = xstrdup (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 = xstrdup ("");
+
+  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..3555463f88e4d353c84bcd366b223955d4217604 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 <config.h>
 
-#include <locale.h>
+#include <libpspp/i18n.h>
 #include <assert.h>
 #include <libintl.h>
 #include <gsl/gsl_errno.h>
 
+#include <xalloc.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 "executor.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,32 +70,29 @@ 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
 
 
+
+
 void
-initialize (void)
+initialize (struct command_line_processor *clp, int argc, char **argv)
 {
   PsppireDict *dictionary = 0;
 
-  /* gtk_init messes with the locale.
-     So unset the bits we want to control ourselves */
-  setlocale (LC_NUMERIC, "C");
-
-  bindtextdomain (PACKAGE, locale_dir);
 
+  i18n_init ();
 
-  glade_init ();
+  preregister_widgets ();
 
   gsl_set_error_handler_off ();
   fn_init ();
@@ -134,7 +144,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);
 }
 
 
@@ -145,8 +167,27 @@ de_initialize (void)
   message_dialog_done ();
   settings_done ();
   outp_done ();
+  i18n_done ();
+}
+
+
+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 +250,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 +262,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 = xstrdup (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 = xstrdup (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..a096752897ccecf703757980637c43c786230e9c 100644 (file)
@@ -3,32 +3,6 @@
 <!--*- mode: xml -*-->
 <glade-interface>
   <requires lib="psppire"/>
-  <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>
-    <property name="logo">pspplogo.png</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox1">
-        <property name="visible">True</property>
-        <child>
-          <placeholder/>
-        </child>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area1">
-            <property name="visible">True</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
   <widget class="PsppireDialog" id="weight-cases-dialog">
     <property name="title">Weight Cases</property>
     <property name="modal">True</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 +37,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 +46,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 +57,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>
-                                        <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">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">
+                                      <widget class="GtkLabel" id="label11">
                                         <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>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">If condition is satisfied</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">
+                          <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="xalign">0</property>
+                            <property name="label" translatable="yes">Use expression as label</property>
                           </widget>
                           <packing>
-                            <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>
+                            <property name="y_options"></property>
                           </packing>
                         </child>
                         <child>
-                          <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="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radio-button-user-label</property>
-                          </widget>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="x_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkHBox" id="hbox19">
+                          <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>
                           </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>
       </widget>
     </child>
   </widget>
-  <widget class="PsppireDialog" id="find-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">Find Case</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox14">
-        <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>
-        <child>
-          <widget class="GtkHBox" id="hbox10">
-            <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="spacing">5</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_ETCHED_IN</property>
-                <child>
-                  <widget class="GtkTreeView" id="find-variable-treeview">
-                    <property name="height_request">300</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="headers_visible">False</property>
-                    <property name="fixed_height_mode">True</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkVBox" id="vbox27">
-                <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="PsppireSelector" id="find-selector">
-                    <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="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkVBox" id="vbox10">
-                <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="GtkVBox" id="vbox11">
-                    <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="label33">
-                        <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:</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="padding">5</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="find-variable-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>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox15">
-                    <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="label34">
-                        <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">Value:</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="padding">5</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="find-value-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>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckButton" id="find-value-labels-checkbutton">
-                        <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 value labels</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkVButtonBox" id="bb1">
-                    <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="find-match-regexp-checkbutton">
-                        <property name="sensitive">False</property>
-                        <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">Regular expression Match</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="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="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckButton" id="find-wrap">
-                        <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">Wrap around</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkCheckButton" id="find-backwards">
-                        <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">Search backward</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                      <packing>
-                        <property name="position">3</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="position">3</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="find-buttonbox">
-            <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="buttons">PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK | PSPPIRE_BUTTON_RESET_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>
-    </child>
-  </widget>
 </glade-interface>
index 9c99af02ea98cca2588660d6c5c0e8aa04cb9fd2..cfe49e9182aaa0f374eaec352e50e7c652d80cac 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);
+
+const char * output_file_name (void);
+
 
 #endif /* PSPPIRE_H */
index 174ac6d04138b9b1dfa9e4914b9ac68ebf7cc8ed..db5cd4fbb4bf1972065e0230e9ad34cc919a43ef 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 "executor.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..495aefb82914b8026f7f507e75aa72b13759a019 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 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 "recode-dialog.h"
 
+#include "executor.h"
+
 #include <gtk/gtk.h>
 
+#include <xalloc.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/syntax-gen.h>
 
 #include "psppire-acr.h"
@@ -71,7 +74,7 @@ new_value_copy (struct new_value *nv)
   struct new_value *copy = g_memdup (nv, sizeof (*copy));
 
   if ( nv->type == NV_STRING )
-    copy->v.s = strdup (nv->v.s);
+    copy->v.s = xstrdup (nv->v.s);
 
   return copy;
 }
@@ -470,14 +473,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 +489,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 +839,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 (builder, "psppire-selector1");
 
-  GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
+  GtkWidget *oldandnew = get_widget_assert (builder, "button1");
 
-  GtkWidget *oldandnew = get_widget_assert (xml, "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 +885,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 +962,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 +1046,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 +1084,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 +1094,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 +1107,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..a702ffb78df0c8d7681c2ec52ce8b6612efc65f8 100644 (file)
 
 #include "checkbox-treeview.h"
 #include "regression-dialog.h"
+#include "executor.h"
 
 #include <gtk/gtk.h>
 #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 "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -214,7 +215,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 +224,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 +251,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 +284,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 +305,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 +315,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..4db99d9
--- /dev/null
@@ -0,0 +1,229 @@
+/* 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 "executor.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..56b10d2c5482e382da1b8b2df32f5646bb8fe127 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+   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
 
 #include "select-cases-dialog.h"
 #include <gtk/gtk.h>
-#include <glade/glade.h>
-#include "helper.h"
+#include "executor.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 <xalloc.h>
 
 
 #include <gettext.h>
@@ -42,7 +43,7 @@
 struct select_cases_dialog
 {
   /* The XML that created the dialog */
-  GladeXML *xml;
+  GtkBuilder *xml;
 
   GtkWidget *spinbutton ;
   GtkWidget *spinbutton1 ;
@@ -110,7 +111,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 +135,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 +242,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 +321,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 +356,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 +366,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);
       }
@@ -390,7 +389,7 @@ generate_syntax (const struct select_cases_dialog *scd)
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
                                              "radiobutton-all"))))
     {
-      return strdup ("\n");
+      return xstrdup ("\n");
     }
 
   string = g_string_new ("");
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..a11356109daad8f9c4ac3a202dd382bd9a3305af 100644 (file)
 #include <config.h>
 #include <gtk/gtk.h>
 #include "sort-cases-dialog.h"
-#include "helper.h"
+#include "executor.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..458a3c53532a1eafdddbd9552f14cc4b11286ffc 100644 (file)
 #include "split-file-dialog.h"
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
-#include "helper.h"
-#include "data-editor.h"
+#include "executor.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..f5df9f36e3004533789b74a78ae16670f57baab5 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 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 <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 "executor.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));
 
@@ -118,8 +117,8 @@ tt_groups_dialog_create (GladeXML *xml, GtkWindow *parent)
 
   gtk_window_set_transient_for (GTK_WINDOW (grps->dialog), parent);
 
-  grps->val[0] = strdup ("");
-  grps->val[1] = strdup ("");
+  grps->val[0] = xstrdup ("");
+  grps->val[1] = xstrdup ("");
 
   return 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;
@@ -341,10 +340,10 @@ run_define_groups (struct tt_indep_samples_dialog *ttd)
          grps->group_defn = GROUPS_VALUES;
 
          grps->val[0] =
-           strdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[0])));
+           xstrdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[0])));
 
          grps->val[1] =
-           strdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[1])));
+           xstrdup (gtk_entry_get_text (GTK_ENTRY (grps->grp_entry[1])));
        }
       else
        {
@@ -353,7 +352,7 @@ run_define_groups (struct tt_indep_samples_dialog *ttd)
          grps->val[1] = NULL;
 
          grps->val[0] =
-           strdup (gtk_entry_get_text (GTK_ENTRY (grps->cut_point_entry)));
+           xstrdup (gtk_entry_get_text (GTK_ENTRY (grps->cut_point_entry)));
        }
 
       psppire_dialog_notify_change (PSPPIRE_DIALOG (ttd->dialog));
@@ -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..18bdbb5dd3003f506a73a3b265a449a679541d77 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"
 #include "widget-io.h"
+#include "executor.h"
 
 #include "t-test-options.h"
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
 
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
@@ -124,11 +123,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 +144,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 +180,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 +191,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..dcc24fadd85c8ddc75354efdf94100190dd17c28 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 "executor.h"
 
 #include "helper.h"
 
@@ -183,11 +182,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 +202,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 +254,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 +264,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..6f33ff6d8bb8b6547a76a91bc06d35260a3a1881 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 "executor.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;
@@ -337,11 +310,9 @@ apply_dict (const struct dictionary *dict, struct string *s)
           syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
           for (j = 0; j < mv_n_values (mv); j++)
             {
-              union value value;
               if (j)
                 ds_put_cstr (s, ", ");
-              mv_get_value (mv, &value, j);
-              syntax_gen_value (s, &value, width, format);
+              syntax_gen_value (s, mv_get_value (mv, j), width, format);
             }
 
           if (mv_has_range (mv))
@@ -357,18 +328,20 @@ apply_dict (const struct dictionary *dict, struct string *s)
       if (var_has_value_labels (var))
         {
           const struct val_labs *vls = var_get_value_labels (var);
-          struct val_labs_iterator *iter;
-          struct val_lab *vl;
+          const struct val_lab **labels = val_labs_sorted (vls);
+          size_t n_labels = val_labs_count (vls);
+          size_t i;
 
           syntax_gen_pspp (s, "VALUE LABELS %ss", name);
-          for (vl = val_labs_first_sorted (vls, &iter); vl != NULL;
-               vl = val_labs_next (vls, &iter))
+          for (i = 0; i < n_labels; i++)
             {
+              const struct val_lab *vl = labels[i];
               ds_put_cstr (s, "\n  ");
               syntax_gen_value (s, &vl->value, width, format);
               ds_put_char (s, ' ');
-              syntax_gen_string (s, ss_cstr (vl->label));
+              syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
             }
+          free (labels);
           ds_put_cstr (s, ".\n");
         }
       if (var_has_label (var))
@@ -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,14 +1535,15 @@ 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;
+  p->dict = NULL;
 }
 
 /* Frees IA's formats substructure. */
@@ -1623,17 +1632,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 +1650,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);
@@ -1726,7 +1733,7 @@ parse_field (struct import_assistant *ia,
              char **outputp, char **tooltipp)
 {
   struct substring field;
-  union value *val;
+  union value val;
   struct variable *var;
   const struct fmt_spec *in;
   struct fmt_spec out;
@@ -1735,15 +1742,17 @@ parse_field (struct import_assistant *ia,
 
   field = ia->separators.columns[column].contents[row];
   var = dict_get_var (ia->formats.dict, column);
-  val = value_create (var_get_width (var));
+  value_init (&val, var_get_width (var));
   in = var_get_print_format (var);
   out = fmt_for_output_from_input (in);
   tooltip = NULL;
   if (field.string != NULL)
     {
       msg_disable ();
+
       if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0,
-                    val, var_get_width (var)))
+                   ia->formats.dict,
+                    &val, var_get_width (var)))
         {
           char fmt_string[FMT_STRING_LEN_MAX + 1];
           fmt_to_string (in, fmt_string);
@@ -1758,16 +1767,13 @@ parse_field (struct import_assistant *ia,
     {
       tooltip = xstrdup (_("This input line has too few separators "
                            "to fill in this field."));
-      value_set_missing (val, var_get_width (var));
+      value_set_missing (&val, var_get_width (var));
     }
   if (outputp != NULL)
     {
-      char *output = xmalloc (out.w + 1);
-      data_out (val, &out, output);
-      output[out.w] = '\0';
-      *outputp = output;
+      *outputp = data_out (&val, dict_get_encoding (ia->formats.dict),  &out);
     }
-  free (val);
+  value_destroy (&val, var_get_width (var));
 
   ok = tooltip == NULL;
   if (tooltipp != NULL)
@@ -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..beed72d731590eb4089d442a41e684545e2be6da 100644 (file)
 #include "transpose-dialog.h"
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
-#include "helper.h"
-#include "data-editor.h"
+#include "executor.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..4b575d9779799b7d5850d683dd57afe17ba98a66 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2005  Free Software Foundation
+   Copyright (C) 2005, 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 "val-labs-dialog.h"
 #include <data/value-labels.h>
 #include <data/format.h>
-
+#include "psppire-var-sheet.h"
+#include "psppire-var-store.h"
+#include <libpspp/i18n.h>
 
 struct val_labs_dialog
 {
   GtkWidget *window;
 
+  PsppireVarStore *var_store;
+
   /* The variable to be updated */
   struct variable *pv;
 
@@ -68,10 +72,11 @@ on_label_entry_change (GtkEntry *entry, gpointer data)
   text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
 
   text_to_value (text, &v,
+                dialog->var_store->dict,
                *var_get_write_format (dialog->pv));
 
 
-  if ( val_labs_find (dialog->labs, v) )
+  if (val_labs_find (dialog->labs, &v))
     {
       gtk_widget_set_sensitive (dialog->change_button, TRUE);
       gtk_widget_set_sensitive (dialog->add_button, FALSE);
@@ -130,7 +135,7 @@ select_treeview_from_value (GtkTreeView *treeview, union value *val)
 static void
 on_value_entry_change (GtkEntry *entry, gpointer data)
 {
-  char *s;
+  const char *s;
 
   struct val_labs_dialog *dialog = data;
 
@@ -138,6 +143,7 @@ on_value_entry_change (GtkEntry *entry, gpointer data)
 
   union value v;
   text_to_value (text, &v,
+                dialog->var_store->dict,
                *var_get_write_format (dialog->pv));
 
 
@@ -147,7 +153,7 @@ on_value_entry_change (GtkEntry *entry, gpointer data)
   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),"");
 
 
-  if ( (s = val_labs_find (dialog->labs, v)) )
+  if ( (s = val_labs_find (dialog->labs, &v)) )
     {
       gtk_entry_set_text (GTK_ENTRY (dialog->label_entry), s);
       gtk_widget_set_sensitive (dialog->add_button, FALSE);
@@ -223,14 +229,15 @@ on_delete (GtkWidget *w, GdkEvent *e, gpointer data)
 
 
 /* Return the value-label pair currently selected in the dialog box  */
-static struct val_lab *
-get_selected_tuple (struct val_labs_dialog *dialog)
+static void
+get_selected_tuple (struct val_labs_dialog *dialog,
+                    union value *valuep, const char **label)
 {
   GtkTreeView *treeview = GTK_TREE_VIEW (dialog->treeview);
-  static struct val_lab vl;
 
   GtkTreeIter iter ;
   GValue the_value = {0};
+  union value value;
 
   GtkTreeSelection* sel =  gtk_tree_view_get_selection (treeview);
 
@@ -240,12 +247,13 @@ get_selected_tuple (struct val_labs_dialog *dialog)
 
   gtk_tree_model_get_value (model, &iter, 1, &the_value);
 
-  vl.value.f = g_value_get_double (&the_value);
+  value.f = g_value_get_double (&the_value);
   g_value_unset (&the_value);
 
-  vl.label = val_labs_find (dialog->labs, vl.value);
-
-  return &vl;
+  if (valuep != NULL)
+    *valuep = value;
+  if (label != NULL)
+    *label = val_labs_find (dialog->labs, &value);
 }
 
 
@@ -262,14 +270,16 @@ on_change (GtkWidget *w, gpointer data)
   union value v;
 
   text_to_value (val_text, &v,
+                dialog->var_store->dict,
                *var_get_write_format (dialog->pv));
 
-  val_labs_replace (dialog->labs, v,
+  val_labs_replace (dialog->labs, &v,
                    gtk_entry_get_text (GTK_ENTRY (dialog->label_entry)));
 
   gtk_widget_set_sensitive (dialog->change_button, FALSE);
 
   repopulate_dialog (dialog);
+  gtk_widget_grab_focus (dialog->value_entry);
 
   return FALSE;
 }
@@ -285,10 +295,11 @@ on_add (GtkWidget *w, gpointer data)
   const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
 
   text_to_value (text, &v,
+                dialog->var_store->dict,
                *var_get_write_format (dialog->pv));
 
 
-  if ( ! val_labs_add (dialog->labs, v,
+  if ( ! val_labs_add (dialog->labs, &v,
                       gtk_entry_get_text
                       ( GTK_ENTRY (dialog->label_entry)) ) )
     return FALSE;
@@ -296,6 +307,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;
 }
@@ -306,11 +318,16 @@ on_remove (GtkWidget *w, gpointer data)
 {
   struct val_labs_dialog *dialog = data;
 
-  struct val_lab *vl = get_selected_tuple (dialog);
+  union value value;
+  const struct val_lab *vl;
 
-  val_labs_remove (dialog->labs, vl->value);
+  get_selected_tuple (dialog, &value, NULL);
+  vl = val_labs_lookup (dialog->labs, &value);
+  if (vl != NULL)
+    val_labs_remove (dialog->labs, vl);
 
   repopulate_dialog (dialog);
+  gtk_widget_grab_focus (dialog->value_entry);
 
   gtk_widget_set_sensitive (dialog->remove_button, FALSE);
 
@@ -322,16 +339,17 @@ on_remove (GtkWidget *w, gpointer data)
 /* Callback which occurs when a line item is selected in the list of
    value--label pairs.*/
 static void
-on_select_row                  (GtkTreeView *treeview,
-                               gpointer data)
+on_select_row (GtkTreeView *treeview, gpointer data)
 {
-  gchar *labeltext;
   struct val_labs_dialog *dialog = data;
 
-  struct val_lab * vl  = get_selected_tuple (dialog);
+  union value value;
+  const char *label = NULL;
 
-  gchar *const text = value_to_text (vl->value,
-                                   *var_get_write_format (dialog->pv));
+  gchar *text;
+
+  get_selected_tuple (dialog, &value, &label);
+  text = value_to_text (value, dialog->var_store->dict, *var_get_write_format (dialog->pv));
 
   g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
                         dialog->value_handler_id);
@@ -345,10 +363,9 @@ on_select_row                  (GtkTreeView *treeview,
   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
                         dialog->change_handler_id);
 
-  labeltext = pspp_locale_to_utf8 (vl->label, -1, 0);
+
   gtk_entry_set_text (GTK_ENTRY (dialog->label_entry),
-                    labeltext);
-  g_free (labeltext);
+                     label);
 
   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
                         dialog->change_handler_id);
@@ -361,23 +378,23 @@ 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, PsppireVarStore *var_store)
 {
   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->var_store = var_store;
   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 +451,8 @@ val_labs_dialog_create (GladeXML *xml)
 
   dialog->labs = 0;
 
+  g_object_unref (xml);
+
   return dialog;
 }
 
@@ -452,8 +471,9 @@ val_labs_dialog_set_target_variable (struct val_labs_dialog *dialog,
 static void
 repopulate_dialog (struct val_labs_dialog *dialog)
 {
-  struct val_labs_iterator *vli = 0;
-  struct val_lab *vl;
+  const struct val_lab **labels;
+  size_t n_labels;
+  size_t i;
 
   GtkTreeIter iter;
 
@@ -474,22 +494,18 @@ repopulate_dialog (struct val_labs_dialog *dialog)
   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
                           dialog->change_handler_id);
 
-
-  for (vl = val_labs_first_sorted (dialog->labs, &vli);
-      vl;
-      vl = val_labs_next (dialog->labs, &vli))
+  labels = val_labs_sorted (dialog->labs);
+  n_labels = val_labs_count (dialog->labs);
+  for (i = 0; i < n_labels; i++)
     {
+      const struct val_lab *vl = labels[i];
 
       gchar *const vstr  =
-       value_to_text (vl->value,
+       value_to_text (vl->value, dialog->var_store->dict,
                      *var_get_write_format (dialog->pv));
 
-      gchar *labeltext =
-       pspp_locale_to_utf8 (vl->label, -1, 0);
-
       gchar *const text = g_strdup_printf ("%s = \"%s\"",
-                                         vstr, labeltext);
-
+                                          vstr, val_lab_get_label (vl));
 
       gtk_list_store_append (list_store, &iter);
       gtk_list_store_set (list_store, &iter,
@@ -497,10 +513,10 @@ repopulate_dialog (struct val_labs_dialog *dialog)
                          1, vl->value.f,
                          -1);
 
-      g_free (labeltext);
       g_free (text);
       g_free (vstr);
     }
+  free (labels);
 
   gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
                          GTK_TREE_MODEL (list_store));
@@ -528,6 +544,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..745e0a0ae5f795b2786a078d9e435ca633ad242c 100644 (file)
@@ -14,8 +14,6 @@
    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_VAL_LABS_DIALOG_H
 #define __PSPPIRE_VAL_LABS_DIALOG_H
 
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 #include <data/variable.h>
-
+//#include <gtk-contrib/psppire-sheet.h>
+#include "psppire-var-store.h"
 
 struct val_labs;
 
 
-struct val_labs_dialog * val_labs_dialog_create (GladeXML *);
+struct val_labs_dialog * val_labs_dialog_create (GtkWindow *, PsppireVarStore *);
 
 void val_labs_dialog_show (struct val_labs_dialog *);
 
index c69730f6f2a104abf607d64a9a335bb15bb15d7e..7081e9e2c0bde74fc4c9424f729733ba8a74c1c2 100644 (file)
@@ -4,34 +4,17 @@
 #include <data/variable.h>
 #include <data/format.h>
 #include <stdlib.h>
+#include "psppire-dict.h"
 
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
 #include "helper.h"
+#include <libpspp/i18n.h>
 
-const static gchar none[] = N_("None");
+static const gchar none[] = N_("None");
 
-gchar *
-name_to_string (const struct variable *var, GError **err)
-{
-  const char *name = var_get_name (var);
-  g_assert (name);
-
-  return pspp_locale_to_utf8 (name, -1, err);
-}
-
-
-gchar *
-label_to_string (const struct variable *var, GError **err)
-{
-  const char *label = var_get_label (var);
-
-  if ( ! label ) return g_strdup (none);
-
-  return pspp_locale_to_utf8 (label, -1, err);
-}
 
 gchar *
 measure_to_string (const struct variable *var, GError **err)
@@ -45,7 +28,7 @@ measure_to_string (const struct variable *var, GError **err)
 
 
 gchar *
-missing_values_to_string (const struct variable *pv, GError **err)
+missing_values_to_string (const PsppireDict *dict, const struct variable *pv, GError **err)
 {
   const struct fmt_spec *fmt =  var_get_print_format (pv);
   gchar *s;
@@ -62,16 +45,14 @@ missing_values_to_string (const struct variable *pv, GError **err)
          gint i;
          for (i = 0 ; i < n; ++i )
            {
-             union value v;
-             mv_get_value (miss, &v, i);
-             mv[i] = value_to_text (v, *fmt);
+             mv[i] = value_to_text (*mv_get_value (miss, i), dict, *fmt);
              if ( i > 0 )
                g_string_append (gstr, ", ");
              g_string_append (gstr, mv[i]);
              g_free (mv[i]);
            }
-         s = pspp_locale_to_utf8 (gstr->str, gstr->len, err);
-         g_string_free (gstr, TRUE);
+         s = gstr->str;
+         g_string_free (gstr, FALSE);
        }
       else
        {
@@ -80,8 +61,8 @@ missing_values_to_string (const struct variable *pv, GError **err)
          union value low, high;
          mv_get_range (miss, &low.f, &high.f);
 
-         l = value_to_text (low, *fmt);
-         h = value_to_text (high, *fmt);
+         l = value_to_text (low, dict, *fmt);
+         h = value_to_text (high, dict,*fmt);
 
          g_string_printf (gstr, "%s - %s", l, h);
          g_free (l);
@@ -90,17 +71,15 @@ missing_values_to_string (const struct variable *pv, GError **err)
          if ( mv_has_value (miss))
            {
              gchar *ss = 0;
-             union value v;
-             mv_get_value (miss, &v, 0);
 
-             ss = value_to_text (v, *fmt);
+             ss = value_to_text (*mv_get_value (miss, 0), dict, *fmt);
 
              g_string_append (gstr, ", ");
              g_string_append (gstr, ss);
              free (ss);
            }
-         s = pspp_locale_to_utf8 (gstr->str, gstr->len, err);
-         g_string_free (gstr, TRUE);
+         s = gstr->str;
+         g_string_free (gstr, FALSE);
        }
 
       return s;
index 40404b896d1ae7be087830d39d5f5fbc4ab9b8df..927e235c21419d48e80875284412dd9268424f3e 100644 (file)
 
 #include <glib.h>
 #include <data/variable.h>
+#include "psppire-dict.h"
 
 struct variable;
 
 #define n_ALIGNMENTS 3
 
 extern const gchar *const alignments[n_ALIGNMENTS + 1];
-
 extern const gchar *const measures[n_MEASURES + 1];
 
-
-gchar * name_to_string (const struct variable *var, GError **err);
-
-
-gchar * missing_values_to_string (const struct variable *pv, GError **err);
-
-gchar * measure_to_string (const struct variable *var, GError **err);
-
-gchar * label_to_string (const struct variable *var, GError **err);
-
+gchar *missing_values_to_string (const PsppireDict *dict, const struct variable *pv, GError **err);
+gchar *measure_to_string (const struct variable *var, GError **err);
 
 #endif
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..05ba5d3c9e748c61662bc8ee48914a521938490f 100644 (file)
@@ -21,7 +21,6 @@
 #include <config.h>
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 #include <stdlib.h>
 #include <string.h>
@@ -263,12 +262,12 @@ preview_custom (GtkWidget *w, gpointer data)
       union value v;
       v.f = 1234.56;
 
-      sample_text = value_to_text (v, dialog->fmt_l);
+      sample_text = value_to_text (v, NULL, dialog->fmt_l);
       gtk_label_set_text (GTK_LABEL (dialog->label_psample), sample_text);
       g_free (sample_text);
 
       v.f = -v.f;
-      sample_text = value_to_text (v, dialog->fmt_l);
+      sample_text = value_to_text (v, NULL, dialog->fmt_l);
       gtk_label_set_text (GTK_LABEL (dialog->label_nsample), sample_text);
       g_free (sample_text);
     }
@@ -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..178a834c7a0d8357c642997c649f81f9612488b6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 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 <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 <libpspp/i18n.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;
+static const gchar none[] = N_("None");
 
-  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));
+static const gchar *
+label_to_string (const struct variable *var)
+{
+  const char *label = var_get_label (var);
 
-  gtk_tree_model_get (model,
-                     &iter, DICT_TVM_COL_VAR, &var, -1);
+  if (NULL == label) return g_strdup (none);
 
-  return var;
+  return label;
 }
 
 
-
 static void
-populate_text (GtkTreeView *treeview, gpointer data)
+populate_text (PsppireDictView *treeview, gpointer data)
 {
   gchar *text = 0;
   GString *gstring;
+  PsppireDict *dict;
+
+  GtkTextBuffer *textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (data));
+  const struct variable *var =
+    psppire_dict_view_get_selected_variable (treeview);
 
-  GtkTextBuffer *textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(data));
-  const struct variable *var = get_selected_variable (treeview);
   if ( var == NULL)
     return;
 
+  g_object_get (treeview,
+               "dictionary", &dict,
+               NULL);
+
   gstring = g_string_sized_new (200);
-  text = name_to_string (var, NULL);
-  g_string_assign (gstring, text);
-  g_free (text);
+  g_string_assign (gstring, var_get_name (var));
   g_string_append (gstring, "\n");
 
 
-  text = label_to_string (var, NULL);
-  g_string_append_printf (gstring, _("Label: %s\n"), text);
-  g_free (text);
-
+  g_string_append_printf (gstring, _("Label: %s\n"), label_to_string (var));
   {
     const struct fmt_spec *fmt = var_get_print_format (var);
     char buffer[FMT_STRING_LEN_MAX + 1];
@@ -98,7 +84,7 @@ populate_text (GtkTreeView *treeview, gpointer data)
     g_string_append_printf (gstring, _("Type: %s\n"), buffer);
   }
 
-  text = missing_values_to_string (var, NULL);
+  text = missing_values_to_string (dict, var, NULL);
   g_string_append_printf (gstring, _("Missing Values: %s\n"),
                          text);
   g_free (text);
@@ -113,29 +99,27 @@ populate_text (GtkTreeView *treeview, gpointer data)
   /* Value Labels */
   if ( var_has_value_labels (var))
     {
-      struct val_labs_iterator *vli = 0;
-      struct val_lab *vl;
-      const struct val_labs *labs = var_get_value_labels (var);
+      const struct val_labs *vls = var_get_value_labels (var);
+      const struct val_lab **labels;
+      size_t n_labels;
+      size_t i;
 
       g_string_append (gstring, "\n");
       g_string_append (gstring, _("Value Labels:\n"));
 
-#if 1
-      for (vl = val_labs_first_sorted (labs, &vli);
-          vl;
-          vl = val_labs_next (labs, &vli))
-       {
+      labels = val_labs_sorted (vls);
+      n_labels = val_labs_count (vls);
+      for (i = 0; i < n_labels; i++)
+        {
+          const struct val_lab *vl = labels[i];
          gchar *const vstr  =
-           value_to_text (vl->value,  *var_get_print_format (var));
+           value_to_text (vl->value,  dict, *var_get_print_format (var));
 
-         text = pspp_locale_to_utf8 (vl->label, -1, NULL);
+         g_string_append_printf (gstring, _("%s %s\n"), vstr, val_lab_get_label (vl));
 
-         g_string_append_printf (gstring, _("%s %s\n"), vstr, text);
-
-         g_free (text);
          g_free (vstr);
        }
-#endif
+      free (labels);
     }
 
   gtk_text_buffer_set_text (textbuffer, gstring->str, gstring->len);
@@ -158,17 +142,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 +162,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 +185,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 +213,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..11b528ba76d1ebf2f3f06cea96cabad8d8eac7f9 100644 (file)
 #include "weight-cases-dialog.h"
 #include "psppire-selector.h"
 #include "psppire-dialog.h"
-#include "helper.h"
-#include "data-editor.h"
+#include "executor.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;
index e64719907f8ecb910f62efabd2e6604cc9ce6524..3771cad6454a6e8598427dd0834139184ec31f27 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 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
@@ -26,6 +26,7 @@
 
 #include <gl/gettext.h>
 
+#include "xalloc.h"
 
 /* Create a GtkLabel and pack it into BOX.
    The label is created using part of the string at S, and the directives
@@ -90,7 +91,7 @@ widget_printf (const gchar *fmt, ...)
   if ( 0 !=  printf_parse (fmt, &d, &a) )
     return NULL;
 
-  widgets = calloc (sizeof (*widgets), d.count);
+  widgets = xcalloc (sizeof (*widgets), d.count);
   va_start (ap, fmt);
   for (i = 0 ; i < d.count ; ++i )
     {
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 84415c3e92f38adac840c6842c433c2b8aeab49d..22e717ac965903acdb5a8b12f95e5e3dd6492832 100644 (file)
@@ -146,20 +146,23 @@ syntax_gen_number (struct string *output,
           & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)))
     {
       union value v_in, v_out;
-      char buffer[FMT_MAX_NUMERIC_WIDTH];
+      char *s;
       bool ok;
 
       v_in.f = number;
-      data_out (&v_in, format, buffer);
+      s = data_out (&v_in, "FIXME",  format);
       msg_disable ();
-      ok = data_in (ss_buffer (buffer, format->w), LEGACY_NATIVE,
-                    format->type, false, 0, 0, &v_out, 0);
+      /* FIXME: UTF8 encoded strings will fail here */
+      ok = data_in (ss_cstr (s), LEGACY_NATIVE,
+                    format->type, false, 0, 0, NULL, &v_out, 0);
       msg_enable ();
       if (ok && v_out.f == number)
         {
-          syntax_gen_string (output, ss_buffer (buffer, format->w));
+          syntax_gen_string (output, ss_cstr (s));
+         free (s);
           return;
         }
+      free (s);
     }
 
   if (number == SYSMIS)
@@ -194,7 +197,7 @@ syntax_gen_value (struct string *output, const union value *value, int width,
   if (width == 0)
     syntax_gen_number (output, value->f, format);
   else
-    syntax_gen_string (output, ss_buffer (value->s, width));
+    syntax_gen_string (output, ss_buffer (value_str (value, width), width));
 }
 
 /* Appends <low> THRU <high> to OUTPUT.  If LOW is LOWEST, then
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..af8f7f2dd4df9828361a1dccfdb54d09473875b9 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
@@ -16,7 +16,6 @@
 
 #include <config.h>
 
-#include <locale.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,6 +29,8 @@
 #include <ieeefp.h>
 #endif
 
+
+#include <libpspp/i18n.h>
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
 #include <libpspp/getl.h>
 #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"
@@ -59,7 +62,6 @@
 #define _(msgid) gettext (msgid)
 
 
-static void i18n_init (void);
 static void fpu_init (void);
 static void clean_up (void);
 
@@ -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,56 +110,62 @@ 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,
+                                     _("Diagnostic 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 ();
 }
 \f
-static void
-i18n_init (void)
-{
-#if ENABLE_NLS
-#if HAVE_LC_MESSAGES
-  setlocale (LC_MESSAGES, "");
-#endif
-#if HAVE_LC_PAPER
-  setlocale (LC_PAPER, "");
-#endif
-  bindtextdomain (PACKAGE, locale_dir);
-  textdomain (PACKAGE);
-#endif /* ENABLE_NLS */
-}
-
 static void
 fpu_init (void)
 {
@@ -208,5 +219,6 @@ clean_up (void)
       readln_uninitialize ();
       outp_done ();
       msg_ui_done ();
+      i18n_done ();
     }
 }
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 5092eca226b83fbb06c477522b609defc83b4ce7..15e297a0150209c1586636de126a7a92a5d8defe 100644 (file)
@@ -6,13 +6,16 @@ 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 \
        tests/command/count.sh \
-       tests/command/datasheet.sh \
        tests/command/data-list.sh \
        tests/command/do-if.sh \
        tests/command/do-repeat.sh \
@@ -28,9 +31,9 @@ dist_TESTS = \
        tests/command/get-data-txt-examples.sh \
        tests/command/get-data-txt-importcases.sh \
        tests/command/import-export.sh \
-       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 +43,8 @@ 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/npar-sign.sh \
        tests/command/oneway.sh \
        tests/command/oneway-missing.sh \
        tests/command/oneway-with-splits.sh \
@@ -50,6 +55,9 @@ dist_TESTS = \
        tests/command/rename.sh \
        tests/command/regression.sh \
        tests/command/regression-qr.sh \
+       tests/command/reliability.sh \
+       tests/command/roc.sh \
+       tests/command/roc2.sh \
        tests/command/sample.sh \
        tests/command/sort.sh \
        tests/command/sysfiles.sh \
@@ -68,6 +76,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 \
@@ -100,6 +109,7 @@ dist_TESTS = \
        tests/bugs/compute-fmt.sh \
        tests/bugs/compression.sh \
        tests/bugs/crosstabs.sh \
+       tests/bugs/crosstabs2.sh \
        tests/bugs/crosstabs-crash.sh \
        tests/bugs/crosstabs-crash2.sh \
        tests/bugs/curtailed.sh \
@@ -107,6 +117,9 @@ 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-crash3.sh \
        tests/bugs/examine-1sample.sh \
        tests/bugs/examine-missing.sh \
        tests/bugs/examine-missing2.sh \
@@ -123,6 +136,7 @@ dist_TESTS = \
        tests/bugs/overwrite-special-file.sh \
        tests/bugs/piechart.sh \
        tests/bugs/random.sh \
+       tests/bugs/shbang.sh \
        tests/bugs/signals.sh \
        tests/bugs/t-test-with-temp.sh \
        tests/bugs/t-test.sh \
@@ -141,6 +155,8 @@ dist_TESTS = \
        tests/bugs/temp-freq.sh \
        tests/bugs/print-crash.sh \
        tests/bugs/keep-all.sh \
+       tests/data/datasheet-test.sh \
+       tests/libpspp/sparse-xarray-test.sh \
        tests/output/paper-size.sh \
        tests/xforms/recode.sh \
        tests/stats/descript-basic.sh \
@@ -169,6 +185,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 \
@@ -181,13 +199,20 @@ TESTS = $(dist_TESTS) $(nodist_TESTS)
 
 check_PROGRAMS += \
        $(nodist_TESTS) \
-       tests/formats/inexactify
+       tests/data/datasheet-test \
+       tests/formats/inexactify \
+       tests/libpspp/sparse-xarray-test
+
+tests_data_datasheet_test_SOURCES = \
+       tests/data/datasheet-test.c
+tests_data_datasheet_test_LDADD = src/libpspp-core.la @LIBINTL@ 
 
 tests_libpspp_ll_test_SOURCES = \
        src/libpspp/ll.c \
        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 \
@@ -196,6 +221,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 \
@@ -203,9 +229,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 \
@@ -237,12 +279,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 \
@@ -252,7 +294,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 = \
@@ -261,9 +303,24 @@ 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_libpspp_sparse_xarray_test_SOURCES = \
+       src/libpspp/argv-parser.c \
+       src/libpspp/bt.c \
+       src/libpspp/deque.c \
+       src/libpspp/model-checker.c \
+       src/libpspp/range-set.c \
+       src/libpspp/sparse-array.c \
+       src/libpspp/sparse-xarray.c \
+       src/libpspp/str.c \
+       src/libpspp/pool.c \
+       src/libpspp/tmpfile.c \
+       tests/libpspp/sparse-xarray-test.c
+tests_libpspp_sparse_xarray_test_LDADD = gl/libgl.la @LIBINTL@ 
+tests_libpspp_sparse_xarray_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
 tests_formats_inexactify_SOURCES = tests/formats/inexactify.c
 
 noinst_PROGRAMS += tests/dissect-sysfile
@@ -271,7 +328,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 += \
@@ -314,11 +371,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
index eea89304599d63e3c7558ed21f9775f84f40c9f1..4454319c709420bb47181ef010698499a87bdb20 100755 (executable)
@@ -95,7 +95,7 @@ diff -b  -w $TEMPDIR/pspp.list - << EOF
 #---------------#--------+--------+--------+--------+--------+--------#
 #X * Y          #       1|  100.0%|       0|    0.0%|       1|  100.0%#
 #===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  X by Y [count].
+2.2 CROSSTABS.  X * Y [count].
 #===============#==============================================================#========#
 #               #                               Y                              |        #
 #               #--------+--------+--------+--------+--------+--------+--------+        #
index 060fa234378eaa0a926da02cf08d64d77a01a386..b0db128a1c54e01947a704fdacb7bb6f24d59d19 100755 (executable)
@@ -99,11 +99,13 @@ $TEMPDIR/crosstabs-crash2.sh.sps:6: warning: BEGIN DATA: Missing value(s) for al
 #---------------#--------+--------+--------+--------+--------+--------#
 #x * y          #       4|   66.7%|       2|   33.3%|       6|  100.0%#
 #===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  x by y [count].
+2.2 CROSSTABS.  x * y [count].
 #===============#===================================#========#
 #               #                 y                 |        #
 #               #--------+--------+--------+--------+        #
-#              x#one unit|three lo|two dual|zero non|  Total #
+#              x#     one|   three|     two|    zero|  Total #
+#               #unity   |lots    |duality |none    |        #
+#               #        |        |        |        |        #
 #---------------#--------+--------+--------+--------+--------#
 #           1.00#     1.0|      .0|      .0|     1.0|     2.0#
 #           2.00#      .0|      .0|     1.0|      .0|     1.0#
diff --git a/tests/bugs/crosstabs2.sh b/tests/bugs/crosstabs2.sh
new file mode 100755 (executable)
index 0000000..3e66299
--- /dev/null
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+# This program tests for bug #26739, which caused CROSSTABS to crash
+# or to fail to output chi-square results.
+
+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
+
+LANG=C
+export LANG
+
+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 LIST /x * y *.
+BEGIN DATA.
+2 2
+3 1
+4 2
+4 1
+END DATA.
+
+CROSSTABS
+        /TABLES= x BY y
+        /STATISTICS=CHISQ
+       .
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+$SUPERVISOR $PSPP --testing-mode $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  -w $TEMPDIR/pspp.list - << EOF
+1.1 DATA LIST.  Reading free-form data from INLINE.
++--------+------+
+|Variable|Format|
+#========#======#
+|x       |F8.0  |
+|y       |F8.0  |
++--------+------+
+2.1 CROSSTABS.  Summary.
+#===============#=====================================================#
+#               #                        Cases                        #
+#               #-----------------+-----------------+-----------------#
+#               #      Valid      |     Missing     |      Total      #
+#               #--------+--------+--------+--------+--------+--------#
+#               #       N| Percent|       N| Percent|       N| Percent#
+#---------------#--------+--------+--------+--------+--------+--------#
+#x * y          #       4|  100.0%|       0|    0.0%|       4|  100.0%#
+#===============#========#========#========#========#========#========#
+2.2 CROSSTABS.  x * y [count].
+#===============#=================#========#
+#               #        y        |        #
+#               #--------+--------+        #
+#              x#    1.00|    2.00|  Total #
+#---------------#--------+--------+--------#
+#           2.00#      .0|     1.0|     1.0#
+#           3.00#     1.0|      .0|     1.0#
+#           4.00#     1.0|     1.0|     2.0#
+#Total          #     2.0|     2.0|     4.0#
+#===============#========#========#========#
+2.3 CROSSTABS.  Chi-square tests.
+#===============#========#========#========#
+#Statistic      #   Value|      df|  Asymp.#
+#               #        |        |    Sig.#
+#               #        |        |(2-sided#
+#               #        |        |       )#
+#---------------#--------+--------+--------#
+#Pearson        #    2.00|       2|     .37#
+#Chi-Square     #        |        |        #
+#Likelihood     #    2.77|       2|     .25#
+#Ratio          #        |        |        #
+#Linear-by-Linea#     .27|       1|     .60#
+#r Association  #        |        |        #
+#N of Valid     #       4|        |        #
+#Cases          #        |        |        #
+#===============#========#========#========#
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass;
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;
diff --git a/tests/bugs/examine-crash3.sh b/tests/bugs/examine-crash3.sh
new file mode 100755 (executable)
index 0000000..6093c34
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+# This program tests for a bug which crashed pspp
+# when the /DESCRIPTIVES subcommand of EXAMINE
+# encountered missing values.
+
+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 <<EOF > $TESTFILE
+data list list /x * y *.
+begin data.
+1 0
+2 0
+. 0
+3 1
+4 1
+end data.
+examine 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;
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/bugs/shbang.sh b/tests/bugs/shbang.sh
new file mode 100755 (executable)
index 0000000..826d5dc
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+# This program tests that PSPP ignores the first line of a PSPP syntax
+# file that begins with #!, without issuing an error (bug #26518).
+
+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
+
+
+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
+
+cat > $TESTFILE << EOF
+#! $PSPP
+DATA LIST LIST NOTABLE /a.
+BEGIN DATA.
+1
+2
+END DATA.
+LIST.
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode -e /dev/null $TESTFILE 
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+activity="compare output"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  -w $TEMPDIR/pspp.list - << EOF
+       a
+--------
+    1.00
+    2.00
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;
index f4fc10266fcb8acaa5b24be9fdc786d20c4e1244..7a614b091787b427776fcdbd4c7250ee05c35402 100755 (executable)
@@ -98,14 +98,14 @@ diff  -b $TEMPDIR/pspp.list - <<EOF
 |DEP2    |F8.0  |
 +--------+------+
 2.1 T-TEST.  Group Statistics
-#=============#=#====#==============#========#
-#       INDEP |N|Mean|Std. Deviation|SE. Mean#
-#=============#=#====#==============#========#
-#DEP1 a       |5|2.00|           .71|     .32#
-#     b       |5|4.00|           .71|     .32#
-#DEP2 a       |5|4.00|           .71|     .32#
-#     b       |5|2.00|           .71|     .32#
-#=============#=#====#==============#========#
+#==========#=#====#==============#========#
+#     INDEP|N|Mean|Std. Deviation|SE. Mean#
+#==========#=#====#==============#========#
+#DEP1 a    |5|2.00|           .71|     .32#
+#     b    |5|4.00|           .71|     .32#
+#DEP2 a    |5|4.00|           .71|     .32#
+#     b    |5|2.00|           .71|     .32#
+#==========#=#====#==============#========#
 2.2 T-TEST.  Independent Samples Test
 #===============================#========#============================================================================#
 #                               #Levene's|                        t-test for Equality of Means                        #
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;
diff --git a/tests/command/datasheet.sh b/tests/command/datasheet.sh
deleted file mode 100755 (executable)
index 2ad30d7..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/sh
-
-# This program tests datasheet support.
-
-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
-
-LANG=C
-export LANG
-
-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 File 1"
-cat > $TESTFILE <<EOF
-debug datasheet max=3,3 backing=0,0/progress=none/output=file("/dev/null").
-debug datasheet max=3,3 backing=3,3/progress=none/output=file("/dev/null").
-debug datasheet max=3,3 backing=3,1/progress=none/output=file("/dev/null").
-debug datasheet max=3,3 backing=1,3/progress=none/output=file("/dev/null").
-EOF
-if [ $? -ne 0 ] ; then no_result ; fi
-
-
-activity="Run pspp 1"
-$SUPERVISOR $PSPP --testing-mode $TESTFILE > datasheet.out
-if [ $? -ne 0 ] ; then no_result ; fi
-
-activity="compare results"
-diff -b  $TEMPDIR/datasheet.out - <<EOF
-Datasheet test max(3,3) backing(0,0) successful.
-Datasheet test max(3,3) backing(3,3) successful.
-Datasheet test max(3,3) backing(3,1) successful.
-Datasheet test max(3,3) backing(1,3) successful.
-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 746f1a670911e629e2121bf8187cb06e00f7bef4..c28c8dd478c9f6dc4dd04ea8ddd5dbb2ac27a069 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-# This program tests that pspp can read gnumeric files
+# This program tests that pspp can read Gnumeric files
 
 TEMPDIR=/tmp/pspp-tst-$$
 TESTFILE=$TEMPDIR/`basename $0`.sps
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 49a7cd4c2a26c449034973e35e8f415fb7c5d0c3..3501f2183f04034748f9c6da83f6499952e815ba 100755 (executable)
@@ -106,7 +106,7 @@ AlphaBetaGamma        B        X Yabbadabbadoo
 #X *            #       1|  100.0%|       0|    0.0%|       1|  100.0%#
 #Yabbadabbadoo  #        |        |        |        |        |        #
 #===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  X by Yabbadabbadoo [count].
+2.2 CROSSTABS.  X * Yabbadabbadoo [count].
 #===============#==============================================================#========#
 #               #                         Yabbadabbadoo                        |        #
 #               #--------+--------+--------+--------+--------+--------+--------+        #
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 6641b520b9d34a112a3306a13759d71a6228cd5e..c01adf3112749194e5037eb80cfe3cf8fc01d4fa 100755 (executable)
@@ -66,7 +66,8 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program"
 cat > $TEMPDIR/missing-values.stat << foobar
-DATA LIST NOTABLE/str1 1-5 (A) str2 6-8 (A) date1 9-19 (DATE) num1 20-25.
+DATA LIST NOTABLE/str1 1-5 (A) str2 6-8 (A) date1 9-19 (DATE) num1 20-25
+                  longstr 26-36 (A).
 
 /* Valid: numeric missing values.
 MISSING VALUES date1 num1 (1).
@@ -96,10 +97,13 @@ MISSING VALUES num1 (1 THRU HI, -1).
 MISSING VALUES num1 (1 THRU HIGHEST, -1).
 
 /* Valid: string missing values.
-MISSING VALUES str1 str2 ('abc  ','def').
+MISSING VALUES str1 str2 longstr ('abc  ','def').
 
 /* Invalid: too long for str2.
-MISSING VALUES str1 str2 ('abcde').
+MISSING VALUES str1 str2 longstr ('abcde').
+
+/* Invalid: long string missing value longer than 8 bytes.
+MISSING VALUES longstr ('abcdefghijk').
 
 /* Invalid: no string ranges.
 MISSING VALUES str1 ('a' THRU 'z').
@@ -120,11 +124,12 @@ $SUPERVISOR $PSPP --testing-mode --error-file=$TEMPDIR/errs $TEMPDIR/missing-val
 if [ $? -eq 0 ] ; then fail ; fi
 
 activity="compare error messages"
-diff -w $TEMPDIR/errs - <<EOF
-$TEMPDIR/missing-values.stat:34: error: MISSING VALUES: Missing values provided are too long to assign to variable of width 3.
-$TEMPDIR/missing-values.stat:37: error: MISSING VALUES: Syntax error expecting string at \`THRU'.
-$TEMPDIR/missing-values.stat:37: error: MISSING VALUES: THRU is not a variable name.
-$TEMPDIR/missing-values.stat:40: error: MISSING VALUES: Cannot mix numeric variables (e.g. num1) and string variables (e.g. str1) within a single list.
+diff -u -w $TEMPDIR/errs - <<EOF
+$TEMPDIR/missing-values.stat:35: error: MISSING VALUES: Missing values provided are too long to assign to variable of width 3.
+$TEMPDIR/missing-values.stat:38: error: MISSING VALUES: Truncating missing value to maximum acceptable length (8 bytes).
+$TEMPDIR/missing-values.stat:41: error: MISSING VALUES: Syntax error expecting string at \`THRU'.
+$TEMPDIR/missing-values.stat:41: error: MISSING VALUES: THRU is not a variable name.
+$TEMPDIR/missing-values.stat:44: error: MISSING VALUES: Cannot mix numeric variables (e.g. num1) and string variables (e.g. str1) within a single list.
 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-sign.sh b/tests/command/npar-sign.sh
new file mode 100755 (executable)
index 0000000..17c0c23
--- /dev/null
@@ -0,0 +1,117 @@
+#!/bin/sh
+
+# This program tests the SIGN 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
+set format = F9.3.
+
+data list notable list /age * height rank *.
+begin data.
+10 12 11
+12 13 13 
+13 14 12
+12 12 10
+9   9 10
+10.3 10.2 12
+end data.
+
+npar tests
+       /sign=age height WITH height rank (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="compare output 1"
+diff - $TEMPDIR/pspp.list <<EOF
+1.1 NPAR TESTS.  Frequencies
+#=================================#=#
+#                                 |N#
+#---------------------------------+-#
+#height - age Negative Differences|1#
+#             Positive Differences|3#
+#             Ties                |2#
+#             Total               |6#
+#---------------------------------+-#
+#rank - heightNegative Differences|3#
+#             Positive Differences|2#
+#             Ties                |1#
+#             Total               |6#
+#=================================#=#
+
+1.2 NPAR TESTS.  Test Statistics
+#=====================#============#=============#
+#                     |height - age|rank - height#
+#=====================#============#=============#
+#Exact Sig. (2-tailed)|        .625|        1.000#
+#Exact Sig. (1-tailed)|        .312|         .500#
+#Point Probability    |        .250|         .312#
+#=====================#============#=============#
+
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+pass;
diff --git a/tests/command/npar-wilcoxon.sh b/tests/command/npar-wilcoxon.sh
new file mode 100755 (executable)
index 0000000..4242b55
--- /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/roc.sh b/tests/command/roc.sh
new file mode 100755 (executable)
index 0000000..0c24f0c
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/sh
+
+# This program tests  the ROC command.
+
+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
+
+LANG=C
+export LANG
+
+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
+set format F10.3.
+data list notable list /x * y * w * a *.
+begin data.
+1 1 2  1
+1 2 28 0
+2 3 4  1
+2 4 14 0
+3 5 10 1
+. . 1  0
+3 1 5  0
+4 2 14 1
+4 3 2  0
+5 4 20 1
+5 4 20 .
+5 5 1  0
+end data.
+
+weight by w.
+
+roc x by a (1)
+       /plot = none
+       /print = se coordinates
+       /criteria = testpos(large) distribution(free) ci(99)
+       /missing = exclude .
+
+roc x y by a (1)
+       /plot = curve(reference)
+        /print = se coordinates
+       /criteria = testpos(large) distribution(negexpo) ci(95)
+       /missing = exclude .
+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 results"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  $TEMPDIR/pspp.list - << EOF
+1.1 ROC.  Case Summary
+#========#===================#
+#        # Valid N (listwise)#
+#        #==========#========#
+#a       #Unweighted|Weighted#
+#========#==========#========#
+#Positive#         5|  50.000#
+#Negative#         5|  50.000#
+#========#==========#========#
+1.2 ROC.  Area Under the Curve (x)
+#====#==========#===============#=======================#
+#    |          |               | Asymp. 99% Confidence #
+#    |          |               +-----------+-----------#
+#Area|Std. Error|Asymptotic Sig.|Lower Bound|Upper Bound#
+#====#==========#===============#===========#===========#
+#.910|      .030|           .000|       .839|       .981#
+#====#==========#===============#===========#===========#
+1.3 ROC.  Coordinates of the Curve (x)
+#====================================#===========#===============#
+#Positive if greater than or equal to|Sensitivity|1 - Specificity#
+#====================================#===========#===============#
+#                                .000|      1.000|          1.000#
+#                               1.500|       .960|           .440#
+#                               2.500|       .880|           .160#
+#                               3.500|       .680|           .060#
+#                               4.500|       .400|           .020#
+#                               6.000|       .000|           .000#
+#====================================#===========#===============#
+2.1 ROC.  Case Summary
+#========#===================#
+#        # Valid N (listwise)#
+#        #==========#========#
+#a       #Unweighted|Weighted#
+#========#==========#========#
+#Positive#         5|  50.000#
+#Negative#         5|  50.000#
+#========#==========#========#
+See pspp-1.png for a chart.
+2.2 ROC.  Area Under the Curve
+#===================#====#==========#===============#=======================#
+#                   #    |          |               | Asymp. 95%            #
+#                   #    |          |               +-----------+-----------#
+#Variable under test#Area|Std. Error|Asymptotic Sig.|Lower Bound|Upper Bound#
+#===================#====#==========#===============#===========#===========#
+#                  x#.910|      .030|           .000|       .860|       .960#
+#                  y#.697|      .052|           .001|       .611|       .783#
+#===================#====#==========#===============#===========#===========#
+2.3 ROC.  Coordinates of the Curve
+#=============#====================================#===========#===============#
+#Test variable#Positive if greater than or equal to|Sensitivity|1 - Specificity#
+#=============#====================================#===========#===============#
+#            x#                                .000|      1.000|          1.000#
+#             #                               1.500|       .960|           .440#
+#             #                               2.500|       .880|           .160#
+#             #                               3.500|       .680|           .060#
+#             #                               4.500|       .400|           .020#
+#             #                               6.000|       .000|           .000#
+#-------------#------------------------------------+-----------+---------------#
+#            y#                                .000|      1.000|          1.000#
+#             #                               1.500|       .960|           .900#
+#             #                               2.500|       .680|           .340#
+#             #                               3.000|       .600|           .340#
+#             #                               3.500|       .600|           .300#
+#             #                               4.500|       .200|           .020#
+#             #                               6.000|       .000|           .000#
+#=============#====================================#===========#===============#
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass
diff --git a/tests/command/roc2.sh b/tests/command/roc2.sh
new file mode 100755 (executable)
index 0000000..a8020b0
--- /dev/null
@@ -0,0 +1,131 @@
+#!/bin/sh
+
+# This program tests  the ROC command.
+
+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
+
+LANG=C
+export LANG
+
+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
+set format F10.3.
+data list notable list /x * a * comment (a20).
+begin data.
+0  1 ""
+0  0 ""
+1  1 ""
+1  0 ""
+2  1 ""
+2  0 ""
+5  1 ""
+5  0 ""
+10 1 ""
+10 0 ""
+15 1 ""
+15 0 ""
+20 1 ""
+20 1 ""
+22 0 "here and"
+22 0 "here is the anomoly"
+25 1 ""
+25 0 ""
+30 1 ""
+30 0 ""
+35 1 ""
+35 0 ""
+38 1 ""
+38 0 ""
+39 1 ""
+39 0 ""
+40 1 ""
+40 0 ""
+end data.
+
+roc x by a (1)
+       /plot = none
+       print = se 
+       .
+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 results"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  $TEMPDIR/pspp.list - << EOF
+1.1 ROC.  Case Summary
+#========#===================#
+#        # Valid N (listwise)#
+#        #==========#========#
+#a       #Unweighted|Weighted#
+#========#==========#========#
+#Positive#        14|  14.000#
+#Negative#        14|  14.000#
+#========#==========#========#
+1.2 ROC.  Area Under the Curve (x)
+#====#==========#===============#=======================#
+#    |          |               | Asymp. 95% Confidence #
+#    |          |               +-----------+-----------#
+#Area|Std. Error|Asymptotic Sig.|Lower Bound|Upper Bound#
+#====#==========#===============#===========#===========#
+#.490|      .111|           .927|       .307|       .673#
+#====#==========#===============#===========#===========#
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass
index 3456fba2e33f0791a7d2960eb6eb7d0b427947b1..bc11fa3a20ad6120b45901fd57d79995c7255158 100755 (executable)
@@ -99,6 +99,7 @@ Cases:          3
 Type:           System File.
 Weight:         Not weighted.
 Mode:           Compression on.
+Charset:        Unknown
 +--------+-------------+---+
 |Variable|Description  |Pos|
 |        |             |iti|
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;
diff --git a/tests/data/datasheet-test.c b/tests/data/datasheet-test.c
new file mode 100644 (file)
index 0000000..c46c268
--- /dev/null
@@ -0,0 +1,944 @@
+/* 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 <ctype.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <data/casereader-provider.h>
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <data/lazy-casereader.h>
+#include <libpspp/argv-parser.h>
+#include <libpspp/array.h>
+#include <libpspp/assertion.h>
+#include <libpspp/hash-functions.h>
+#include <libpspp/model-checker.h>
+#include <libpspp/range-map.h>
+#include <libpspp/range-set.h>
+#include <libpspp/str.h>
+#include <libpspp/taint.h>
+#include <libpspp/tower.h>
+
+#include "error.h"
+#include "minmax.h"
+#include "progname.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
+#define MAX_WIDTHS 5
+
+/* Test params. */
+struct datasheet_test_params
+  {
+    /* Parameters. */
+    int max_rows;               /* Maximum number of rows. */
+    int max_cols;               /* Maximum number of columns. */
+    int backing_rows;           /* Number of rows of backing store. */
+    int backing_widths[MAX_COLS]; /* Widths of columns of backing store. */
+    int n_backing_cols;           /* Number of columns of backing store. */
+    int widths[MAX_WIDTHS];     /* Allowed column widths. */
+    int n_widths;
+
+    /* State. */
+    unsigned int next_value;
+  };
+
+static bool
+check_caseproto (struct mc *mc, const struct caseproto *benchmark,
+                 const struct caseproto *test, const char *test_name)
+{
+  size_t n_columns = caseproto_get_n_widths (benchmark);
+  size_t col;
+  bool ok;
+
+  if (n_columns != caseproto_get_n_widths (test))
+    {
+      mc_error (mc, "%s column count (%zu) does not match expected (%zu)",
+                test_name, caseproto_get_n_widths (test), n_columns);
+      return false;
+    }
+
+  ok = true;
+  for (col = 0; col < n_columns; col++)
+    {
+      int benchmark_width = caseproto_get_width (benchmark, col);
+      int test_width = caseproto_get_width (test, col);
+      if (benchmark_width != test_width)
+        {
+          mc_error (mc, "%s column %zu width (%d) differs from expected (%d)",
+                    test_name, col, test_width, benchmark_width);
+          ok = false;
+        }
+    }
+  return ok;
+}
+
+/* Checks that READER contains the N_ROWS rows and N_COLUMNS
+   columns of data in ARRAY, reporting any errors via MC. */
+static void
+check_datasheet_casereader (struct mc *mc, struct casereader *reader,
+                            union value array[MAX_ROWS][MAX_COLS],
+                            size_t n_rows, const struct caseproto *proto)
+{
+  size_t n_columns = caseproto_get_n_widths (proto);
+
+  if (!check_caseproto (mc, proto, casereader_get_proto (reader),
+                        "casereader"))
+    return;
+  else if (casereader_get_case_cnt (reader) != n_rows)
+    {
+      if (casereader_get_case_cnt (reader) == CASENUMBER_MAX
+          && casereader_count_cases (reader) == n_rows)
+        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),
+                  n_rows);
+    }
+  else
+    {
+      struct ccase *c;
+      size_t row;
+
+      for (row = 0; row < n_rows; 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, n_rows, n_columns);
+              return;
+            }
+
+          for (col = 0; col < n_columns; col++)
+            {
+              int width = caseproto_get_width (proto, col);
+              if (!value_equal (case_data_idx (c, col), &array[row][col],
+                                width))
+                {
+                  if (width == 0)
+                    mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
+                              "%g != %g",
+                              row, col, n_rows, n_columns,
+                              case_num_idx (c, col), array[row][col].f);
+                  else
+                    mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
+                              "'%.*s' != '%.*s'",
+                              row, col, n_rows, n_columns,
+                              width, case_str_idx (c, col),
+                              width, value_str (&array[row][col], width));
+                }
+            }
+
+         case_unref (c);
+        }
+
+      c = casereader_read (reader);
+      if (c != NULL)
+        mc_error (mc, "casereader has extra cases (expected %zu)", n_rows);
+    }
+}
+
+/* Checks that datasheet DS contains has N_ROWS rows, N_COLUMNS
+   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,
+                 union value array[MAX_ROWS][MAX_COLS],
+                 size_t n_rows, const struct caseproto *proto)
+{
+  size_t n_columns = caseproto_get_n_widths (proto);
+  struct datasheet *ds2;
+  struct casereader *reader;
+  unsigned long int serial = 0;
+
+  assert (n_rows < MAX_ROWS);
+  assert (n_columns < MAX_COLS);
+
+  /* Check contents of datasheet via datasheet functions. */
+  if (!check_caseproto (mc, proto, datasheet_get_proto (ds), "datasheet"))
+    {
+      /* check_caseproto emitted errors already. */
+    }
+  else if (n_rows != datasheet_get_n_rows (ds))
+    mc_error (mc, "row count (%lu) does not match expected (%zu)",
+              (unsigned long int) datasheet_get_n_rows (ds), n_rows);
+  else
+    {
+      size_t row, col;
+      bool difference = false;
+
+      for (row = 0; row < n_rows; row++)
+        for (col = 0; col < n_columns; col++)
+          {
+            int width = caseproto_get_width (proto, col);
+            union value *av = &array[row][col];
+            union value v;
+
+            value_init (&v, width);
+            if (!datasheet_get_value (ds, row, col, &v))
+              NOT_REACHED ();
+            if (!value_equal (&v, av, width))
+              {
+                if (width == 0)
+                  mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
+                            "%g != %g", row, col, n_rows, n_columns,
+                            v.f, av->f);
+                else
+                  mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
+                            "'%.*s' != '%.*s'",
+                            row, col, n_rows, n_columns,
+                            width, value_str (&v, width),
+                            width, value_str (av, width));
+                difference = true;
+              }
+            value_destroy (&v, width);
+          }
+
+      if (difference)
+        {
+          struct string s;
+
+          mc_error (mc, "expected:");
+          ds_init_empty (&s);
+          for (row = 0; row < n_rows; row++)
+            {
+              ds_clear (&s);
+              ds_put_format (&s, "row %zu:", row);
+              for (col = 0; col < n_columns; col++)
+                {
+                  const union value *v = &array[row][col];
+                  int width = caseproto_get_width (proto, col);
+                  if (width == 0)
+                    ds_put_format (&s, " %g", v->f);
+                  else
+                    ds_put_format (&s, " '%.*s'", width, value_str (v, width));
+                }
+              mc_error (mc, "%s", ds_cstr (&s));
+            }
+
+          mc_error (mc, "actual:");
+          ds_init_empty (&s);
+          for (row = 0; row < n_rows; row++)
+            {
+              ds_clear (&s);
+              ds_put_format (&s, "row %zu:", row);
+              for (col = 0; col < n_columns; col++)
+                {
+                  int width = caseproto_get_width (proto, col);
+                  union value v;
+                  value_init (&v, width);
+                  if (!datasheet_get_value (ds, row, col, &v))
+                    NOT_REACHED ();
+                  if (width == 0)
+                    ds_put_format (&s, " %g", v.f);
+                  else
+                    ds_put_format (&s, " '%.*s'",
+                                   width, value_str (&v, width));
+                }
+              mc_error (mc, "%s", ds_cstr (&s));
+            }
+
+          ds_destroy (&s);
+        }
+    }
+
+  /* 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, n_rows, proto);
+  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 (datasheet_get_proto (ds2), n_rows,
+                                   lazy_callback, ds2, &serial);
+  check_datasheet_casereader (mc, reader, array, n_rows, proto);
+  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 (n_rows != 0)
+        mc_error (mc, "lazy casereader not instantiated, but should "
+                  "have been (size %zu,%zu)", n_rows, n_columns);
+    }
+  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 (n_rows == 0)
+        mc_error (mc, "lazy casereader instantiated, but should not "
+                  "have been (size %zu,%zu)", n_rows, n_columns);
+    }
+
+  if (mc_discard_dup_state (mc, hash_datasheet (ds)))
+    datasheet_destroy (ds);
+  else
+    mc_add_state (mc, ds);
+}
+
+/* Extracts the contents of DS into DATA. */
+static void
+extract_data (const struct datasheet *ds, union value data[MAX_ROWS][MAX_COLS])
+{
+  const struct caseproto *proto = datasheet_get_proto (ds);
+  size_t n_columns = datasheet_get_n_columns (ds);
+  size_t n_rows = datasheet_get_n_rows (ds);
+  size_t row, col;
+
+  assert (n_rows < MAX_ROWS);
+  assert (n_columns < MAX_COLS);
+  for (row = 0; row < n_rows; row++)
+    for (col = 0; col < n_columns; col++)
+      {
+        int width = caseproto_get_width (proto, col);
+        union value *v = &data[row][col];
+        value_init (v, width);
+        if (!datasheet_get_value (ds, row, col, v))
+          NOT_REACHED ();
+      }
+}
+
+/* Copies the contents of ODATA into DATA.  Each of the N_ROWS
+   rows of ODATA and DATA must have prototype PROTO. */
+static void
+clone_data (size_t n_rows, const struct caseproto *proto,
+            union value odata[MAX_ROWS][MAX_COLS],
+            union value data[MAX_ROWS][MAX_COLS])
+{
+  size_t n_columns = caseproto_get_n_widths (proto);
+  size_t row, col;
+
+  assert (n_rows < MAX_ROWS);
+  assert (n_columns < MAX_COLS);
+  for (row = 0; row < n_rows; row++)
+    for (col = 0; col < n_columns; col++)
+      {
+        int width = caseproto_get_width (proto, col);
+        const union value *ov = &odata[row][col];
+        union value *v = &data[row][col];
+        value_init (v, width);
+        value_copy (v, ov, width);
+      }
+}
+
+static void
+release_data (size_t n_rows, const struct caseproto *proto,
+              union value data[MAX_ROWS][MAX_COLS])
+{
+  size_t n_columns = caseproto_get_n_widths (proto);
+  size_t row, col;
+
+  assert (n_rows < MAX_ROWS);
+  assert (n_columns < MAX_COLS);
+  for (col = 0; col < n_columns; col++)
+    {
+      int width = caseproto_get_width (proto, col);
+      if (value_needs_init (width))
+        for (row = 0; row < n_rows; row++)
+          value_destroy (&data[row][col], width);
+    }
+}
+
+/* Clones the structure and contents of ODS into *DS,
+   and the contents of ODATA into DATA. */
+static void
+clone_model (const struct datasheet *ods,
+             union value odata[MAX_ROWS][MAX_COLS],
+             struct datasheet **ds,
+             union value data[MAX_ROWS][MAX_COLS])
+{
+  *ds = clone_datasheet (ods);
+  clone_data (datasheet_get_n_rows (ods), datasheet_get_proto (ods),
+              odata, data);
+}
+
+static void
+value_from_param (union value *value, int width, unsigned int idx)
+{
+  if (width == 0)
+    value->f = idx & 0xffff;
+  else
+    {
+      unsigned int hash = hash_int (idx, 0);
+      uint8_t *string = value_str_rw (value, width);
+      int offset;
+
+      assert (width < 32);
+      for (offset = 0; offset < width; offset++)
+        string[offset] = "ABCDEFGHIJ"[(hash >> offset) % 10];
+    }
+}
+
+/* "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->n_backing_cols == 0)
+    {
+      /* Create unbacked datasheet. */
+      ds = datasheet_create (NULL);
+      mc_name_operation (mc, "empty datasheet");
+      check_datasheet (mc, ds, NULL, 0, caseproto_create ());
+    }
+  else
+    {
+      /* Create datasheet with backing. */
+      struct casewriter *writer;
+      struct casereader *reader;
+      union value data[MAX_ROWS][MAX_COLS];
+      struct caseproto *proto;
+      int row, col;
+
+      assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
+      assert (params->n_backing_cols > 0
+              && params->n_backing_cols <= MAX_COLS);
+
+      proto = caseproto_create ();
+      for (col = 0; col < params->n_backing_cols; col++)
+        proto = caseproto_add_width (proto, params->backing_widths[col]);
+
+      writer = mem_writer_create (proto);
+      for (row = 0; row < params->backing_rows; row++)
+        {
+          struct ccase *c;
+
+          c = case_create (proto);
+          for (col = 0; col < params->n_backing_cols; col++)
+            {
+              int width = params->backing_widths[col];
+              union value *value = &data[row][col];
+              value_init (value, width);
+              value_from_param (value, width, params->next_value++);
+              value_copy (case_data_rw_idx (c, col), value, width);
+            }
+          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->n_backing_cols);
+      check_datasheet (mc, ds, data,
+                       params->backing_rows, proto);
+      release_data (params->backing_rows, proto, data);
+      caseproto_unref (proto);
+    }
+}
+
+struct resize_cb_aux
+  {
+    int old_width;
+    int new_width;
+  };
+
+static void
+resize_cb (const union value *old_value, union value *new_value, void *aux_)
+{
+  struct resize_cb_aux *aux = aux_;
+
+  value_from_param (new_value, aux->new_width,
+                    value_hash (old_value, aux->old_width, 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_;
+  union value odata[MAX_ROWS][MAX_COLS];
+  union value data[MAX_ROWS][MAX_COLS];
+  const struct caseproto *oproto = datasheet_get_proto (ods);
+  size_t n_columns = datasheet_get_n_columns (ods);
+  size_t n_rows = datasheet_get_n_rows (ods);
+  size_t pos, new_pos, cnt, width_idx;
+
+  extract_data (ods, odata);
+
+  /* Insert a column in each possible position. */
+  if (n_columns < params->max_cols)
+    for (pos = 0; pos <= n_columns; pos++)
+      for (width_idx = 0; width_idx < params->n_widths; width_idx++)
+        if (mc_include_state (mc))
+          {
+            int width = params->widths[width_idx];
+            struct caseproto *proto;
+            struct datasheet *ds;
+            union value new;
+            size_t i;
+
+            mc_name_operation (mc, "insert column at %zu "
+                               "(from %zu to %zu columns)",
+                               pos, n_columns, n_columns + 1);
+            clone_model (ods, odata, &ds, data);
+
+            value_init (&new, width);
+            value_from_param (&new, width, params->next_value++);
+            if (!datasheet_insert_column (ds, &new, width, pos))
+              mc_error (mc, "datasheet_insert_column failed");
+            proto = caseproto_insert_width (caseproto_ref (oproto),
+                                            pos, width);
+
+            for (i = 0; i < n_rows; i++)
+              {
+                insert_element (&data[i][0], n_columns, sizeof data[i][0],
+                                pos);
+                value_init (&data[i][pos], width);
+                value_copy (&data[i][pos], &new, width);
+              }
+            value_destroy (&new, width);
+
+            check_datasheet (mc, ds, data, n_rows, proto);
+            release_data (n_rows, proto, data);
+            caseproto_unref (proto);
+          }
+
+  /* Resize each column to each possible new size. */
+  for (pos = 0; pos < n_columns; pos++)
+    for (width_idx = 0; width_idx < params->n_widths; width_idx++)
+      {
+        int owidth = caseproto_get_width (oproto, pos);
+        int width = params->widths[width_idx];
+        if (mc_include_state (mc))
+          {
+            struct resize_cb_aux aux;
+            struct caseproto *proto;
+            struct datasheet *ds;
+            size_t i;
+
+            mc_name_operation (mc, "resize column %zu (of %zu) "
+                               "from width %d to %d",
+                               pos, n_columns, owidth, width);
+            clone_model (ods, odata, &ds, data);
+
+            aux.old_width = owidth;
+            aux.new_width = width;
+            if (!datasheet_resize_column (ds, pos, width, resize_cb, &aux))
+              NOT_REACHED ();
+            proto = caseproto_set_width (caseproto_ref (oproto), pos, width);
+
+            for (i = 0; i < n_rows; i++)
+              {
+                union value *old_value = &data[i][pos];
+                union value new_value;
+                value_init (&new_value, width);
+                resize_cb (old_value, &new_value, &aux);
+                value_swap (old_value, &new_value);
+                value_destroy (&new_value, owidth);
+              }
+
+            check_datasheet (mc, ds, data, n_rows, proto);
+            release_data (n_rows, proto, data);
+            caseproto_unref (proto);
+          }
+      }
+
+  /* Delete all possible numbers of columns from all possible
+     positions. */
+  for (pos = 0; pos < n_columns; pos++)
+    for (cnt = 1; cnt < n_columns - pos; cnt++)
+      if (mc_include_state (mc))
+        {
+          struct caseproto *proto;
+          struct datasheet *ds;
+          size_t i, j;
+
+          mc_name_operation (mc, "delete %zu columns at %zu "
+                             "(from %zu to %zu columns)",
+                             cnt, pos, n_columns, n_columns - cnt);
+          clone_model (ods, odata, &ds, data);
+
+          datasheet_delete_columns (ds, pos, cnt);
+          proto = caseproto_remove_widths (caseproto_ref (oproto), pos, cnt);
+
+          for (i = 0; i < n_rows; i++)
+            {
+              for (j = pos; j < pos + cnt; j++)
+                value_destroy (&data[i][j], caseproto_get_width (oproto, j));
+              remove_range (&data[i], n_columns, sizeof *data[i], pos, cnt);
+            }
+
+          check_datasheet (mc, ds, data, n_rows, proto);
+          release_data (n_rows, proto, data);
+          caseproto_unref (proto);
+        }
+
+  /* Move all possible numbers of columns from all possible
+     existing positions to all possible new positions. */
+  for (pos = 0; pos < n_columns; pos++)
+    for (cnt = 1; cnt < n_columns - pos; cnt++)
+      for (new_pos = 0; new_pos < n_columns - cnt; new_pos++)
+        if (mc_include_state (mc))
+          {
+            struct caseproto *proto;
+            struct datasheet *ds;
+            size_t i;
+
+            clone_model (ods, odata, &ds, data);
+            mc_name_operation (mc, "move %zu columns (of %zu) from %zu to %zu",
+                               cnt, n_columns, pos, new_pos);
+
+            datasheet_move_columns (ds, pos, new_pos, cnt);
+
+            for (i = 0; i < n_rows; i++)
+              move_range (&data[i], n_columns, sizeof data[i][0],
+                          pos, new_pos, cnt);
+            proto = caseproto_move_widths (caseproto_ref (oproto),
+                                           pos, new_pos, cnt);
+
+            check_datasheet (mc, ds, data, n_rows, proto);
+            release_data (n_rows, proto, data);
+            caseproto_unref (proto);
+          }
+
+  /* Insert all possible numbers of rows in all possible
+     positions. */
+  for (pos = 0; pos <= n_rows; pos++)
+    for (cnt = 1; cnt <= params->max_rows - n_rows; 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 "
+                             "(from %zu to %zu rows)",
+                             cnt, pos, n_rows, n_rows + cnt);
+
+          for (i = 0; i < cnt; i++)
+            {
+              c[i] = case_create (oproto);
+              for (j = 0; j < n_columns; j++)
+                value_from_param (case_data_rw_idx (c[i], j),
+                                  caseproto_get_width (oproto, j),
+                                  params->next_value++);
+            }
+
+          insert_range (data, n_rows, sizeof data[pos], pos, cnt);
+          for (i = 0; i < cnt; i++)
+            for (j = 0; j < n_columns; j++)
+              {
+                int width = caseproto_get_width (oproto, j);
+                value_init (&data[i + pos][j], width);
+                value_copy (&data[i + pos][j], case_data_idx (c[i], j), width);
+              }
+
+          if (!datasheet_insert_rows (ds, pos, c, cnt))
+            mc_error (mc, "datasheet_insert_rows failed");
+
+          check_datasheet (mc, ds, data, n_rows + cnt, oproto);
+          release_data (n_rows + cnt, oproto, data);
+        }
+
+  /* Delete all possible numbers of rows from all possible
+     positions. */
+  for (pos = 0; pos < n_rows; pos++)
+    for (cnt = 1; cnt < n_rows - 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 "
+                             "(from %zu to %zu rows)",
+                             cnt, pos, n_rows, n_rows - cnt);
+
+          datasheet_delete_rows (ds, pos, cnt);
+
+          release_data (cnt, oproto, &data[pos]);
+          remove_range (&data[0], n_rows, sizeof data[0], pos, cnt);
+
+          check_datasheet (mc, ds, data, n_rows - cnt, oproto);
+          release_data (n_rows - cnt, oproto, data);
+        }
+
+  /* Move all possible numbers of rows from all possible existing
+     positions to all possible new positions. */
+  for (pos = 0; pos < n_rows; pos++)
+    for (cnt = 1; cnt < n_rows - pos; cnt++)
+      for (new_pos = 0; new_pos < n_rows - cnt; new_pos++)
+        if (mc_include_state (mc))
+          {
+            struct datasheet *ds;
+
+            clone_model (ods, odata, &ds, data);
+            mc_name_operation (mc, "move %zu rows (of %zu) from %zu to %zu",
+                               cnt, n_rows, pos, new_pos);
+
+            datasheet_move_rows (ds, pos, new_pos, cnt);
+
+            move_range (&data[0], n_rows, sizeof data[0],
+                        pos, new_pos, cnt);
+
+            check_datasheet (mc, ds, data, n_rows, oproto);
+            release_data (n_rows, oproto, data);
+          }
+
+  release_data (n_rows, oproto, odata);
+}
+
+/* "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);
+}
+\f
+enum
+  {
+    OPT_MAX_ROWS,
+    OPT_MAX_COLUMNS,
+    OPT_BACKING_ROWS,
+    OPT_BACKING_WIDTHS,
+    OPT_WIDTHS,
+    OPT_HELP,
+    N_DATASHEET_OPTIONS
+  };
+
+static struct argv_option datasheet_argv_options[N_DATASHEET_OPTIONS] =
+  {
+    {"max-rows", 0, required_argument, OPT_MAX_ROWS},
+    {"max-columns", 0, required_argument, OPT_MAX_COLUMNS},
+    {"backing-rows", 0, required_argument, OPT_BACKING_ROWS},
+    {"backing-widths", 0, required_argument, OPT_BACKING_WIDTHS},
+    {"widths", 0, required_argument, OPT_WIDTHS},
+    {"help", 'h', no_argument, OPT_HELP},
+  };
+
+static void usage (void);
+
+static void
+datasheet_option_callback (int id, void *params_)
+{
+  struct datasheet_test_params *params = params_;
+  switch (id)
+    {
+    case OPT_MAX_ROWS:
+      params->max_rows = atoi (optarg);
+      break;
+
+    case OPT_MAX_COLUMNS:
+      params->max_cols = atoi (optarg);
+      break;
+
+    case OPT_BACKING_ROWS:
+      params->backing_rows = atoi (optarg);
+      break;
+
+    case OPT_BACKING_WIDTHS:
+      {
+        char *w;
+
+        params->n_backing_cols = 0;
+        for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
+          {
+            int value = atoi (w);
+
+            if (params->n_backing_cols >= MAX_COLS)
+              error (1, 0, "Too many widths on --backing-widths "
+                     "(only %d are allowed)", MAX_COLS);
+            if (!isdigit (w[0]) || value < 0 || value > 31)
+              error (1, 0, "--backing-widths argument must be a list of 1 to "
+                     "%d integers between 0 and 31 in increasing order",
+                     MAX_COLS);
+            params->backing_widths[params->n_backing_cols++] = value;
+          }
+      }
+      break;
+
+    case OPT_WIDTHS:
+      {
+        int last = -1;
+        char *w;
+
+        params->n_widths = 0;
+        for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
+          {
+            int value = atoi (w);
+
+            if (params->n_widths >= MAX_WIDTHS)
+              error (1, 0, "Too many widths on --widths (only %d are allowed)",
+                     MAX_WIDTHS);
+            if (!isdigit (w[0]) || value < 0 || value > 31)
+              error (1, 0, "--widths argument must be a list of 1 to %d "
+                     "integers between 0 and 31 in increasing order",
+                     MAX_WIDTHS);
+
+            /* This is an artificial requirement merely to ensure
+               that there are no duplicates.  Duplicates aren't a
+               real problem but they would waste time. */
+            if (value <= last)
+              error (1, 0, "--widths arguments must be in increasing order");
+
+            params->widths[params->n_widths++] = value;
+          }
+        if (params->n_widths == 0)
+          error (1, 0, "at least one value must be specified on --widths");
+      }
+      break;
+
+    case OPT_HELP:
+      usage ();
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+}
+
+static void
+usage (void)
+{
+  printf ("%s, for testing the datasheet implementation.\n"
+          "Usage: %s [OPTION]...\n"
+          "\nTest state space parameters (min...max, default):\n"
+          "  --max-rows=N         Maximum number of rows (0...5, 3)\n"
+          "  --max-rows=N         Maximum number of columns (0...5, 3)\n"
+          "  --backing-rows=N     Rows of backing store (0...max_rows, 0)\n"
+          "  --backing-widths=W[,W]...  Backing store widths to test (0=num)\n"
+          "  --widths=W[,W]...    Column widths to test, where 0=numeric,\n"
+          "                       other values are string widths (0,1,11)\n",
+          program_name, program_name);
+  mc_options_usage ();
+  fputs ("\nOther options:\n"
+         "  --help               Display this help message\n"
+         "\nReport bugs to <bug-gnu-pspp@gnu.org>\n",
+         stdout);
+  exit (0);
+}
+
+int
+main (int argc, char *argv[])
+{
+  static const struct mc_class datasheet_mc_class =
+    {
+      datasheet_mc_init,
+      datasheet_mc_mutate,
+      datasheet_mc_destroy,
+    };
+
+  struct datasheet_test_params params;
+  struct mc_options *options;
+  struct mc_results *results;
+  struct argv_parser *parser;
+  int verbosity;
+  bool success;
+
+  set_program_name (argv[0]);
+
+  /* Default parameters. */
+  params.max_rows = 3;
+  params.max_cols = 3;
+  params.backing_rows = 0;
+  params.n_backing_cols = 0;
+  params.widths[0] = 0;
+  params.widths[1] = 1;
+  params.widths[2] = 11;
+  params.n_widths = 3;
+  params.next_value = 1;
+
+  /* Parse comand line. */
+  parser = argv_parser_create ();
+  options = mc_options_create ();
+  mc_options_register_argv_parser (options, parser);
+  argv_parser_add_options (parser, datasheet_argv_options, N_DATASHEET_OPTIONS,
+                           datasheet_option_callback, &params);
+  if (!argv_parser_run (parser, argc, argv))
+    exit (EXIT_FAILURE);
+  argv_parser_destroy (parser);
+  verbosity = mc_options_get_verbosity (options);
+
+  /* Force parameters into allowed ranges. */
+  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.n_backing_cols = MIN (params.n_backing_cols, params.max_cols);
+  mc_options_set_aux (options, &params);
+  results = mc_run (&datasheet_mc_class, options);
+
+  /* Output results. */
+  success = (mc_results_get_stop_reason (results) != MC_MAX_ERROR_COUNT
+             && mc_results_get_stop_reason (results) != MC_INTERRUPTED);
+  if (verbosity > 0 || !success)
+    {
+      int i;
+
+      printf ("Parameters: --max-rows=%d --max-columns=%d --backing-rows=%d ",
+              params.max_rows, params.max_cols, params.backing_rows);
+
+      printf ("--backing-widths=");
+      for (i = 0; i < params.n_backing_cols; i++)
+        {
+          if (i > 0)
+            printf (",");
+          printf ("%d", params.backing_widths[i]);
+        }
+      printf (" ");
+
+      printf ("--widths=");
+      for (i = 0; i < params.n_widths; i++)
+        {
+          if (i > 0)
+            printf (",");
+          printf ("%d", params.widths[i]);
+        }
+      printf ("\n\n");
+      mc_results_print (results, stdout);
+    }
+  mc_results_destroy (results);
+
+  return success ? 0 : EXIT_FAILURE;
+}
diff --git a/tests/data/datasheet-test.sh b/tests/data/datasheet-test.sh
new file mode 100755 (executable)
index 0000000..7de13e7
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# This program tests the datasheet implementation.
+
+set -e
+
+: ${top_builddir:=.}
+RUN_TEST="${top_builddir}/tests/data/datasheet-test --verbosity=0"
+
+$RUN_TEST --max-rows=3 --max-columns=3 --backing-rows=0 --backing-widths=
+$RUN_TEST --max-rows=3 --max-columns=3 --backing-rows=3 --backing-widths=0,0,0
+$RUN_TEST --max-rows=3 --max-columns=3 --backing-rows=3 --backing-widths=0
+$RUN_TEST --max-rows=3 --max-columns=3 --backing-rows=3 --backing-widths=5
+$RUN_TEST --max-rows=3 --max-columns=3 --backing-rows=1 --backing-widths=0,9,0
index 532684237745e5eef82aafa0eefa8f977f160e4a..85f3644211d3171423239c59b14ab7136f28b36a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   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
@@ -36,6 +36,8 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+#define VAR_NAME_LEN 64
+
 struct sfm_reader
   {
     const char *file_name;
@@ -62,12 +64,22 @@ 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 struct variable_to_value_map *open_variable_to_value_map (
+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 void read_character_encoding (struct sfm_reader *r,
+                                      size_t size, size_t count);
+static void read_long_string_value_labels (struct sfm_reader *r,
+                                           size_t size, size_t count);
+
+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 +99,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 +509,20 @@ 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;
+
+    case 20:
+      read_character_encoding (r, size, count);
+      return;
+
+    case 21:
+      read_long_string_value_labels (r, size, count);
+      return;
 
     default:
       sys_warn (r, _("Unrecognized record type 7, subtype %d."), subtype);
@@ -549,13 +583,18 @@ read_machine_float_info (struct sfm_reader *r, size_t size, size_t count)
 
   printf ("\tsysmis: %g\n", sysmis);
   if (sysmis != SYSMIS)
-    sys_warn (r, _("File specifies unexpected value %g as SYSMIS."), sysmis);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              sysmis, "SYSMIS");
+
   printf ("\thighest: %g\n", highest);
   if (highest != HIGHEST)
-    sys_warn (r, _("File specifies unexpected value %g as HIGHEST."), highest);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              highest, "HIGHEST");
+
   printf ("\tlowest: %g\n", lowest);
   if (lowest != LOWEST)
-    sys_warn (r, _("File specifies unexpected value %g as LOWEST."), lowest);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              lowest, "LOWEST");
 }
 
 /* Read record type 7, subtype 11. */
@@ -613,15 +652,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 +668,240 @@ 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_character_encoding (struct sfm_reader *r, size_t size, size_t count)
+{
+  const unsigned long int posn =  ftell (r->file);
+  char *encoding = xcalloc (size, count + 1);
+  read_string (r, encoding, count + 1);
+
+  printf ("%08lx: Character Encoding: %s\n", posn, encoding);
+}
+
+static void
+read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count)
+{
+  const long start = ftell (r->file);
+
+  printf ("%08lx: long string value labels\n", start);
+  while (ftell (r->file) - start < size * count)
+    {
+      long posn = ftell (r->file);
+      char var_name[VAR_NAME_LEN + 1];
+      int var_name_len;
+      int n_values;
+      int width;
+      int i;
+
+      /* Read variable name. */
+      var_name_len = read_int (r);
+      if (var_name_len > VAR_NAME_LEN)
+        sys_error (r, _("Variable name length in long string value label "
+                        "record (%d) exceeds %d-byte limit."),
+                   var_name_len, VAR_NAME_LEN);
+      read_string (r, var_name, var_name_len + 1);
+
+      /* Read width, number of values. */
+      width = read_int (r);
+      n_values = read_int (r);
+
+      printf ("\t%08lx: %s, width %d, %d values\n",
+              posn, var_name, width, n_values);
+
+      /* Read values. */
+      for (i = 0; i < n_values; i++)
+       {
+          char *value;
+          int value_length;
+
+          char *label;
+         int label_length;
+
+          posn = ftell (r->file);
+
+          /* Read value. */
+          value_length = read_int (r);
+          value = xmalloc (value_length + 1);
+          read_string (r, value, value_length + 1);
+
+          /* Read label. */
+          label_length = read_int (r);
+          label = xmalloc (label_length + 1);
+          read_string (r, label, label_length + 1);
+
+          printf ("\t\t%08lx: \"%s\" (%d bytes) => \"%s\" (%d bytes)\n",
+                  posn, value, value_length, label, label_length);
+
+          free (value);
+          free (label);
+       }
+    }
+}
+
+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)
-{
-  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)
+text_tokenize (struct text_record *text, int delimiter)
+{
+  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 894c679726b3f946baac3922448905becff92634..e8cbffa5e6334408bbd69dc4c7c2a7e9dbb72829 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
@@ -119,6 +119,47 @@ next_region (unsigned int pattern, unsigned int offset,
   return false;
 }
 
+/* Searches the bits in PATTERN from left to right starting from
+   just beyond bit OFFSET for one or more 1-bits.  If any are
+   found, sets *START to the bit index of the first and *WIDTH to
+   the number of contiguous 1-bits and returns true.  Otherwise,
+   returns false.
+   This implementation is designed to be obviously correct, not
+   to be efficient. */
+static bool
+prev_region (unsigned int pattern, unsigned int offset,
+             unsigned long int *start, unsigned long int *width)
+{
+  unsigned int i;
+
+  assert (offset <= UINT_BIT);
+  for (i = offset; i-- > 0; )
+    if (pattern & (1u << i))
+      {
+        *start = i;
+        *width = 1;
+        while (i-- > 0 && pattern & (1u << i))
+          {
+            ++*width;
+            --*start;
+          }
+        return true;
+      }
+  return false;
+}
+
+/* Searches the bits in PATTERN from right to left starting from
+   bit OFFSET.  Returns the bit index of the first 1-bit found,
+   or ULONG_MAX if none is found. */
+static unsigned long int
+next_1bit (unsigned int pattern, unsigned int offset)
+{
+  for (; offset < UINT_BIT; offset++)
+    if (pattern & (1u << offset))
+      return offset;
+  return ULONG_MAX;
+}
+
 /* Prints the regions in RS to stdout. */
 static void UNUSED
 print_regions (const struct range_set *rs)
@@ -139,6 +180,7 @@ check_pattern (const struct range_set *rs, unsigned int pattern)
 {
   const struct range_set_node *node;
   unsigned long int start, width;
+  unsigned long int s1, s2;
   int i;
 
   for (node = rand () % 2 ? range_set_first (rs) : range_set_next (rs, NULL),
@@ -153,6 +195,52 @@ check_pattern (const struct range_set *rs, unsigned int pattern)
     }
   check (node == NULL);
 
+  for (node = rand () % 2 ? range_set_last (rs) : range_set_prev (rs, NULL),
+         start = UINT_BIT;
+       prev_region (pattern, start, &start, &width);
+       node = range_set_prev (rs, node))
+    {
+      check (node != NULL);
+      check (range_set_node_get_start (node) == start);
+      check (range_set_node_get_end (node) == start + width);
+      check (range_set_node_get_width (node) == width);
+    }
+  check (node == NULL);
+
+  /* Scan from all possible positions, resetting the cache each
+     time, to ensure that we get the correct answers without
+     caching. */
+  for (start = 0; start <= 32; start++)
+    {
+      struct range_set *nonconst_rs = (struct range_set *) rs;
+      nonconst_rs->cache_end = 0;
+      s1 = range_set_scan (rs, start);
+      s2 = next_1bit (pattern, start);
+      check (s1 == s2);
+    }
+
+  /* Scan in forward order to exercise expected cache behavior. */
+  for (s1 = range_set_scan (rs, 0), s2 = next_1bit (pattern, 0); ;
+       s1 = range_set_scan (rs, s1 + 1), s2 = next_1bit (pattern, s2 + 1))
+    {
+      check (s1 == s2);
+      if (s1 == ULONG_MAX)
+        break;
+    }
+
+  /* Scan in random order to frustrate cache. */
+  for (i = 0; i < 32; i++)
+    {
+      start = rand () % 32;
+      s1 = range_set_scan (rs, start);
+      s2 = next_1bit (pattern, start);
+      check (s1 == s2);
+    }
+
+  /* Test range_set_scan() with negative cache. */
+  check (!range_set_contains (rs, 999));
+  check (range_set_scan (rs, 1111) == ULONG_MAX);
+
   for (i = 0; i < UINT_BIT; i++)
     check (range_set_contains (rs, i) == ((pattern & (1u << i)) != 0));
   check (!range_set_contains (rs,
@@ -288,6 +376,60 @@ test_allocate (void)
       }
 }
 
+/* Tests all possible full allocations in all possible range sets
+   (up to a small maximum number of bits). */
+static void
+test_allocate_fully (void)
+{
+  const int positions = 9;
+  unsigned int init_pat;
+  int request;
+
+  for (init_pat = 0; init_pat < (1u << positions); init_pat++)
+    for (request = 1; request <= positions + 1; request++)
+      {
+        struct range_set *rs;
+        unsigned long int start, expect_start;
+        bool success, expect_success;
+        unsigned int final_pat;
+        int i;
+
+        /* Figure out expected results. */
+        expect_success = false;
+        expect_start = 0;
+        final_pat = init_pat;
+        for (i = 0; i < positions - request + 1; i++)
+          {
+            int j;
+
+            final_pat = init_pat;
+            for (j = i; j < i + request; j++)
+              {
+                if (!(init_pat & (1u << j)))
+                  goto next;
+                final_pat &= ~(1u << j);
+              }
+
+            expect_success = true;
+            expect_start = i;
+            break;
+          next:
+            final_pat = init_pat;
+          }
+
+        /* Test. */
+        rs = make_pattern (init_pat);
+        success = range_set_allocate_fully (rs, request, &start);
+        check_pattern (rs, final_pat);
+        range_set_destroy (rs);
+
+        /* Check results. */
+        check (success == expect_success);
+        if (expect_success)
+          check (start == expect_start);
+      }
+}
+
 /* Tests freeing a range set through a pool. */
 static void
 test_pool (void)
@@ -310,6 +452,13 @@ test_pool (void)
   range_set_insert (rs, 1, 10);
   pool_destroy (pool);
 }
+
+/* Tests range_set_destroy(NULL). */
+static void
+test_destroy_null (void)
+{
+  range_set_destroy (NULL);
+}
 \f
 /* Main program. */
 
@@ -329,7 +478,9 @@ main (void)
   run_test (test_insert, "insert");
   run_test (test_delete, "delete");
   run_test (test_allocate, "allocate");
+  run_test (test_allocate_fully, "allocate_fully");
   run_test (test_pool, "pool");
+  run_test (test_destroy_null, "destroy null");
   putchar ('\n');
 
   return 0;
index c8d14b2bb98f51a95bdf84955cd1b64cc4c87155..e146c3dbf537b7c765dfec60a16267756f8b4356 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
@@ -18,7 +18,8 @@
    in sparse-array.c.  This test program aims to be as
    comprehensive as possible.  "gcov -b" should report 100%
    coverage of lines and branches in sparse-array.c when compiled
-   with -DNDEBUG.  "valgrind --leak-check=yes
+   with -DNDEBUG and BITS_PER_LEVEL is greater than the number of
+   bits in a long.  "valgrind --leak-check=yes
    --show-reachable=yes" should give a clean report. */
 
 #ifdef HAVE_CONFIG_H
@@ -164,8 +165,8 @@ check_sparse_array (struct sparse_array *spar,
       check (!sparse_array_remove (spar, order[cnt - 1] + 1));
     }
 
-  for (i = 0, p = sparse_array_scan (spar, NULL, &idx); i < cnt;
-       i++, p = sparse_array_scan (spar, &idx, &idx))
+  for (i = 0, p = sparse_array_first (spar, &idx); i < cnt;
+       i++, p = sparse_array_next (spar, idx, &idx))
     {
       check (p != NULL);
       check (idx == order[i]);
@@ -173,6 +174,15 @@ check_sparse_array (struct sparse_array *spar,
     }
   check (p == NULL);
 
+  for (i = 0, p = sparse_array_last (spar, &idx); i < cnt;
+       i++, p = sparse_array_prev (spar, idx, &idx))
+    {
+      check (p != NULL);
+      check (idx == order[cnt - i - 1]);
+      check (*p == order[cnt - i - 1]);
+    }
+  check (p == NULL);
+
   free (order);
 }
 
diff --git a/tests/libpspp/sparse-xarray-test.c b/tests/libpspp/sparse-xarray-test.c
new file mode 100644 (file)
index 0000000..dd5a2a9
--- /dev/null
@@ -0,0 +1,637 @@
+/* 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/>. */
+
+/* This is a test program for the sparse array routines defined
+   in sparse-xarray.c. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/argv-parser.h>
+#include <libpspp/assertion.h>
+#include <libpspp/hash-functions.h>
+#include <libpspp/model-checker.h>
+#include <libpspp/sparse-xarray.h>
+#include <libpspp/str.h>
+
+#include "minmax.h"
+#include "progname.h"
+#include "xalloc.h"
+\f
+/* Maximum size of sparse_xarray supported for model checking
+   purposes. */
+#define MAX_ROWS 5
+#define MAX_COLS 5
+
+/* Test parameters. */
+struct test_params
+  {
+    /* Controlling the test state space. */
+    int n_columns;              /* Number of columns in each row. */
+    int max_rows;               /* Maximum number of rows. */
+    int max_memory_rows;        /* Max rows before writing to disk. */
+    unsigned char n_values;     /* Number of unique cell values. */
+    int n_xarrays;              /* Number of sparse_xarrays in state. */
+
+    /* Types of operations to perform. */
+    bool write_cells;           /* Write to individual cells. */
+    bool write_rows;            /* Write whole rows. */
+    bool write_columns;         /* Write whole columns. */
+    bool copy_within_xarray;    /* Copy column ranges in a single xarray. */
+  };
+
+struct test_state
+  {
+    struct sparse_xarray *xarrays[2];
+  };
+
+static void
+test_state_destroy (const struct test_params *params, struct test_state *ts)
+{
+  int i;
+
+  for (i = 0; i < params->n_xarrays; i++)
+    sparse_xarray_destroy (ts->xarrays[i]);
+}
+
+static struct test_state *
+test_state_clone (const struct test_params *params,
+                  const struct test_state *ots)
+{
+  struct test_state *ts;
+  int i;
+
+  ts = xmalloc (sizeof *ts);
+  for (i = 0; i < params->n_xarrays; i++)
+    {
+      ts->xarrays[i] = sparse_xarray_clone (ots->xarrays[i]);
+      if (ts->xarrays[i] == NULL)
+        NOT_REACHED ();
+    }
+  return ts;
+}
+
+struct xarray_model
+  {
+    uint8_t data[MAX_ROWS][MAX_COLS];
+    bool contains_row[MAX_ROWS];
+  };
+
+struct test_model
+  {
+    struct xarray_model models[2];
+  };
+
+/* Extracts the contents of TS into TM. */
+static void
+test_model_extract (const struct test_params *params,
+                    const struct test_state *ts, struct test_model *tm)
+{
+  int i;
+
+  for (i = 0; i < params->n_xarrays; i++)
+    {
+      const struct sparse_xarray *sx = ts->xarrays[i];
+      struct xarray_model *model = &tm->models[i];
+      size_t n_columns = sparse_xarray_get_n_columns (sx);
+      size_t n_rows = sparse_xarray_get_n_rows (sx);
+      size_t row;
+
+      assert (n_rows < MAX_ROWS);
+      assert (n_columns < MAX_COLS);
+      for (row = 0; row < params->max_rows; row++)
+        {
+          model->contains_row[row] = sparse_xarray_contains_row (sx, row);
+          if (!sparse_xarray_read (sx, row, 0, n_columns, model->data[row]))
+            NOT_REACHED ();
+        }
+    }
+}
+
+/* Checks that test state TS matches the test model TM and
+   reports any mismatches via mc_error.  Then, adds SX to MC as a
+   new state. */
+static void
+check_state (struct mc *mc, struct test_state *ts, const struct test_model *tm)
+{
+  const struct test_params *params = mc_get_aux (mc);
+  int n_columns = params->n_columns;
+  unsigned int hash;
+  int i;
+
+  for (i = 0; i < params->n_xarrays; i++)
+    {
+      const struct xarray_model *model = &tm->models[i];
+      const struct sparse_xarray *sx = ts->xarrays[i];
+      bool difference;
+      int row, col;
+      int n_rows;
+
+      assert (n_columns < MAX_COLS);
+
+      /* Check row count. */
+      n_rows = 0;
+      for (row = 0; row < params->max_rows; row++)
+        if (model->contains_row[row])
+          n_rows = row + 1;
+      if (n_rows != sparse_xarray_get_n_rows (sx))
+        mc_error (mc, "xarray %d: row count (%zu) does not match expected "
+                  "(%d)", i, sparse_xarray_get_n_rows (sx), n_rows);
+
+      /* Check row containment. */
+      for (row = 0; row < params->max_rows; row++)
+        {
+          bool contains = sparse_xarray_contains_row (sx, row);
+          if (contains && !model->contains_row[row])
+            mc_error (mc, "xarray %d: row %d is contained by sparse_xarray "
+                      "but should not be", i, row);
+          else if (!contains && model->contains_row[row])
+            mc_error (mc, "xarray %d: row %d is not contained by "
+                      "sparse_xarray but should be", i, row);
+        }
+
+      /* Check contents. */
+      difference = false;
+      for (row = 0; row < params->max_rows; row++)
+        {
+          unsigned char data[MAX_COLS];
+
+          if (!sparse_xarray_read (sx, row, 0, n_columns, data))
+            NOT_REACHED ();
+          for (col = 0; col < params->n_columns; col++)
+            if (data[col] != model->data[row][col])
+              {
+                mc_error (mc, "xarray %d: element %zu,%zu (of %zu,%zu) "
+                          "differs: %d should be %d",
+                          i, row, col, n_rows, n_columns, data[col],
+                          model->data[row][col]);
+                difference = true;
+              }
+        }
+
+      if (difference)
+        {
+          struct string ds;
+
+          mc_error (mc, "xarray %d: expected:", i);
+          ds_init_empty (&ds);
+          for (row = 0; row < params->max_rows; row++)
+            {
+              ds_clear (&ds);
+              for (col = 0; col < n_columns; col++)
+                ds_put_format (&ds, " %d", model->data[row][col]);
+              mc_error (mc, "xarray %d: row %zu:%s", i, row, ds_cstr (&ds));
+            }
+
+          mc_error (mc, "xarray %d: actual:", i);
+          ds_init_empty (&ds);
+          for (row = 0; row < params->max_rows; row++)
+            {
+              unsigned char data[MAX_COLS];
+
+              if (!sparse_xarray_read (sx, row, 0, n_columns, data))
+                NOT_REACHED ();
+
+              ds_clear (&ds);
+              for (col = 0; col < n_columns; col++)
+                ds_put_format (&ds, " %d", data[col]);
+              mc_error (mc, "xarray %d: row %zu:%s", i, row, ds_cstr (&ds));
+            }
+
+          ds_destroy (&ds);
+        }
+    }
+
+  hash = 0;
+  for (i = 0; i < params->n_xarrays; i++)
+    hash = sparse_xarray_model_checker_hash (ts->xarrays[i], hash);
+  if (mc_discard_dup_state (mc, hash))
+    test_state_destroy (params, ts);
+  else
+    mc_add_state (mc, ts);
+}
+
+static bool
+next_data (unsigned char *data, int n, int n_values)
+{
+  int i;
+  for (i = n - 1; i >= 0; i--)
+    {
+      data[i]++;
+      if (data[i] < n_values)
+        return true;
+      data[i] = 0;
+    }
+  return false;
+}
+
+struct copy_columns_params
+  {
+    int n;                      /* Number of columns to copy. */
+    int src;                    /* Offset of first source column. */
+    int dst;                    /* Offset of first destination column. */
+  };
+
+static bool
+copy_columns (const void *src_, void *dst_, void *copy_)
+{
+  const struct copy_columns_params *copy = copy_;
+  const uint8_t *src = src_;
+  uint8_t *dst = dst_;
+
+  memmove (dst + copy->dst, src + copy->src, copy->n);
+  return true;
+}
+
+/* "init" function for struct mc_class. */
+static void
+sparse_xarray_mc_init (struct mc *mc)
+{
+  struct test_params *params = mc_get_aux (mc);
+  struct test_state *ts;
+  struct test_model tm;
+  int i;
+
+  mc_name_operation (mc, "empty sparse_xarray with n_columns=%zu, "
+                     "max_memory_rows=%zu",
+                     params->n_columns, params->max_memory_rows);
+  ts = xmalloc (sizeof *ts);
+  for (i = 0; i < params->n_xarrays; i++)
+    ts->xarrays[i] = sparse_xarray_create (params->n_columns,
+                                           params->max_memory_rows);
+  memset (&tm, 0, sizeof tm);
+  check_state (mc, ts, &tm);
+}
+
+/* "mutate" function for struct mc_class. */
+static void
+sparse_xarray_mc_mutate (struct mc *mc, const void *ots_)
+{
+  struct test_params *params = mc_get_aux (mc);
+  size_t n_columns = params->n_columns;
+  const struct test_state *ots = ots_;
+  struct test_model otm;
+  int i;
+
+  test_model_extract (params, ots, &otm);
+  for (i = 0; i < params->n_xarrays; i++)
+    {
+      unsigned char value;
+      int row, col, n, src, dst;
+
+      /* Write all possible values to each possible single cell. */
+      if (params->write_cells)
+        for (row = 0; row < params->max_rows; row++)
+          for (col = 0; col < n_columns; col++)
+            for (value = 0; value < params->n_values; value++)
+              if (mc_include_state (mc))
+                {
+                  struct test_state *ts = test_state_clone (params, ots);
+                  struct sparse_xarray *sx = ts->xarrays[i];
+                  struct test_model tm = otm;
+                  struct xarray_model *model = &tm.models[i];
+
+                  mc_name_operation (mc, "xarray %d: set (%d,%d) to %d",
+                                     i, row, col, value);
+                  if (!sparse_xarray_write (sx, row, col, 1, &value))
+                    NOT_REACHED ();
+                  model->data[row][col] = value;
+                  model->contains_row[row] = true;
+                  check_state (mc, ts, &tm);
+                }
+
+      /* Write all possible row contents to each row. */
+      if (params->write_rows)
+        for (row = 0; row < params->max_rows; row++)
+          {
+            struct test_model tm = otm;
+            struct xarray_model *model = &tm.models[i];
+
+            memset (model->data[row], 0, n_columns);
+            model->contains_row[row] = true;
+            do
+              {
+                if (mc_include_state (mc))
+                  {
+                    struct test_state *ts = test_state_clone (params, ots);
+                    struct sparse_xarray *sx = ts->xarrays[i];
+                    char row_string[MAX_COLS + 1];
+
+                    mc_name_operation (mc, "xarray %d: set row %d to %s",
+                                       i, row, row_string);
+                    for (col = 0; col < n_columns; col++)
+                      {
+                        value = model->data[row][col];
+                        row_string[col] = value < 10 ? '0' + value : '*';
+                      }
+                    row_string[n_columns] = '\0';
+                    if (!sparse_xarray_write (sx, row, 0, n_columns,
+                                              model->data[row]))
+                      NOT_REACHED ();
+                    check_state (mc, ts, &tm);
+                  }
+              }
+            while (next_data (model->data[row], n_columns, params->n_values));
+          }
+
+      /* Write all possible values to each possible column. */
+      if (params->write_columns)
+        for (col = 0; col < n_columns; col++)
+          for (value = 0; value < params->n_values; value++)
+            if (mc_include_state (mc))
+              {
+                struct test_state *ts = test_state_clone (params, ots);
+                struct sparse_xarray *sx = ts->xarrays[i];
+                struct test_model tm = otm;
+                struct xarray_model *model = &tm.models[i];
+
+                mc_name_operation (mc, "xarray %d: write value %d to "
+                                   "column %d", i, value, col);
+                if (!sparse_xarray_write_columns (sx, col, 1, &value))
+                  NOT_REACHED ();
+                for (row = 0; row < params->max_rows; row++)
+                  model->data[row][col] = value;
+                check_state (mc, ts, &tm);
+              }
+
+      /* Copy all possible column ranges within a single sparse_xarray. */
+      if (params->copy_within_xarray)
+        for (n = 1; n <= n_columns; n++)
+          for (src = 0; src <= n_columns - n; src++)
+            for (dst = 0; dst <= n_columns - n; dst++)
+              if (mc_include_state (mc))
+                {
+                  struct copy_columns_params copy_aux;
+                  struct test_state *ts = test_state_clone (params, ots);
+                  struct sparse_xarray *sx = ts->xarrays[i];
+                  struct test_model tm = otm;
+                  struct xarray_model *model = &tm.models[i];
+
+                  mc_name_operation (mc, "xarray %d: copy %d columns from "
+                                     "offset %d to offset %d", i, n, src, dst);
+
+                  copy_aux.n = n;
+                  copy_aux.src = src;
+                  copy_aux.dst = dst;
+                  if (!sparse_xarray_copy (sx, sx, copy_columns, &copy_aux))
+                    NOT_REACHED ();
+
+                  for (row = 0; row < params->max_rows; row++)
+                    memmove (&model->data[row][dst],
+                             &model->data[row][src], n);
+
+                  check_state (mc, ts, &tm);
+                }
+    }
+
+  if (params->n_xarrays == 2)
+    {
+      int row, n, src, dst;
+
+      /* Copy all possible column ranges from xarrays[0] to xarrays[1]. */
+      for (n = 1; n <= n_columns; n++)
+        for (src = 0; src <= n_columns - n; src++)
+          for (dst = 0; dst <= n_columns - n; dst++)
+            if (mc_include_state (mc))
+              {
+                struct copy_columns_params copy_aux;
+                struct test_state *ts = test_state_clone (params, ots);
+                struct test_model tm = otm;
+
+                mc_name_operation (mc, "copy %d columns from offset %d in "
+                                   "xarray 0 to offset %d in xarray 1",
+                                   n, src, dst);
+
+                copy_aux.n = n;
+                copy_aux.src = src;
+                copy_aux.dst = dst;
+                if (!sparse_xarray_copy (ts->xarrays[0], ts->xarrays[1],
+                                         copy_columns, &copy_aux))
+                  NOT_REACHED ();
+
+                for (row = 0; row < params->max_rows; row++)
+                  {
+                    if (tm.models[0].contains_row[row])
+                      tm.models[1].contains_row[row] = true;
+                    memmove (&tm.models[1].data[row][dst],
+                             &tm.models[0].data[row][src], n);
+                  }
+
+                check_state (mc, ts, &tm);
+              }
+    }
+}
+
+/* "destroy" function for struct mc_class. */
+static void
+sparse_xarray_mc_destroy (const struct mc *mc UNUSED, void *ts_)
+{
+  struct test_params *params = mc_get_aux (mc);
+  struct test_state *ts = ts_;
+
+  test_state_destroy (params, ts);
+}
+
+static void
+usage (void)
+{
+  printf ("%s, for testing the sparse_xarray implementation.\n"
+          "Usage: %s [OPTION]...\n"
+          "\nTest state space parameters (min...max, default):\n"
+          "  --columns=N          Number of columns per row (0...5, 3)\n"
+          "  --max-rows=N         Maximum number of rows (0...5, 3)\n"
+          "  --max-memory-rows=N  Max rows before paging to disk (0...5, 3)\n"
+          "  --values=N           Number of unique cell values (1...254, 3)\n"
+          "  --xarrays=N          Number of xarrays at a time (1...2, 1)\n"
+          "\nTest operation parameters:\n"
+          "  --no-write-cells     Do not write individual cells\n"
+          "  --no-write-rows      Do not write whole rows\n"
+          "  --no-write-columns   Do not write whole columns\n"
+          "  --no-copy-columns    Do not copy column ranges in an xarray\n",
+          program_name, program_name);
+  mc_options_usage ();
+  fputs ("\nOther options:\n"
+         "  --help               Display this help message\n"
+         "\nReport bugs to <bug-gnu-pspp@gnu.org>\n",
+         stdout);
+  exit (0);
+}
+\f
+enum
+  {
+    OPT_COLUMNS,
+    OPT_MAX_ROWS,
+    OPT_MAX_MEMORY_ROWS,
+    OPT_VALUES,
+    OPT_XARRAYS,
+    OPT_NO_WRITE_CELLS,
+    OPT_NO_WRITE_ROWS,
+    OPT_NO_WRITE_COLUMNS,
+    OPT_NO_COPY_COLUMNS,
+    OPT_HELP,
+    N_SPARSE_XARRAY_OPTIONS
+  };
+
+static struct argv_option sparse_xarray_argv_options[N_SPARSE_XARRAY_OPTIONS] =
+  {
+    {"columns", 0, required_argument, OPT_COLUMNS},
+    {"max-rows", 0, required_argument, OPT_MAX_ROWS},
+    {"max-memory-rows", 0, required_argument, OPT_MAX_MEMORY_ROWS},
+    {"values", 0, required_argument, OPT_VALUES},
+    {"xarrays", 0, required_argument, OPT_XARRAYS},
+    {"no-write-cells", 0, no_argument, OPT_NO_WRITE_CELLS},
+    {"no-write-rows", 0, no_argument, OPT_NO_WRITE_ROWS},
+    {"no-write-columns", 0, no_argument, OPT_NO_WRITE_COLUMNS},
+    {"no-copy-columns", 0, no_argument, OPT_NO_COPY_COLUMNS},
+    {"help", 'h', no_argument, OPT_HELP},
+  };
+
+static void
+sparse_xarray_option_callback (int id, void *params_)
+{
+  struct test_params *params = params_;
+  switch (id)
+    {
+    case OPT_COLUMNS:
+      params->n_columns = atoi (optarg);
+      break;
+
+    case OPT_MAX_ROWS:
+      params->max_rows = atoi (optarg);
+      break;
+
+    case OPT_MAX_MEMORY_ROWS:
+      params->max_memory_rows = atoi (optarg);
+      break;
+
+    case OPT_VALUES:
+      params->n_values = atoi (optarg);
+      break;
+
+    case OPT_XARRAYS:
+      params->n_xarrays = atoi (optarg);
+      break;
+
+    case OPT_NO_WRITE_CELLS:
+      params->write_cells = false;
+      break;
+
+    case OPT_NO_WRITE_ROWS:
+      params->write_rows = false;
+      break;
+
+    case OPT_NO_WRITE_COLUMNS:
+      params->write_columns = false;
+      break;
+
+    case OPT_NO_COPY_COLUMNS:
+      params->copy_within_xarray = false;
+      break;
+
+    case OPT_HELP:
+      usage ();
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  static const struct mc_class sparse_xarray_mc_class =
+    {
+      sparse_xarray_mc_init,
+      sparse_xarray_mc_mutate,
+      sparse_xarray_mc_destroy,
+    };
+
+  struct test_params params;
+  struct mc_options *options;
+  struct mc_results *results;
+  struct argv_parser *parser;
+  int verbosity;
+  bool success;
+
+  set_program_name (argv[0]);
+
+  /* Default parameters. */
+  params.n_columns = 3;
+  params.max_rows = 3;
+  params.max_memory_rows = 3;
+  params.n_values = 3;
+  params.n_xarrays = 1;
+  params.write_cells = true;
+  params.write_rows = true;
+  params.write_columns = true;
+  params.copy_within_xarray = true;
+
+  /* Parse command line. */
+  parser = argv_parser_create ();
+  options = mc_options_create ();
+  mc_options_register_argv_parser (options, parser);
+  argv_parser_add_options (parser, sparse_xarray_argv_options,
+                           N_SPARSE_XARRAY_OPTIONS,
+                           sparse_xarray_option_callback, &params);
+  if (!argv_parser_run (parser, argc, argv))
+    exit (EXIT_FAILURE);
+  argv_parser_destroy (parser);
+  verbosity = mc_options_get_verbosity (options);
+
+  /* Force parameters into allowed ranges. */
+  params.n_columns = MAX (0, MIN (params.n_columns, MAX_COLS));
+  params.max_rows = MAX (0, MIN (params.max_rows, MAX_ROWS));
+  params.max_memory_rows = MAX (0, MIN (params.max_memory_rows,
+                                        params.max_rows));
+  params.n_values = MIN (254, MAX (1, params.n_values));
+  params.n_xarrays = MAX (1, MIN (2, params.n_xarrays));
+  mc_options_set_aux (options, &params);
+  results = mc_run (&sparse_xarray_mc_class, options);
+
+  /* Output results. */
+  success = (mc_results_get_stop_reason (results) != MC_MAX_ERROR_COUNT
+             && mc_results_get_stop_reason (results) != MC_INTERRUPTED);
+  if (verbosity > 0 || !success)
+    {
+      printf ("Parameters: "
+              "--columns=%d --max-rows=%d --max-memory-rows=%d --values=%d "
+              "--xarrays=%d",
+              params.n_columns, params.max_rows, params.max_memory_rows,
+              params.n_values, params.n_xarrays);
+      if (!params.write_cells)
+        printf (" --no-write-cells");
+      if (!params.write_rows)
+        printf (" --no-write-rows");
+      if (!params.write_columns)
+        printf (" --no-write-columns");
+      if (!params.copy_within_xarray)
+        printf (" --no-copy-columns");
+      printf ("\n\n");
+      mc_results_print (results, stdout);
+    }
+  mc_results_destroy (results);
+
+  return success ? 0 : EXIT_FAILURE;
+}
diff --git a/tests/libpspp/sparse-xarray-test.sh b/tests/libpspp/sparse-xarray-test.sh
new file mode 100755 (executable)
index 0000000..87defa9
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+# This program tests the sparse_xarray abstract data type implementation.
+
+set -e
+
+: ${top_builddir:=.}
+RUN_TEST="${top_builddir}/tests/libpspp/sparse-xarray-test --verbosity=0"
+
+# Each on-disk sparse_xarray eats up a file descriptor, so for the
+# tests that involve on-disk sparse_xarrays we need to limit the
+# maximum length of the queue.  Figure out how many file descriptors
+# we can let the test program open at once.
+OPEN_MAX=`getconf OPEN_MAX 2>/dev/null`
+case $OPEN_MAX in
+    [0-9]*)
+       # Divide by 2 because some fds are used by other code.
+       queue_limit=`expr $OPEN_MAX / 2` 
+       ;;
+    undefined) 
+       # Assume that any system with a dynamic fd limit has a large limit.
+       queue_limit=500 
+       ;;
+    *)
+       case `uname -m 2>/dev/null` in
+           CYGWIN*)
+                # Cygwin claims a 256-fd limit as OPEN_MAX in <limits.h>.
+               queue_limit=128
+               ;;
+           MINGW*)
+               # The following email claims that Mingw should have a
+               # 2048-fd limit:
+               # http://www.mail-archive.com/squid-users@squid-cache.org/msg35249.html
+               queue_limit=1024
+               ;;
+           *)
+               # This seems fairly conservative these days.
+               queue_limit=50
+               ;;
+       esac
+       ;;
+esac
+
+# Test completely in-memory sparse_xarray.  --values=3 would be a
+# slightly better test but takes much longer.
+$RUN_TEST --columns=3 --max-rows=3 --max-memory-rows=3 --values=2
+
+# Test on-disk sparse_xarrays.
+for max_memory_rows in 0 1 2; do
+    $RUN_TEST --columns=2 --max-rows=3 --max-memory-rows=$max_memory_rows --values=2 --queue-limit=$queue_limit
+done
+
+# Test copying back and forth between a pair of sparse_xarrays in
+# memory.
+$RUN_TEST --columns=2 --max-rows=2 --max-memory-rows=2 --values=2 --xarrays=2 --no-write-rows --no-copy-columns
+
+# Test copying back and forth between a pair of sparse_xarrays on
+# disk.  These parameters are ridiculously low, but it's necessary
+# unless we want the tests to take a very long time.
+for max_memory_rows in 0 1; do
+    $RUN_TEST --columns=1 --max-rows=2 --max-memory-rows=$max_memory_rows --values=2 --xarrays=2 --queue-limit=`expr $queue_limit / 2` --no-write-rows --no-copy-columns
+done
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