Merge commit 'origin/stable' fc11-i386-build58 fc11-x64-build55 lenny-x64-build79 sid-i386-build125
authorJohn Darrington <john@darrington.wattle.id.au>
Wed, 16 Dec 2009 20:09:55 +0000 (21:09 +0100)
committerJohn Darrington <john@darrington.wattle.id.au>
Wed, 16 Dec 2009 20:09:55 +0000 (21:09 +0100)
Conflicts:

NEWS
configure.ac
po/nl.po
src/ui/gui/dict-display.c

611 files changed:
.gitignore
AUTHORS
INSTALL
Makefile.am
NEWS
README.CVS [deleted file]
README.Git [new file with mode: 0644]
Smake
acinclude.m4
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
glade/var-view.c [new file with mode: 0644]
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
lib/linreg/sweep.c
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.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 [deleted file]
po/Makevars
po/automake.mk [new file with mode: 0644]
po/en_GB.po
po/nl.po
po/pt_BR.po
src/automake.mk
src/data/attributes.c [new file with mode: 0644]
src/data/attributes.h [new file with mode: 0644]
src/data/automake.mk
src/data/case-map.c
src/data/case-map.h
src/data/case-matcher.c [new file with mode: 0644]
src/data/case-matcher.h [new file with mode: 0644]
src/data/case-ordering.c [deleted file]
src/data/case-ordering.h [deleted file]
src/data/case-tmpfile.c
src/data/case-tmpfile.h
src/data/case.c
src/data/case.h
src/data/casegrouper.c
src/data/casegrouper.h
src/data/caseinit.c
src/data/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/modify-variables.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/automake.mk
src/language/expressions/evaluate.c
src/language/expressions/evaluate.h.pl [deleted file]
src/language/expressions/evaluate.hpl [new file with mode: 0644]
src/language/expressions/evaluate.inc.pl [deleted file]
src/language/expressions/evaluate.incpl [new file with mode: 0644]
src/language/expressions/helpers.c
src/language/expressions/operations.def
src/language/expressions/operations.h.pl [deleted file]
src/language/expressions/operations.hpl [new file with mode: 0644]
src/language/expressions/optimize.inc.pl [deleted file]
src/language/expressions/optimize.incpl [new file with mode: 0644]
src/language/expressions/parse.inc.pl [deleted file]
src/language/expressions/parse.incpl [new file with mode: 0644]
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.c [new file with mode: 0644]
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/compiler.h
src/libpspp/getl.c
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/correlation.c [new file with mode: 0644]
src/math/correlation.h [new file with mode: 0644]
src/math/covariance-matrix.c
src/math/covariance-matrix.h
src/math/covariance.c [new file with mode: 0644]
src/math/covariance.h [new file with mode: 0644]
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/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/correlation-dialog.c [new file with mode: 0644]
src/ui/gui/correlation-dialog.h [new file with mode: 0644]
src/ui/gui/correlation.ui [new file with mode: 0644]
src/ui/gui/crosstabs-dialog.c
src/ui/gui/crosstabs-dialog.h
src/ui/gui/crosstabs.glade [deleted file]
src/ui/gui/crosstabs.ui [new file with mode: 0644]
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 [deleted file]
src/ui/gui/descriptives-dialog.h
src/ui/gui/descriptives.ui [new file with mode: 0644]
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/examine-dialog.c
src/ui/gui/examine-dialog.h
src/ui/gui/examine.glade [deleted file]
src/ui/gui/examine.ui [new file with mode: 0644]
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.ui [new file with mode: 0644]
src/ui/gui/frequencies-dialog.c
src/ui/gui/frequencies-dialog.h
src/ui/gui/frequencies.glade [deleted file]
src/ui/gui/frequencies.ui [new file with mode: 0644]
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 [deleted file]
src/ui/gui/message-dialog.h
src/ui/gui/message-dialog.ui [new file with mode: 0644]
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 [deleted file]
src/ui/gui/oneway.ui [new file with mode: 0644]
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-select-dest.c [new file with mode: 0644]
src/ui/gui/psppire-select-dest.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-var-view.c [new file with mode: 0644]
src/ui/gui/psppire-var-view.h [new file with mode: 0644]
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 [deleted file]
src/ui/gui/psppire.h
src/ui/gui/psppire.ui [new file with mode: 0644]
src/ui/gui/rank-dialog.c
src/ui/gui/rank-dialog.h
src/ui/gui/rank.glade [deleted file]
src/ui/gui/rank.ui [new file with mode: 0644]
src/ui/gui/recode-dialog.c
src/ui/gui/recode.glade [deleted file]
src/ui/gui/recode.ui [new file with mode: 0644]
src/ui/gui/regression-dialog.c
src/ui/gui/regression-dialog.h
src/ui/gui/regression.glade [deleted file]
src/ui/gui/regression.ui [new file with mode: 0644]
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.ui [new file with mode: 0644]
src/ui/gui/roc-dialog.c [new file with mode: 0644]
src/ui/gui/roc-dialog.h [new file with mode: 0644]
src/ui/gui/roc.ui [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/sort.ui [new file with mode: 0644]
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 [deleted file]
src/ui/gui/t-test.ui [new file with mode: 0644]
src/ui/gui/text-data-import-dialog.c
src/ui/gui/text-data-import.glade [deleted file]
src/ui/gui/text-data-import.ui [new file with mode: 0644]
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.ui [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.h
src/ui/gui/variable-info.ui [new file with mode: 0644]
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/bugs/t-test-alpha3.sh
tests/bugs/t-test-paired.sh
tests/bugs/temp-freq.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/correlation.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/t-test-1-indep-val.sh
tests/command/t-test-1s.sh
tests/command/t-test-pairs.sh
tests/command/update.sh [new file with mode: 0755]
tests/command/weight.sh
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/stats/descript-basic.sh
tests/stats/descript-missing.sh
tests/stats/percentiles-compatible.sh
tests/stats/percentiles-enhanced.sh
tests/xforms/recode.sh
texinfo.tex [new file with mode: 0644]

index c29aff49c289ab840d09237d12b2b4002885adf8..ad89bf36e4884d85df1c47754057f8b2e1635d28 100644 (file)
@@ -30,5 +30,12 @@ potfiles.tmp
 pref.h
 reloc-ldflags
 stamp-h1
-texinfo.tex
 gitlog-to-changelog
+*~
+*.o
+*.lo
+*.a
+*.dirstamp
+*.deps
+*.la
+*.libs
diff --git a/AUTHORS b/AUTHORS
index 262d422caade6bde7ba5cfd7e59876afd46a0bb1..38ed6fe0428e4c9e4967860054f06cee66eae708 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,9 +6,9 @@ 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 
@@ -23,3 +23,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..4f1bc84cef9b3c3eda18fc9165cdbc0f5e61d5aa 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -24,7 +24,7 @@ The following packages are required to install PSPP:
       MinGW (http://www.mingw.org/) are known to work.
 
     * The GNU Scientific Library (http://www.gnu.org/software/gsl/),
-      version 1.6 or later, including libgslcblas included with GSL.
+      version 1.8 or later, including libgslcblas included with GSL.
 
     * Perl (http://www.perl.org/), version 5.005_03 or later.  Perl is
       required during build but not after installation.
@@ -50,19 +50,16 @@ 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.
-
     * pkg-config (http://pkg-config.freedesktop.org/wiki/).  Versions
       0.18 and 0.19 have a bug that will prevent library detection,
       but other versions should be fine.
 
-      To cross-compile PSPP, you will likely need to set the
-      PKG_CONFIG_LIBDIR environment variable to point to an
-      appropriate pkg-config for the cross-compilation environment.
+To cross-compile PSPP, you will likely need to set the
+PKG_CONFIG_LIBDIR environment variable to point to an
+appropriate pkg-config for the cross-compilation environment.
+
+Installing the following packages will allow your PSPP binary to read
+Gnumeric files.
 
     * zlib (http://www.zlib.net/).
 
@@ -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..02ae7d5b2129e882d72e65559bf81c9d7d3ad5fd 100644 (file)
@@ -1,12 +1,16 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-AUTOMAKE_OPTIONS = gnits 1.10 subdir-objects
+AUTOMAKE_OPTIONS = gnits 1.10.1 subdir-objects
 
-SUBDIRS = gl po
+SUBDIRS = gl
 DISTCLEANFILES = ./po/stamp-po xconfigure 
 BUILT_SOURCES =
 
-AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_builddir)/gl
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/gl \
+       -I$(top_builddir)/gl \
+       -DINSTALLDIR=\"$(bindir)\" \
+       -DDOCDIR=\"$(docdir)\"
 
 AM_CFLAGS=
 
@@ -15,8 +19,6 @@ AM_CFLAGS+=-Wall -W -Wwrite-strings -Wstrict-prototypes \
 -Wpointer-arith -Wno-sign-compare -Wmissing-prototypes
 endif
 
-CC_FOR_BUILD = @CC_FOR_BUILD@
-EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
 .q.c:
        @$(MKDIR_P) `dirname $@`
        ./src/language/lexer/q2c$(EXEEXT_FOR_BUILD) $< $@
@@ -24,21 +26,26 @@ EXEEXT_FOR_BUILD = @EXEEXT_FOR_BUILD@
 $(all_q_sources:.q=.c): src/language/lexer/q2c$(EXEEXT_FOR_BUILD)
 all_q_sources =
 
-pkgsysconfdir = $(sysconfdir)/@PACKAGE@
+pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
 
 
 EXTRA_DIST = OChangeLog ONEWS config.rpath pspp-mode.el
 
 CLEANFILES = 
+CLEAN_LOCAL =
+ALL_LOCAL =
 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 =
+SUFFIXES = .q
 
-DIST_HOOKS += generate-changelog
 generate-changelog:
        if test -d $(top_srcdir)/.git; then                     \
          $(top_srcdir)/gitlog-to-changelog --since=2008-07-27  \
@@ -47,6 +54,10 @@ generate-changelog:
          mv $(distdir)/cl-t $(distdir)/ChangeLog;              \
        fi
 
+DIST_HOOKS += generate-changelog
+
+
+include $(top_srcdir)/po/automake.mk
 include $(top_srcdir)/lib/automake.mk
 include $(top_srcdir)/doc/automake.mk
 include $(top_srcdir)/config/automake.mk
@@ -59,6 +70,22 @@ 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)
+
+clean-local: $(CLEAN_LOCAL)
+all-local: $(ALL_LOCAL)
+
+# A convenience target to build all the binaries
+programs: $(PROGRAMS)
diff --git a/NEWS b/NEWS
index f99fb17374fd2baf74b409df9b4d37fdbaf1a306..149d1deadbad2b44269d9a6b787ac9ed9ed10c80 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,56 @@ See the end for copying conditions.
 
 Please send PSPP bug reports to bug-gnu-pspp@gnu.org.
 
-Changes from 0.6.1 to 0.6.2-rc1:
+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.2-pre6 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.
+
+  * Numererous improvements to the Graphical User Interface have
+    made.  Notable improvements include:
+
+    - Non-Ascii characters in strings, labels and variable names are
+      now supported.
+
+    - A "Split Window" function is available, which makes it easier to
+      see different parts of a large data file.
+
+    - Data files can now be opened by specifing their name as the first
+      argument.  This means that on a properly configured desktop, double
+      clicking on an icon will open the file.
+    
+
+  * New statistical procedures:
+    - CORRELATIONS
+    - ROC
+    - RELIABILITY
+
+    NPAR TESTS now supports the WILCOXON and SIGN subcommands.
+
+    The CROSSTABS command has been completely re-implemented to fix numerous bugs.
+
+  * Three new commands to combine data files have been added: MATCH FILES,
+   UPDATE and  ADD FILES.
+
+  * A tutorial chapter has been added to the user manual.
+
+Changes from 0.6.1 to 0.6.2
 
   * New translations:
 
diff --git a/README.CVS b/README.CVS
deleted file mode 100644 (file)
index 2e45fef..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-To build this project from the sources direct from the cvs archive,
-you must install the prerequisites listed in README, plus the
-following:
-
-       * Autoconf 2.60 (or later).
-
-       * Automake 1.10 (or later).
-
-       * Gettext 0.17 (or later).
-
-       * GNU M4 1.4.9 (or later).
-
-       * pkg-config 0.21 (or later).
-
-        * gperf 3.0.1 (or later).
-
-       * Gnulib, from Git at <git://git.savannah.gnu.org/gnulib.git>.
-         If you do not have Git installed, up-to-date snapshots are
-         at <http://git.savannah.gnu.org/gitweb/?p=gnulib.git>.
-         Note that Gnulib does not require any form of installation:
-         simply checking it out into a directory is sufficient.  We
-         recommend checking out gnulib into a directory named `gnulib'
-          at the same level as PSPP.
-
-       * libtool 1.5.22 (or later).
-
-       * Texinfo 4.7 or later, to build the documentation.
-
-Once you have these installed, execute
-       make -f Smake
-If you checked Gnulib out in a directory named `gnulib' at the same
-level as PSPP, then this is sufficient.  Otherwise, provide the
-location of GNULIB on the `make' command line:
-       make -f Smake GNULIB=/gnulib/base/directory/name
-
-After executing Smake, you may configure the source tree in the usual
-way with ./configure, e.g.
-       ./configure
-For a list of options:
-       ./configure --help
-
-To test:
-       make check
-
-For a very thorough test: 
-       make distcheck
diff --git a/README.Git b/README.Git
new file mode 100644 (file)
index 0000000..7d36529
--- /dev/null
@@ -0,0 +1,77 @@
+Prerequisites for Building PSPP from Git
+----------------------------------------
+
+To build this project from the sources direct from the Git archive,
+you must install the prerequisites listed in README, plus the
+following:
+
+       * Autoconf 2.60 (or later).
+
+       * Automake 1.10 (or later).
+
+       * Gettext 0.17 (or later).
+
+       * GNU M4 1.4.9 (or later).
+
+       * pkg-config 0.21 (or later).
+
+        * gperf 3.0.1 (or later).
+
+       * Gnulib (see below for details).
+
+       * libtool 1.5.22 (or later).
+
+       * Texinfo 4.7 or later, to build the documentation.
+
+After you install PSPP's prerequisites, you must obtain a copy of
+Gnulib, then bootstrap the tree, as described in the sections below.
+After that, you may follow the procedure described in INSTALL.
+
+Obtaining Gnulib
+----------------
+
+This version of PSPP should work with the Gnulib commit shown below.
+Gnulib does not maintain a stable API or ABI, so it is possible that
+PSPP will not work with older or newer versions of Gnulib.
+
+    commit 0883405cc751858a633bebc56acb75381a6e50c8
+    Author: Eric Blake <ebb9@byu.net>
+    Date:   Wed Nov 18 06:59:44 2009 -0700
+
+        utimens: fix regression on Solaris
+
+
+To clone Gnulib into a directory named "gnulib" using Git, and then
+check out this particular commit, run these commands:
+       git clone git://git.savannah.gnu.org/gnulib.git gnulib
+       cd gnulib
+       git checkout $COMMIT
+where $COMMIT should be replaced by the commit number listed above
+(usually it is sufficient to just type the first 6 or so digits).
+
+If you do not have Git installed, then you may alternately download
+http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=snapshot;h=$COMMIT;sf=tgz
+where $COMMIT is, again, at least the first few digits of the commit
+number listed above.  This download will yield a tar.gz file that you
+may extract with "gunzip" and "tar" to yield identical results.
+
+Bootstrapping
+-------------
+
+Once you have Gnulib installed, PSPP must be "bootstrapped" using the
+following command:
+       make -f Smake
+If you checked Gnulib out in a directory named `gnulib' at the same
+level as PSPP, then this is sufficient.  Otherwise, provide the
+location of GNULIB on the `make' command line:
+       make -f Smake GNULIB=/gnulib/base/directory/name
+
+After executing Smake, follow the procedure described in INSTALL to
+build and install PSPP.  On some systems this may be as simple as:
+       ./configure
+       make
+
+Once PSPP is built, you may run its self-tests with:
+       make check
+or for a more thorough test:
+       make distcheck
diff --git a/Smake b/Smake
index b63e7021162594cb52186c64355a1abee380b346..e8ffdd5705b2802df79ef79b6ccf90b075a521c0 100644 (file)
--- a/Smake
+++ b/Smake
@@ -5,12 +5,13 @@ GNULIB = ../gnulib
 GNULIB_TOOL = $(GNULIB)/gnulib-tool
 
 GNULIB_MODULES = \
+       argp \
        assert \
        byteswap \
        c-ctype \
        c-strtod \
-       canonicalize \
        close \
+       count-one-bits \
        crypto/md4 \
        dirname \
        environ \
@@ -75,6 +76,9 @@ GNULIB_MODULES = \
        trunc \
        unilbrk/ulc-width-linebreaks \
        unistd \
+       unistr/u8-cpy \
+       unistr/u8-strlen \
+       unistr/u8-strncat \
        unlocked-io \
        vasprintf-posix \
        vfprintf-posix \
@@ -85,11 +89,12 @@ GNULIB_MODULES = \
        xalloc \
        xalloc-die \
        xmalloca \
+       xmemdup0 \
        xsize \
        xstrndup \
        xvasprintf
 
-all: po/POTFILES.in
+all: 
        test -e ChangeLog || touch ChangeLog
        test -d m4 || mkdir m4
        echo '*' > m4/.cvsignore
@@ -100,7 +105,6 @@ all: po/POTFILES.in
        echo '*' > gl/.cvsignore
        echo '*' > gl/m4/.cvsignore
        libtoolize --force --automake
-       autopoint --force
        aclocal -I m4 -I gl/m4
        autoconf
        autoheader
@@ -111,15 +115,6 @@ gettextize:
        touch m4/Makefile.am
        gettextize --force --no-changelog
 
-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 -e `dirname $$f`/`basename $$f .c`.q ; then continue; fi; \
-               echo $$f ; \
-       done | sort | uniq > $@.tmp
-       if test ! -e $@ || ! cmp -s $@.tmp $@; then mv $@.tmp $@; fi
-       rm -f $@.tmp
-
 check: all
        rm -rf _check
        mkdir _check
@@ -135,8 +130,8 @@ _debug: all
        test -d _debug || mkdir _debug
        cd _debug && ../configure $(CONFIGUREFLAGS) --enable-debug
 
-PO_FILES = po/ChangeLog po/Makefile po/Makefile.in po/Makefile.in.in   \
-po/POTFILES po/POTFILES.in po/Rules-quot po/boldquot.sed               \
+PO_FILES = po/ChangeLog \
+po/Rules-quot po/boldquot.sed          \
 po/cat-id-tbl.c po/en@boldquot.header po/en@quot.header                        \
 po/insert-header.sin po/quot.sed po/remove-potcdate.sin po/stamp-po    \
 po/Makevars.template po/pspp.pot
@@ -162,4 +157,4 @@ clean:
        rm -f ltmain.sh
        rm -f reloc-ldflags install-reloc
 
-.PHONY: all gettextize potfiles clean
+.PHONY: all gettextize clean
index 75fcb0d023d5e547b559afa9e7c9bf6a82f85694..307ce149a4344f3f5e373146cac3fefea7b5a08c 100644 (file)
@@ -1,4 +1,4 @@
-dnl Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+dnl Copyright (C) 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
@@ -34,6 +34,13 @@ AC_DEFUN([PSPP_PERL],
   if test "$PERL" != no && $PERL -e 'require 5.005_03;'; then :; else
     PSPP_REQUIRED_PREREQ([Perl 5.005_03 (or later)])
   fi
+
+  # The PSPP autobuilder appends a build number to the PSPP version number,
+  # e.g. "0.7.2-build40".  But Perl won't parse version numbers that contain
+  # anything other than digits and periods, so "-build" causes an error.  So we
+  # define $(VERSION_FOR_PERL) that drops everything from the hyphen onward.
+  VERSION_FOR_PERL=`echo "$VERSION" | sed 's/-.*//'`
+  AC_SUBST([VERSION_FOR_PERL])
 ])
 
 dnl Check that libplot is available.
@@ -282,4 +289,26 @@ AC_DEFUN([PSPP_GSL_NEEDS_FGNU89_INLINE],
         CFLAGS="$CFLAGS -fgnu89-inline"
      fi])
 ])
-dnl acinclude.m4 ends here
+
+AC_DEFUN([PSPP_CHECK_CLICKSEQUENCE],
+  [AC_REQUIRE([AM_INIT_AUTOMAKE])  # Defines MAKEINFO
+   AC_CACHE_CHECK([whether makeinfo supports @clicksequence],
+     [pspp_cv_have_clicksequence],
+     [cat > conftest.texi  <<EOF
+@setfilename conftest.info
+@clicksequence{File @click{} Open}
+EOF
+      echo "configure:__oline__: running $MAKEINFO conftest.texi >&AS_MESSAGE_LOG_FD" >&AS_MESSAGE_LOG_FD
+      eval "$MAKEINFO conftest.texi >&AS_MESSAGE_LOG_FD 2>&1"
+      retval=$?
+      echo "configure:__oline__: \$? = $retval" >&AS_MESSAGE_LOG_FD
+      if test $retval = 0; then
+       pspp_cv_have_clicksequence=yes
+      else
+       pspp_cv_have_clicksequence=no
+      fi
+      rm -f conftest.texi conftest.info])
+   if test $pspp_cv_have_clicksequence = no; then
+       AM_MAKEINFOFLAGS="$AM_MAKEINFOFLAGS -DMISSING_CLICKSEQUENCE"
+       AC_SUBST([AM_MAKEINFOFLAGS])
+   fi])
index bb37d2151121922d7e30e02100718b23631494a0..1ef538bca60ae2c120a433df8aca080f616e5963 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.2-rc1],[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,8 @@ AC_LIBTOOL_WIN32_DLL
 AC_LIBTOOL_DLOPEN
 AC_PROG_LIBTOOL
 PKG_PROG_PKG_CONFIG
+m4_pattern_forbid([PKG_CHECK_MODULES])
+PSPP_CHECK_CLICKSEQUENCE
 
 AC_ARG_ENABLE(
   anachronistic-dependencies, 
@@ -29,12 +31,16 @@ PSPP_CC_FOR_BUILD
 PSPP_PERL
 
 dnl Internationalization macros.
-AM_GNU_GETTEXT([external], [need-ngettext])
-AM_GNU_GETTEXT_VERSION([0.17])
+AC_ARG_ENABLE(nls, [AS_HELP_STRING([--disable-nls], [do not use Native Language Support])])
+if  test x"$enable_nls" != x"no"  ; then
+ AC_DEFINE(ENABLE_NLS, 1, [Define to 1 if translation of program messages to the user's native language is requested.])
+fi
+
 
 dnl Checks for libraries.
 AC_SYS_LARGEFILE
 AC_SEARCH_LIBS([sin], [m])
+AC_SEARCH_LIBS([dcgettext], [intl])
 PSPP_LIBPLOT
 PSPP_LC_PAPER
 AM_CONDITIONAL(WITHCHARTS, test x"$with_libplot" != x"no")
@@ -49,15 +55,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 +111,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 +126,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
@@ -204,9 +207,14 @@ if test "$am_cv_func_iconv" != "yes"; then
    PSPP_REQUIRED_PREREQ([iconv (see http://www.gnu.org/software/libiconv/)])
 fi
 
+dnl Required by the gnulib 'relocatable-prog' module.
+dnl See doc/relocatable-maint.texi in the gnulib tree for details.
+RELOCATABLE_LIBRARY_PATH='$(libdir)'
+RELOCATABLE_STRIP=:
+
 PSPP_CHECK_PREREQS
 
-AC_CONFIG_FILES([Makefile gl/Makefile po/Makefile.in])
+AC_CONFIG_FILES([Makefile gl/Makefile])
 
 AC_OUTPUT
 echo "PSPP configured successfully."
index 3445379a1ca756e00e62751623ab4759cdf12dc0..b24eb51e7e6c5458fc0ae726fc7a92e0f46a74eb 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 \
@@ -42,24 +46,22 @@ EXTRA_DIST += doc/pspp.man \
 
 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 > $@
+       $(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) $(AM_MAKEINFOFLAGS) --docbook -I $(top_srcdir) \
+               $(top_srcdir)/doc/pspp.texinfo -o - \
+               | $(SED) '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 b1be385334bf5ef3f8aa1adfcfdecb10d9adaf3d..c1d1e42129a02c5e7fb7dfbf9f6daa528c2550dc 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..562b328dbab3bb595574c9ee804d66703dc1d407 100644 (file)
@@ -6,12 +6,26 @@
 @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}
 \CMDNAME\
 @end macro
 
+@ifset MISSING_CLICKSEQUENCE
+@alias clicksequence = asis
+@macro click {}
+->
+@end macro
+@end ifset
+
 @iftex
 @finalout
 @end iftex
@@ -25,7 +39,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 +65,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 +88,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 +118,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..63e8f35aef9ba37f540661aee7018c3b6a9fadb4 100644 (file)
@@ -8,12 +8,15 @@ far.
 * DESCRIPTIVES::                Descriptive statistics.
 * FREQUENCIES::                 Frequency tables.
 * EXAMINE::                     Testing data for normality.
+* CORRELATIONS::                Correlation tables.
 * CROSSTABS::                   Crosstabulation tables.
 * NPAR TESTS::                  Nonparametric tests.
 * T-TEST::                      Test hypotheses about means.
 * 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 +207,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 +236,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 +275,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%.
@@ -292,6 +302,69 @@ If many dependent variable are given, or factors are given for which
 there are many distinct values, then @cmd{EXAMINE} will produce a very
 large quantity of output.
 
+@node CORRELATIONS
+@section CORRELATIONS
+
+@vindex CORRELATIONS
+@display
+CORRELATIONS
+     /VARIABLES = varlist [ WITH varlist ]
+     [
+      .
+      .
+      .
+      /VARIABLES = varlist [ WITH varlist ]
+      /VARIABLES = varlist [ WITH varlist ]
+     ]
+
+     [ /PRINT=@{TWOTAIL, ONETAIL@} @{SIG, NOSIG@} ]
+     [ /STATISTICS=DESCRIPTIVES XPROD ALL]
+     [ /MISSING=@{PAIRWISE, LISTWISE@} @{INCLUDE, EXCLUDE@} ]
+@end display    
+
+@cindex correlation
+The @cmd{CORRELATIONS} procedure produces tables of the Pearson correlation coefficient
+for a set of variables.  The significance of the coefficients are also given.
+
+At least one VARIABLES subcommand is required. If the WITH keyword is used, then a non-square
+correlation table will be produced.
+The variables preceding WITH, will be used as the rows of the table, and the variables following
+will be the columns of the table.
+If no WITH subcommand is given, then a square, symmetrical table using all variables is produced.
+
+
+The @cmd{MISSING} subcommand determines the handling of missing variables.  
+If INCLUDE is set, then user-missing values are included in the
+calculations, but system-missing values are not.
+If EXCLUDE is set, which is the default, user-missing
+values are excluded as well as system-missing values. 
+This is the default.
+
+If LISTWISE is set, then the entire case is excluded from analysis
+whenever any variable  specified in the any @cmd{/VARIABLES} subcommand
+contains a missing value.   
+If PAIRWISE is set, then a case is considered missing only if either of the
+values  for the particular coefficient are missing.
+The default is PAIRWISE.
+
+The PRINT subcommand is used to control how the reported significance values are printed.
+If the TWOTAIL option is used, then a two-tailed test of significance is 
+printed.  If the ONETAIL option is given, then a one-tailed test is used.
+The default is TWOTAIL.
+
+If the NOSIG option is specified, then correlation coefficients with significance less than
+0.05 are highlighted.
+If SIG is specified, then no highlighting is performed.  This is the default.
+
+@cindex covariance
+The STATISTICS subcommand requests additional statistics to be displayed.  The keyword 
+DESCRIPTIVES requests that the mean, number of non-missing cases, and the non-biased
+estimator of the standard deviation are displayed.
+These statistics will be displayed in a separated table, for all the variables listed
+in any /VARIABLES subcommand.
+The XPROD keyword requests cross-product deviations and covariance estimators to 
+be displayed for each pair of variables.
+The keyword ALL is the union of DESCRIPTIVES and XPROD.
 
 @node CROSSTABS
 @section CROSSTABS
@@ -340,9 +413,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 +570,8 @@ NPAR TESTS
      [ /STATISTICS=@{DESCRIPTIVES@} ]
 
      [ /MISSING=@{ANALYSIS, LISTWISE@} @{INCLUDE, EXCLUDE@} ]
+
+     [ /METHOD=EXACT [ TIMER [(n)] ] ]
 @end display
 
 NPAR TESTS performs nonparametric tests. 
@@ -508,10 +581,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 +609,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 +649,7 @@ even for very large sample sizes.
 
 
 @node    CHISQUARE
-@subsection Chisquare test
+@subsection Chisquare Test
 @vindex CHISQUARE
 @cindex chisquare test
 
@@ -574,7 +659,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 +676,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 +904,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 +968,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..1f0bf0ba1922303faf17e3b9bc6c28b65c3b82d2 100644 (file)
@@ -14,13 +14,19 @@ libglade_psppire_la_SOURCES = \
        glade/bbox.c \
        glade/selector.c \
        glade/acr.c \
+       glade/dictview.c \
+       glade/var-view.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-selector.c
+       src/ui/gui/psppire-dictview.c \
+       src/ui/gui/psppire-selector.c \
+       src/ui/gui/psppire-select-dest.c \
+       src/ui/gui/psppire-var-view.c
 
 dist_catalog_DATA = \
        glade/psppire.xml
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..db8956b70ac7880d179d1a706911c69c6552938b 100644 (file)
          </displayable-values>
        </property>
 
-
-       <property id="visible" ignore="True" default="True" />
+       <property id="source-widget"     ignore="True" />
+       <property id="dest-widget"       ignore="True" />
+       <property id="visible"           ignore="True" default="True" />
+       <property id="primary"           default="False" />
      </properties>
 
     </glade-widget-class>
 
     </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" />
+      <property id="model"                visible="False"  query="False" />
+    </properties>
+
+    </glade-widget-class>
+
+
+    <glade-widget-class name="PsppireVarView" generic-name="psppire-var-view" title="Variable Treeview">
+
+      <post-create-function>glade_psppire_var_view_post_create</post-create-function>
+      <get-children-function>glade_psppire_var_view_get_children</get-children-function>
+      <get-internal-child-function>glade_psppire_var_view_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" />
+      <property id="model"                visible="False"  query="False" disabled="True" />
+      <property id="n-cols"    ignore="True" />
+
+    </properties>
+
+    </glade-widget-class>
+
+
   </glade-widget-classes>
+
  
 
  <glade-widget-group name="psppire" title="Psppire">
   <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="PsppireVarView"/>
   <glade-widget-class-ref name="PsppireSelector"/>
+  <glade-widget-class-ref name="PsppireKeypad"/>
   <glade-widget-class-ref name="PsppireAcr"/>
  </glade-widget-group>
 
diff --git a/glade/var-view.c b/glade/var-view.c
new file mode 100644 (file)
index 0000000..7eb6c62
--- /dev/null
@@ -0,0 +1,83 @@
+#include <config.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "psppire-var-view.h"
+
+#include <gladeui/glade.h>
+
+
+/* Dummy function to keep the linker happy.
+   Glade never actually needs to use this return value.
+ */
+GType
+psppire_var_ptr_get_type (void)
+{
+  return 0;
+}
+
+void
+glade_psppire_var_view_post_create (GladeWidgetAdaptor *adaptor,
+                                   GObject            *object,
+                                   GladeCreateReason   reason)
+{
+  GladeWidget *widget ;
+
+  PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
+
+  g_return_if_fail (PSPPIRE_IS_VAR_VIEW (var_view));
+
+  widget = glade_widget_get_from_gobject (GTK_WIDGET (var_view));
+  if (!widget)
+    return;
+
+  if (reason == GLADE_CREATE_USER)
+    {
+      /* HIG complient border-width defaults on var_views */
+      glade_widget_property_set (widget, "border-width", 5);
+    }
+}
+
+
+GtkWidget *
+glade_psppire_var_view_get_internal_child (GladeWidgetAdaptor  *adaptor,
+                                        PsppireVarView       *var_view,
+                                        const gchar         *name)
+{
+#if DEBUGGING
+  g_print ("%s\n", __FUNCTION__);
+#endif
+  return GTK_WIDGET (var_view);
+}
+
+
+
+void
+glade_psppire_var_view_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_var_view_get_children (GladeWidgetAdaptor  *adaptor,
+                                    PsppireVarView  *dv)
+{
+  GList *list = NULL;
+
+  g_return_val_if_fail (PSPPIRE_IS_VAR_VIEW (dv), NULL);
+
+  list = glade_util_container_get_all_children (GTK_CONTAINER (dv));
+
+  return list;
+}
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..35b4916
--- /dev/null
@@ -0,0 +1,776 @@
+#!/usr/bin/env python
+#    This file was downloaded from 
+#    http://git.gnome.org/cgit/gtk+/plain/gtk/gtk-builder-convert
+#    on 4 Oct 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 output.
+
+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
+
+DIALOGS = ['GtkDialog',
+           'GtkFileChooserDialog',
+           'GtkMessageDialog']
+WINDOWS = ['GtkWindow'] + DIALOGS
+
+# 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)
+            if self._get_object(node.getAttribute('id')) is not None:
+               print "WARNING: duplicate id \"" + node.getAttribute('id') + "\""
+            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
+        # FIXME: Use sorted(self.root_objects,
+        #                   key=lambda n: n.getAttribute('id'),
+        #                   reverse=True):
+        # when we can depend on python 2.4 or higher
+        root_objects = self.root_objects[:]
+        root_objects.sort(lambda a, b: cmp(b.getAttribute('id'),
+                                           a.getAttribute('id')))
+        for obj in root_objects:
+            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') in DIALOGS):
+                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
+
+        translatable_attr = prop.attributes.get('translatable')
+        translatable = translatable_attr is not None and translatable_attr.value == 'yes'
+        has_context_attr = prop.attributes.get('context')
+        has_context = has_context_attr is not None and has_context_attr.value == 'yes'
+        comments_attr = prop.attributes.get('comments')
+        comments = comments_attr is not None and comments_attr.value or None
+
+        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)
+
+        if value.endswith('\n'):
+            value = value[:-1]
+        for item in value.split('\n'):
+            row = self._dom.createElement('row')
+            data.appendChild(row)
+
+            col = self._dom.createElement('col')
+            col.setAttribute('id', '0')
+            if translatable:
+                col.setAttribute('translatable', 'yes')
+            if has_context:
+                splitting = item.split('|', 1)
+                if len(splitting) == 2:
+                    context, item = splitting
+                    col.setAttribute('context', context)
+            if comments is not None:
+                col.setAttribute('comments', comments)
+            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..6d1c0af
--- /dev/null
@@ -0,0 +1,4904 @@
+/*
+  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 <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>
+
+/* sheet flags */
+enum
+  {
+    PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
+    PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
+    PSPPIRE_SHEET_IN_DRAG = 1 << 3,
+
+    /* This flag is set when the user is actually in the process
+       of making a selection - ie while the mouse button is
+       depressed.
+    */
+    PSPPIRE_SHEET_IN_SELECTION = 1 << 4
+  };
+
+#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 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 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 void
+rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
+                     GdkRectangle *r)
+{
+  gint col0 = MIN (range->col0, range->coli);
+  gint coli = MAX (range->col0, range->coli);
+  gint row0 = MIN (range->row0, range->rowi);
+  gint rowi = MAX (range->row0, range->rowi);
+
+  if ( row0 == -1 ) row0 = min_visible_row (sheet);
+  if ( rowi == -1 ) rowi = max_visible_row (sheet);
+  if ( col0 == -1 ) col0 = min_visible_column (sheet);
+  if ( coli == -1 ) coli = max_visible_column (sheet);
+
+  r->x = psppire_axis_start_pixel (sheet->haxis, col0);
+  r->x -= round (sheet->hadjustment->value);
+
+  r->y = psppire_axis_start_pixel (sheet->vaxis, row0);
+  r->y -= round (sheet->vadjustment->value);
+
+  r->width = psppire_axis_start_pixel (sheet->haxis, coli) -
+    psppire_axis_start_pixel (sheet->haxis, col0) +
+    psppire_axis_unit_size (sheet->haxis, coli);
+
+  r->height = psppire_axis_start_pixel (sheet->vaxis, rowi) -
+    psppire_axis_start_pixel (sheet->vaxis, row0) +
+    psppire_axis_unit_size (sheet->vaxis, 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;
+    }
+}
+
+static void
+rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
+                    GdkRectangle *r)
+{
+  PsppireSheetRange range;
+  g_return_if_fail (row >= 0);
+  g_return_if_fail (col >= 0);
+
+  range.row0 = range.rowi = row;
+  range.col0 = range.coli = col;
+
+  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);
+
+
+/* Selection */
+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, or -1 if no row is selected.
+   *
+   * 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, or -1 if no column is selected.
+   *
+   * 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->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->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)
+{
+  g_return_if_fail (sheet != NULL);
+  g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
+
+  if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
+    psppire_sheet_hide_entry_widget (sheet);
+
+  sheet->entry_type = entry_type;
+
+  create_sheet_entry (sheet);
+
+  if (sheet->select_status == 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);
+    }
+}
+
+
+
+
+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 &&
+      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_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 != g_strcmp0 (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);
+}
+
+
+/* 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 != PSPPIRE_SHEET_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;
+
+  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->active_cell.row = row;
+  sheet->active_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_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);
+}
+
+\f
+
+/* Selection related functions */
+
+void
+psppire_sheet_select_row (PsppireSheet *sheet,  gint row)
+{
+  GdkRectangle area;
+  sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
+
+  sheet->range.col0 = sheet->range.coli = -1;
+  sheet->range.row0 = sheet->range.rowi = row;
+
+  rectangle_from_range (sheet, &sheet->range, &area);
+  area.x++;
+  area.y++;
+
+  gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
+
+  g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
+}
+
+void
+psppire_sheet_select_column (PsppireSheet *sheet,  gint column)
+{
+  GdkRectangle area;
+  sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
+
+  sheet->range.col0 = sheet->range.coli = column;
+  sheet->range.row0 = sheet->range.rowi = -1;
+
+  rectangle_from_range (sheet, &sheet->range, &area);
+  area.x++;
+  area.y++;
+
+  gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
+
+  g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
+}
+
+
+void
+psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
+{
+  GdkRectangle area;
+  sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
+
+  sheet->range = *range;
+
+  rectangle_from_range (sheet, range, &area);
+  area.x++;
+  area.y++;
+  gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
+}
+
+
+void
+psppire_sheet_unselect_range (PsppireSheet *sheet)
+{
+  GdkRectangle area;
+  sheet->select_status = PSPPIRE_SHEET_NORMAL;
+
+  rectangle_from_range (sheet, &sheet->range, &area);
+  area.x++;
+  area.y++;
+  gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);      
+
+  g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
+  g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);  
+}
+
+void
+psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
+{
+  g_return_if_fail (sheet != NULL);
+  *range = sheet->range;
+}
+\f
+
+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 (sheet->select_status != PSPPIRE_SHEET_NORMAL)
+       {
+         GdkRectangle area;
+
+         rectangle_from_range (sheet, &sheet->range, &area);
+             
+         gdk_draw_rectangle (sheet->sheet_window,
+                             sheet->xor_gc,
+                             TRUE,
+                             area.x + 1, area.y + 1,
+                             area.width, area.height);
+       }
+
+
+      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);
+       }
+    }
+  
+  if (event->window == sheet->row_title_window)
+    {
+      g_signal_emit (sheet,
+                    sheet_signals[BUTTON_EVENT_ROW], 0,
+                    row, event);
+
+      if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
+       {
+         if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
+           g_signal_emit (sheet,
+                          sheet_signals[DOUBLE_CLICK_ROW], 0, row);
+       }
+    }
+
+  gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
+
+  if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
+
+
+  /* press on resize windows */
+  if (event->window == sheet->column_title_window)
+    {
+      sheet->x_drag = event->x;
+
+      if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
+       {
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
+         gdk_pointer_grab (sheet->column_title_window, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK |
+                           GDK_BUTTON1_MOTION_MASK |
+                           GDK_BUTTON_RELEASE_MASK,
+                           NULL, NULL, event->time);
+
+         draw_xor_vline (sheet);
+         return TRUE;
+       }
+    }
+
+  if (event->window == sheet->row_title_window)
+    {
+      sheet->y_drag = event->y;
+
+      if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
+       {
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
+         gdk_pointer_grab (sheet->row_title_window, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK |
+                           GDK_BUTTON1_MOTION_MASK |
+                           GDK_BUTTON_RELEASE_MASK,
+                           NULL, NULL, event->time);
+
+         draw_xor_hline (sheet);
+         return TRUE;
+       }
+    }
+
+  /* the sheet itself does not handle other than single click events */
+  if (event->type != GDK_BUTTON_PRESS) return FALSE;
+
+  /* selections on the sheet */
+  if (event->window == sheet->sheet_window)
+    {
+      gtk_widget_get_pointer (widget, &x, &y);
+      psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
+      gdk_pointer_grab (sheet->sheet_window, FALSE,
+                       GDK_POINTER_MOTION_HINT_MASK |
+                       GDK_BUTTON1_MOTION_MASK |
+                       GDK_BUTTON_RELEASE_MASK,
+                       NULL, NULL, event->time);
+      gtk_grab_add (GTK_WIDGET (sheet));
+
+      if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
+       {
+         sheet->range.row0 = row;
+         sheet->range.col0 = column;
+       }
+      else
+       {
+         psppire_sheet_unselect_range (sheet);
+       }
+      psppire_sheet_click_cell (sheet, row, column);
+    }
+
+  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 == PSPPIRE_SHEET_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)
+    change_active_cell (sheet, row, column);
+
+  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_unselect_range (sheet);
+
+      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_SELECTION (sheet))
+    {
+      PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+      sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
+
+      change_active_cell (sheet, sheet->active_cell.row,
+                         sheet->active_cell.col);
+    }
+
+  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) &&
+       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) ) &&
+       (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);
+    }
+
+  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;
+    }
+
+  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 ( mods & GDK_BUTTON1_MASK)
+    {
+      if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
+       {
+         /* Redraw the old range */
+         psppire_sheet_unselect_range (sheet);
+
+         sheet->range.rowi = row;
+         sheet->range.coli = column;
+
+         /* Redraw the new range */
+         psppire_sheet_select_range (sheet, &sheet->range);
+       }
+      else
+       {
+         PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
+       }
+    }
+
+  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 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_unselect_range (sheet);
+}
+
+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..db58d4b
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+   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;
+
+  /* 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;
+
+  /* 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 703215c..0000000
+++ /dev/null
@@ -1,2488 +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"
-
-#if GTK_CHECK_VERSION (2,17,4)
-#define HAVE_GTKENTRYBUFFER 1
-#endif
-
-#if !GTK_CHECK_VERSION (2,14,0)
-static guint16
-gtk_entry_get_text_length (GtkEntry *entry)
-{
-  return entry->text_length;
-}
-#endif
-
-#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)
-{
-#if HAVE_GTKENTRYBUFFER
-  g_object_ref (editable);
-
-  /*
-   * The incoming text may a password or other secret. We make sure
-   * not to copy it into temporary buffers.
-   */
-
-  g_signal_emit_by_name (editable, "insert-text", new_text, new_text_length, position);
-
-  g_object_unref (editable);
-#else  /* !HAVE_GTKENTRYBUFFER */
-  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));
-#endif  /* !HAVE_GTKENTRYBUFFER */
-}
-
-static void
-gtk_entry_delete_text (GtkEditable *editable,
-                      gint         start_pos,
-                      gint         end_pos)
-{
-#if HAVE_GTKENTRYBUFFER
-  g_object_ref (editable);
-
-  g_signal_emit_by_name (editable, "delete-text", start_pos, end_pos);
-
-  g_object_unref (editable);
-#else  /* !HAVE_GTKENTRYBUFFER */
-  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));
-#endif  /* !HAVE_GTKENTRYBUFFER */
-}
-
-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);
-  guint length;
-
-  length = gtk_entry_get_text_length (entry);
-  if (position < 0 || position > length)
-    position = 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)
-{
-#if HAVE_GTKENTRYBUFFER
-  GtkEntry *entry = GTK_ENTRY (editable);
-  GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry);
-
-  guint n_inserted;
-  gint n_chars;
-
-  n_chars = g_utf8_strlen (new_text, new_text_length);
-
-  /*
-   * The actual insertion into the buffer. This will end up firing the
-   * following signal handlers: buffer_inserted_text(), buffer_notify_display_text(),
-   * buffer_notify_text(), buffer_notify_length()
-   */
-  n_inserted = gtk_entry_buffer_insert_text (buffer, *position, new_text, n_chars);
-
-  if (n_inserted != n_chars)
-      gtk_widget_error_bell (GTK_WIDGET (editable));
-
-  *position += n_inserted;
-#else  /* !HAVE_GTKENTRYBUFFER */
-  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");
-#endif  /* !HAVE_GTKENTRYBUFFER */
-}
-
-static void
-gtk_entry_real_delete_text (GtkEditable *editable,
-                           gint         start_pos,
-                           gint         end_pos)
-{
-#ifdef HAVE_GTKENTRYBUFFER
-  GtkEntry *entry = GTK_ENTRY (editable);
-  GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry);
-  /*
-   * The actual deletion from the buffer. This will end up firing the
-   * following signal handlers: buffer_deleted_text(), buffer_notify_display_text(),
-   * buffer_notify_text(), buffer_notify_length()
-   */
-
-  gtk_entry_buffer_delete_text (buffer, start_pos, end_pos - start_pos);
-#else  /* !HAVE_GTKENTRYBUFFER */
-  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");
-    }
-#endif  /* !HAVE_GTKENTRYBUFFER */
-}
-
-/* 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);
-  const char *text = gtk_entry_get_text (entry);
-  gint index = g_utf8_offset_to_pointer (text, offset) - 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 : gtk_entry_get_text_length (entry);
-         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 : gtk_entry_get_text_length (entry);
-         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)
-{
-  const char *text = gtk_entry_get_text (entry);
-  size_t n_bytes = strlen (text);
-
-  gtk_im_context_set_surrounding (context,
-                                  text,
-                                  n_bytes,
-                                  g_utf8_offset_to_pointer (text, entry->current_pos) - 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;
-
-  const char *text = gtk_entry_get_text (entry);
-  size_t n_bytes = strlen (text);
-
-  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 (text, entry->current_pos) - text;
-
-      if (entry->visible)
-        {
-          g_string_prepend_len (tmp_string, text, 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 (text, 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, text, 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, gtk_entry_get_text_length (entry));
-          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 */
-    {
-      const char *text = gtk_entry_get_text (entry);
-      index = g_utf8_offset_to_pointer (text, entry->dnd_position) - 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(gtk_entry_get_text (entry)){
-
-            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(gtk_entry_get_text (entry)){
-
-            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)
-    {
-      const char *text = gtk_entry_get_text (entry);
-      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 (text + new_index) - text;
-    }
-
-  return g_utf8_pointer_to_offset (text, text + index);
-}
-
-static gint
-gtk_entry_move_logically (GtkEntry *entry,
-                         gint      start,
-                         gint      count)
-{
-  const char *text = gtk_entry_get_text (entry);
-  guint16 text_length = gtk_entry_get_text_length (entry);
-  gint new_pos = start;
-
-  /* Prevent any leak of information */
-  if (!entry->visible)
-    {
-      new_pos = CLAMP (start + count, 0, text_length);
-    }
-  else if (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 < text_length)
-       {
-         do
-           new_pos++;
-         while (new_pos < 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)
-{
-  const char *text = gtk_entry_get_text (entry);
-  guint16 text_length = gtk_entry_get_text_length (entry);
-  gint new_pos = start;
-
-  /* Prevent any leak of information */
-  if (!entry->visible)
-    {
-      new_pos = text_length;
-    }
-  else if (text && (new_pos < 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 (gtk_entry_get_text (entry) && 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 = gtk_entry_get_text_length (entry);
-
-  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_get_text (GTK_ENTRY(entry)), 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
index bbab8e2aad4adcdf9f9cbe41ec5d863a5084837b..000a5e3e0f740ed6b26957ccb2ee5703649db1e6 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
@@ -40,6 +40,8 @@
   Springer. 1998. ISBN 0-387-98542-5.
  */
 
+#include <config.h>
+
 #include "sweep.h"
 
 /*
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.xs b/perl-module/PSPP.xs
new file mode 100644 (file)
index 0000000..fef8551
--- /dev/null
@@ -0,0 +1,737 @@
+/* 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 <config.h>
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.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:
+ /* Check that the version is correct up to the length of 'ver'.
+    This allows PSPP autobuilders to add a "-build#" suffix to the
+    PSPP version without causing failures here. */
+ assert (0 == strncmp (ver, bare_version, strlen (ver)));
+
+ 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));
+ dict_set_encoding (dict, "UTF-8");
+ 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..ef9acc3
--- /dev/null
@@ -0,0 +1,72 @@
+## 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
+       (echo '%Locations = (';\
+        printf "  SourceDir => '";\
+        (cd $(top_srcdir) && echo `pwd`\', ) ;\
+        printf "  BuildDir => '";\
+        (cd $(top_builddir) && echo `pwd`\' );\
+        echo ');') > $(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_FOR_PERL).tar.gz: $(module_sources)
+       rm -f $@
+       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)
+
+perl_module_tarball:
+       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_FOR_PERL).tar.gz
+
+ALL_LOCAL += perl_module_tarball
+
+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"
+
+
+perl_module_CLEAN:
+       cd perl-module && $(MAKE) $(AM_MAKEFLAGS) clean || true
+       if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \
+         rm -f $(module_sources) ; \
+       fi
+       rm -f perl-module/Makefile.old
+
+CLEAN_LOCAL += perl_module_CLEAN
+
+CLEANFILES += \
+        perl-module/PSPP-Perl-$(VERSION_FOR_PERL).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..2dccd10
--- /dev/null
@@ -0,0 +1,562 @@
+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>.  C<name> must be a valid UTF8 string.
+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>, which must be a valid UTF8 string.
+
+
+=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>.
+C<label> must be a valid UTF8 string.
+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, $label)
+
+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.
+If the case contains strings, then the strings must be UTF8 encoded.
+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 usage,  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);
+   }
+
+ }
diff --git a/po/LINGUAS b/po/LINGUAS
deleted file mode 100644 (file)
index 0f98304..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# Available languages
-en_GB
-nl
-pt_BR
index b5c0a8003ec1a40f14baa21f67ca7339147ae405..4952d35daffae50f21a0858fed0e5e877ae89a67 100644 (file)
@@ -3,13 +3,6 @@
 # Usually the message domain is the same as the package name.
 DOMAIN = $(PACKAGE)
 
-# These two variables depend on the location of this directory.
-subdir = po
-top_builddir = ..
-
-# These options get passed to xgettext.
-XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ 
-
 # This is the copyright holder that gets inserted into the header of the
 # $(DOMAIN).pot file.  Set this to the copyright holder of the surrounding
 # package.  (Note that the msgstr strings, extracted from the package's
diff --git a/po/automake.mk b/po/automake.mk
new file mode 100644 (file)
index 0000000..0b18ef8
--- /dev/null
@@ -0,0 +1,64 @@
+include $(top_srcdir)/po/Makevars
+
+XGETTEXT=xgettext
+MSGMERGE=msgmerge
+MSGFMT=msgfmt
+
+POFILES=po/en_GB.po po/nl.po po/pt_BR.po
+
+POTFILE=po/$(DOMAIN).pot
+
+TRANSLATABLE_FILES = $(DIST_SOURCES) $(all_q_sources)
+
+XGETTEXT_OPTIONS = \
+       --copyright-holder="$(COPYRIGHT_HOLDER)" \
+       --package-name=$(PACKAGE) \
+       --package-version=$(VERSION) \
+       --msgid-bugs-address=$(MSGID_BUGS_ADDRESS) \
+       --add-comments='TRANSLATORS:'
+
+$(POTFILE): $(TRANSLATABLE_FILES) $(UI_FILES)
+       @$(MKDIR_P) po
+       $(XGETTEXT) --directory=$(top_srcdir) $(XGETTEXT_OPTIONS)    $(TRANSLATABLE_FILES) --language=C --keyword=_ --keyword=N_ -o $@
+       $(XGETTEXT) --directory=$(top_srcdir) $(XGETTEXT_OPTIONS) -j $(UI_FILES) --language=glade -o $@
+
+
+$(POFILES): $(POTFILE)
+       $(MSGMERGE) $(top_srcdir)/$@ $? -o $@
+
+
+SUFFIXES += .po .gmo
+.po.gmo:
+       @$(MKDIR_P) `dirname $@`
+       $(MSGFMT) $< -o $@
+
+
+GMOFILES = $(POFILES:.po=.gmo)
+
+ALL_LOCAL += $(GMOFILES)
+
+install-data-hook: $(GMOFILES)
+       for f in $(GMOFILES); do \
+         lang=`echo $$f | sed -e 's%po/\(.*\)\.gmo%\1%' ` ; \
+         $(INSTALL) -D $$f $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/$(DOMAIN).mo ; \
+       done
+
+uninstall-hook:
+       for f in $(GMOFILES); do \
+         lang=`echo $$f | sed -e 's%po/\(.*\)\.gmo%\1%' ` ; \
+         rm -f $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/$(DOMAIN).mo ; \
+       done
+
+
+EXTRA_DIST += $(POFILES) $(POTFILE)
+
+CLEANFILES += $(GMOFILES) $(POTFILE)
+
+# Clean $(POFILES) from build directory but not if that's the same as
+# the source directory.
+po_CLEAN:
+       @if test "$(srcdir)" != .; then \
+               echo rm -f $(POFILES); \
+               rm -f $(POFILES); \
+       fi
+CLEAN_LOCAL += po_CLEAN
index 2c3bc865a77dfb02f95900927fc4bba92bdd3609..9db312669c527f0e248e08a71b99e5d55ba84f3d 100644 (file)
@@ -1,20 +1,81 @@
 # 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-08-30 17:52+0200\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"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#: src/ui/gui/psppire-buttonbox.c:143
+msgid "Buttons"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:144
+msgid "The mask that decides what buttons appear in the button box"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:435
+msgid "Continue"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:433
+msgid "OK"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:434
+msgid "Go To"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:436
+msgid "Cancel"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:437
+msgid "Help"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:438
+msgid "Reset"
+msgstr ""
+
+#: src/ui/gui/psppire-buttonbox.c:439
+msgid "Paste"
+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 ""
+
+#: src/ui/gui/psppire-dictview.c:491 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/language/data-io/data-parser.c:650
+#: src/language/data-io/data-parser.c:691 src/language/data-io/print.c:404
+msgid "Variable"
+msgstr ""
+
+#: src/ui/gui/psppire-dictview.c:528
+msgid "Prefer variable labels"
+msgstr ""
+
 #: src/data/any-reader.c:57
 #, c-format
 msgid "An error occurred while opening \"%s\": %s."
@@ -50,156 +111,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:274 src/data/data-in.c:464
 msgid "Field contents are not numeric."
 msgstr ""
 
-#: src/data/data-in.c:264 src/data/data-in.c:454
+#: src/data/data-in.c:276 src/data/data-in.c:466
 msgid "Number followed by garbage."
 msgstr ""
 
-#: src/data/data-in.c:275
+#: src/data/data-in.c:287
 msgid "Invalid numeric syntax."
 msgstr ""
 
-#: src/data/data-in.c:284 src/data/data-in.c:467
+#: src/data/data-in.c:296 src/data/data-in.c:479
 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:301 src/data/data-in.c:484
 msgid "Too-small number set to zero."
 msgstr ""
 
-#: src/data/data-in.c:315
+#: src/data/data-in.c:327
 msgid "All characters in field must be digits."
 msgstr ""
 
-#: src/data/data-in.c:338
+#: src/data/data-in.c:350
 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:374 src/data/data-in.c:650
 msgid "Field must have even length."
 msgstr ""
 
-#: src/data/data-in.c:367 src/data/data-in.c:647
+#: src/data/data-in.c:379 src/data/data-in.c:661
 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:700 src/data/data-in.c:747
 msgid "Syntax error in date field."
 msgstr ""
 
-#: src/data/data-in.c:702
+#: src/data/data-in.c:716
 #, c-format
 msgid "Day (%ld) must be between 1 and 31."
 msgstr ""
 
-#: src/data/data-in.c:749
+#: src/data/data-in.c:763
 msgid "Delimiter expected between fields in date."
 msgstr ""
 
-#: src/data/data-in.c:823
+#: src/data/data-in.c:837
 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:864
 #, c-format
 msgid "Year (%ld) must be between 1582 and 19999."
 msgstr ""
 
-#: src/data/data-in.c:862
+#: src/data/data-in.c:876
 #, c-format
 msgid "Trailing garbage \"%.*s\" following date."
 msgstr ""
 
-#: src/data/data-in.c:878
+#: src/data/data-in.c:892
 msgid "Julian day must have exactly three digits."
 msgstr ""
 
-#: src/data/data-in.c:883
+#: src/data/data-in.c:897
 #, c-format
 msgid "Julian day (%ld) must be between 1 and 366."
 msgstr ""
 
-#: src/data/data-in.c:907
+#: src/data/data-in.c:921
 #, c-format
 msgid "Quarter (%ld) must be between 1 and 4."
 msgstr ""
 
-#: src/data/data-in.c:927
+#: src/data/data-in.c:941
 #, c-format
 msgid "Week (%ld) must be between 1 and 53."
 msgstr ""
 
-#: src/data/data-in.c:940
+#: src/data/data-in.c:954
 msgid "Delimiter expected between fields in time."
 msgstr ""
 
-#: src/data/data-in.c:960
+#: src/data/data-in.c:974
 #, c-format
 msgid "Minute (%ld) must be between 0 and 59."
 msgstr ""
 
-#: src/data/data-in.c:1000
+#: src/data/data-in.c:1014
 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:1152
 #, c-format
 msgid "`%c' expected in date field."
 msgstr ""
 
-#: src/data/data-in.c:1179
+#: src/data/data-in.c:1193
 #, c-format
 msgid "column %d"
 msgstr ""
 
-#: src/data/data-in.c:1181
+#: src/data/data-in.c:1195
 #, c-format
 msgid "columns %d-%d"
 msgstr ""
 
-#: src/data/data-in.c:1185
+#: src/data/data-in.c:1199
 #, c-format
 msgid "%s field) "
 msgstr ""
 
-#: src/data/data-out.c:446
+#: src/data/data-out.c:481
 #, c-format
 msgid "Weekday number %f is not between 1 and 7."
 msgstr ""
 
-#: src/data/data-out.c:467
+#: src/data/data-out.c:502
 #, c-format
 msgid "Month number %f is not between 1 and 12."
 msgstr ""
@@ -216,13 +256,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 +287,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 +359,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:734
+#: src/ui/gui/psppire-var-store.c:628 src/ui/gui/psppire.glade:2009
+#: 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:734
+#: src/ui/gui/psppire-var-store.c:621 src/ui/gui/psppire.glade:2084
+#: 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:1299
+#: src/data/sys-file-reader.c:1301 src/language/xforms/recode.c:493
+#: src/language/xforms/recode.c:494 src/language/xforms/recode.c:506
+#: src/language/xforms/recode.c:507
 #: 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
 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:1299
+#: src/data/sys-file-reader.c:1301 src/language/xforms/recode.c:493
+#: src/language/xforms/recode.c:494 src/language/xforms/recode.c:506
+#: src/language/xforms/recode.c:507
 #: 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
 msgid "string"
 msgstr ""
 
@@ -354,27 +394,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,183 +485,183 @@ 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 src/data/por-file-reader.c:460
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:502
+#: src/data/por-file-reader.c:523
 #, c-format
 msgid "%s: Not a portable file."
 msgstr ""
 
-#: src/data/por-file-reader.c:519
+#: src/data/por-file-reader.c:540
 #, 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:549
 #, c-format
 msgid "Bad date string length %zu."
 msgstr ""
 
-#: src/data/por-file-reader.c:530
+#: src/data/por-file-reader.c:551
 #, c-format
 msgid "Bad time string length %zu."
 msgstr ""
 
-#: src/data/por-file-reader.c:572
+#: src/data/por-file-reader.c:593
 #, 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:614
 #, 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:618
 #, 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:642
 msgid "Expected variable count record."
 msgstr ""
 
-#: src/data/por-file-reader.c:625
+#: src/data/por-file-reader.c:646
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:635
+#: src/data/por-file-reader.c:655
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr ""
 
-#: src/data/por-file-reader.c:650
+#: src/data/por-file-reader.c:670
 msgid "Expected variable record."
 msgstr ""
 
-#: src/data/por-file-reader.c:654
+#: src/data/por-file-reader.c:674
 #, c-format
 msgid "Invalid variable width %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:662
+#: src/data/por-file-reader.c:681
 #, c-format
 msgid "Invalid variable name `%s' in position %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:666
+#: src/data/por-file-reader.c:685 src/data/sys-file-reader.c:592
 #, c-format
 msgid "Bad width %d for variable %s."
 msgstr ""
 
-#: src/data/por-file-reader.c:681
+#: src/data/por-file-reader.c:700
 #, c-format
 msgid "Duplicate variable name %s in position %d."
 msgstr ""
 
-#: src/data/por-file-reader.c:682
+#: src/data/por-file-reader.c:701
 #, 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:750
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr ""
 
-#: src/data/por-file-reader.c:772
+#: src/data/por-file-reader.c:794
 #, c-format
 msgid "Unknown variable %s while parsing value labels."
 msgstr ""
 
-#: src/data/por-file-reader.c:775
+#: src/data/por-file-reader.c:797
 #, c-format
 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 +675,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
 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 "
@@ -650,361 +690,408 @@ msgstr ""
 msgid "Variable suffix too large."
 msgstr ""
 
+#: src/data/sys-file-reader.c:213
+#, c-format
+msgid "Recoded variable name duplicates an existing `%s' within system file."
+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:276 src/data/sys-file-writer.c:203
 msgid "system file"
 msgstr ""
 
-#: src/data/sys-file-reader.c:205
+#: src/data/sys-file-reader.c:283
 #, 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:322 tests/dissect-sysfile.c:136
 msgid "Misplaced type 4 record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:255
+#: src/data/sys-file-reader.c:333 tests/dissect-sysfile.c:147
 #, 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:374
 #, 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:414
 #, 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:479 src/data/sys-file-reader.c:489
+#: tests/dissect-sysfile.c:181 tests/dissect-sysfile.c:191
 msgid "This is not an SPSS system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:428
+#: src/data/sys-file-reader.c:511 tests/dissect-sysfile.c:204
 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:588
 #, 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:596
 #, c-format
 msgid "Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/data/sys-file-reader.c:512
+#: src/data/sys-file-reader.c:604 tests/dissect-sysfile.c:328
 msgid "Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/data/sys-file-reader.c:520
+#: src/data/sys-file-reader.c:612 tests/dissect-sysfile.c:337
 #, 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:631 tests/dissect-sysfile.c:355
 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:649 tests/dissect-sysfile.c:370
 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:681
 msgid "Missing string continuation record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:620
+#: src/data/sys-file-reader.c:715
 #, c-format
 msgid "Unknown variable format %<PRIu8>."
 msgstr ""
 
-#: src/data/sys-file-reader.c:638
+#: src/data/sys-file-reader.c:733
 #, 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:736
 msgid "print"
 msgstr ""
 
-#: src/data/sys-file-reader.c:641
+#: src/data/sys-file-reader.c:736
 msgid "write"
 msgstr ""
 
-#: src/data/sys-file-reader.c:645
+#: src/data/sys-file-reader.c:740
 msgid "Suppressing further invalid format warnings."
 msgstr ""
 
-#: src/data/sys-file-reader.c:663
+#: src/data/sys-file-reader.c:758
 msgid "Weighting variable must be numeric."
 msgstr ""
 
-#: src/data/sys-file-reader.c:677
+#: src/data/sys-file-reader.c:772
 msgid "Multiple type 6 (document) records."
 msgstr ""
 
-#: src/data/sys-file-reader.c:681
+#: src/data/sys-file-reader.c:776
 #, 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:784
 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:874
 #, 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:901 tests/dissect-sysfile.c:550
 #, 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:921
 #, 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:934
 msgid "little-endian"
 msgstr ""
 
-#: src/data/sys-file-reader.c:826
+#: src/data/sys-file-reader.c:934
 msgid "big-endian"
 msgstr ""
 
-#: src/data/sys-file-reader.c:827
+#: src/data/sys-file-reader.c:935
 #, 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:992 tests/dissect-sysfile.c:581
 #, c-format
 msgid "Bad size (%zu) or count (%zu) on extension 4."
 msgstr ""
 
-#: src/data/sys-file-reader.c:847
+#: src/data/sys-file-reader.c:996 src/data/sys-file-reader.c:1000
+#: src/data/sys-file-reader.c:1004 tests/dissect-sysfile.c:586
+#: tests/dissect-sysfile.c:591 tests/dissect-sysfile.c:596
 #, c-format
-msgid "File specifies unexpected value %g as SYSMIS."
+msgid "File specifies unexpected value %g as %s."
 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
-#, c-format
-msgid "File specifies unexpected value %g as LOWEST."
-msgstr ""
-
-#: src/data/sys-file-reader.c:867
+#: src/data/sys-file-reader.c:1021 tests/dissect-sysfile.c:611
 #, c-format
 msgid "Bad size %zu on extension 11."
 msgstr ""
 
-#: src/data/sys-file-reader.c:879
+#: src/data/sys-file-reader.c:1033 tests/dissect-sysfile.c:623
 #, 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:1054
 #, 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:1098
 #, 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:1108
 #, 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:1161
 #, 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:1171
 #, 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:1177
 #, c-format
 msgid "Very long string %s overflows dictionary."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1041
+#: src/data/sys-file-reader.c:1191
 #, 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:1237
 #, c-format
 msgid "Invalid number of labels: %d.  Ignoring labels."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1117
+#: src/data/sys-file-reader.c:1268 tests/dissect-sysfile.c:426
 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:1275
 #, 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:1286
 #, 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:1295
 #, 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:1329
 #, c-format
 msgid "Duplicate value label for %g on %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1177
+#: src/data/sys-file-reader.c:1332 src/data/sys-file-reader.c:1513
 #, c-format
 msgid "Duplicate value label for \"%.*s\" on %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1255
+#: src/data/sys-file-reader.c:1370
+#, c-format
+msgid "Error parsing attribute value %s[%d]"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1384
+#, c-format
+msgid "Attribute value %s[%d] is not quoted: %s"
+msgstr ""
+
+#: src/data/sys-file-reader.c:1447 tests/dissect-sysfile.c:762
+#, c-format
+msgid ""
+"Variable name length in long string value label record (%d) exceeds %d-byte "
+"limit."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1457
+#, c-format
+msgid "Ignoring long string value record for unknown variable %s."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1464
+#, c-format
+msgid "Ignoring long string value record for numeric variable %s."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1471
+#, 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:1493
+#, 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:1608
 msgid "File ends in partial case."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1263
+#: src/data/sys-file-reader.c:1616
 #, 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:1713 src/data/sys-file-reader.c:1749
 msgid "Compressed data is corrupt."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1483
+#: src/data/sys-file-reader.c:1836
 #, 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:1841
 #, 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:1909
 #, 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:1950
 #, c-format
 msgid "Variable map refers to unknown variable %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1663
+#: src/data/sys-file-reader.c:2058 tests/dissect-sysfile.c:959
 #, c-format
 msgid "System error: %s."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1665
+#: src/data/sys-file-reader.c:2060 tests/dissect-sysfile.c:961
 msgid "Unexpected end of file."
 msgstr ""
 
-#: src/data/sys-file-writer.c:163
+#: src/data/sys-file-writer.c:176
 #, 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:215
 #, 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:923
 #, 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/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/syntax-file.c:107
+#, c-format
+msgid "Reading `%s': %s."
+msgstr ""
+
+#: src/language/syntax-file.c:127
+#, c-format
+msgid "Closing `%s': %s."
+msgstr ""
+
+#: 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
@@ -1135,674 +1222,471 @@ msgstr ""
 msgid "Error executing command: %s."
 msgstr ""
 
-#: src/language/control/control-stack.c:27
+#: src/language/lexer/lexer.c:283
 #, c-format
-msgid "%s without %s."
+msgid "%s does not form a valid number."
 msgstr ""
 
-#: src/language/control/control-stack.c:55
+#: src/language/lexer/lexer.c:389
 #, c-format
-msgid "This command must appear inside %s...%s, without intermediate %s...%s."
+msgid "Bad character in input: `%s'."
 msgstr ""
 
-#: src/language/control/control-stack.c:72
+#: src/language/lexer/lexer.c:426
 #, c-format
-msgid "This command cannot appear outside %s...%s."
+msgid "Subcommand %s may only be specified once."
 msgstr ""
 
-#: src/language/control/do-if.c:177
-msgid "This command may not follow ELSE in DO IF...END IF."
+#: src/language/lexer/lexer.c:434
+#, c-format
+msgid "missing required subcommand %s"
 msgstr ""
 
-#: src/language/control/loop.c:214
-msgid "Only one index clause may be specified."
+#: src/language/lexer/lexer.c:463
+#, c-format
+msgid "Syntax error %s at %s."
 msgstr ""
 
-#: src/language/control/repeat.c:171
+#: src/language/lexer/lexer.c:466
 #, c-format
-msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
+msgid "Syntax error at %s."
 msgstr ""
 
-#: src/language/control/repeat.c:176
-#, c-format
-msgid "Dummy variable name \"%s\" is given twice."
+#: src/language/lexer/lexer.c:478 src/language/xforms/select-if.c:60
+#: src/language/stats/autorecode.c:154 src/language/data-io/print-space.c:73
+msgid "expecting end of command"
 msgstr ""
 
-#: src/language/control/repeat.c:222
+#: src/language/lexer/lexer.c:600 src/language/lexer/lexer.c:617
 #, c-format
-msgid ""
-"Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
-"were specified."
+msgid "expecting `%s'"
 msgstr ""
 
-#: src/language/control/repeat.c:334
-msgid "DO REPEAT may not nest in compatibility mode."
+#: src/language/lexer/lexer.c:631
+msgid "expecting string"
 msgstr ""
 
-#: src/language/control/repeat.c:436
-msgid "Ranges may only have integer bounds"
+#: src/language/lexer/lexer.c:645
+msgid "expecting integer"
 msgstr ""
 
-#: src/language/control/repeat.c:445
-#, c-format
-msgid "%g TO %g is an invalid range."
+#: src/language/lexer/lexer.c:658
+msgid "expecting number"
 msgstr ""
 
-#: src/language/control/repeat.c:480
-msgid "String expected."
+#: src/language/lexer/lexer.c:670
+msgid "expecting identifier"
 msgstr ""
 
-#: src/language/control/repeat.c:499
-msgid "No matching DO REPEAT."
+#: src/language/lexer/lexer.c:1064
+msgid "binary"
 msgstr ""
 
-#: src/language/control/temporary.c:46
-msgid ""
-"This command may only appear once between procedures and procedure-like "
-"commands."
+#: src/language/lexer/lexer.c:1069
+msgid "octal"
 msgstr ""
 
-#: src/language/data-io/data-list.c:128
-msgid "The END subcommand may only be used within INPUT PROGRAM."
+#: src/language/lexer/lexer.c:1074
+msgid "hex"
 msgstr ""
 
-#: src/language/data-io/data-list.c:134
-msgid "The END subcommand may only be specified once."
+#: 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/data-io/data-list.c:172
-msgid "Only one of FIXED, FREE, or LIST may be specified."
+#: src/language/lexer/lexer.c:1113
+#, c-format
+msgid "`%c' is not a valid %s digit."
 msgstr ""
 
-#: src/language/data-io/data-list.c:237
-msgid "The END subcommand may be used only with DATA LIST FIXED."
+#: src/language/lexer/lexer.c:1147
+msgid "Unterminated string constant."
 msgstr ""
 
-#: src/language/data-io/data-list.c:252
-msgid "At least one variable must be specified."
+#: src/language/lexer/lexer.c:1201
+msgid "Unexpected end of file in string concatenation."
 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
-#, c-format
-msgid "%s is a duplicate variable name."
+#: src/language/lexer/lexer.c:1209
+msgid "String expected following `+'."
 msgstr ""
 
-#: src/language/data-io/data-list.c:355
+#: src/language/lexer/lexer.c:1222
 #, c-format
-msgid "There is already a variable %s of a different type."
+msgid "String exceeds 255 characters in length (%zu characters)."
 msgstr ""
 
-#: src/language/data-io/data-list.c:362
-#, c-format
-msgid "There is already a string variable %s of a different width."
+#: src/language/lexer/format-parser.c:88
+msgid "expecting valid format specifier"
 msgstr ""
 
-#: src/language/data-io/data-list.c:370
+#: src/language/lexer/format-parser.c:107
+#: src/language/lexer/format-parser.c:126
+#: src/language/data-io/placement-parser.c:226
 #, c-format
-msgid "Cannot place variable %s on record %d when RECORDS=%d is specified."
+msgid "Unknown format type \"%s\"."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:455
-#: src/language/data-io/data-parser.c:464
-msgid "Quoted string extends beyond end of line."
+#: src/language/lexer/format-parser.c:121
+msgid "expecting format type"
 msgstr ""
 
-#: src/language/data-io/data-parser.c:519
+#: src/language/lexer/value-parser.c:60
 #, c-format
-msgid "Partial case of %d of %d records discarded."
+msgid ""
+"Low end of range (%g) is below high end (%g).  The range will be treated as "
+"reversed."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:565
+#: src/language/lexer/value-parser.c:68
 #, c-format
-msgid "Partial case discarded.  The first variable missing was %s."
+msgid "Ends of range are equal (%g)."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:602
-#, 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."
+#: src/language/lexer/value-parser.c:76
+msgid "LO or LOWEST must be part of a range."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:621
-msgid "Record ends in data not part of any field."
+#: src/language/lexer/value-parser.c:109
+msgid "System-missing value is not valid here."
 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/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
-msgid "Variable"
+#: src/language/lexer/value-parser.c:117
+msgid "expecting number or data string"
 msgstr ""
 
-#: src/language/data-io/data-parser.c:642 src/language/data-io/print.c:404
-msgid "Record"
+#: src/language/lexer/variable-parser.c:63
+msgid "expecting variable name"
 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
-msgid "Columns"
+#: src/language/lexer/variable-parser.c:73
+#, c-format
+msgid "%s is not a variable name."
 msgstr ""
 
-#: src/language/data-io/data-parser.c:644
-#: src/language/data-io/data-parser.c:683 src/language/data-io/print.c:406
-msgid "Format"
+#: 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 ""
 
-#: src/language/data-io/data-parser.c:663
+#: src/language/lexer/variable-parser.c:179
 #, c-format
-msgid "Reading %d record from %s."
-msgid_plural "Reading %d records from %s."
-msgstr[0] ""
-msgstr[1] ""
+msgid ""
+"%s is not a string variable.  It will not be included in the variable list."
+msgstr ""
 
-#: src/language/data-io/data-parser.c:699
+#: src/language/lexer/variable-parser.c:183
 #, c-format
-msgid "Reading free-form data from %s."
+msgid "Scratch variables (such as %s) are not allowed here."
 msgstr ""
 
-#. 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 ""
-
-#: src/language/data-io/data-reader.c:149
+#: src/language/lexer/variable-parser.c:187
 #, c-format
-msgid "Could not open \"%s\" for reading as a data file: %s."
-msgstr ""
-
-#: src/language/data-io/data-reader.c:190
 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
-#, c-format
-msgid "Error reading file %s: %s."
+"%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 ""
 
-#: src/language/data-io/data-reader.c:218
+#: src/language/lexer/variable-parser.c:193
 #, c-format
-msgid "Unexpected end of file reading %s."
+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 ""
 
-#: src/language/data-io/data-reader.c:227
+#: src/language/lexer/variable-parser.c:198
 #, c-format
-msgid "Unexpected end of file in partial record reading %s."
+msgid "Variable %s appears twice in variable list."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:287
+#: src/language/lexer/variable-parser.c:311
 #, c-format
-msgid "Corrupt block descriptor word at offset 0x%lx in %s."
+msgid "%s TO %s is not valid syntax since %s precedes %s in the dictionary."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:288
+#: src/language/lexer/variable-parser.c:319
 #, c-format
-msgid "Corrupt record descriptor word at offset 0x%lx in %s."
+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 ""
 
-#: src/language/data-io/data-reader.c:301
-#, c-format
-msgid "Corrupt record size at offset 0x%lx in %s."
+#: src/language/lexer/variable-parser.c:393
+msgid "incorrect use of TO convention"
 msgstr ""
 
-#: src/language/data-io/data-reader.c:443
-msgid "Record exceeds remaining block length."
+#: src/language/lexer/variable-parser.c:436
+msgid "Scratch variables not allowed here."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:517
-#, c-format
-msgid "Attempt to read beyond end-of-file on file %s."
+#: src/language/lexer/variable-parser.c:458
+msgid "Prefixes don't match in use of TO convention."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:520
-msgid "Attempt to read beyond END DATA."
+#: src/language/lexer/variable-parser.c:463
+msgid "Bad bounds in use of TO convention."
 msgstr ""
 
-#: src/language/data-io/data-reader.c:706
+#: src/language/xforms/compute.c:149 src/language/xforms/compute.c:203
+#, c-format
 msgid ""
-"This command is not valid here since the current input program does not "
-"access the inline file."
+"When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
+"s."
 msgstr ""
 
-#: src/language/data-io/data-writer.c:74
+#: src/language/xforms/compute.c:153 src/language/xforms/compute.c:210
 #, c-format
-msgid "An error occurred while opening \"%s\" for writing as a data file: %s."
+msgid ""
+"When executing COMPUTE: %g is not a valid value as an index into vector %s."
 msgstr ""
 
-#: src/language/data-io/data-writer.c:191
+#: src/language/xforms/compute.c:353
 #, c-format
-msgid "I/O error occurred writing data file \"%s\"."
+msgid "There is no vector named %s."
 msgstr ""
 
-#: 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."
+#: src/language/xforms/count.c:123
+msgid "Destination cannot be a string variable."
 msgstr ""
 
-#: src/language/data-io/file-handle.q:120
-msgid "RECFORM must be specified with MODE=360."
+#: src/language/xforms/sample.c:76
+msgid "The sampling factor must be between 0 and 1 exclusive."
 msgstr ""
 
-#: src/language/data-io/file-handle.q:131
+#: src/language/xforms/sample.c:96
 #, c-format
-msgid "The specified file mode requires LRECL.  Assuming %d-character records."
+msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
-#: src/language/data-io/file-handle.q:135
-#, c-format
+#: src/language/xforms/recode.c:248
 msgid ""
-"Record length (%ld) must be between 1 and %lu bytes.  Assuming %d-character "
-"records."
-msgstr ""
-
-#: src/language/data-io/file-handle.q:177
-msgid "file"
-msgstr ""
-
-#: src/language/data-io/file-handle.q:179
-msgid "inline file"
+"Inconsistent target variable types.  Target variables must be all numeric or "
+"all string."
 msgstr ""
 
-#: src/language/data-io/file-handle.q:205
-msgid "expecting a file name or handle name"
+#: src/language/xforms/recode.c:269
+msgid "CONVERT requires string input values and numeric output values."
 msgstr ""
 
-#: src/language/data-io/file-handle.q:225
-#, c-format
-msgid "Handle for %s not allowed here."
+#: src/language/xforms/recode.c:324
+msgid "THRU is not allowed with string variables."
 msgstr ""
 
-#: src/language/data-io/get.c:99
-msgid "expecting COMM or TAPE"
+#: src/language/xforms/recode.c:403
+msgid "expecting output value"
 msgstr ""
 
-#: src/language/data-io/get.c:272 src/language/data-io/get.c:286
-#: src/language/data-io/get.c:311
+#: src/language/xforms/recode.c:460
 #, 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"
+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/data-io/get.c:539
+#: src/language/xforms/recode.c:475
 #, 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."
+"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/data-io/get.c:572
+#: src/language/xforms/recode.c:491
 #, 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."
+msgid "INTO is required with %s input values and %s output values."
 msgstr ""
 
-#: src/language/data-io/get.c:585
+#: src/language/xforms/recode.c:504
 #, c-format
-msgid "Requested renaming duplicates variable name %s."
+msgid "Type mismatch.  Cannot store %s data in %s variable %s."
 msgstr ""
 
-#: src/language/data-io/get.c:615
-msgid "Cannot DROP all variables from dictionary."
+#: src/language/xforms/select-if.c:100
+msgid "Syntax error expecting OFF or BY.  Turning off case filtering."
 msgstr ""
 
-#: src/language/data-io/get.c:788
-msgid "Cannot specify the active file since no active file has been defined."
+#: src/language/xforms/select-if.c:115
+msgid "The filter variable must be numeric."
 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."
+#: src/language/xforms/select-if.c:121
+msgid "The filter variable may not be scratch."
 msgstr ""
 
-#: src/language/data-io/get.c:829
-msgid "Multiple IN subcommands for a single FILE or TABLE."
+#: src/language/control/control-stack.c:27
+#, c-format
+msgid "%s without %s."
 msgstr ""
 
-#: src/language/data-io/get.c:873
+#: src/language/control/control-stack.c:55
 #, c-format
-msgid "File %s lacks BY variable %s."
+msgid "This command must appear inside %s...%s, without intermediate %s...%s."
 msgstr ""
 
-#: src/language/data-io/get.c:876
+#: src/language/control/control-stack.c:72
 #, c-format
-msgid "Active file lacks BY variable %s."
+msgid "This command cannot appear outside %s...%s."
 msgstr ""
 
-#: src/language/data-io/get.c:946
-msgid "BY is required when TABLE is specified."
+#: src/language/control/do-if.c:177
+msgid "This command may not follow ELSE in DO IF...END IF."
 msgstr ""
 
-#: src/language/data-io/get.c:951
-msgid "BY is required when IN is specified."
+#: src/language/control/loop.c:214
+msgid "Only one index clause may be specified."
 msgstr ""
 
-#: src/language/data-io/get.c:1056
-#, c-format
+#: src/language/control/temporary.c:46
 msgid ""
-"Variable name %s specified on %s subcommand duplicates an existing variable "
-"name."
+"This command may only appear once between procedures and procedure-like "
+"commands."
 msgstr ""
 
-#: src/language/data-io/get.c:1303
+#: src/language/control/repeat.c:171
 #, c-format
-msgid ""
-"Variable %s in file %s (%s) has different type or width from the same "
-"variable in earlier file (%s)."
+msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
 msgstr ""
 
-#: src/language/data-io/get-data.c:62
+#: src/language/control/repeat.c:176
 #, c-format
-msgid "Unsupported TYPE %s"
+msgid "Dummy variable name \"%s\" is given twice."
 msgstr ""
 
-#: src/language/data-io/get-data.c:258
+#: src/language/control/repeat.c:222
 #, c-format
 msgid ""
-"%s is allowed only with %s arrangement, but %s arrangement was stated or "
-"implied earlier in this command."
+"Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d "
+"were specified."
 msgstr ""
 
-#: src/language/data-io/get-data.c:313
-msgid "expecting FIXED or DELIMITED"
+#: src/language/control/repeat.c:334
+msgid "DO REPEAT may not nest in compatibility mode."
 msgstr ""
 
-#: src/language/data-io/get-data.c:326
-msgid "Value of FIRSTCASE must be 1 or greater."
+#: src/language/control/repeat.c:436
+msgid "Ranges may only have integer bounds"
 msgstr ""
 
-#: src/language/data-io/get-data.c:351
-msgid "expecting LINE or VARIABLES"
+#: src/language/control/repeat.c:445
+#, c-format
+msgid "%g TO %g is an invalid range."
 msgstr ""
 
-#: src/language/data-io/get-data.c:364
-msgid "Value of FIXCASE must be at least 1."
+#: src/language/control/repeat.c:480
+msgid "String expected."
 msgstr ""
 
-#: src/language/data-io/get-data.c:384
-msgid "Value of FIRST must be at least 1."
+#: src/language/control/repeat.c:499
+msgid "No matching DO REPEAT."
 msgstr ""
 
-#: src/language/data-io/get-data.c:396
-msgid "Value of PERCENT must be between 1 and 100."
+#: src/language/dictionary/attributes.c:108
+msgid "Attribute array index must be between 1 and 65535."
 msgstr ""
 
-#: src/language/data-io/get-data.c:445
-msgid ""
-"In compatible syntax mode, the QUALIFIER string must contain exactly one "
-"character."
+#: src/language/dictionary/attributes.c:189
+msgid "expecting ATTRIBUTE= or DELETE="
 msgstr ""
 
-#: src/language/data-io/get-data.c:460
-msgid "expecting VARIABLES"
+#: 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/data-io/get-data.c:482
-#: src/language/data-io/placement-parser.c:378
-#, c-format
+#: src/language/dictionary/apply-dictionary.c:115
+msgid "No matching variables found between the source and target files."
+msgstr ""
+
+#: src/language/dictionary/delete-variables.c:40
 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."
+"DELETE VARIABLES may not be used after TEMPORARY.  Temporary transformations "
+"will be made permanent."
 msgstr ""
 
-#: src/language/data-io/get-data.c:491
-#, c-format
+#: src/language/dictionary/delete-variables.c:48
 msgid ""
-"The record number specified, %ld, exceeds the number of records per case "
-"specified on FIXCASE, %d."
+"DELETE VARIABLES may not be used to delete all variables from the active "
+"file dictionary.  Use NEW FILE instead."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:129
-msgid "Unexpected end-of-file within INPUT PROGRAM."
+#: src/language/dictionary/formats.c:90
+msgid "`(' expected after variable list."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:142
-msgid "Input program did not create any variables."
+#: src/language/dictionary/formats.c:100 src/language/dictionary/numeric.c:74
+msgid "`)' expected after output format."
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:287
-msgid "COLUMN subcommand multiply specified."
+#: src/language/dictionary/missing-values.c:56
+#: src/language/stats/aggregate.c:458
+msgid "expecting `('"
 msgstr ""
 
-#: src/language/data-io/inpt-pgm.c:337
+#: src/language/dictionary/missing-values.c:72
+#, c-format
 msgid ""
-"REREAD: Column numbers must be positive finite numbers.  Column set to 1."
+"Cannot mix numeric variables (e.g. %s) and string variables (e.g. %s) within "
+"a single list."
 msgstr ""
 
-#: src/language/data-io/list.q:157 src/language/stats/descriptives.c:362
-msgid "No variables specified."
+#: src/language/dictionary/missing-values.c:116
+#, c-format
+msgid "Truncating missing value to maximum acceptable length (%d bytes)."
 msgstr ""
 
-#: src/language/data-io/list.q:165
+#: src/language/dictionary/missing-values.c:138
 #, c-format
-msgid ""
-"The first case (%ld) specified precedes the last case (%ld) specified.  The "
-"values will be swapped."
+msgid "Missing values provided are too long to assign to variable of width %d."
 msgstr ""
 
-#: src/language/data-io/list.q:173
-#, c-format
+#: src/language/dictionary/modify-variables.c:92
 msgid ""
-"The first case (%ld) to list is less than 1.  The value is being reset to 1."
+"MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
+"be made permanent."
 msgstr ""
 
-#: 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."
+#: src/language/dictionary/modify-variables.c:114
+msgid "REORDER subcommand may be given at most once."
 msgstr ""
 
-#: 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."
+#: src/language/dictionary/modify-variables.c:137
+msgid "Cannot specify ALL after specifying a set of variables."
 msgstr ""
 
-#: src/language/data-io/list.q:211
-msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
+#: src/language/dictionary/modify-variables.c:147
+msgid "`(' expected on REORDER subcommand."
 msgstr ""
 
-#: src/language/data-io/list.q:467
-msgid "Line"
+#: src/language/dictionary/modify-variables.c:159
+msgid "`)' expected following variable names on REORDER subcommand."
 msgstr ""
 
-#: src/language/data-io/placement-parser.c:87
-#, c-format
-msgid ""
-"Number of variables specified (%zu) differs from number of variable formats "
-"(%zu)."
+#: src/language/dictionary/modify-variables.c:177
+msgid "RENAME subcommand may be given at most once."
 msgstr ""
 
-#: src/language/data-io/placement-parser.c:97
-msgid ""
-"SPSS-like or Fortran-like format specification expected after variable names."
+#: src/language/dictionary/modify-variables.c:190
+msgid "`(' expected on RENAME subcommand."
 msgstr ""
 
-#: src/language/data-io/placement-parser.c:119
-#, c-format
-msgid "The %d columns %d-%d can't be evenly divided into %zu fields."
+#: src/language/dictionary/modify-variables.c:199
+msgid ""
+"`=' expected between lists of new and old variable names on RENAME "
+"subcommand."
 msgstr ""
 
-#: src/language/data-io/placement-parser.c:226
-#: src/language/lexer/format-parser.c:107
-#: src/language/lexer/format-parser.c:126
+#: src/language/dictionary/modify-variables.c:208
+#: src/language/dictionary/rename-variables.c:76
 #, c-format
-msgid "Unknown format type \"%s\"."
+msgid ""
+"Differing number of variables in old name list (%zu) and in new name list (%"
+"zu)."
 msgstr ""
 
-#: src/language/data-io/placement-parser.c:305
-msgid "Column positions for fields must be positive."
-msgstr ""
-
-#: src/language/data-io/placement-parser.c:307
-msgid "Column positions for fields must not be negative."
-msgstr ""
-
-#: src/language/data-io/placement-parser.c:344
-msgid "The ending column for a field must be greater than the starting column."
-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
-#, 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
-#, 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/stats/autorecode.c:154 src/language/xforms/select-if.c:60
-msgid "expecting end of command"
-msgstr ""
-
-#: src/language/data-io/print-space.c:116
-msgid "The expression on PRINT SPACE evaluated to the system-missing value."
-msgstr ""
-
-#: src/language/data-io/print-space.c:119
-#, c-format
-msgid "The expression on PRINT SPACE evaluated to %g."
-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:99
-#, c-format
-msgid "Cannot add value labels from source file to long string variable %s."
-msgstr ""
-
-#: src/language/dictionary/apply-dictionary.c:113
-#, c-format
-msgid ""
-"Cannot apply missing values from source file to long string variable %s."
-msgstr ""
-
-#: src/language/dictionary/apply-dictionary.c:126
-msgid "No matching variables found between the source and target files."
-msgstr ""
-
-#: src/language/dictionary/delete-variables.c:40
-msgid ""
-"DELETE VARIABLES may not be used after TEMPORARY.  Temporary transformations "
-"will be made permanent."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/dictionary/formats.c:90
-msgid "`(' expected after variable list."
-msgstr ""
-
-#: src/language/dictionary/formats.c:100 src/language/dictionary/numeric.c:70
-msgid "`)' expected after output format."
-msgstr ""
-
-#: src/language/dictionary/missing-values.c:56
-#: src/language/stats/aggregate.c:451
-msgid "expecting `('"
-msgstr ""
-
-#: 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 ""
-
-#: src/language/dictionary/missing-values.c:117
-#, c-format
-msgid "Truncating missing value to short string length (%d characters)."
-msgstr ""
-
-#: src/language/dictionary/missing-values.c:139
-#, c-format
-msgid "Missing values provided are too long to assign to variable of width %d."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:92
-msgid ""
-"MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
-"be made permanent."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:114
-msgid "REORDER subcommand may be given at most once."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:137
-msgid "Cannot specify ALL after specifying a set of variables."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:147
-msgid "`(' expected on REORDER subcommand."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:159
-msgid "`)' expected following variable names on REORDER subcommand."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:177
-msgid "RENAME subcommand may be given at most once."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:190
-msgid "`(' expected on RENAME subcommand."
-msgstr ""
-
-#: src/language/dictionary/modify-variables.c:199
-msgid ""
-"`=' expected between lists of new and old variable names on RENAME "
-"subcommand."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/dictionary/modify-variables.c:219
-msgid "`)' expected after variable lists on RENAME subcommand."
+#: src/language/dictionary/modify-variables.c:219
+msgid "`)' expected after variable lists on RENAME subcommand."
 msgstr ""
 
 #: src/language/dictionary/modify-variables.c:233
@@ -1820,7 +1704,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 +1714,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,1132 +1753,549 @@ 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
 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/psppire-var-sheet.c:537 src/ui/gui/psppire-var-store.c:836
+#: src/ui/gui/crosstabs.glade:275 src/ui/gui/psppire.glade:1974
 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
-#, c-format
-msgid "Format: %s"
-msgstr ""
-
 #: src/language/dictionary/sys-file-info.c:485
-#, c-format
-msgid "Print Format: %s"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:488
-#, c-format
-msgid "Write Format: %s"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:494
-#, c-format
-msgid "Measure: %s"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:495
-#: src/ui/gui/psppire-var-sheet.c:123
-msgid "Nominal"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:496
-#: src/ui/gui/psppire-var-sheet.c:124
-msgid "Ordinal"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:497
-#: src/ui/gui/psppire-var-sheet.c:125
-msgid "Scale"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:500
-#, c-format
-msgid "Display Alignment: %s"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:501
-#: src/ui/gui/psppire-var-sheet.c:116
-msgid "Left"
+msgid "Attribute"
 msgstr ""
 
-#: src/language/dictionary/sys-file-info.c:502
-#: src/ui/gui/psppire-var-sheet.c:118
-msgid "Center"
-msgstr "Centre"
-
-#: src/language/dictionary/sys-file-info.c:503
-#: src/ui/gui/psppire-var-sheet.c:117
-msgid "Right"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:506
-#, c-format
-msgid "Display Width: %d"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:517
-msgid "Missing Values: "
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:611
-msgid "No vectors defined."
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:632
-msgid "Vector"
-msgstr ""
-
-#: src/language/dictionary/sys-file-info.c:635
-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
-msgid "Truncating value label to 60 characters."
-msgstr ""
-
-#: src/language/dictionary/variable-display.c:119
-msgid "Variable display width must be a positive integer."
-msgstr ""
-
-#: src/language/dictionary/variable-label.c:51
-msgid "String expected for variable label."
-msgstr ""
-
-#: src/language/dictionary/variable-label.c:59
-msgid "Truncating variable label to 255 characters."
-msgstr ""
-
-#: src/language/dictionary/vector.c:64
-#, c-format
-msgid "A vector named %s already exists."
-msgstr ""
-
-#: src/language/dictionary/vector.c:72
+#: src/language/dictionary/sys-file-info.c:543
 #, c-format
-msgid "Vector name %s is given twice."
-msgstr ""
-
-#: src/language/dictionary/vector.c:96
-msgid "A slash must separate each vector specification in VECTOR's long form."
-msgstr ""
-
-#: src/language/dictionary/vector.c:129
-msgid "Vectors must have at least one element."
-msgstr ""
-
-#: src/language/dictionary/vector.c:150
-msgid "expecting vector length"
-msgstr ""
-
-#: src/language/dictionary/vector.c:166
-#, c-format
-msgid "%s is too long for a variable name."
-msgstr ""
-
-#: src/language/dictionary/vector.c:171
-#, c-format
-msgid "%s is an existing variable name."
-msgstr ""
-
-#: src/language/dictionary/weight.c:49
-msgid "The weighting variable must be numeric."
-msgstr ""
-
-#: src/language/dictionary/weight.c:54
-msgid "The weighting variable may not be scratch."
-msgstr ""
-
-#: src/language/expressions/evaluate.c:154
-msgid "expecting number or string"
-msgstr ""
-
-#: src/language/expressions/evaluate.c:168
-#, c-format
-msgid "Duplicate variable name %s."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/expressions/helpers.c:73
-msgid ""
-"The week argument to DATE.WKYR is not an integer.  The result will be system-"
-"missing."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/expressions/helpers.c:101
-msgid ""
-"The day argument to DATE.YRDAY is not an integer.  The result will be system-"
-"missing."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/expressions/helpers.c:129
-msgid ""
-"The year argument to YRMODA is greater than 47516.  The result will be "
-"system-missing."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/expressions/helpers.c:332
-msgid ""
-"Invalid DATESUM method.  Valid choices are \"closest\" and \"rollover\"."
-msgstr ""
-
-#: src/language/expressions/parse.c:259
-#, c-format
-msgid ""
-"Type mismatch: expression has %s type, but a numeric value is required here."
-msgstr ""
-
-#: src/language/expressions/parse.c:271
-#, c-format
-msgid ""
-"Type mismatch: expression has %s type, but a string value is required here."
-msgstr ""
-
-#: src/language/expressions/parse.c:427
-#, c-format
-msgid "Type mismatch while applying %s operator: cannot convert %s to %s."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/expressions/parse.c:857
-#, c-format
-msgid "Unknown identifier %s."
-msgstr ""
-
-#: src/language/expressions/parse.c:885 src/language/stats/aggregate.c:509
-msgid "expecting `)'"
-msgstr ""
-
-#: src/language/expressions/parse.c:892
-msgid "in expression"
-msgstr ""
-
-#: src/language/expressions/parse.c:1073
-#, c-format
-msgid "%s must have at least %d arguments in list."
-msgstr ""
-
-#: src/language/expressions/parse.c:1082
-#, c-format
-msgid "%s must have even number of arguments in list."
-msgstr ""
-
-#: src/language/expressions/parse.c:1085
-#, c-format
-msgid "%s must have multiple of %d arguments in list."
-msgstr ""
-
-#: src/language/expressions/parse.c:1095
-#, c-format
-msgid "%s function does not accept a minimum valid argument count."
-msgstr ""
-
-#: src/language/expressions/parse.c:1104
-#, c-format
-msgid "%s requires at least %d valid arguments in list."
-msgstr ""
-
-#: 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 ""
-
-#: src/language/expressions/parse.c:1164
-#, c-format
-msgid "Type mismatch invoking %s as "
-msgstr ""
-
-#: src/language/expressions/parse.c:1169
-msgid "Function invocation "
-msgstr ""
-
-#: src/language/expressions/parse.c:1171
-msgid " does not match any known function.  Candidates are:"
-msgstr ""
-
-#: src/language/expressions/parse.c:1201
-#, c-format
-msgid "No function or vector named %s."
-msgstr ""
-
-#: src/language/expressions/parse.c:1244
-#, c-format
-msgid "expecting `,' or `)' invoking %s function"
-msgstr ""
-
-#: src/language/expressions/parse.c:1264
-#, c-format
-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."
-msgstr ""
-
-#: src/language/lexer/format-parser.c:88
-msgid "expecting valid format specifier"
-msgstr ""
-
-#: src/language/lexer/format-parser.c:121
-msgid "expecting format type"
-msgstr ""
-
-#: src/language/lexer/lexer.c:282
-#, 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
-#, c-format
-msgid "Bad character in input: `\\%o'."
-msgstr ""
-
-#: src/language/lexer/lexer.c:424
-#, c-format
-msgid "Subcommand %s may only be specified once."
-msgstr ""
-
-#: src/language/lexer/lexer.c:432
-#, c-format
-msgid "missing required subcommand %s"
-msgstr ""
-
-#: src/language/lexer/lexer.c:461
-#, c-format
-msgid "Syntax error %s at %s."
-msgstr ""
-
-#: src/language/lexer/lexer.c:464
-#, c-format
-msgid "Syntax error at %s."
-msgstr ""
-
-#: src/language/lexer/lexer.c:598 src/language/lexer/lexer.c:615
-#, c-format
-msgid "expecting `%s'"
-msgstr ""
-
-#: src/language/lexer/lexer.c:656
-msgid "expecting number"
-msgstr ""
-
-#: src/language/lexer/lexer.c:668
-msgid "expecting identifier"
-msgstr ""
-
-#: src/language/lexer/lexer.c:1062
-msgid "binary"
-msgstr ""
-
-#: src/language/lexer/lexer.c:1067
-msgid "octal"
-msgstr ""
-
-#: src/language/lexer/lexer.c:1072
-msgid "hex"
-msgstr ""
-
-#: src/language/lexer/lexer.c:1082
-#, c-format
-msgid "String of %s digits has %zu characters, which is not a multiple of %d."
-msgstr ""
-
-#: src/language/lexer/lexer.c:1111
-#, c-format
-msgid "`%c' is not a valid %s digit."
-msgstr ""
-
-#: src/language/lexer/lexer.c:1145
-msgid "Unterminated string constant."
-msgstr ""
-
-#: src/language/lexer/lexer.c:1199
-msgid "Unexpected end of file in string concatenation."
-msgstr ""
-
-#: src/language/lexer/lexer.c:1207
-msgid "String expected following `+'."
-msgstr ""
-
-#: src/language/lexer/lexer.c:1220
-#, c-format
-msgid "String exceeds 255 characters in length (%zu characters)."
-msgstr ""
-
-#: src/language/lexer/range-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
-#, c-format
-msgid "Ends of range are equal (%g)."
-msgstr ""
-
-#: src/language/lexer/range-parser.c:76
-msgid "LO or LOWEST must be part of a range."
-msgstr ""
-
-#: src/language/lexer/range-parser.c:108
-msgid "System-missing value is not valid here."
-msgstr ""
-
-#: src/language/lexer/range-parser.c:116
-msgid "expecting number or data string"
-msgstr ""
-
-#: src/language/lexer/variable-parser.c:63
-msgid "expecting variable name"
-msgstr ""
-
-#: src/language/lexer/variable-parser.c:73
-#, c-format
-msgid "%s is not a variable name."
-msgstr ""
-
-#: 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 ""
-
-#: 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 ""
-
-#: src/language/lexer/variable-parser.c:183
-#, c-format
-msgid "Scratch variables (such as %s) are not allowed here."
-msgstr ""
-
-#: 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 ""
-
-#: 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 ""
-
-#: src/language/lexer/variable-parser.c:198
-#, c-format
-msgid "Variable %s appears twice in variable list."
-msgstr ""
-
-#: 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 ""
-
-#: 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 ""
-
-#: src/language/lexer/variable-parser.c:393
-msgid "incorrect use of TO convention"
-msgstr ""
-
-#: src/language/lexer/variable-parser.c:436
-msgid "Scratch variables not allowed here."
-msgstr ""
-
-#: src/language/lexer/variable-parser.c:458
-msgid "Prefixes don't match in use of TO convention."
-msgstr ""
-
-#: src/language/lexer/variable-parser.c:463
-msgid "Bad bounds in use of TO convention."
-msgstr ""
-
-#: src/language/stats/aggregate.c:209
-msgid "while expecting COLUMNWISE"
-msgstr ""
-
-#: src/language/stats/aggregate.c:240
-msgid "expecting BREAK"
-msgstr ""
-
-#: src/language/stats/aggregate.c:245
-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
-msgid "expecting aggregation function"
-msgstr ""
-
-#: src/language/stats/aggregate.c:434
-#, c-format
-msgid "Unknown aggregation function %s."
-msgstr ""
-
-#: src/language/stats/aggregate.c:490
-#, c-format
-msgid "Missing argument %zu to %s."
-msgstr ""
-
-#: src/language/stats/aggregate.c:499
-#, c-format
-msgid "Arguments to %s must be of same type as source variables."
-msgstr ""
-
-#: src/language/stats/aggregate.c:521
-#, c-format
-msgid ""
-"Number of source variables (%zu) does not match number of target variables (%"
-"zu)."
-msgstr ""
-
-#: src/language/stats/aggregate.c:537
-#, 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
-#, c-format
-msgid ""
-"Variable name %s is not unique within the aggregate file dictionary, which "
-"contains the aggregate variables and the break variables."
-msgstr ""
-
-#: src/language/stats/autorecode.c:136
-#, c-format
-msgid "Source variable count (%zu) does not match target variable count (%zu)."
-msgstr ""
-
-#: src/language/stats/autorecode.c:164
-#, c-format
-msgid "Target variable %s duplicates existing variable %s."
-msgstr ""
-
-#: src/language/stats/autorecode.c:171
-#, c-format
-msgid "Duplicate variable name %s among target variables."
-msgstr ""
-
-#: src/language/stats/binomial.c:132
-#, c-format
-msgid "Variable %s is not dichotomous"
-msgstr ""
-
-#: src/language/stats/binomial.c:177
-msgid "Binomial Test"
-msgstr ""
-
-#: src/language/stats/binomial.c:201
-msgid "Group1"
-msgstr ""
-
-#: src/language/stats/binomial.c:202
-msgid "Group2"
+msgid "Format: %s"
 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
-msgid "Total"
+#: src/language/dictionary/sys-file-info.c:550
+#, c-format
+msgid "Print Format: %s"
 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
-msgid "Category"
+#: src/language/dictionary/sys-file-info.c:554
+#, c-format
+msgid "Write Format: %s"
 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
-msgid "N"
+#: src/language/dictionary/sys-file-info.c:567
+#, c-format
+msgid "Measure: %s"
 msgstr ""
 
-#: src/language/stats/binomial.c:237
-msgid "Observed Prop."
+#: src/language/dictionary/sys-file-info.c:568
+#: src/ui/gui/psppire-var-sheet.c:111
+msgid "Nominal"
 msgstr ""
 
-#: src/language/stats/binomial.c:238
-msgid "Test Prop."
+#: src/language/dictionary/sys-file-info.c:569
+#: src/ui/gui/psppire-var-sheet.c:112
+msgid "Ordinal"
 msgstr ""
 
-#: src/language/stats/binomial.c:241
-#, c-format
-msgid "Exact Sig. (%d-tailed)"
+#: src/language/dictionary/sys-file-info.c:570
+#: src/ui/gui/psppire-var-sheet.c:113
+msgid "Scale"
 msgstr ""
 
-#: src/language/stats/chisquare.c:193
+#: src/language/dictionary/sys-file-info.c:573
 #, c-format
-msgid ""
-"CHISQUARE test specified %d expected values, but %d distinct values were "
-"encountered in variable %s."
+msgid "Display Alignment: %s"
 msgstr ""
 
-#: src/language/stats/chisquare.c:207 src/language/stats/chisquare.c:247
-msgid "Observed N"
+#: src/language/dictionary/sys-file-info.c:574
+#: src/ui/gui/psppire-var-sheet.c:104
+msgid "Left"
 msgstr ""
 
-#: src/language/stats/chisquare.c:208 src/language/stats/chisquare.c:248
-msgid "Expected N"
-msgstr ""
+#: src/language/dictionary/sys-file-info.c:575
+#: src/ui/gui/psppire-var-sheet.c:106
+msgid "Center"
+msgstr "Centre"
 
-#: src/language/stats/chisquare.c:209 src/language/stats/chisquare.c:249
-#: src/language/stats/regression.q:308 src/ui/gui/crosstabs-dialog.c:61
-msgid "Residual"
+#: src/language/dictionary/sys-file-info.c:576
+#: src/ui/gui/psppire-var-sheet.c:105
+msgid "Right"
 msgstr ""
 
-#: src/language/stats/chisquare.c:242
-msgid "Frequencies"
+#: src/language/dictionary/sys-file-info.c:579
+#, c-format
+msgid "Display Width: %d"
 msgstr ""
 
-#: src/language/stats/chisquare.c:297
-msgid "Test Statistics"
+#: src/language/dictionary/sys-file-info.c:593
+msgid "Missing Values: "
 msgstr ""
 
-#: src/language/stats/chisquare.c:311
-msgid "Chi-Square"
+#: src/language/dictionary/sys-file-info.c:702
+msgid "No vectors defined."
 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
-msgid "df"
+#: src/language/dictionary/sys-file-info.c:723
+msgid "Vector"
 msgstr ""
 
-#: src/language/stats/chisquare.c:313
-msgid "Asymp. Sig."
+#: src/language/dictionary/sys-file-info.c:726
+msgid "Print Format"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:277
-msgid ""
-"Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
+#: src/language/dictionary/value-labels.c:150
+msgid "Truncating value label to 60 characters."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:287
-msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
+#: src/language/dictionary/variable-label.c:51
+msgid "String expected for variable label."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:363
-msgid "Too many cross-tabulation variables or dimensions."
+#: src/language/dictionary/variable-label.c:59
+msgid "Truncating variable label to 255 characters."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:373
-msgid "expecting BY"
+#: src/language/dictionary/vector.c:64
+#, c-format
+msgid "A vector named %s already exists."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:440
-msgid "VARIABLES must be specified before TABLES."
+#: src/language/dictionary/vector.c:72
+#, c-format
+msgid "Vector name %s is given twice."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:478
-#, c-format
-msgid "Maximum value (%ld) less than minimum value (%ld)."
+#: src/language/dictionary/vector.c:96
+msgid "A slash must separate each vector specification in VECTOR's long form."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:857
-msgid "Summary."
+#: src/language/dictionary/vector.c:129
+msgid "Vectors must have at least one element."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:859 src/language/stats/examine.q:981
-msgid "Cases"
+#: src/language/dictionary/vector.c:150
+msgid "expecting vector length"
 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
-msgid "Valid"
+#: src/language/dictionary/vector.c:166
+#, c-format
+msgid "%s is too long for a variable name."
 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
-msgid "Missing"
+#: src/language/dictionary/vector.c:171
+#, c-format
+msgid "%s is an existing variable name."
 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
-msgid "Percent"
+#: src/language/dictionary/variable-display.c:120
+msgid "Variable display width must be a positive integer."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1114
-msgid "count"
+#: src/language/dictionary/weight.c:49
+msgid "The weighting variable must be numeric."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1115
-msgid "row %"
+#: src/language/dictionary/weight.c:54
+msgid "The weighting variable may not be scratch."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1116
-msgid "column %"
+#: src/language/tests/float-format.c:124
+#, c-format
+msgid "%zu-byte string needed but %zu-byte string supplied."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1117
-msgid "total %"
+#: src/language/tests/float-format.c:136
+msgid "Hexadecimal floating constant too long."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1118
-msgid "expected"
+#: 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 ""
 
-#: src/language/stats/crosstabs.q:1119
-msgid "residual"
+#: src/language/tests/float-format.c:247
+msgid "Too many values in single command."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1120
-msgid "std. resid."
+#: src/language/tests/moments-test.c:47
+msgid "expecting weight value"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1121
-msgid "adj. resid."
+#: src/language/utilities/cd.c:41
+#, c-format
+msgid "Cannot change directory to %s:  %s "
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1151
-msgid "Chi-square tests."
+#: src/language/utilities/date.c:32
+msgid "Only USE ALL is currently implemented."
 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
-msgid "Statistic"
+#: src/language/utilities/title.c:68
+#, c-format
+msgid "%s: `.' expected after string."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1158
-msgid "Asymp. Sig. (2-sided)"
+#: src/language/utilities/title.c:108
+#, c-format
+msgid "   (Entered %s)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1160
-msgid "Exact. Sig. (2-sided)"
+#: src/language/utilities/include.c:92
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1162
-msgid "Exact. Sig. (1-sided)"
+#: src/language/utilities/include.c:109
+msgid "Expecting YES or NO after CD."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1177
-msgid "Symmetric measures."
+#: src/language/utilities/include.c:126
+msgid "Expecting CONTINUE or STOP after ERROR."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1183 src/language/stats/crosstabs.q:1225
-msgid "Asymp. Std. Error"
+#: src/language/utilities/include.c:133
+#, c-format
+msgid "Unexpected token: `%s'."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1184 src/language/stats/crosstabs.q:1226
-msgid "Approx. T"
+#: src/language/utilities/include.c:178
+msgid "expecting file name"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1185 src/language/stats/crosstabs.q:1227
-msgid "Approx. Sig."
+#: src/language/utilities/include.c:190
+#, c-format
+msgid "Can't find `%s' in include file search path."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1196
-msgid "Risk estimate."
+#: src/language/utilities/include.c:198
+#, c-format
+msgid "Unable to open `%s': %s."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1200
+#: src/language/utilities/permissions.c:73
 #, c-format
-msgid "95%% Confidence Interval"
+msgid "Expecting %s or %s."
 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
-msgid "Lower"
+#: src/language/utilities/permissions.c:106
+#, c-format
+msgid "Cannot stat %s: %s"
 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
-msgid "Upper"
+#: src/language/utilities/permissions.c:119
+#, c-format
+msgid "Cannot change mode of %s: %s"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1218
-msgid "Directional measures."
+#: src/language/stats/aggregate.c:219
+msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1223 src/ui/gui/psppire.glade:2223
-#: src/ui/gui/psppire-var-sheet.c:101
-msgid "Type"
+#: src/language/stats/aggregate.c:247
+msgid "expecting BREAK"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1977
-msgid "Pearson Chi-Square"
+#: 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/crosstabs.q:1978
-msgid "Likelihood Ratio"
+#: src/language/stats/aggregate.c:423
+msgid "expecting aggregation function"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1979
-msgid "Fisher's Exact Test"
+#: src/language/stats/aggregate.c:441
+#, c-format
+msgid "Unknown aggregation function %s."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1980
-msgid "Continuity Correction"
+#: src/language/stats/aggregate.c:497
+#, c-format
+msgid "Missing argument %zu to %s."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:1981
-msgid "Linear-by-Linear Association"
+#: src/language/stats/aggregate.c:506
+#, c-format
+msgid "Arguments to %s must be of same type as source variables."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2018 src/language/stats/crosstabs.q:2088
-#: src/language/stats/crosstabs.q:2147
-msgid "N of Valid Cases"
+#: src/language/stats/aggregate.c:516 src/language/expressions/parse.c:885
+msgid "expecting `)'"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2034 src/language/stats/crosstabs.q:2163
-msgid "Nominal by Nominal"
+#: 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/crosstabs.q:2035 src/language/stats/crosstabs.q:2164
-msgid "Ordinal by Ordinal"
+#: 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/crosstabs.q:2036
-msgid "Interval by Interval"
+#: 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 ""
 
-#: src/language/stats/crosstabs.q:2037
-msgid "Measure of Agreement"
+#: src/language/stats/autorecode.c:136
+#, c-format
+msgid "Source variable count (%zu) does not match target variable count (%zu)."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2042 src/ui/gui/crosstabs-dialog.c:41
-msgid "Phi"
+#: src/language/stats/autorecode.c:164
+#, c-format
+msgid "Target variable %s duplicates existing variable %s."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2043
-msgid "Cramer's V"
+#: src/language/stats/autorecode.c:171
+#, c-format
+msgid "Duplicate variable name %s among target variables."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2044
-msgid "Contingency Coefficient"
+#: src/language/stats/binomial.c:141
+#, c-format
+msgid "Variable %s is not dichotomous"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2045
-msgid "Kendall's tau-b"
+#: src/language/stats/binomial.c:194
+msgid "Binomial Test"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2046
-msgid "Kendall's tau-c"
+#: src/language/stats/binomial.c:224
+msgid "Group1"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2047 src/ui/gui/crosstabs-dialog.c:48
-msgid "Gamma"
+#: src/language/stats/binomial.c:225
+msgid "Group2"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2048
-msgid "Spearman Correlation"
+#: src/language/stats/binomial.c:226 src/language/stats/chisquare.c:202
+#: src/language/stats/chisquare.c:262 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/crosstabs.q:2049
-msgid "Pearson's R"
+#: src/language/stats/binomial.c:259 src/language/stats/chisquare.c:225
+msgid "Category"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2050 src/ui/gui/crosstabs-dialog.c:50
-msgid "Kappa"
+#: src/language/stats/binomial.c:260 src/language/stats/npar-summary.c:123
+#: src/language/stats/sign.c:74 src/language/stats/wilcoxon.c:245
+msgid "N"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2120
-#, c-format
-msgid "Odds Ratio for %s (%g / %g)"
+#: src/language/stats/binomial.c:261
+msgid "Observed Prop."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2123
-#, c-format
-msgid "Odds Ratio for %s (%.*s / %.*s)"
+#: src/language/stats/binomial.c:262
+msgid "Test Prop."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2131
+#: src/language/stats/binomial.c:265
 #, c-format
-msgid "For cohort %s = %g"
+msgid "Exact Sig. (%d-tailed)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2134
+#: src/language/stats/chisquare.c:172
 #, c-format
-msgid "For cohort %s = %.*s"
+msgid ""
+"CHISQUARE test specified %d expected values, but %d distinct values were "
+"encountered in variable %s."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2165
-msgid "Nominal by Interval"
+#: src/language/stats/chisquare.c:186 src/language/stats/chisquare.c:226
+msgid "Observed N"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2170 src/ui/gui/crosstabs-dialog.c:43
-msgid "Lambda"
+#: src/language/stats/chisquare.c:187 src/language/stats/chisquare.c:227
+msgid "Expected N"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2171
-msgid "Goodman and Kruskal tau"
+#: src/language/stats/chisquare.c:188 src/language/stats/chisquare.c:228
+#: src/ui/gui/crosstabs-dialog.c:61
+msgid "Residual"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2172
-msgid "Uncertainty Coefficient"
+#: src/language/stats/chisquare.c:221 src/language/stats/sign.c:62
+msgid "Frequencies"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2173
-msgid "Somers' d"
+#: 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/crosstabs.q:2174 src/ui/gui/crosstabs-dialog.c:51
-msgid "Eta"
+#: src/language/stats/chisquare.c:290
+msgid "Chi-Square"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2179
-msgid "Symmetric"
+#: src/language/stats/chisquare.c:291
+msgid "df"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2180 src/language/stats/crosstabs.q:2181
-#, c-format
-msgid "%s Dependent"
+#: src/language/stats/chisquare.c:292
+msgid "Asymp. Sig."
 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/ui/gui/frequencies-dialog.c:40
+#: src/language/stats/descriptives.c:102 src/language/stats/npar-summary.c:126
+#: src/ui/gui/descriptives-dialog.c:39 src/ui/gui/frequencies-dialog.c:40
 msgid "Mean"
 msgstr ""
 
@@ -3002,18 +2303,16 @@ msgstr ""
 msgid "S E Mean"
 msgstr ""
 
-#: src/language/stats/descriptives.c:104 src/language/stats/frequencies.q:127
+#: src/language/stats/descriptives.c:104
 msgid "Std Dev"
 msgstr ""
 
-#: src/language/stats/descriptives.c:105 src/language/stats/examine.q:1636
-#: src/language/stats/frequencies.q:128 src/ui/gui/descriptives-dialog.c:46
+#: src/language/stats/descriptives.c:105 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/frequencies.q:129 src/ui/gui/descriptives-dialog.c:47
+#: src/language/stats/descriptives.c:106 src/ui/gui/descriptives-dialog.c:47
 #: src/ui/gui/frequencies-dialog.c:50
 msgid "Kurtosis"
 msgstr ""
@@ -3022,8 +2321,7 @@ msgstr ""
 msgid "S E Kurt"
 msgstr ""
 
-#: src/language/stats/descriptives.c:108 src/language/stats/examine.q:1723
-#: src/language/stats/frequencies.q:131 src/ui/gui/descriptives-dialog.c:48
+#: src/language/stats/descriptives.c:108 src/ui/gui/descriptives-dialog.c:48
 #: src/ui/gui/frequencies-dialog.c:46
 msgid "Skewness"
 msgstr ""
@@ -3032,28 +2330,23 @@ msgstr ""
 msgid "S E Skew"
 msgstr ""
 
-#: src/language/stats/descriptives.c:110 src/language/stats/examine.q:1684
-#: src/language/stats/frequencies.q:133 src/ui/gui/descriptives-dialog.c:43
+#: src/language/stats/descriptives.c:110 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/oneway.q:404 src/ui/gui/descriptives-dialog.c:41
-#: src/ui/gui/frequencies-dialog.c:42
+#: src/language/stats/descriptives.c:111 src/language/stats/npar-summary.c:132
+#: 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/oneway.q:405 src/ui/gui/descriptives-dialog.c:42
-#: src/ui/gui/frequencies-dialog.c:43
+#: src/language/stats/descriptives.c:112 src/language/stats/npar-summary.c:135
+#: 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
+#: src/language/stats/descriptives.c:113 src/ui/gui/descriptives-dialog.c:44
+#: src/ui/gui/frequencies-dialog.c:53
 msgid "Sum"
 msgstr ""
 
@@ -3062,6 +2355,10 @@ msgstr ""
 msgid "Z-score variable name %s would be a duplicate variable name."
 msgstr ""
 
+#: src/language/stats/descriptives.c:362
+msgid "No variables specified."
+msgstr ""
+
 #: src/language/stats/descriptives.c:451
 msgid "expecting statistic name: reverting to default"
 msgstr ""
@@ -3084,1059 +2381,927 @@ 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."
-msgstr ""
-
-#: src/language/stats/examine.q:501 src/language/stats/examine.q:514
-#, c-format
-msgid "%s and %s are mutually exclusive"
-msgstr ""
-
-#: src/language/stats/examine.q:976
-msgid "Case Processing Summary"
-msgstr ""
-
-#: src/language/stats/examine.q:1183
-msgid "Extreme Values"
-msgstr ""
-
-#: src/language/stats/examine.q:1199
-msgid "Case Number"
-msgstr ""
-
-#: src/language/stats/examine.q:1297
-msgid "Highest"
-msgstr ""
-
-#: src/language/stats/examine.q:1302
-msgid "Lowest"
-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"
-msgstr ""
-
-#: src/language/stats/examine.q:1445 src/language/stats/oneway.q:408
-#: src/ui/gui/examine.glade:307
-msgid "Descriptives"
-msgstr ""
-
-#: src/language/stats/examine.q:1574 src/language/stats/oneway.q:399
-#, c-format
-msgid "%g%% Confidence Interval for Mean"
-msgstr ""
-
-#: src/language/stats/examine.q:1580 src/language/stats/oneway.q:401
-msgid "Lower Bound"
-msgstr ""
-
-#: src/language/stats/examine.q:1591 src/language/stats/oneway.q:402
-msgid "Upper Bound"
-msgstr ""
-
-#: src/language/stats/examine.q:1603
-#, c-format
-msgid "5%% Trimmed Mean"
-msgstr ""
-
-#: src/language/stats/examine.q:1614 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
-msgid "Std. Deviation"
-msgstr ""
-
-#: src/language/stats/examine.q:1696
-msgid "Interquartile Range"
-msgstr ""
-
-#: src/language/stats/examine.q:1850
-#, c-format
-msgid "Boxplot of %s vs. %s"
-msgstr ""
-
-#: src/language/stats/examine.q:1877
-msgid "Boxplot"
-msgstr ""
-
-#: src/language/stats/examine.q:1919
-#, c-format
-msgid "Normal Q-Q Plot of %s"
+#: src/language/stats/sort-cases.c:64
+msgid "Buffer limit must be at least 2."
 msgstr ""
 
-#: src/language/stats/examine.q:1920 src/language/stats/examine.q:1926
-msgid "Observed Value"
+#: src/language/stats/sort-criteria.c:74
+msgid "`A' or `D' expected inside parentheses."
 msgstr ""
 
-#: src/language/stats/examine.q:1921
-msgid "Expected Normal"
+#: src/language/stats/sort-criteria.c:79
+msgid "`)' expected."
 msgstr ""
 
-#: src/language/stats/examine.q:1924
+#: src/language/stats/sort-criteria.c:92
 #, c-format
-msgid "Detrended Normal Q-Q Plot of %s"
-msgstr ""
-
-#: src/language/stats/examine.q:1927
-msgid "Dev from Normal"
-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
-msgid "Percentiles"
-msgstr ""
-
-#: src/language/stats/examine.q:2204
-msgid "Tukey's Hinges"
+msgid "Variable %s specified twice in sort criteria."
 msgstr ""
 
-#: src/language/stats/flip.c:96
+#: src/language/stats/flip.c:98
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/language/stats/flip.c:151
-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."
+#: src/language/stats/flip.c:150
+msgid "Could not create temporary file for FLIP."
 msgstr ""
 
-#: src/language/stats/flip.c:394
+#: src/language/stats/flip.c:327
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:401
+#: src/language/stats/flip.c:334
 msgid "Error creating FLIP source file."
 msgstr ""
 
-#: src/language/stats/flip.c:414
+#: src/language/stats/flip.c:347
 #, c-format
 msgid "Error reading FLIP file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:416
+#: src/language/stats/flip.c:349
 msgid "Unexpected end of file reading FLIP file."
 msgstr ""
 
-#: src/language/stats/flip.c:432
+#: src/language/stats/flip.c:365
 #, c-format
 msgid "Error seeking FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:440
+#: src/language/stats/flip.c:373
 #, c-format
 msgid "Error writing FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:451
+#: src/language/stats/flip.c:384
 #, c-format
 msgid "Error closing FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:459
+#: src/language/stats/flip.c:392
 #, c-format
 msgid "Error rewinding FLIP source file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:488
+#: src/language/stats/flip.c:426
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
-#: src/language/stats/flip.c:491
+#: src/language/stats/flip.c:429
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
-#: 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"
+#: src/language/stats/npar-summary.c:109
+msgid "Descriptive Statistics"
 msgstr ""
 
-#: src/language/stats/frequencies.q:132
-msgid "S.E. Skew"
+#: src/language/stats/npar-summary.c:129
+msgid "Std. Deviation"
 msgstr ""
 
-#: src/language/stats/frequencies.q:409
-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."
+#: src/language/stats/npar-summary.c:142 src/ui/gui/examine.glade:333
+msgid "Percentiles"
 msgstr ""
 
-#: src/language/stats/frequencies.q:492
-#, 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."
+#: src/language/stats/npar-summary.c:146
+msgid "25th"
 msgstr ""
 
-#: src/language/stats/frequencies.q:759
-#, c-format
-msgid "Variable %s specified multiple times on VARIABLES subcommand."
+#: src/language/stats/npar-summary.c:149
+msgid "50th (Median)"
 msgstr ""
 
-#: src/language/stats/frequencies.q:822
-msgid "`)' expected after GROUPED interval list."
+#: src/language/stats/npar-summary.c:152
+msgid "75th"
 msgstr ""
 
-#: src/language/stats/frequencies.q:834
-#, c-format
-msgid "Variables %s specified on GROUPED but not on VARIABLES."
+#: src/language/stats/roc.c:938
+msgid "Area Under the Curve"
 msgstr ""
 
-#: src/language/stats/frequencies.q:841
+#: src/language/stats/roc.c:940
 #, c-format
-msgid "Variables %s specified multiple times on GROUPED subcommand."
+msgid "Area Under the Curve (%s)"
 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
-msgid "Cum"
+#: src/language/stats/roc.c:946
+msgid "Area"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1061 src/output/charts/plot-hist.c:126
-msgid "Frequency"
+#: src/language/stats/roc.c:959
+msgid "Std. Error"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1082
-msgid "Value Label"
+#: src/language/stats/roc.c:960
+msgid "Asymptotic Sig."
 msgstr ""
 
-#: src/language/stats/frequencies.q:1185
-msgid "Freq"
+#: src/language/stats/roc.c:962
+msgid "Lower Bound"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1186 src/language/stats/frequencies.q:1188
-msgid "Pct"
+#: src/language/stats/roc.c:963
+msgid "Upper Bound"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1379
+#: src/language/stats/roc.c:967
 #, c-format
-msgid "No valid data for variable %s; statistics not displayed."
+msgid "Asymp. %g%% Confidence Interval"
 msgstr ""
 
-#: src/language/stats/frequencies.q:1421
-msgid "50 (Median)"
+#: src/language/stats/roc.c:973
+msgid "Variable under test"
 msgstr ""
 
-#: src/language/stats/glm.q:148
-msgid "Multivariate GLM not yet supported"
+#: src/language/stats/roc.c:1032
+msgid "Case Summary"
 msgstr ""
 
-#: src/language/stats/glm.q:356 src/language/stats/regression.q:1026
-msgid "No valid data found. This command was skipped."
+#: src/language/stats/roc.c:1054
+msgid "Unweighted"
 msgstr ""
 
-#: src/language/stats/means.q:100
-msgid "Missing required subcommand TABLES."
+#: src/language/stats/roc.c:1055
+msgid "Weighted"
 msgstr ""
 
-#: src/language/stats/means.q:134
-msgid "TABLES subcommand may not appear more than once."
+#: src/language/stats/roc.c:1059
+msgid "Valid N (listwise)"
 msgstr ""
 
-#: src/language/stats/npar.q:98
-msgid "NPAR subcommand not currently implemented."
+#: src/language/stats/roc.c:1062
+msgid "Positive"
 msgstr ""
 
-#: src/language/stats/npar.q:236
-#, c-format
-msgid ""
-"The specified value of HI (%d) is lower than the specified value of LO (%d)"
+#: src/language/stats/roc.c:1063
+msgid "Negative"
 msgstr ""
 
-#: src/language/stats/npar.q:291
-#, c-format
-msgid ""
-"%d expected values were given, but the specified range (%d-%d) requires "
-"exactly %d values."
+#: src/language/stats/roc.c:1091
+msgid "Coordinates of the Curve"
 msgstr ""
 
-#: src/language/stats/npar.q:425 src/language/stats/t-test.q:496
+#: src/language/stats/roc.c:1093
 #, c-format
-msgid ""
-"PAIRED was specified but the number of variables preceding WITH (%zu) did "
-"not match the number following (%zu)."
+msgid "Coordinates of the Curve (%s)"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:108
-msgid "Descriptive Statistics"
+#: src/language/stats/roc.c:1103
+msgid "Test variable"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:145
-msgid "25th"
+#: src/language/stats/roc.c:1105
+msgid "Positive if greater than or equal to"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:148
-msgid "50th (Median)"
+#: src/language/stats/roc.c:1106 src/language/stats/roc.c:1171
+msgid "Sensitivity"
 msgstr ""
 
-#: src/language/stats/npar-summary.c:151
-msgid "75th"
+#: src/language/stats/roc.c:1107 src/language/stats/roc.c:1170
+msgid "1 - Specificity"
 msgstr ""
 
-#: src/language/stats/oneway.q:169
-msgid "Number of contrast coefficients must equal the number of groups"
+#: src/language/stats/roc.c:1169
+msgid "ROC Curve"
 msgstr ""
 
-#: src/language/stats/oneway.q:178
-#, c-format
-msgid "Coefficients for contrast %zu do not total zero"
+#: src/language/stats/sign.c:91
+msgid "Negative Differences"
 msgstr ""
 
-#: src/language/stats/oneway.q:244
-#, c-format
-msgid "`%s' is not a variable name"
+#: src/language/stats/sign.c:92
+msgid "Positive Differences"
 msgstr ""
 
-#: src/language/stats/oneway.q:278 src/language/stats/regression.q:301
-msgid "Sum of Squares"
+#: src/language/stats/sign.c:93 src/language/stats/wilcoxon.c:261
+msgid "Ties"
 msgstr ""
 
-#: src/language/stats/oneway.q:280 src/language/stats/regression.q:303
-msgid "Mean Square"
+#: src/language/stats/sign.c:134 src/language/stats/wilcoxon.c:331
+msgid "Exact Sig. (2-tailed)"
 msgstr ""
 
-#: src/language/stats/oneway.q:281 src/language/stats/regression.q:304
-#: src/language/stats/t-test.q:998
-msgid "F"
+#: src/language/stats/sign.c:137 src/language/stats/wilcoxon.c:332
+msgid "Exact Sig. (1-tailed)"
 msgstr ""
 
-#: src/language/stats/oneway.q:282 src/language/stats/oneway.q:542
-#: src/language/stats/regression.q:206 src/language/stats/regression.q:305
-msgid "Significance"
+#: src/language/stats/sign.c:140 src/language/stats/wilcoxon.c:335
+msgid "Point Probability"
 msgstr ""
 
-#: src/language/stats/oneway.q:304
-msgid "Between Groups"
+#: src/language/stats/wilcoxon.c:232
+msgid "Ranks"
 msgstr ""
 
-#: src/language/stats/oneway.q:305
-msgid "Within Groups"
+#: src/language/stats/wilcoxon.c:246
+msgid "Mean Rank"
 msgstr ""
 
-#: src/language/stats/oneway.q:352 src/language/stats/regression.q:330
-msgid "ANOVA"
+#: src/language/stats/wilcoxon.c:247
+msgid "Sum of Ranks"
 msgstr ""
 
-#: src/language/stats/oneway.q:539
-msgid "Levene Statistic"
+#: src/language/stats/wilcoxon.c:259
+msgid "Negative Ranks"
 msgstr ""
 
-#: src/language/stats/oneway.q:540
-msgid "df1"
+#: src/language/stats/wilcoxon.c:260
+msgid "Positive Ranks"
 msgstr ""
 
-#: src/language/stats/oneway.q:541
-msgid "df2"
+#: src/language/stats/wilcoxon.c:326
+msgid "Z"
 msgstr ""
 
-#: src/language/stats/oneway.q:545
-msgid "Test of Homogeneity of Variances"
+#: src/language/stats/wilcoxon.c:327
+msgid "Asymp. Sig. (2-tailed)"
 msgstr ""
 
-#: src/language/stats/oneway.q:613
-msgid "Contrast Coefficients"
+#: src/language/data-io/combine-files.c:210
+msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/language/stats/oneway.q:615 src/language/stats/oneway.q:690
-msgid "Contrast"
+#: 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/stats/oneway.q:688
-msgid "Contrast Tests"
+#: src/language/data-io/combine-files.c:250
+msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/language/stats/oneway.q:691
-msgid "Value of Contrast"
+#: src/language/data-io/combine-files.c:302
+#, c-format
+msgid "File %s lacks BY variable %s."
 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
-msgid "t"
+#: src/language/data-io/combine-files.c:305
+#, c-format
+msgid "Active file lacks BY variable %s."
 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
-msgid "Sig. (2-tailed)"
+#: src/language/data-io/combine-files.c:376
+msgid "The BY subcommand is required."
 msgstr ""
 
-#: src/language/stats/oneway.q:739
-msgid "Assume equal variances"
+#: src/language/data-io/combine-files.c:381
+msgid "BY is required when TABLE is specified."
 msgstr ""
 
-#: src/language/stats/oneway.q:743
-msgid "Does not assume equal"
+#: src/language/data-io/combine-files.c:386
+msgid "BY is required when SORT is specified."
 msgstr ""
 
-#: src/language/stats/rank.q:221
-#, c-format
-msgid "%s of %s by %s"
+#: src/language/data-io/combine-files.c:513
+msgid ""
+"Combining files with incompatible encodings. String data may not be "
+"represented correctly."
 msgstr ""
 
-#: src/language/stats/rank.q:226
+#: src/language/data-io/combine-files.c:545
 #, c-format
-msgid "%s of %s"
-msgstr ""
-
-#: src/language/stats/rank.q:602
-msgid "Cannot create new rank variable.  All candidates in use."
-msgstr ""
-
-#: src/language/stats/rank.q:695
-msgid "Variables Created By RANK"
+msgid ""
+"Variable %s in file %s has different type or width from the same variable in "
+"earlier file."
 msgstr ""
 
-#: src/language/stats/rank.q:719
+#: src/language/data-io/combine-files.c:551
 #, c-format
-msgid "%s into %s(%s of %s using %s BY %s)"
+msgid "In file %s, %s is numeric."
 msgstr ""
 
-#: src/language/stats/rank.q:730
+#: src/language/data-io/combine-files.c:554
 #, c-format
-msgid "%s into %s(%s of %s BY %s)"
+msgid "In file %s, %s is a string variable with width %d."
 msgstr ""
 
-#: src/language/stats/rank.q:744
+#: src/language/data-io/combine-files.c:559
 #, c-format
-msgid "%s into %s(%s of %s using %s)"
+msgid "In an earlier file, %s was numeric."
 msgstr ""
 
-#: src/language/stats/rank.q:754
+#: src/language/data-io/combine-files.c:562
 #, c-format
-msgid "%s into %s(%s of %s)"
+msgid "In an earlier file, %s was a string variable with width %d."
 msgstr ""
 
-#: src/language/stats/rank.q:767
+#: src/language/data-io/combine-files.c:601
+#, c-format
 msgid ""
-"FRACTION has been specified, but NORMAL and PROPORTION rank functions have "
-"not been requested.  The FRACTION subcommand will be ignored."
+"Variable name %s specified on %s subcommand duplicates an existing variable "
+"name."
 msgstr ""
 
-#: src/language/stats/rank.q:860
+#: src/language/data-io/combine-files.c:762
 #, c-format
-msgid "Variable %s already exists."
-msgstr ""
-
-#: src/language/stats/rank.q:865
-msgid "Too many variables in INTO clause."
+msgid "Encountered %zu sets of duplicate cases in the master file."
 msgstr ""
 
-#: src/language/stats/regression.q:159 src/ui/gui/regression-dialog.c:41
-msgid "R"
+#: src/language/data-io/data-list.c:137
+msgid "The END subcommand may only be used within INPUT PROGRAM."
 msgstr ""
 
-#: src/language/stats/regression.q:160
-msgid "R Square"
+#: src/language/data-io/data-list.c:143
+msgid "The END subcommand may only be specified once."
 msgstr ""
 
-#: src/language/stats/regression.q:161
-msgid "Adjusted R Square"
+#: src/language/data-io/data-list.c:181
+msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
-#: src/language/stats/regression.q:162
-msgid "Std. Error of the Estimate"
+#: src/language/data-io/data-list.c:243
+msgid "Encoding should not be specified for inline data. It will be ignored."
 msgstr ""
 
-#: src/language/stats/regression.q:167
-msgid "Model Summary"
+#: src/language/data-io/data-list.c:254
+msgid "The END subcommand may be used only with DATA LIST FIXED."
 msgstr ""
 
-#: src/language/stats/regression.q:202
-msgid "B"
+#: src/language/data-io/data-list.c:269
+msgid "At least one variable must be specified."
 msgstr ""
 
-#: src/language/stats/regression.q:204
-msgid "Beta"
+#: 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/stats/regression.q:207
-msgid "(Constant)"
+#: src/language/data-io/data-list.c:375
+#, c-format
+msgid "There is already a variable %s of a different type."
 msgstr ""
 
-#: src/language/stats/regression.q:271
-msgid "Coefficients"
+#: 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/stats/regression.q:307
-msgid "Regression"
+#: 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/stats/regression.q:389
-msgid "Model"
+#: src/language/data-io/data-parser.c:460
+#: src/language/data-io/data-parser.c:469
+msgid "Quoted string extends beyond end of line."
 msgstr ""
 
-#: src/language/stats/regression.q:390
-msgid "Covariances"
+#: src/language/data-io/data-parser.c:525
+#, c-format
+msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
-#: src/language/stats/regression.q:405
-msgid "Coefficient Correlations"
+#: src/language/data-io/data-parser.c:572
+#, c-format
+msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
-#: src/language/stats/regression.q:807
+#: src/language/data-io/data-parser.c:610
+#, c-format
 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."
+"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/stats/regression.q:931
-msgid "Dependent variable must be numeric."
+#: src/language/data-io/data-parser.c:630
+msgid "Record ends in data not part of any field."
 msgstr ""
 
-#: src/language/stats/sort-cases.c:64
-msgid "Buffer limit must be at least 2."
+#: src/language/data-io/data-parser.c:651 src/language/data-io/print.c:405
+msgid "Record"
 msgstr ""
 
-#: src/language/stats/sort-criteria.c:69
-msgid "`A' or `D' expected inside parentheses."
+#: src/language/data-io/data-parser.c:652 src/language/data-io/print.c:406
+#: src/ui/gui/psppire-var-sheet.c:540 src/ui/gui/psppire-var-store.c:839
+#: src/ui/gui/crosstabs.glade:92
+msgid "Columns"
 msgstr ""
 
-#: src/language/stats/sort-criteria.c:74
-msgid "`)' expected."
+#: src/language/data-io/data-parser.c:653
+#: src/language/data-io/data-parser.c:692 src/language/data-io/print.c:407
+msgid "Format"
 msgstr ""
 
-#: src/language/stats/sort-criteria.c:85
+#: src/language/data-io/data-parser.c:672
 #, 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."
-msgstr ""
+msgid "Reading %d record from %s."
+msgid_plural "Reading %d records from %s."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/language/stats/t-test.q:293
-msgid "VARIABLES subcommand is not appropriate with PAIRS"
+#: src/language/data-io/data-parser.c:708
+#, c-format
+msgid "Reading free-form data from %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:331
-msgid "One or more VARIABLES must be specified."
+#. 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 ""
 
-#: src/language/stats/t-test.q:381
+#: src/language/data-io/data-reader.c:149
 #, c-format
-msgid "Long string variable %s is not valid here."
+msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:401 src/language/stats/t-test.q:415
+#: src/language/data-io/data-reader.c:191
 msgid ""
-"When applying GROUPS to a string variable, two values must be specified."
+"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/stats/t-test.q:513
-msgid "At least two variables must be specified on PAIRS."
+#: src/language/data-io/data-reader.c:216
+#, c-format
+msgid "Error reading file %s: %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:691
-msgid "One-Sample Statistics"
+#: src/language/data-io/data-reader.c:219
+#, c-format
+msgid "Unexpected end of file reading %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:696 src/language/stats/t-test.q:719
-#: src/language/stats/t-test.q:852
-msgid "SE. Mean"
+#: src/language/data-io/data-reader.c:228
+#, c-format
+msgid "Unexpected end of file in partial record reading %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:714
-msgid "Group Statistics"
+#: src/language/data-io/data-reader.c:288
+#, c-format
+msgid "Corrupt block descriptor word at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:846
-msgid "Paired Sample Statistics"
+#: src/language/data-io/data-reader.c:289
+#, c-format
+msgid "Corrupt record descriptor word at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:868 src/language/stats/t-test.q:1213
-#: src/language/stats/t-test.q:1404
+#: src/language/data-io/data-reader.c:302
 #, c-format
-msgid "Pair %d"
+msgid "Corrupt record size at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:986
-msgid "Independent Samples Test"
+#: src/language/data-io/data-reader.c:444
+msgid "Record exceeds remaining block length."
 msgstr ""
 
-#: src/language/stats/t-test.q:994
-msgid "Levene's Test for Equality of Variances"
+#: src/language/data-io/data-reader.c:518
+#, c-format
+msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
-#: src/language/stats/t-test.q:996
-msgid "t-test for Equality of Means"
+#: src/language/data-io/data-reader.c:521
+msgid "Attempt to read beyond END DATA."
 msgstr ""
 
-#: src/language/stats/t-test.q:999 src/language/stats/t-test.q:1389
-msgid "Sig."
+#: 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 ""
 
-#: src/language/stats/t-test.q:1003 src/language/stats/t-test.q:1288
-msgid "Mean Difference"
+#: 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 ""
 
-#: src/language/stats/t-test.q:1004
-msgid "Std. Error Difference"
+#: src/language/data-io/data-writer.c:191
+#, c-format
+msgid "I/O error occurred writing data file \"%s\"."
 msgstr ""
 
-#: src/language/stats/t-test.q:1009 src/language/stats/t-test.q:1184
-#: src/language/stats/t-test.q:1280
+#: src/language/data-io/get-data.c:64
 #, c-format
-msgid "%g%% Confidence Interval of the Difference"
+msgid "Unsupported TYPE %s"
 msgstr ""
 
-#: src/language/stats/t-test.q:1064
-msgid "Equal variances assumed"
+#: 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/stats/t-test.q:1116
-msgid "Equal variances not assumed"
+#: src/language/data-io/get-data.c:315
+msgid "expecting FIXED or DELIMITED"
 msgstr ""
 
-#: src/language/stats/t-test.q:1174
-msgid "Paired Samples Test"
+#: src/language/data-io/get-data.c:328
+msgid "Value of FIRSTCASE must be 1 or greater."
 msgstr ""
 
-#: src/language/stats/t-test.q:1177
-msgid "Paired Differences"
+#: src/language/data-io/get-data.c:353
+msgid "expecting LINE or VARIABLES"
 msgstr ""
 
-#: src/language/stats/t-test.q:1189
-msgid "Std. Error Mean"
+#: src/language/data-io/get-data.c:366
+msgid "Value of FIXCASE must be at least 1."
 msgstr ""
 
-#: src/language/stats/t-test.q:1269
-msgid "One-Sample Test"
+#: src/language/data-io/get-data.c:386
+msgid "Value of FIRST must be at least 1."
 msgstr ""
 
-#: src/language/stats/t-test.q:1274
-#, c-format
-msgid "Test Value = %f"
+#: src/language/data-io/get-data.c:398
+msgid "Value of PERCENT must be between 1 and 100."
 msgstr ""
 
-#: src/language/stats/t-test.q:1384
-msgid "Paired Samples Correlations"
+#: src/language/data-io/get-data.c:447
+msgid ""
+"In compatible syntax mode, the QUALIFIER string must contain exactly one "
+"character."
 msgstr ""
 
-#: src/language/stats/t-test.q:1388
-msgid "Correlation"
+#: src/language/data-io/get-data.c:462
+msgid "expecting VARIABLES"
 msgstr ""
 
-#: src/language/stats/t-test.q:1407
+#: src/language/data-io/get-data.c:484
+#: src/language/data-io/placement-parser.c:378
 #, c-format
-msgid "%s & %s"
+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 ""
 
-#: src/language/syntax-file.c:88
+#: src/language/data-io/get-data.c:493
 #, c-format
-msgid "opening \"%s\" as syntax file"
+msgid ""
+"The record number specified, %ld, exceeds the number of records per case "
+"specified on FIXCASE, %d."
 msgstr ""
 
-#: src/language/syntax-file.c:93
-#, c-format
-msgid "Opening `%s': %s."
+#: src/language/data-io/get.c:99
+msgid "expecting COMM or TAPE"
 msgstr ""
 
-#: src/language/syntax-file.c:106
-#, c-format
-msgid "Reading `%s': %s."
+#: src/language/data-io/inpt-pgm.c:130
+msgid "Unexpected end-of-file within INPUT PROGRAM."
 msgstr ""
 
-#: src/language/syntax-file.c:126
-#, c-format
-msgid "Closing `%s': %s."
+#: src/language/data-io/inpt-pgm.c:143
+msgid "Input program did not create any variables."
 msgstr ""
 
-#: src/language/tests/check-model.q:138
-msgid "PATH and SEARCH subcommands are mutually exclusive.  Ignoring PATH."
+#: src/language/data-io/inpt-pgm.c:288
+msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
-#: src/language/tests/check-model.q:156
-msgid "At least one value must be specified on PATH."
+#: src/language/data-io/inpt-pgm.c:338
+msgid ""
+"REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
 
-#: src/language/tests/check-model.q:167
+#: src/language/data-io/placement-parser.c:87
 #, c-format
-msgid "Hash bits adjusted to %d."
+msgid ""
+"Number of variables specified (%zu) differs from number of variable formats "
+"(%zu)."
 msgstr ""
 
-#: src/language/tests/check-model.q:208
-#, c-format
-msgid "error opening \"%s\" for writing"
+#: src/language/data-io/placement-parser.c:97
+msgid ""
+"SPSS-like or Fortran-like format specification expected after variable names."
 msgstr ""
 
-#: src/language/tests/float-format.c:124
+#: src/language/data-io/placement-parser.c:119
 #, c-format
-msgid "%zu-byte string needed but %zu-byte string supplied."
+msgid "The %d columns %d-%d can't be evenly divided into %zu fields."
 msgstr ""
 
-#: src/language/tests/float-format.c:136
-msgid "Hexadecimal floating constant too long."
+#: src/language/data-io/placement-parser.c:305
+msgid "Column positions for fields must be positive."
 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."
+#: src/language/data-io/placement-parser.c:307
+msgid "Column positions for fields must not be negative."
 msgstr ""
 
-#: src/language/tests/float-format.c:247
-msgid "Too many values in single command."
+#: src/language/data-io/placement-parser.c:344
+msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
-#: src/language/tests/moments-test.c:47
-msgid "expecting weight value"
+#: src/language/data-io/print-space.c:116
+msgid "The expression on PRINT SPACE evaluated to the system-missing value."
 msgstr ""
 
-#: src/language/utilities/cd.c:41
+#: src/language/data-io/print-space.c:119
 #, c-format
-msgid "Cannot change directory to %s:  %s "
-msgstr ""
-
-#: src/language/utilities/date.c:32
-msgid "Only USE ALL is currently implemented."
-msgstr ""
-
-#: src/language/utilities/include.c:91
-msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
-msgstr ""
-
-#: src/language/utilities/include.c:108
-msgid "Expecting YES or NO after CD."
+msgid "The expression on PRINT SPACE evaluated to %g."
 msgstr ""
 
-#: src/language/utilities/include.c:125
-msgid "Expecting CONTINUE or STOP after ERROR."
+#: src/language/data-io/print.c:179 src/language/data-io/trim.c:54
+msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/language/utilities/include.c:132
+#: src/language/data-io/print.c:267
 #, c-format
-msgid "Unexpected token: `%s'."
-msgstr ""
-
-#: src/language/utilities/include.c:177
-msgid "expecting file name"
+msgid "Output calls for %d records but %zu specified on RECORDS subcommand."
 msgstr ""
 
-#: src/language/utilities/include.c:189
+#: src/language/data-io/print.c:438
 #, c-format
-msgid "Can't find `%s' in include file search path."
-msgstr ""
+msgid "Writing %d record to %s."
+msgid_plural "Writing %d records to %s."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/language/utilities/permissions.c:73
+#: src/language/data-io/print.c:442
 #, c-format
-msgid "Expecting %s or %s."
-msgstr ""
+msgid "Writing %d record."
+msgid_plural "Writing %d records."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/language/utilities/permissions.c:106
+#: src/language/data-io/save.c:223 src/language/data-io/save.c:238
+#: src/language/data-io/save.c:266
 #, c-format
-msgid "Cannot stat %s: %s"
+msgid "expecting %s or %s"
 msgstr ""
 
-#: src/language/utilities/permissions.c:119
+#: src/language/data-io/trim.c:88
 #, c-format
-msgid "Cannot change mode of %s: %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/utilities/set.q:200
-msgid "WORKSPACE must be at least 1MB"
+#: src/language/data-io/trim.c:114
+msgid "`=' expected after variable list."
 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/data-io/trim.c:121
 #, c-format
-msgid "%s is obsolete."
+msgid ""
+"Number of variables on left side of `=' (%zu) does not match number of "
+"variables on right side (%zu), in parenthesized group %d of RENAME "
+"subcommand."
 msgstr ""
+"Number of variables on left side of `=' (%zu) does not match number of \n"
+"variables on right side (%zu), in parenthesised group %d of RENAME \n"
+"subcommand."
 
-#: src/language/utilities/set.q:225
+#: src/language/data-io/trim.c:134
 #, c-format
-msgid "%s is not implemented."
+msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/language/utilities/set.q:228
-msgid "Active file compression is not implemented."
+#: src/language/data-io/trim.c:165
+msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
-#: src/language/utilities/set.q:323
-msgid "EPOCH must be 1500 or later."
+#: src/language/expressions/evaluate.c:155
+msgid "expecting number or string"
 msgstr ""
 
-#: src/language/utilities/set.q:330
-msgid "expecting AUTOMATIC or year"
+#: src/language/expressions/evaluate.c:169
+#, c-format
+msgid "Duplicate variable name %s."
 msgstr ""
 
-#: src/language/utilities/set.q:351
-msgid "LENGTH must be at least 1."
+#: 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 ""
 
-#: src/language/utilities/set.q:395
-msgid "WIDTH must be at least 40."
+#: src/language/expressions/helpers.c:73
+msgid ""
+"The week argument to DATE.WKYR is not an integer.  The result will be system-"
+"missing."
 msgstr ""
 
-#: src/language/utilities/set.q:418
-#, c-format
+#: src/language/expressions/helpers.c:79
 msgid ""
-"FORMAT requires numeric output format as an argument.  Specified format %s "
-"is of type string."
+"The week argument to DATE.WKYR is outside the acceptable range of 1 to 53.  "
+"The result will be system-missing."
 msgstr ""
 
-#: src/language/utilities/set.q:485
-msgid "BLANKS is SYSMIS."
+#: src/language/expressions/helpers.c:101
+msgid ""
+"The day argument to DATE.YRDAY is not an integer.  The result will be system-"
+"missing."
 msgstr ""
 
-#: src/language/utilities/set.q:487
-#, c-format
-msgid "BLANKS is %g."
+#: 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 ""
 
-#: src/language/utilities/set.q:522
-#, c-format
-msgid "%s is \"%s\"."
+#: src/language/expressions/helpers.c:129
+msgid ""
+"The year argument to YRMODA is greater than 47516.  The result will be "
+"system-missing."
 msgstr ""
 
-#: src/language/utilities/set.q:558
+#: src/language/expressions/helpers.c:182
 #, c-format
-msgid "DECIMAL is \"%c\"."
+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/utilities/set.q:564
-#, c-format
-msgid "ENDCMD is \"%c\"."
+#: src/language/expressions/helpers.c:332
+msgid ""
+"Invalid DATESUM method.  Valid choices are \"closest\" and \"rollover\"."
 msgstr ""
 
-#: src/language/utilities/set.q:572
+#: src/language/expressions/parse.c:259
 #, c-format
-msgid "ERRORS is \"%s\"."
+msgid ""
+"Type mismatch: expression has %s type, but a numeric value is required here."
 msgstr ""
 
-#: src/language/utilities/set.q:583
+#: src/language/expressions/parse.c:271
 #, c-format
-msgid "FORMAT is %s."
+msgid ""
+"Type mismatch: expression has %s type, but a string value is required here."
 msgstr ""
 
-#: src/language/utilities/set.q:589
+#: src/language/expressions/parse.c:427
 #, c-format
-msgid "LENGTH is %d."
+msgid "Type mismatch while applying %s operator: cannot convert %s to %s."
 msgstr ""
 
-#: src/language/utilities/set.q:595
-#, c-format
-msgid "MXERRS is %d."
+#: 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/utilities/set.q:601
-#, c-format
-msgid "MXLOOPS is %d."
+#: 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/utilities/set.q:607
+#: src/language/expressions/parse.c:809
 #, c-format
-msgid "MXWARNS is %d."
+msgid "Unknown system variable %s."
 msgstr ""
 
-#: src/language/utilities/set.q:614 src/language/utilities/set.q:665
+#: src/language/expressions/parse.c:857
 #, c-format
-msgid "%s is %s (%s)."
-msgstr ""
-
-#: src/language/utilities/set.q:686
-msgid "SCOMPRESSION is ON."
-msgstr ""
-
-#: src/language/utilities/set.q:688
-msgid "SCOMPRESSION is OFF."
-msgstr ""
-
-#: src/language/utilities/set.q:695
-msgid "UNDEFINED is WARN."
-msgstr ""
-
-#: src/language/utilities/set.q:697
-msgid "UNDEFINED is NOWARN."
+msgid "Unknown identifier %s."
 msgstr ""
 
-#: src/language/utilities/set.q:705
-msgid "WEIGHT is off."
+#: src/language/expressions/parse.c:892
+msgid "in expression"
 msgstr ""
 
-#: src/language/utilities/set.q:707
+#: src/language/expressions/parse.c:1073
 #, c-format
-msgid "WEIGHT is variable %s."
+msgid "%s must have at least %d arguments in list."
 msgstr ""
 
-#: src/language/utilities/set.q:725
+#: src/language/expressions/parse.c:1082
 #, c-format
-msgid "WIDTH is %d."
+msgid "%s must have even number of arguments in list."
 msgstr ""
 
-#: src/language/utilities/title.c:68
+#: src/language/expressions/parse.c:1085
 #, c-format
-msgid "%s: `.' expected after string."
+msgid "%s must have multiple of %d arguments in list."
 msgstr ""
 
-#: src/language/utilities/title.c:108
+#: src/language/expressions/parse.c:1095
 #, c-format
-msgid "   (Entered %s)"
+msgid "%s function does not accept a minimum valid argument count."
 msgstr ""
 
-#: src/language/xforms/compute.c:146 src/language/xforms/compute.c:194
+#: src/language/expressions/parse.c:1104
 #, c-format
-msgid ""
-"When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
-"s."
+msgid "%s requires at least %d valid arguments in list."
 msgstr ""
 
-#: src/language/xforms/compute.c:150 src/language/xforms/compute.c:201
+#: src/language/expressions/parse.c:1110
 #, c-format
 msgid ""
-"When executing COMPUTE: %g is not a valid value as an index into vector %s."
+"With %s, using minimum valid argument count of %d does not make sense when "
+"passing only %d arguments in list."
 msgstr ""
 
-#: src/language/xforms/compute.c:344
+#: src/language/expressions/parse.c:1164
 #, c-format
-msgid "There is no vector named %s."
-msgstr ""
-
-#: src/language/xforms/count.c:123
-msgid "Destination cannot be a string variable."
-msgstr ""
-
-#: src/language/xforms/recode.c:246
-msgid ""
-"Inconsistent target variable types.  Target variables must be all numeric or "
-"all string."
-msgstr ""
-
-#: src/language/xforms/recode.c:267
-msgid "CONVERT requires string input values and numeric output values."
+msgid "Type mismatch invoking %s as "
 msgstr ""
 
-#: src/language/xforms/recode.c:317
-msgid "THRU is not allowed with string variables."
+#: src/language/expressions/parse.c:1169
+msgid "Function invocation "
 msgstr ""
 
-#: src/language/xforms/recode.c:391
-msgid "expecting output value"
+#: src/language/expressions/parse.c:1171
+msgid " does not match any known function.  Candidates are:"
 msgstr ""
 
-#: src/language/xforms/recode.c:440
+#: src/language/expressions/parse.c:1201
 #, c-format
-msgid ""
-"%zu variable(s) cannot be recoded into %zu variable(s).  Specify the same "
-"number of variables as source and target variables."
+msgid "No function or vector named %s."
 msgstr ""
 
-#: src/language/xforms/recode.c:455
+#: src/language/expressions/parse.c:1244
 #, 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.)"
+msgid "expecting `,' or `)' invoking %s function"
 msgstr ""
 
-#: src/language/xforms/recode.c:470
+#: src/language/expressions/parse.c:1264
 #, c-format
-msgid "INTO is required with %s input values and %s output values."
+msgid "%s is a PSPP extension."
 msgstr ""
 
-#: src/language/xforms/recode.c:483
+#: src/language/expressions/parse.c:1273
 #, c-format
-msgid "Type mismatch.  Cannot store %s data in %s variable %s."
+msgid "%s may not appear after TEMPORARY."
 msgstr ""
 
-#: src/language/xforms/sample.c:76
-msgid "The sampling factor must be between 0 and 1 exclusive."
+#: src/libpspp/hash.c:545
+#, c-format
+msgid "hash table:"
 msgstr ""
 
-#: src/language/xforms/sample.c:96
-#, c-format
-msgid "Cannot sample %d observations from a population of %d."
+#: src/libpspp/tmpfile.c:55
+msgid "failed to create temporary file"
 msgstr ""
 
-#: src/language/xforms/select-if.c:100
-msgid "Syntax error expecting OFF or BY.  Turning off case filtering."
+#: src/libpspp/tmpfile.c:96
+msgid "seeking in temporary file"
 msgstr ""
 
-#: src/language/xforms/select-if.c:115
-msgid "The filter variable must be numeric."
+#: src/libpspp/tmpfile.c:115
+msgid "reading temporary file"
 msgstr ""
 
-#: src/language/xforms/select-if.c:121
-msgid "The filter variable may not be scratch."
+#: src/libpspp/tmpfile.c:117
+msgid "unexpected end of file reading temporary file"
 msgstr ""
 
-#: src/libpspp/hash.c:614
-#, c-format
-msgid "hash table:"
+#: src/libpspp/tmpfile.c:136
+msgid "writing to temporary file"
 msgstr ""
 
-#: src/math/percentiles.c:41
+#: src/math/percentiles.c:35
 msgid "HAverage"
 msgstr ""
 
-#: src/math/percentiles.c:42
+#: src/math/percentiles.c:36
 msgid "Weighted Average"
 msgstr ""
 
-#: src/math/percentiles.c:43
+#: src/math/percentiles.c:37
 msgid "Rounded"
 msgstr ""
 
-#: src/math/percentiles.c:44
+#: src/math/percentiles.c:38
 msgid "Empirical"
 msgstr ""
 
-#: src/math/percentiles.c:45
+#: src/math/percentiles.c:39
 msgid "Empirical with averaging"
 msgstr ""
 
+#: src/output/charts/plot-hist.c:138
+msgid "HISTOGRAM"
+msgstr ""
+
+#: src/output/charts/plot-hist.c:140
+msgid "Frequency"
+msgstr ""
+
 #: src/output/afm.c:149
 #, c-format
 msgid "opening font metrics file \"%s\""
@@ -4229,7 +3394,6 @@ msgid "ascii: unknown parameter `%s'"
 msgstr ""
 
 #: src/output/ascii.c:360
-#, c-format
 msgid "ascii: only screen devices may have `auto' length or width"
 msgstr ""
 
@@ -4239,7 +3403,6 @@ msgid "ascii: positive integer required as `%s' value"
 msgstr ""
 
 #: src/output/ascii.c:402
-#, c-format
 msgid "ascii: `emphasis' value must be `bold', `underline', or `none'"
 msgstr ""
 
@@ -4254,7 +3417,6 @@ msgid "ascii: boolean value expected for `%s'"
 msgstr ""
 
 #: src/output/ascii.c:478 src/output/html.c:187
-#, c-format
 msgid "`chart-files' value must contain `#'"
 msgstr ""
 
@@ -4278,15 +3440,6 @@ msgstr ""
 msgid "ascii: closing output file \"%s\""
 msgstr ""
 
-#: src/output/chart.c:145
-#, c-format
-msgid "creating \"%s\""
-msgstr ""
-
-#: src/output/charts/plot-hist.c:124
-msgid "HISTOGRAM"
-msgstr ""
-
 #: src/output/html.c:71
 #, c-format
 msgid "opening HTML output file: %s"
@@ -4301,12 +3454,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 ""
@@ -4322,12 +3475,10 @@ msgid "output driver `%s' referenced but never defined"
 msgstr ""
 
 #: src/output/output.c:261
-#, c-format
 msgid "using default output driver configuration"
 msgstr ""
 
 #: src/output/output.c:290
-#, c-format
 msgid "cannot find output initialization file (use `-vv' to view search path)"
 msgstr ""
 
@@ -4341,8 +3492,7 @@ msgstr ""
 msgid "reading \"%s\""
 msgstr ""
 
-#: src/output/output.c:332 src/ui/gui/message-dialog.c:97
-#, c-format
+#: src/output/output.c:332 src/ui/gui/message-dialog.c:99
 msgid "syntax error"
 msgstr ""
 
@@ -4352,12 +3502,10 @@ msgid "error closing \"%s\""
 msgstr ""
 
 #: src/output/output.c:349
-#, c-format
 msgid "no active output drivers"
 msgstr ""
 
 #: src/output/output.c:352
-#, c-format
 msgid "error reading device definition file"
 msgstr ""
 
@@ -4402,10 +3550,9 @@ 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
 msgid "driver definition line missing driver name or class name"
 msgstr ""
 
@@ -4519,7 +3666,6 @@ msgid "cannot open font encoding file \"%s\""
 msgstr ""
 
 #: src/output/postscript.c:1399
-#, c-format
 msgid "invalid numeric format"
 msgstr ""
 
@@ -4528,17 +3674,151 @@ msgstr ""
 msgid "closing Postscript encoding \"%s\""
 msgstr ""
 
-#: src/output/table.c:234
+#: src/output/table.c:237
 #, 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:308
 #, 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/output/chart.c:154
+#, c-format
+msgid "creating \"%s\""
+msgstr ""
+
+#: src/ui/source-init-opts.c:42
+msgid ""
+"set to `compatible' if you want output calculated from broken algorithms"
+msgstr ""
+
+#: src/ui/source-init-opts.c:43
+msgid "Append DIR to include path"
+msgstr ""
+
+#: src/ui/source-init-opts.c:44
+msgid "Clear include path"
+msgstr ""
+
+#: src/ui/source-init-opts.c:45
+msgid "Disable execution of .pspp/rc at startup"
+msgstr ""
+
+#: src/ui/source-init-opts.c:46
+msgid "Set configuration directory to DIR"
+msgstr ""
+
+#: src/ui/source-init-opts.c:47
+msgid "Don't allow some unsafe operations"
+msgstr ""
+
+#: src/ui/source-init-opts.c:48
+msgid "Set to `compatible' if you want only to accept SPSS compatible syntax"
+msgstr ""
+
+#: src/ui/source-init-opts.c:83
+msgid "Algorithm must be either \"compatible\" or \"enhanced\"."
+msgstr ""
+
+#: src/ui/source-init-opts.c:124
+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:125 src/ui/gui/main.c:177
+msgid "Options affecting syntax and behavior:"
+msgstr "Options affecting syntax and behaviour:"
+
+#: src/ui/terminal/main.c:156
+msgid ""
+"Stopping syntax file processing here to avoid a cascade of dependent command "
+"failures."
+msgstr ""
+
+#: src/ui/terminal/msg-ui.c:67
+#, c-format
+msgid "Cannot open %s (%s). Writing errors to stdout instead.\n"
+msgstr ""
+
+#: src/ui/terminal/msg-ui.c:94
+msgid "Terminating execution of syntax file due to error."
+msgstr ""
+
+#: src/ui/terminal/msg-ui.c:96
+#, c-format
+msgid "Errors (%d) exceeds limit (%d)."
+msgstr ""
+
+#: src/ui/terminal/msg-ui.c:99
+#, c-format
+msgid "Warnings (%d) exceed limit (%d)."
+msgstr ""
+
+#: src/ui/terminal/msg-ui.c:150
+msgid "error"
+msgstr ""
+
+#: src/ui/terminal/msg-ui.c:151
+msgid "warning"
+msgstr ""
+
+#: src/ui/terminal/terminal.c:72
+#, c-format
+msgid "could not access definition for terminal `%s'"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:41
+msgid "Increase diagnostic verbosity level"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:68
+msgid "Send error messages to FILE (appended)"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:71
+msgid "Select output driver DEVICE and disable defaults"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:74
+msgid "Print a list of known driver classes, then exit"
+msgstr ""
+
+#: src/ui/terminal/terminal-opts.c:76
+msgid "Start an interactive session"
+msgstr ""
+
+#: src/ui/gui/about.c:64
+msgid "A program for the analysis of sampled data"
+msgstr ""
+
+#. TRANSLATORS: Use this string to list the people who have helped with
+#. translation to your language.
+#: src/ui/gui/about.c:74
+msgid "translator-credits"
+msgstr "John Darrington"
+
+#: src/ui/gui/checkbox-treeview.c:92
+msgid "Statistic"
+msgstr ""
+
 #: src/ui/gui/comments-dialog.c:58
 #, c-format
 msgid "Column Number: %d"
@@ -4548,10 +3828,18 @@ msgstr ""
 msgid "Chisq"
 msgstr ""
 
+#: src/ui/gui/crosstabs-dialog.c:41
+msgid "Phi"
+msgstr ""
+
 #: src/ui/gui/crosstabs-dialog.c:42
 msgid "CC"
 msgstr ""
 
+#: src/ui/gui/crosstabs-dialog.c:43
+msgid "Lambda"
+msgstr ""
+
 #: src/ui/gui/crosstabs-dialog.c:44
 msgid "UC"
 msgstr ""
@@ -4568,17 +3856,30 @@ msgstr ""
 msgid "Risk"
 msgstr ""
 
+#: src/ui/gui/crosstabs-dialog.c:48
+msgid "Gamma"
+msgstr ""
+
 #: src/ui/gui/crosstabs-dialog.c:49
 msgid "D"
 msgstr ""
 
+#: src/ui/gui/crosstabs-dialog.c:50
+msgid "Kappa"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:51
+msgid "Eta"
+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:591 src/ui/gui/var-display.c:14
+#: src/ui/gui/psppire-var-store.c:612 src/ui/gui/var-display.c:16
+#: src/ui/gui/variable-info-dialog.c:40
 msgid "None"
 msgstr ""
 
@@ -4606,1312 +3907,1427 @@ msgstr ""
 msgid "Adjusted Std. Residual"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:50
-msgid "Rows"
+#: src/ui/gui/customentry.c:334
+msgid "Style of bevel around the custom entry button"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:131 src/ui/gui/frequencies.glade:185
-msgid "Format..."
+#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
+msgid "Standard deviation"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:138 src/ui/gui/examine.glade:246
-#: src/ui/gui/regression.glade:31
-msgid "Statistics..."
+#: src/ui/gui/descriptives-dialog.c:45
+msgid "Standard error"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:148
-msgid "Cells..."
+#: src/ui/gui/find-dialog.c:652
+#, c-format
+msgid "Bad regular expression: %s"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:230
-msgid "Print tables"
+#: src/ui/gui/frequencies-dialog.c:44
+msgid "Standard error of the mean"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:240
-msgid "Pivot"
+#: src/ui/gui/frequencies-dialog.c:47
+msgid "Standard error of the skewness"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:778
-msgid "Ascending"
+#: src/ui/gui/frequencies-dialog.c:49
+msgid "Mode"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:283
-msgid "No label"
+#: src/ui/gui/frequencies-dialog.c:51
+msgid "Standard error of the kurtosis"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:295
-msgid "Suppress value labels"
+#: src/ui/gui/frequencies-dialog.c:52
+msgid "Median"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:311
-msgid "Labeling"
+#: src/ui/gui/helper.c:197
+msgid "Sorry. The help system hasn't yet been implemented."
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:378
-msgid "Cell Display"
+#: src/ui/gui/helper.c:242
+#, c-format
+msgid "Cannot open reference manual: %s"
 msgstr ""
 
-#: src/ui/gui/crosstabs.glade:439 src/ui/gui/oneway.glade:207
-#: src/ui/gui/regression.glade:322
-msgid "Statistics"
+#: src/ui/gui/main.c:43
+msgid "Don't show the splash screen"
 msgstr ""
 
-#: src/ui/gui/customentry.c:334
-msgid "Style of bevel around the custom entry button"
+#: src/ui/gui/main.c:173
+msgid "PSPPIRE --- A user interface for PSPP"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:150
-msgid "Transformations Pending"
+#: src/ui/gui/main.c:175
+msgid "Miscellaneous options:"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:323
-msgid "_Labels"
+#: src/ui/gui/message-dialog.c:103
+msgid "data file error"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:324
-msgid "Show/hide value labels"
+#: src/ui/gui/message-dialog.c:108
+msgid "PSPP error"
 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/message-dialog.c:116
+msgid "syntax warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:343
-msgid "Delete the cases at the selected position(s)"
+#: src/ui/gui/message-dialog.c:120
+msgid "data file warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:362
-msgid "Delete the variables at the selected position(s)"
+#: src/ui/gui/message-dialog.c:125
+msgid "PSPP warning"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:377
-msgid "Insert _Variable"
+#: src/ui/gui/message-dialog.c:134
+msgid "syntax information"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:378
-msgid "Create a new variable at the current position"
+#: src/ui/gui/message-dialog.c:138
+msgid "data file information"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:396
-msgid "Insert Ca_se"
+#: src/ui/gui/message-dialog.c:143
+msgid "PSPP information"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:397
-msgid "Create a new case at the current position"
+#: 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/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/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/missing-val-dialog.c:113 src/ui/gui/missing-val-dialog.c:167
+msgid "Incorrect value for variable type"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:417
-msgid "_Goto Case"
+#: src/ui/gui/missing-val-dialog.c:134 src/ui/gui/missing-val-dialog.c:143
+msgid "Incorrect range specification"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:418
-msgid "Jump to a Case in the Data Sheet"
+#: src/ui/gui/oneway-anova-dialog.c:331
+#, c-format
+msgid "Contrast %d of %d"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:437
-msgid "_Weights"
+#: src/ui/gui/psppire.c:247
+msgid "_Reset"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:438
-msgid "Weight cases by variable"
+#: src/ui/gui/psppire.c:248
+msgid "_Select"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:447 src/ui/gui/data-editor.glade:319
-msgid "_Transpose"
+#: src/ui/gui/psppire-data-editor.c:951
+msgid "Data View"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:448
-msgid "Transpose the cases with the variables"
+#: src/ui/gui/psppire-data-editor.c:954
+msgid "Variable View"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:459
-msgid "S_plit"
+#: src/ui/gui/psppire-data-store.c:744
+msgid "var"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:460
-msgid "Split the active file"
+#: src/ui/gui/psppire-data-store.c:755 src/ui/gui/psppire-var-store.c:699
+#: src/ui/gui/psppire-var-store.c:709 src/ui/gui/psppire-var-store.c:719
+#: src/ui/gui/psppire-var-store.c:825
+#, c-format
+msgid "%d"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:470
-msgid "_Sort"
+#: src/ui/gui/psppire-data-window.c:213
+msgid "Transformations Pending"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:471
-msgid "Sort cases in the active file"
+#: src/ui/gui/psppire-data-window.c:229
+msgid "Filter off"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:479 src/ui/gui/data-editor.glade:340
-msgid "Select _Cases"
+#: src/ui/gui/psppire-data-window.c:243
+#, c-format
+msgid "Filter by %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:480
-msgid "Select cases from the active file"
+#: src/ui/gui/psppire-data-window.c:264
+msgid "No Split"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:489 src/ui/gui/data-editor.glade:369
-msgid "_Compute"
+#: src/ui/gui/psppire-data-window.c:273
+msgid "Split by "
 msgstr ""
 
-#: src/ui/gui/data-editor.c:490
-msgid "Compute new values for a variable"
+#: src/ui/gui/psppire-data-window.c:301
+msgid "Weights off"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:498
-msgid "Oneway _ANOVA"
+#: src/ui/gui/psppire-data-window.c:315
+#, c-format
+msgid "Weight by %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:499
-msgid "Perform one way analysis of variance"
+#: src/ui/gui/psppire-data-window.c:383 src/ui/gui/data-editor.glade:702
+msgid "Open"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:507 src/ui/gui/data-editor.glade:496
-msgid "_Independent Samples T Test"
+#: src/ui/gui/psppire-data-window.c:391 src/ui/gui/psppire-data-window.c:593
+msgid "System Files (*.sav)"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:508
-msgid "Calculate T Test for samples from independent groups"
+#: src/ui/gui/psppire-data-window.c:397 src/ui/gui/psppire-data-window.c:599
+msgid "Portable Files (*.por) "
 msgstr ""
 
-#: src/ui/gui/data-editor.c:517 src/ui/gui/data-editor.glade:504
-msgid "_Paired Samples T Test"
+#: src/ui/gui/psppire-data-window.c:403 src/ui/gui/psppire-data-window.c:605
+#: src/ui/gui/psppire-syntax-window.c:298
+#: src/ui/gui/psppire-syntax-window.c:385
+msgid "All Files"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:518
-msgid "Calculate T Test for paired samples"
+#: src/ui/gui/psppire-data-window.c:585 src/ui/gui/data-editor.glade:712
+msgid "Save"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:527
-msgid "One _Sample T Test"
+#: src/ui/gui/psppire-data-window.c:613
+msgid "System File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:528
-msgid "Calculate T Test for sample from a single distribution"
+#: src/ui/gui/psppire-data-window.c:618
+msgid "Portable File"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:537 src/ui/gui/data-editor.glade:593
-msgid "Data File _Comments"
+#: src/ui/gui/psppire-data-window.c:768
+msgid "Font Selection"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:538
-msgid "Commentary text for the data file"
+#: src/ui/gui/psppire-data-window.c:836
+msgid "Sort Ascending"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:546 src/ui/gui/data-editor.glade:228
-msgid "_Find"
+#: src/ui/gui/psppire-data-window.c:842
+msgid "Sort Descending"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:547
-msgid "Find Case"
+#: src/ui/gui/psppire-data-window.c:847 src/ui/gui/psppire-data-window.c:937
+#: src/ui/gui/data-editor.glade:174 src/ui/gui/data-editor.glade:843
+msgid "Insert Variable"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:556 src/ui/gui/data-editor.glade:377
-msgid "Ran_k Cases"
+#: src/ui/gui/psppire-data-window.c:850 src/ui/gui/psppire-data-window.c:904
+#: src/ui/gui/psppire-data-window.c:940 src/ui/gui/psppire-data-window.c:1307
+#: src/ui/gui/psppire-data-window.c:1325
+msgid "Clear"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:557
-msgid "Rank Cases"
+#: src/ui/gui/psppire-data-window.c:901 src/ui/gui/data-editor.glade:831
+msgid "Insert Case"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:566 src/ui/gui/data-editor.glade:389
-msgid "Recode into _Same Variables"
+#: src/ui/gui/psppire-data-window.c:1185
+msgid "Open a data file"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:567
-msgid "Recode values into the same Variables"
+#: src/ui/gui/psppire-data-window.c:1203
+msgid "New data file"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:576 src/ui/gui/data-editor.glade:396
-msgid "Recode into _Different Variables"
+#: src/ui/gui/psppire-data-window.c:1218
+msgid "Import text data file"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:577
-msgid "Recode values into different Variables"
+#: src/ui/gui/psppire-data-window.c:1234 src/ui/gui/psppire-data-window.c:1250
+msgid "Save data to file"
 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/psppire-data-window.c:1288
+msgid "Show/hide value labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:587
-msgid "Jump to Variable"
+#: src/ui/gui/psppire-data-window.c:1308
+msgid "Delete the cases at the selected position(s)"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:595 src/ui/gui/data-editor.glade:450
-#: src/ui/gui/oneway.glade:179
-msgid "_Descriptives"
+#: src/ui/gui/psppire-data-window.c:1326
+msgid "Delete the variables at the selected position(s)"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:596
-msgid "Calculate descriptive statistics (mean, variance, ...)"
+#: src/ui/gui/psppire-data-window.c:1344
+msgid "Create a new variable at the current position"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:605 src/ui/gui/data-editor.glade:442
-msgid "_Frequencies"
+#: src/ui/gui/psppire-data-window.c:1359
+msgid "Create a new case at the current position"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:606
-msgid "Generate frequency statistics"
+#: src/ui/gui/psppire-data-window.c:1375
+msgid "Jump to a Case in the Data Sheet"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:614 src/ui/gui/data-editor.glade:466
-msgid "_Crosstabs"
+#: src/ui/gui/psppire-data-window.c:1391
+msgid "Weight cases by variable"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:615
-msgid "Generate crosstabulations"
+#: src/ui/gui/psppire-data-window.c:1405
+msgid "Transpose the cases with the variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:624 src/ui/gui/data-editor.glade:458
-msgid "_Explore"
+#: src/ui/gui/psppire-data-window.c:1419
+msgid "Split the active file"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:625
-msgid "Examine Data by Factors"
+#: src/ui/gui/psppire-data-window.c:1434
+msgid "Sort cases in the active file"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:634 src/ui/gui/data-editor.glade:532
-msgid "Linear _Regression"
+#: src/ui/gui/psppire-data-window.c:1448
+msgid "Select cases from the active file"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1462
+msgid "Compute new values for a variable"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1476
+msgid "Perform one way analysis of variance"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1491
+msgid "Calculate T Test for samples from independent groups"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1505
+msgid "Calculate T Test for paired samples"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1519
+msgid "Calculate T Test for sample from a single distribution"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1534
+msgid "Commentary text for the data file"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1560
+msgid "Rank Cases"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1574
+msgid "Recode values into the same variables"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1588
+msgid "Recode values into different variables"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1602
+msgid "Jump to variable"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1615
+msgid "Calculate descriptive statistics (mean, variance, ...)"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1629
+msgid "Generate frequency statistics"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1643
+msgid "Generate crosstabulations"
+msgstr ""
+
+#: src/ui/gui/psppire-data-window.c:1658
+msgid "Examine Data by Factors"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:635
+#: src/ui/gui/psppire-data-window.c:1672
 msgid "Estimate parameters of the linear model"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1027
-msgid "Font Selection"
+#: src/ui/gui/psppire-data-window.c:1686 src/ui/gui/reliability.glade:7
+msgid "Reliability Analysis"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1099
-msgid "No Split"
+#: src/ui/gui/psppire-data-window.c:1849
+msgid "Split the window vertically and horizontally"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1108
-msgid "Split by "
+#: src/ui/gui/psppire-data-window.c:1891
+msgid "Data Editor"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1133
-msgid "Filter off"
+#: src/ui/gui/psppire-output-window.c:269
+msgid "Output Viewer"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1145
+#: src/ui/gui/psppire-syntax-window.c:265
 #, c-format
-msgid "Filter by %s"
+msgid "Saved file \"%s\""
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1163
-msgid "Weights off"
+#: src/ui/gui/psppire-syntax-window.c:284
+msgid "Save Syntax"
+msgstr ""
+
+#: 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-syntax-window.c:371
+msgid "Open Syntax"
+msgstr ""
+
+#: src/ui/gui/psppire-syntax-window.c:551
+msgid "Syntax Editor"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1175
+#: src/ui/gui/psppire-syntax-window.c:565
 #, c-format
-msgid "Weight by %s"
+msgid "Cannot load syntax file '%s'"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1198 src/ui/gui/data-editor.c:1440
-#: src/ui/gui/data-editor.glade:660
-msgid "Open"
+#: src/ui/gui/psppire-var-sheet.c:533 src/ui/gui/psppire-var-store.c:832
+msgid "Name"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1199
-msgid "Open a data file"
+#: src/ui/gui/psppire-var-sheet.c:534 src/ui/gui/psppire-var-store.c:833
+#: src/ui/gui/psppire.glade:2099
+msgid "Type"
 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/psppire-var-sheet.c:535 src/ui/gui/psppire-var-store.c:834
+#: src/ui/gui/psppire.glade:2020
+msgid "Width"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1208 src/ui/gui/data-editor.c:1218
-msgid "Save data to file"
+#: src/ui/gui/psppire-var-sheet.c:536 src/ui/gui/psppire-var-store.c:835
+msgid "Decimals"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1217
-msgid "Save As"
+#: src/ui/gui/psppire-var-sheet.c:538 src/ui/gui/psppire-var-store.c:837
+msgid "Values"
 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/psppire-var-sheet.c:539 src/ui/gui/psppire-var-store.c:838
+msgid "Missing"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1227
-msgid "New data file"
+#: src/ui/gui/psppire-var-sheet.c:541 src/ui/gui/psppire-var-store.c:840
+msgid "Align"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1235
-msgid "_Import Text Data"
+#: src/ui/gui/psppire-var-sheet.c:542 src/ui/gui/psppire-var-store.c:841
+msgid "Measure"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1236
-msgid "Import text data file"
+#: src/ui/gui/psppire-var-store.c:622 src/ui/gui/var-sheet-dialogs.glade:43
+msgid "Comma"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1331 src/ui/gui/data-editor.c:1448
-msgid "System Files (*.sav)"
+#: src/ui/gui/psppire-var-store.c:623 src/ui/gui/var-sheet-dialogs.glade:59
+msgid "Dot"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1337 src/ui/gui/data-editor.c:1454
-msgid "Portable Files (*.por) "
+#: src/ui/gui/psppire-var-store.c:624
+msgid "Scientific"
 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/psppire-var-store.c:625 src/ui/gui/var-sheet-dialogs.glade:91
+msgid "Date"
+msgstr ""
+
+#: src/ui/gui/psppire-var-store.c:626 src/ui/gui/var-sheet-dialogs.glade:107
+msgid "Dollar"
+msgstr ""
+
+#: src/ui/gui/psppire-var-store.c:627
+msgid "Custom"
+msgstr ""
+
+#: src/ui/gui/psppire-window.c:97
+#, c-format
+msgid "%s %s PSPPIRE %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1351
-msgid "System File"
+#: src/ui/gui/psppire-window.c:480
+#, c-format
+msgid "Save the changes to \"%s\" before closing?"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1356
-msgid "Portable File"
+#: 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/data-editor.c:1498
-msgid "Sort Ascending"
+#: src/ui/gui/psppire-window.c:491
+msgid "Close _without saving"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1501
-msgid "Sort Descending"
+#: src/ui/gui/recode-dialog.c:881
+msgid "Recode into Different Variables"
 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/recode-dialog.c:884
+msgid "Recode into Same Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:1558 src/ui/gui/data-editor.glade:789
-msgid "Insert Case"
+#: src/ui/gui/recode-dialog.c:912 src/ui/gui/recode-dialog.c:1014
+msgid "Old"
 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/recode-dialog.c:927 src/ui/gui/recode-dialog.c:1022
+msgid "New"
 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/recode-dialog.c:1270
+msgid "Recode into Different Variables: Old and New Values "
 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/recode-dialog.c:1271
+msgid "Recode into Same Variables: Old and New Values"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:78
-msgid "_Import Delimited Text Data"
+#: src/ui/gui/regression-dialog.c:41
+msgid "Coeff"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:111
-msgid "Recently Used Da_ta"
+#: src/ui/gui/regression-dialog.c:42
+msgid "R"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:118
-msgid "Recently Used _Files"
+#: src/ui/gui/regression-dialog.c:43
+msgid "Anova"
 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/regression-dialog.c:44
+msgid "Bcov"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:158
-msgid "Insert Cases"
+#: src/ui/gui/select-cases-dialog.c:82
+#, c-format
+msgid "Approximately %3d%% of all cases."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:166 src/ui/gui/data-editor.glade:738
-msgid "Go To Case"
+#: src/ui/gui/select-cases-dialog.c:83
+#, c-format
+msgid "Exactly %3d cases from the first %3d cases."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:207
-msgid "Cl_ear Variables"
+#: src/ui/gui/select-cases-dialog.c:223
+#, c-format
+msgid "%d thru %d"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:215
-msgid "_Clear Cases"
+#: src/ui/gui/text-data-import-dialog.c:461
+#, c-format
+msgid "Could not open \"%s\": %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:239
-msgid "_View"
+#: src/ui/gui/text-data-import-dialog.c:477
+#, c-format
+msgid "Error reading \"%s\": %s"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:246
-msgid "_Status Bar"
+#: 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/data-editor.glade:259
-msgid "_Fonts"
+#: src/ui/gui/text-data-import-dialog.c:494
+#, c-format
+msgid "\"%s\" is empty."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:266
-msgid "_Grid Lines"
+#: src/ui/gui/text-data-import-dialog.c:539
+msgid "Import Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:274
-msgid "Value _Labels"
+#: src/ui/gui/text-data-import-dialog.c:590
+msgid "Importing Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:311
-msgid "_Sort Cases"
+#: 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 ""
 
-#: src/ui/gui/data-editor.glade:332
-msgid "S_plit File"
-msgstr ""
+#: 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/data-editor.glade:347
-msgid "_Weight Cases"
+#: 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: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] ""
+msgstr[1] ""
+
+#: 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/data-editor.glade:359
-msgid "_Transform"
+#: src/ui/gui/text-data-import-dialog.c:1523
+#: src/ui/gui/text-data-import-dialog.c:1768
+msgid "This input line has too few separators to fill in this field."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:409
-msgid "_Run Pending Transforms"
+#: src/ui/gui/text-data-import-dialog.c:1759
+#, c-format
+msgid "Field content \"%.*s\" cannot be parsed in format %s."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:422
-msgid "_Analyze"
+#: src/ui/gui/t-test-options.c:60
+#, c-format
+msgid "Confidence Interval: %2d %%"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:432
-msgid "_Descriptive Statistics"
+#: src/ui/gui/t-test-paired-samples.c:226
+msgid "Var 1"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:478
-msgid "Compare _Means"
+#: src/ui/gui/t-test-paired-samples.c:227
+msgid "Var 2"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:488
-msgid "_One Sample T Test"
+#: src/ui/gui/variable-info-dialog.c:76
+#, c-format
+msgid "Label: %s\n"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:512
-msgid "One Way _ANOVA"
+#: src/ui/gui/variable-info-dialog.c:83
+#, c-format
+msgid "Type: %s\n"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:524
-msgid "Bivariate _Correlation"
+#: src/ui/gui/variable-info-dialog.c:87
+#, c-format
+msgid "Missing Values: %s\n"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:540
-msgid "_Non-Parametric Statistics"
+#: src/ui/gui/variable-info-dialog.c:92
+#, c-format
+msgid "Measurement Level: %s\n"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:550
-msgid "_Chi-Square"
+#: src/ui/gui/variable-info-dialog.c:107
+msgid "Value Labels:\n"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:558
-msgid "_Binomial"
+#: src/ui/gui/variable-info-dialog.c:117
+#, c-format
+msgid "%s %s\n"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:574
-msgid "_Utilities"
+#: src/ui/gui/weight-cases-dialog.c:79 src/ui/gui/psppire.glade:47
+#: src/ui/gui/psppire.glade:130
+msgid "Do not weight cases"
 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/weight-cases-dialog.c:85
+#, c-format
+msgid "Weight cases by %s"
 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/crosstabs.glade:50
+msgid "Rows"
 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/crosstabs.glade:131 src/ui/gui/frequencies.glade:185
+msgid "Format..."
 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/crosstabs.glade:138 src/ui/gui/examine.glade:247
+#: src/ui/gui/regression.glade:31
+msgid "Statistics..."
 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/crosstabs.glade:148
+msgid "Cells..."
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:680
-msgid "Print"
+#: src/ui/gui/crosstabs.glade:230
+msgid "Print tables"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:690
-msgid "Recall"
+#: src/ui/gui/crosstabs.glade:240
+msgid "Pivot"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:708
-msgid "Undo"
+#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:756
+msgid "Ascending"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:718
-msgid "Redo"
+#: src/ui/gui/crosstabs.glade:283
+msgid "No label"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:748
-msgid "Variables"
+#: src/ui/gui/crosstabs.glade:295
+msgid "Suppress value labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:769
-msgid "Find"
+#: src/ui/gui/crosstabs.glade:311
+msgid "Labeling"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:821
-msgid "Split File"
+#: src/ui/gui/crosstabs.glade:378
+msgid "Cell Display"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:832
-msgid "Weight Cases"
+#: src/ui/gui/crosstabs.glade:439 src/ui/gui/oneway.glade:207
+#: src/ui/gui/regression.glade:322
+msgid "Statistics"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:844
-msgid "Select Cases"
+#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
+msgid "Statistics:"
 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/descriptives-dialog.glade:184
+msgid "Exclude entire case if any selected variable is missing"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:875
-msgid "Use Sets"
+#: src/ui/gui/descriptives-dialog.glade:194
+msgid "Include user-missing data in analysis"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:907
-msgid "Information Area"
+#: src/ui/gui/descriptives-dialog.glade:207
+msgid "Save Z-scores of selected variables as new variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:926
-msgid "Processor Area"
+#: src/ui/gui/descriptives-dialog.glade:223
+msgid "Options:"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:951
-msgid "Case Counter Area"
+#: 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.glade:976
-msgid "Filter Use Status Area"
+#: 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:1002
-msgid "Weight Status Area"
+#: 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:1028
-msgid "Split File Status Area"
+#: src/ui/gui/data-editor.glade:70
+msgid "_Import Delimited Text Data"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1058
-msgid "Variable Type"
+#: src/ui/gui/data-editor.glade:103
+msgid "D_isplay Data File Information"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1094 src/ui/gui/psppire-var-store.c:599
-msgid "Comma"
+#: src/ui/gui/data-editor.glade:112
+msgid "Working File"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1110 src/ui/gui/psppire-var-store.c:600
-msgid "Dot"
+#: src/ui/gui/data-editor.glade:119
+msgid "External File"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1126
-msgid "Scientific notation"
+#: src/ui/gui/data-editor.glade:135
+msgid "Recently Used Da_ta"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1142 src/ui/gui/psppire-var-store.c:602
-msgid "Date"
+#: src/ui/gui/data-editor.glade:142
+msgid "Recently Used _Files"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1158 src/ui/gui/psppire-var-store.c:603
-msgid "Dollar"
+#: 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:1174
-msgid "Custom currency"
+#: src/ui/gui/data-editor.glade:182
+msgid "Insert Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1268
-msgid "positive"
+#: src/ui/gui/data-editor.glade:190 src/ui/gui/data-editor.glade:780
+msgid "Go To Case"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1274
-msgid "negative"
+#: src/ui/gui/data-editor.glade:231
+msgid "Cl_ear Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1287
-msgid "Sample"
+#: src/ui/gui/data-editor.glade:239
+msgid "_Clear Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1337
-msgid "Width:"
+#: src/ui/gui/data-editor.glade:252
+msgid "gtk-find"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1381
-msgid "Decimal Places:"
+#: src/ui/gui/data-editor.glade:264
+msgid "_View"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1550
-msgid "Value Label:"
+#: src/ui/gui/data-editor.glade:271
+msgid "_Status Bar"
 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/data-editor.glade:284
+msgid "_Fonts"
 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/data-editor.glade:291
+msgid "_Grid Lines"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1718
-msgid "_Range plus one optional discrete missing value"
+#: src/ui/gui/data-editor.glade:299
+msgid "Value _Labels"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1743
-msgid "_Low:"
+#: src/ui/gui/data-editor.glade:318 src/ui/gui/data-editor.glade:613
+msgid "_Variables"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1772
-msgid "_High:"
+#: src/ui/gui/data-editor.glade:336
+msgid "_Sort Cases"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1813
-msgid "Di_screte value:"
+#: src/ui/gui/data-editor.glade:350
+msgid "_Transpose"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1860
-msgid "_No missing values"
+#: src/ui/gui/data-editor.glade:363
+msgid "S_plit File"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:1878
-msgid "_Discrete missing values"
+#: src/ui/gui/data-editor.glade:371
+msgid "Select _Cases"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
-msgid "Standard deviation"
+#: src/ui/gui/data-editor.glade:378
+msgid "_Weight Cases"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.c:45
-msgid "Standard error"
+#: src/ui/gui/data-editor.glade:390
+msgid "_Transform"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
-msgid "Statistics:"
+#: src/ui/gui/data-editor.glade:400
+msgid "_Compute"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:184
-msgid "Exclude entire case if any selected variable is missing"
+#: src/ui/gui/data-editor.glade:408
+msgid "Ran_k Cases"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:194
-msgid "Include user-missing data in analysis"
+#: src/ui/gui/data-editor.glade:420
+msgid "Recode into _Same Variables"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:207
-msgid "Save Z-scores of selected variables as new variables"
+#: src/ui/gui/data-editor.glade:427
+msgid "Recode into _Different Variables"
 msgstr ""
 
-#: src/ui/gui/descriptives-dialog.glade:223
-msgid "Options:"
+#: src/ui/gui/data-editor.glade:440
+msgid "_Run Pending Transforms"
 msgstr ""
 
-#: src/ui/gui/examine.glade:132
-msgid "Dependent List:"
+#: 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/examine.glade:180
-msgid "Factor List:"
+#: src/ui/gui/data-editor.glade:473
+msgid "_Frequencies"
 msgstr ""
 
-#: src/ui/gui/examine.glade:218
-msgid "Label Cases by:"
+#: src/ui/gui/data-editor.glade:481 src/ui/gui/oneway.glade:179
+msgid "_Descriptives"
 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/data-editor.glade:489
+msgid "_Explore"
 msgstr ""
 
-#: src/ui/gui/examine.glade:316
-msgid "Extremes"
+#: src/ui/gui/data-editor.glade:497
+msgid "_Crosstabs"
 msgstr ""
 
-#: src/ui/gui/examine.glade:382
-msgid "Exclude cases listwise"
+#: src/ui/gui/data-editor.glade:509
+msgid "Compare _Means"
 msgstr ""
 
-#: src/ui/gui/examine.glade:392
-msgid "Exclude cases pairwise"
+#: src/ui/gui/data-editor.glade:519
+msgid "_One Sample T Test"
 msgstr ""
 
-#: src/ui/gui/examine.glade:406
-msgid "Repeat values"
+#: src/ui/gui/data-editor.glade:527
+msgid "_Independent Samples T Test"
 msgstr ""
 
-#: src/ui/gui/find-dialog.c:659
-#, c-format
-msgid "Bad regular expression: %s"
+#: src/ui/gui/data-editor.glade:535
+msgid "_Paired Samples T Test"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:44
-msgid "Standard error of the mean"
+#: src/ui/gui/data-editor.glade:543
+msgid "One Way _ANOVA"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:47
-msgid "Standard error of the skewness"
+#: src/ui/gui/data-editor.glade:554
+msgid "Re_liability"
 msgstr ""
 
-#: src/ui/gui/frequencies-dialog.c:51
-msgid "Standard error of the kurtosis"
+#: src/ui/gui/data-editor.glade:562
+msgid "Linear _Regression"
 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/data-editor.glade:569
+msgid "_Non-Parametric Statistics"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:168
-msgid "Display Frequency Table"
+#: src/ui/gui/data-editor.glade:579
+msgid "_Chi-Square"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:264
-msgid "Ascending Order"
+#: src/ui/gui/data-editor.glade:587
+msgid "_Binomial"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:275
-msgid "Descending Order"
+#: src/ui/gui/data-editor.glade:603
+msgid "_Utilities"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:290
-msgid "Ascending Counts"
+#: src/ui/gui/data-editor.glade:622
+msgid "Data File _Comments"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:305
-msgid "Descending Counts"
+#: 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/frequencies.glade:323
-msgid "Order by"
-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/frequencies.glade:355
-msgid "Supress tables with more than N categories"
+#: src/ui/gui/data-editor.glade:647
+msgid "_Split"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:371
-msgid "Maximum no of categories"
+#: 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/helper.c:139
-msgid "Sorry. The help system hasn't yet been implemented."
+#: 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/helper.c:165
-#, c-format
-msgid "Cannot open reference manual: %s"
+#: 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/message-dialog.c:101
-msgid "data file error"
+#: src/ui/gui/data-editor.glade:722
+msgid "Print"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:106
-msgid "PSPP error"
+#: src/ui/gui/data-editor.glade:732
+msgid "Recall"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:114
-msgid "syntax warning"
+#: src/ui/gui/data-editor.glade:750
+msgid "Undo"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:118
-msgid "data file warning"
+#: src/ui/gui/data-editor.glade:760
+msgid "Redo"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:123
-msgid "PSPP warning"
+#: src/ui/gui/data-editor.glade:790
+msgid "Variables"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:132
-msgid "syntax information"
+#: src/ui/gui/data-editor.glade:811
+msgid "Find"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:136
-msgid "data file information"
+#: src/ui/gui/data-editor.glade:863
+msgid "Split File"
 msgstr ""
 
-#: src/ui/gui/message-dialog.c:141
-msgid "PSPP information"
+#: src/ui/gui/data-editor.glade:874
+msgid "Weight Cases"
 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/data-editor.glade:886
+msgid "Select Cases"
+msgstr ""
 
-#: 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/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/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/data-editor.glade:917
+msgid "Use Sets"
+msgstr ""
 
-#: src/ui/gui/message-dialog.glade:8
-msgid "Messages Reported"
+#: src/ui/gui/data-editor.glade:938
+msgid "Information Area"
 msgstr ""
 
-#: src/ui/gui/message-dialog.glade:42
-msgid ""
-"The PSPP processor reported # errors.  The first # and last # are shown "
-"below:"
+#: src/ui/gui/data-editor.glade:957
+msgid "Processor Area"
 msgstr ""
 
-#: src/ui/gui/message-dialog.glade:94
-msgid "gtk-close"
+#: src/ui/gui/data-editor.glade:982
+msgid "Case Counter Area"
 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/data-editor.glade:1007
+msgid "Filter Use Status Area"
 msgstr ""
 
-#: src/ui/gui/missing-val-dialog.c:136 src/ui/gui/missing-val-dialog.c:143
-msgid "Incorrect range specification"
+#: src/ui/gui/data-editor.glade:1033
+msgid "Weight Status Area"
 msgstr ""
 
-#: src/ui/gui/oneway-anova-dialog.c:335
-#, c-format
-msgid "Contrast %d of %d"
+#: src/ui/gui/data-editor.glade:1059
+msgid "Split File Status Area"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:30
-msgid "_Factor:"
+#: src/ui/gui/examine.glade:49
+msgid "Label Cases by:"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:66
-msgid "Dependent _Variable(s):"
+#: src/ui/gui/examine.glade:100
+msgid "Factor List:"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:190
-msgid "_Homogeneity"
+#: src/ui/gui/examine.glade:150
+msgid "Dependent List:"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:226
-msgid "_Contrasts..."
+#: 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/oneway.glade:309
-msgid "gtk-go-back"
+#: src/ui/gui/examine.glade:310
+msgid "Descriptives"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:320
-msgid "gtk-go-forward"
+#: src/ui/gui/examine.glade:320
+msgid "Extremes"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:343
-msgid "_Coefficients:"
+#: src/ui/gui/examine.glade:388
+msgid "Exclude cases listwise"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:389
-msgid "Coefficient Total: "
+#: src/ui/gui/examine.glade:399
+msgid "Exclude cases pairwise"
 msgstr ""
 
-#: src/ui/gui/oneway.glade:422
-msgid "Contrast 1 of 1"
+#: src/ui/gui/examine.glade:414
+msgid "Repeat values"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:32
-msgid "gtk-save"
+#: 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/output-viewer.glade:41
-msgid "gtk-save-as"
+#: src/ui/gui/find.glade:80
+msgid "Variable:"
 msgstr ""
 
-#: src/ui/gui/output-viewer.glade:65
-msgid "gtk-copy"
+#: 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/psppire-buttonbox.c:143
-msgid "Buttons"
+#: src/ui/gui/find.glade:137
+msgid "Search value labels"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:144
-msgid "The mask that decides what buttons appear in the button box"
+#: src/ui/gui/find.glade:161
+msgid "Regular expression Match"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:429
-msgid "Continue"
+#: src/ui/gui/find.glade:172
+msgid "Search substrings"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:427
-msgid "OK"
+#: src/ui/gui/find.glade:185
+msgid "Wrap around"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:428
-msgid "Go To"
+#: src/ui/gui/find.glade:198
+msgid "Search backward"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:430
-msgid "Cancel"
+#: 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/psppire-buttonbox.c:431
-msgid "Help"
+#: src/ui/gui/frequencies.glade:168
+msgid "Display Frequency Table"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:432
-msgid "Reset"
+#: src/ui/gui/frequencies.glade:264
+msgid "Ascending Order"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:433
-msgid "Paste"
+#: src/ui/gui/frequencies.glade:275
+msgid "Descending Order"
 msgstr ""
 
-#: src/ui/gui/psppire.c:194
-msgid "_Reset"
+#: src/ui/gui/frequencies.glade:290
+msgid "Ascending Counts"
 msgstr ""
 
-#: src/ui/gui/psppire.c:195
-msgid "_Select"
+#: src/ui/gui/frequencies.glade:305
+msgid "Descending Counts"
 msgstr ""
 
-#: src/ui/gui/psppire-data-editor.c:604
-msgid "Data View"
+#: src/ui/gui/frequencies.glade:323
+msgid "Order by"
 msgstr ""
 
-#: src/ui/gui/psppire-data-editor.c:607
-msgid "Variable View"
+#: src/ui/gui/frequencies.glade:355
+msgid "Supress tables with more than N categories"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:828
-msgid "var"
+#: src/ui/gui/frequencies.glade:371
+msgid "Maximum no of categories"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:949 src/ui/gui/psppire-var-store.c:840
-#, c-format
-msgid "%ld"
+#: src/ui/gui/message-dialog.glade:10
+msgid "Messages Reported"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:11
+#: src/ui/gui/message-dialog.glade:47
 msgid ""
-"This is beta status software.  Please report bugs to bug-gnu-pspp@gnu.org"
+"The PSPP processor reported # errors.  The first # and last # are shown "
+"below:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:73 src/ui/gui/psppire.glade:154
-#: src/ui/gui/weight-cases-dialog.c:80
-msgid "Do not weight cases"
+#: src/ui/gui/message-dialog.glade:101
+msgid "gtk-close"
 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
-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:"
-msgstr ""
-
-#: src/ui/gui/psppire.glade:2570
-msgid "Search value labels"
-msgstr ""
-
-#: src/ui/gui/psppire.glade:2593
-msgid "Regular expression Match"
+#: src/ui/gui/oneway.glade:30
+msgid "_Factor:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2603
-msgid "Search substrings"
+#: src/ui/gui/oneway.glade:66
+msgid "Dependent _Variable(s):"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2615
-msgid "Wrap around"
+#: src/ui/gui/oneway.glade:190
+msgid "_Homogeneity"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2627
-msgid "Search backward"
+#: src/ui/gui/oneway.glade:226
+msgid "_Contrasts..."
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:100
-msgid "Name"
+#: src/ui/gui/oneway.glade:309
+msgid "gtk-go-back"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:103
-msgid "Decimals"
+#: src/ui/gui/oneway.glade:320
+msgid "gtk-go-forward"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:105
-msgid "Values"
+#: src/ui/gui/oneway.glade:343
+msgid "_Coefficients:"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:108
-msgid "Align"
+#: src/ui/gui/oneway.glade:389
+msgid "Coefficient Total: "
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:109
-msgid "Measure"
+#: src/ui/gui/oneway.glade:422
+msgid "Contrast 1 of 1"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:601
-msgid "Scientific"
+#: src/ui/gui/output-viewer.glade:32
+msgid "gtk-save"
 msgstr ""
 
-#: src/ui/gui/psppire-var-store.c:604
-msgid "Custom"
+#: src/ui/gui/output-viewer.glade:41
+msgid "gtk-save-as"
 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
-#, c-format
-msgid "%d"
+#: src/ui/gui/output-viewer.glade:65
+msgid "gtk-copy"
 msgstr ""
 
-#: src/ui/gui/rank.glade:111
+#: src/ui/gui/rank.glade:57
 msgid "By:"
 msgstr ""
 
-#: src/ui/gui/rank.glade:197
+#: src/ui/gui/rank.glade:196
 msgid "_Smallest Value"
 msgstr ""
 
-#: src/ui/gui/rank.glade:209
+#: src/ui/gui/rank.glade:208
 msgid "_Largest Value"
 msgstr ""
 
-#: src/ui/gui/rank.glade:228
+#: src/ui/gui/rank.glade:227
 msgid "Assign rank 1 to:"
 msgstr ""
 
-#: src/ui/gui/rank.glade:246
+#: src/ui/gui/rank.glade:245
 msgid "_Display summary tables"
 msgstr ""
 
-#: src/ui/gui/rank.glade:262
+#: src/ui/gui/rank.glade:261
 msgid "Rank T_ypes"
 msgstr ""
 
-#: src/ui/gui/rank.glade:273
+#: src/ui/gui/rank.glade:272
 msgid "_Ties..."
 msgstr ""
 
-#: src/ui/gui/rank.glade:343
-msgid "Ntiles"
+#: src/ui/gui/rank.glade:339
+msgid "Sum of case weights"
 msgstr ""
 
-#: src/ui/gui/rank.glade:376
-msgid "Rank"
+#: src/ui/gui/rank.glade:355
+msgid "Fractional rank as %"
 msgstr ""
 
-#: src/ui/gui/rank.glade:386
-msgid "Savage score"
+#: src/ui/gui/rank.glade:369
+msgid "Fractional rank"
 msgstr ""
 
-#: src/ui/gui/rank.glade:400
-msgid "Fractional rank"
+#: src/ui/gui/rank.glade:383
+msgid "Savage score"
 msgstr ""
 
-#: src/ui/gui/rank.glade:414
-msgid "Fractional rank as %"
+#: src/ui/gui/rank.glade:397
+msgid "Rank"
 msgstr ""
 
-#: src/ui/gui/rank.glade:428
-msgid "Sum of case weights"
+#: src/ui/gui/rank.glade:411
+msgid "Ntiles"
 msgstr ""
 
 #: src/ui/gui/rank.glade:450
@@ -5922,66 +5338,46 @@ 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
-msgid "Recode into Different Variables"
-msgstr ""
-
-#: src/ui/gui/recode-dialog.c:882
-msgid "Recode into Same Variables"
-msgstr ""
-
-#: src/ui/gui/recode-dialog.c:913 src/ui/gui/recode-dialog.c:1015
-msgid "Old"
-msgstr ""
-
-#: src/ui/gui/recode-dialog.c:1274
-msgid "Recode into Different Variables: Old and New Values "
-msgstr ""
-
-#: src/ui/gui/recode-dialog.c:1275
-msgid "Recode into Same Variables: Old and New Values"
-msgstr ""
-
 #: src/ui/gui/recode.glade:197
 msgid "System-Missing"
 msgstr ""
@@ -6062,18 +5458,6 @@ msgstr ""
 msgid "Old and New Values"
 msgstr ""
 
-#: src/ui/gui/regression-dialog.c:40
-msgid "Coeff"
-msgstr ""
-
-#: src/ui/gui/regression-dialog.c:42
-msgid "Anova"
-msgstr ""
-
-#: src/ui/gui/regression-dialog.c:43
-msgid "Bcov"
-msgstr ""
-
 #: src/ui/gui/regression.glade:40
 msgid "Save..."
 msgstr ""
@@ -6094,150 +5478,49 @@ 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."
-msgstr ""
-
-#: src/ui/gui/select-cases-dialog.c:222
-#, c-format
-msgid "%d thru %d"
-msgstr ""
-
-#: src/ui/gui/syntax-editor.c:77
-#, c-format
-msgid "Save contents of syntax editor to %s?"
-msgstr ""
-
-#: src/ui/gui/syntax-editor.c:124
-msgid "Save Syntax"
+#: src/ui/gui/reliability.glade:89
+msgid "_Items:"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:132 src/ui/gui/syntax-editor.c:516
-msgid "Syntax Files (*.sps) "
+#: src/ui/gui/reliability.glade:111
+msgid "Model:\t"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:508
-msgid "Open Syntax"
+#: src/ui/gui/reliability.glade:122
+msgid ""
+"Alpha\n"
+"Split"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:10
-msgid "Psppire Syntax Editor"
+#: src/ui/gui/reliability.glade:144
+msgid "Variables in first split:"
 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
-#, c-format
-msgid "Could not open \"%s\": %s"
-msgstr ""
-
-#: src/ui/gui/text-data-import-dialog.c:504
-#, c-format
-msgid "Error reading \"%s\": %s"
-msgstr ""
-
-#: src/ui/gui/text-data-import-dialog.c:507
-#, 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
-#, c-format
-msgid "\"%s\" is empty."
-msgstr ""
-
-#: src/ui/gui/text-data-import-dialog.c:566
-msgid "Import Delimited Text Data"
-msgstr ""
-
-#: src/ui/gui/text-data-import-dialog.c:617
-msgid "Importing Delimited Text Data"
-msgstr ""
-
-#: src/ui/gui/text-data-import-dialog.c:768
-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 ""
-
-#: src/ui/gui/text-data-import-dialog.c:774
-#, 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
-#, 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
-#, 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] ""
-msgstr[1] ""
-
-#: src/ui/gui/text-data-import-dialog.c:795
-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
-msgid "This input line has too few separators to fill in this field."
-msgstr ""
-
-#: src/ui/gui/text-data-import-dialog.c:1750
-#, 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 +5531,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 ""
 
@@ -6406,172 +5682,83 @@ msgstr ""
 msgid "Test Value: "
 msgstr ""
 
-#: src/ui/gui/t-test-options.c:60
-#, c-format
-msgid "Confidence Interval: %2d %%"
-msgstr ""
-
-#: src/ui/gui/t-test-paired-samples.c:228
-msgid "Var 1"
-msgstr ""
-
-#: src/ui/gui/t-test-paired-samples.c:229
-msgid "Var 2"
-msgstr ""
-
-#: src/ui/gui/variable-info-dialog.c:89
-#, c-format
-msgid "Label: %s\n"
-msgstr ""
-
-#: src/ui/gui/variable-info-dialog.c:98
-#, c-format
-msgid "Type: %s\n"
-msgstr ""
-
-#: src/ui/gui/variable-info-dialog.c:102
-#, c-format
-msgid "Missing Values: %s\n"
+#: src/ui/gui/var-sheet-dialogs.glade:7
+msgid "Variable Type"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:107
-#, c-format
-msgid "Measurement Level: %s\n"
+#: src/ui/gui/var-sheet-dialogs.glade:75
+msgid "Scientific notation"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:121
-msgid "Value Labels:\n"
+#: src/ui/gui/var-sheet-dialogs.glade:123
+msgid "Custom currency"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:133
-#, c-format
-msgid "%s %s\n"
+#: src/ui/gui/var-sheet-dialogs.glade:217
+msgid "positive"
 msgstr ""
 
-#: src/ui/gui/weight-cases-dialog.c:86
-#, c-format
-msgid "Weight cases by %s"
+#: src/ui/gui/var-sheet-dialogs.glade:223
+msgid "negative"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:142
-#, c-format
-msgid "Syntax%d"
+#: src/ui/gui/var-sheet-dialogs.glade:236
+msgid "Sample"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:143 src/ui/gui/window-manager.c:178
-#, c-format
-msgid "%s --- PSPP Syntax Editor"
+#: src/ui/gui/var-sheet-dialogs.glade:286
+msgid "Width:"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:146
-#, c-format
-msgid "Untitled%d"
+#: src/ui/gui/var-sheet-dialogs.glade:330
+msgid "Decimal Places:"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:147 src/ui/gui/window-manager.c:181
-#, c-format
-msgid "%s --- PSPP Data Editor"
+#: src/ui/gui/var-sheet-dialogs.glade:499
+msgid "Value Label:"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:150
-#, c-format
-msgid "Output%d"
+#: src/ui/gui/var-sheet-dialogs.glade:677
+msgid "_No missing values"
 msgstr ""
 
-#: src/ui/gui/window-manager.c:151
-#, c-format
-msgid "%s --- PSPP Output"
+#: src/ui/gui/var-sheet-dialogs.glade:747
+msgid "_Discrete missing values"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:230
-#, 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"
+#: src/ui/gui/var-sheet-dialogs.glade:782
+msgid "_Low:"
 msgstr ""
 
-#: src/ui/terminal/command-line.c:265
-#, c-format
-msgid ""
-"\n"
-"Report bugs to <%s>.\n"
+#: src/ui/gui/var-sheet-dialogs.glade:801
+msgid "_High:"
 msgstr ""
 
-#: src/ui/terminal/main.c:130
-msgid ""
-"Stopping syntax file processing here to avoid a cascade of dependent command "
-"failures."
+#: src/ui/gui/var-sheet-dialogs.glade:826
+msgid "Di_screte value:"
 msgstr ""
 
-#: src/ui/terminal/msg-ui.c:67
-#, c-format
-msgid "Cannot open %s (%s). Writing errors to stdout instead.\n"
+#: src/ui/gui/var-sheet-dialogs.glade:856
+msgid "_Range plus one optional discrete missing value"
 msgstr ""
 
-#: src/ui/terminal/msg-ui.c:94
-msgid "Terminating execution of syntax file due to error."
+#: src/ui/gui/variable-info-dialog.glade:49
+msgid "Variable Information:"
 msgstr ""
 
-#: src/ui/terminal/msg-ui.c:96
+#: tests/dissect-sysfile.c:528
 #, c-format
-msgid "Errors (%d) exceeds limit (%d)."
-msgstr ""
+msgid "Unrecognized record type 7, subtype %d."
+msgstr "Unrecognised record type 7, subtype %d."
 
-#: src/ui/terminal/msg-ui.c:99
+#: tests/dissect-sysfile.c:701
 #, c-format
-msgid "Warnings (%d) exceed limit (%d)."
-msgstr ""
-
-#: src/ui/terminal/msg-ui.c:150
-msgid "error"
-msgstr ""
-
-#: src/ui/terminal/msg-ui.c:151
-msgid "warning"
+msgid "%s: Error parsing attribute value %s[%d]"
 msgstr ""
 
-#: src/ui/terminal/terminal.c:72
+#: tests/dissect-sysfile.c:707
 #, c-format
-msgid "could not access definition for terminal `%s'"
+msgid "%s: Attribute value %s[%d] is not quoted: %s"
 msgstr ""
 
 #~ msgid ""
index afc72e313b068cd4fe5970e24bd8f3fedadc0eaf..0f838d8fb03a1e683306de548070d30756d66fb6 100644 (file)
--- a/po/nl.po
+++ b/po/nl.po
@@ -1,4 +1,4 @@
-# translation of pspp-0.6.2-pre4.po to Dutch
+# translation of pspp-0.7.2-pre1.po to Dutch
 # Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
 # This file is distributed under the same licence as the PSPP package.#
 #
 # Harry Thijssen <pspp@sjpaes.nl>, 2009.
 msgid ""
 msgstr ""
-"Project-Id-Version: pspp-0.6.2-pre4\n"
+"Project-Id-Version: pspp-0.7.2-pre1\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2009-07-08 20:24-0700\n"
-"PO-Revision-Date: 2009-09-10 21:00+0200\n"
+"POT-Creation-Date: 2009-09-08 21:57-0700\n"
+"PO-Revision-Date: 2009-11-16 18:38+0100\n"
 "Last-Translator: Harry Thijssen <pspp@sjpaes.nl>\n"
 "Language-Team: Dutch <vertaling@vrijschrift.org>\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"
+"X-Generator: Lokalize 1.0\n"
+
+#: 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 "OK"
+
+#: 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 "Help"
+
+#: src/ui/gui/psppire-buttonbox.c:438
+msgid "Reset"
+msgstr "Herstel"
+
+#: src/ui/gui/psppire-buttonbox.c:439
+msgid "Paste"
+msgstr "Plak"
+
+#: 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 "Een predicaat functie"
+
+#: src/ui/gui/psppire-dictview.c:221
+msgid "How many things can be selected"
+msgstr "Hoeveel dingen kunnen geselecteerd worden"
+
+#: src/ui/gui/psppire-dictview.c:491 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/language/data-io/data-parser.c:650
+#: src/language/data-io/data-parser.c:691 src/language/data-io/print.c:404
+msgid "Variable"
+msgstr "Variabele"
+
+#: src/ui/gui/psppire-dictview.c:528
+msgid "Prefer variable labels"
+msgstr "Prefereer variable labels"
 
 #: src/data/any-reader.c:57
 #, c-format
@@ -68,156 +130,131 @@ msgstr "Dag %d valt niet in het acceptabele bereik van 0 tot 31."
 msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
 msgstr "Datum %04d-%d-%d ligt voor de eerste acceptabele datum van 1582-10-15."
 
-#: src/data/case-tmpfile.c:57
-#, c-format
-msgid "failed to create temporary file"
-msgstr "aanmaken van tijdelijk bestand is mislukt"
-
-#: src/data/case-tmpfile.c:131
-#, c-format
-msgid "seeking in temporary file"
-msgstr "zoeken in tijdelijk bestand"
-
-#: src/data/case-tmpfile.c:153
-#, c-format
-msgid "reading temporary file"
-msgstr "lezen tijdelijk bestand"
-
-#: src/data/case-tmpfile.c:155
-#, c-format
-msgid "unexpected end of file reading temporary file"
-msgstr "onverwacht einde-bestand bij het lezen van tijdelijk bestand"
-
-#: src/data/case-tmpfile.c:175
-#, c-format
-msgid "writing to temporary file"
-msgstr "schrijven naar tijdelijk bestand"
-
 #: 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 "Tenminste een case in de gelezen gegevens heeft een gewichtwaarde 'user-missing, system-missing, zero, of negatief.  Deze case(s) zijn genegeerd."
 
-#: src/data/data-in.c:262 src/data/data-in.c:452
+#: src/data/data-in.c:274 src/data/data-in.c:464
 msgid "Field contents are not numeric."
 msgstr "Veldinhoud is niet numeriek."
 
-#: src/data/data-in.c:264 src/data/data-in.c:454
+#: src/data/data-in.c:276 src/data/data-in.c:466
 msgid "Number followed by garbage."
 msgstr "Nummer gevolgd door rommel."
 
-#: src/data/data-in.c:275
+#: src/data/data-in.c:287
 msgid "Invalid numeric syntax."
 msgstr "Ongeldige numerieke syntax."
 
-#: src/data/data-in.c:284 src/data/data-in.c:467
+#: src/data/data-in.c:296 src/data/data-in.c:479
 msgid "Too-large number set to system-missing."
 msgstr "Te groot getal, is op system-missing gezet."
 
-#: src/data/data-in.c:289 src/data/data-in.c:472
+#: src/data/data-in.c:301 src/data/data-in.c:484
 msgid "Too-small number set to zero."
 msgstr "Te klein getal, is op nul gezet."
 
-#: src/data/data-in.c:315
+#: src/data/data-in.c:327
 msgid "All characters in field must be digits."
 msgstr "Alle karakters in veld moeten cijfers zijn."
 
-#: src/data/data-in.c:338
+#: src/data/data-in.c:350
 msgid "Unrecognized character in field."
 msgstr "Onherkenbaar karakter in veld."
 
-#: src/data/data-in.c:362 src/data/data-in.c:636
+#: src/data/data-in.c:374 src/data/data-in.c:650
 msgid "Field must have even length."
 msgstr "Veld moet een even lengte hebben."
 
-#: src/data/data-in.c:367 src/data/data-in.c:647
+#: src/data/data-in.c:379 src/data/data-in.c:661
 msgid "Field must contain only hex digits."
 msgstr "Veld mag alleen hexadecimale cijfers bevatten."
 
-#: src/data/data-in.c:686 src/data/data-in.c:733
+#: src/data/data-in.c:700 src/data/data-in.c:747
 msgid "Syntax error in date field."
 msgstr "Syntaxfout in datumveld."
 
-#: src/data/data-in.c:702
+#: src/data/data-in.c:716
 #, c-format
 msgid "Day (%ld) must be between 1 and 31."
 msgstr "Dag (%ld) moet tussen 1 en 31 liggen."
 
-#: src/data/data-in.c:749
+#: src/data/data-in.c:763
 msgid "Delimiter expected between fields in date."
 msgstr "Veldscheider verwacht tussen velden in datum."
 
-#: src/data/data-in.c:823
+#: src/data/data-in.c:837
 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 format.  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:850
+#: src/data/data-in.c:864
 #, c-format
 msgid "Year (%ld) must be between 1582 and 19999."
 msgstr "Jaar (%ld) moet tussen 1582 en 19999 liggen."
 
-#: src/data/data-in.c:862
+#: src/data/data-in.c:876
 #, c-format
 msgid "Trailing garbage \"%.*s\" following date."
 msgstr "Afsluitende rommel \"%.*s\" achter datum."
 
-#: src/data/data-in.c:878
+#: src/data/data-in.c:892
 msgid "Julian day must have exactly three digits."
 msgstr "Juliaanse datum moet bestaan uit precies 3 cijfers."
 
-#: src/data/data-in.c:883
+#: src/data/data-in.c:897
 #, 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:907
+#: src/data/data-in.c:921
 #, 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:927
+#: src/data/data-in.c:941
 #, 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:940
+#: src/data/data-in.c:954
 msgid "Delimiter expected between fields in time."
 msgstr "Veldscheider verwacht tussen velden in tijd."
 
-#: src/data/data-in.c:960
+#: src/data/data-in.c:974
 #, 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:1000
+#: src/data/data-in.c:1014
 msgid "Unrecognized weekday name.  At least the first two letters of an English weekday name must be specified."
 msgstr "Niet-herkende weekdagnaam.  Tenminste de eerste 2 letters van een Engelse weekdagnaam moeten opgegeven worden."
 
-#: src/data/data-in.c:1138
+#: src/data/data-in.c:1152
 #, c-format
 msgid "`%c' expected in date field."
 msgstr "`%c' verwacht in datumveld."
 
-#: src/data/data-in.c:1179
+#: src/data/data-in.c:1193
 #, c-format
 msgid "column %d"
 msgstr "kolom %d"
 
-#: src/data/data-in.c:1181
+#: src/data/data-in.c:1195
 #, c-format
 msgid "columns %d-%d"
 msgstr "kolommen %d-%d"
 
-#: src/data/data-in.c:1185
+#: src/data/data-in.c:1199
 #, c-format
 msgid "%s field) "
 msgstr "%s veld) "
 
-#: src/data/data-out.c:446
+#: src/data/data-out.c:481
 #, c-format
 msgid "Weekday number %f is not between 1 and 7."
 msgstr "Weekdagnummer %f ligt niet tussen 1 en 7."
 
-#: src/data/data-out.c:467
+#: src/data/data-out.c:502
 #, c-format
 msgid "Month number %f is not between 1 and 12."
 msgstr "Maandnummer %f is niet tussen 1 en 12."
@@ -232,13 +269,13 @@ msgstr "systeem"
 
 #: src/data/dict-class.c:56
 msgid "scratch"
-msgstr ""
+msgstr "scratch"
 
-#: 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 "Op zijn minst een case in het gegevensbestand heeft een gewichtwaarde user-missing, system-missing, nul, of negatief.  Deze case(s) zijn genegeerd."
 
-#: src/data/dictionary.c:1180
+#: src/data/dictionary.c:1263
 #, c-format
 msgid "Truncating document line to %d bytes."
 msgstr "Documentregel afgekapt tot %d bytes."
@@ -275,7 +312,7 @@ msgstr "...niet gevonden"
 #: src/data/file-name.c:243
 #, c-format
 msgid "Not opening pipe file `%s' because SAFER option set."
-msgstr ""
+msgstr "Pijpbestand `%s' wordt niet geopend omdat de SAFER optie is gezet."
 
 #: src/data/format.c:235
 msgid "Input format"
@@ -326,33 +363,33 @@ msgstr[1] "%s %s specificeert %d decimalen, maar de opgegeven breedte staat geen
 msgid "%s variables are not compatible with %s format %s."
 msgstr "%s variabelen zijn niet overeenkomstig met %s format %s."
 
-#: src/data/format.c:327 src/data/sys-file-reader.c:651
-#: src/ui/gui/data-editor.glade:1190 src/ui/gui/psppire-var-store.c:605
-#: src/ui/gui/psppire.glade:2176
+#: src/data/format.c:327 src/data/sys-file-reader.c:734
+#: src/ui/gui/psppire-var-store.c:628 src/ui/gui/psppire.glade:2009
+#: src/ui/gui/var-sheet-dialogs.glade:139
 msgid "String"
 msgstr "Tekenreeks"
 
-#: src/data/format.c:327 src/data/sys-file-reader.c:651
-#: src/ui/gui/data-editor.glade:1079 src/ui/gui/psppire-var-store.c:598
-#: src/ui/gui/psppire.glade:2131
+#: src/data/format.c:327 src/data/sys-file-reader.c:734
+#: src/ui/gui/psppire-var-store.c:621 src/ui/gui/psppire.glade:2084
+#: src/ui/gui/var-sheet-dialogs.glade:28
 msgid "Numeric"
 msgstr "Numeriek"
 
-#: src/data/format.c:328 src/data/sys-file-reader.c:1162
-#: src/data/sys-file-reader.c:1164
+#: src/data/format.c:328 src/data/sys-file-reader.c:1299
+#: src/data/sys-file-reader.c:1301 src/language/xforms/recode.c:493
+#: src/language/xforms/recode.c:494 src/language/xforms/recode.c:506
+#: src/language/xforms/recode.c:507
 #: 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
 msgid "numeric"
 msgstr "numeriek"
 
-#: src/data/format.c:328 src/data/sys-file-reader.c:1162
-#: src/data/sys-file-reader.c:1164
+#: src/data/format.c:328 src/data/sys-file-reader.c:1299
+#: src/data/sys-file-reader.c:1301 src/language/xforms/recode.c:493
+#: src/language/xforms/recode.c:494 src/language/xforms/recode.c:506
+#: src/language/xforms/recode.c:507
 #: 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
 msgid "string"
 msgstr "tekenreeks"
 
@@ -361,26 +398,26 @@ msgstr "tekenreeks"
 msgid "String variable with width %d is not compatible with format %s."
 msgstr "Tekenreeksvariabele met breedte %d is niet overeenkomstig met format %s."
 
-#: 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 "Ondersteuning voor Gnumeric-bestanden is niet gecompileerd in deze installatie van PSPP"
 
-#: 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 "Fout bij het openen van \"%s\" voor het lezen als een Gnumeric-bestand: %s."
 
-#: src/data/gnumeric-reader.c:386
+#: src/data/gnumeric-reader.c:388
 #, c-format
 msgid "Invalid cell range \"%s\""
 msgstr "Ongeldig celbereik \"%s\""
 
-#: 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 "Kan geen variabelennaam creëren van %s"
 
-#: 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 "Geselecteerd blad of bereik van werkblad \"%s\" is leeg."
@@ -451,173 +488,173 @@ 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: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 "overdraagbaar bestand"
 
-#: 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 "Fout opgetreden tijdens het openen van \"%s\" voor het lezen als overdraagbaar bestand: %s."
 
-#: src/data/por-file-reader.c:297
+#: src/data/por-file-reader.c:296
 msgid "Data record expected."
 msgstr "Gegevensrecord verwacht."
 
-#: src/data/por-file-reader.c:379
+#: src/data/por-file-reader.c:378
 msgid "Number expected."
 msgstr "Nummer verwacht."
 
-#: src/data/por-file-reader.c:407
+#: src/data/por-file-reader.c:406
 msgid "Missing numeric terminator."
 msgstr "Ontbrekende numerieke afsluiter."
 
-#: src/data/por-file-reader.c:430
+#: src/data/por-file-reader.c:429
 msgid "Invalid integer."
 msgstr "Ongeldige integer."
 
-#: src/data/por-file-reader.c:441
+#: src/data/por-file-reader.c:440 src/data/por-file-reader.c:460
 #, c-format
 msgid "Bad string length %d."
 msgstr "Onjuiste tekenreekslengte %d."
 
-#: src/data/por-file-reader.c:502
+#: src/data/por-file-reader.c:523
 #, c-format
 msgid "%s: Not a portable file."
 msgstr "%s: Geen overdraagbaar bestand."
 
-#: src/data/por-file-reader.c:519
+#: src/data/por-file-reader.c:540
 #, c-format
 msgid "Unrecognized version code `%c'."
 msgstr "Niet-herkende versiecode `%c'."
 
-#: src/data/por-file-reader.c:528
+#: src/data/por-file-reader.c:549
 #, c-format
 msgid "Bad date string length %zu."
 msgstr "Onjuiste datum-tekenreekslengte %zu."
 
-#: src/data/por-file-reader.c:530
+#: src/data/por-file-reader.c:551
 #, c-format
 msgid "Bad time string length %zu."
 msgstr "Onjuiste tijd-tekenreekslengte %zu."
 
-#: src/data/por-file-reader.c:572
+#: src/data/por-file-reader.c:593
 #, c-format
 msgid "%s: Bad format specifier byte (%d).  Variable will be assigned a default format."
 msgstr "%s: Onjuist format-specificatie byte (%d). Variabele krijgt een verstek format."
 
-#: src/data/por-file-reader.c:593
+#: src/data/por-file-reader.c:614
 #, c-format
 msgid "Numeric variable %s has invalid format specifier %s."
 msgstr "Numerieke variabele %s heeft een ongeldig format-specificatie %s."
 
-#: src/data/por-file-reader.c:597
+#: src/data/por-file-reader.c:618
 #, c-format
 msgid "String variable %s with width %d has invalid format specifier %s."
 msgstr "Tekenreeksvariabele %s met breedte %d heeft ongeldig format-specificatie %s."
 
-#: src/data/por-file-reader.c:621
+#: src/data/por-file-reader.c:642
 msgid "Expected variable count record."
 msgstr "Variabelenteller record verwacht."
 
-#: src/data/por-file-reader.c:625
+#: src/data/por-file-reader.c:646
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr "Ongeldig aantal variabelen %d."
 
-#: src/data/por-file-reader.c:635
+#: src/data/por-file-reader.c:655
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr "Wegingvariabelennaam (%s) afgekapt."
 
-#: src/data/por-file-reader.c:650
+#: src/data/por-file-reader.c:670
 msgid "Expected variable record."
 msgstr "Variabelenrecord verwacht."
 
-#: src/data/por-file-reader.c:654
+#: src/data/por-file-reader.c:674
 #, c-format
 msgid "Invalid variable width %d."
 msgstr "Ongeldige variabelenbreedte %d."
 
-#: src/data/por-file-reader.c:662
+#: src/data/por-file-reader.c:681
 #, c-format
 msgid "Invalid variable name `%s' in position %d."
 msgstr "Ongeldige variabelennaam '%s' in positie %d."
 
-#: src/data/por-file-reader.c:666
+#: src/data/por-file-reader.c:685 src/data/sys-file-reader.c:592
 #, c-format
 msgid "Bad width %d for variable %s."
 msgstr "Onjuiste breedte %d voor variabele %s."
 
-#: src/data/por-file-reader.c:681
+#: src/data/por-file-reader.c:700
 #, c-format
 msgid "Duplicate variable name %s in position %d."
 msgstr "Dubbele variabelennaam %s in positie %d."
 
-#: src/data/por-file-reader.c:682
+#: src/data/por-file-reader.c:701
 #, c-format
 msgid "Duplicate variable name %s in position %d renamed to %s."
 msgstr "Dubbele variabelennaam %s in positie %d hernoemd naar %s."
 
-#: src/data/por-file-reader.c:725
+#: src/data/por-file-reader.c:750
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr "Wegingvariabele %s niet aanwezig in woordenboek."
 
-#: src/data/por-file-reader.c:772
+#: src/data/por-file-reader.c:794
 #, c-format
 msgid "Unknown variable %s while parsing value labels."
 msgstr "Onbekende variabele %s tijdens het ontleden van waardelabels."
 
-#: src/data/por-file-reader.c:775
+#: src/data/por-file-reader.c:797
 #, c-format
 msgid "Cannot assign value labels to %s and %s, which have different variable types."
 msgstr "Kan geen waardelabels toekennen aan %s en %s, die verschillende variabelentypes hebben."
 
-#: 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 "Ongeldige decimaal cijfers teller %d. Behandeld als %d."
 
-#: 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 "Fout tijdens openen \"%s\" voor het schrijven als een overdraagbaar bestand: %s."
 
-#: src/data/por-file-writer.c:500
+#: 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 bestand \"%s\"."
 
-#: 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 "Ondersteuning voor het lezen van postgres databases was niet gecompileerd in deze installatie van PSPP"
 
-#: src/data/psql-reader.c:239
+#: src/data/psql-reader.c:242
 msgid "Memory error whilst opening psql source"
 msgstr "Geheugenfout tijdens het openen van psql source"
 
-#: src/data/psql-reader.c:245
+#: 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: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 "Postgres server is versie %s. Lezen van versies ouder dan 8.0 wordt niet ondersteund."
 
-#: src/data/psql-reader.c:280
+#: 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: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 "Fout van psql source: %s."
 
-#: src/data/psql-reader.c:437
+#: 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."
@@ -629,353 +666,392 @@ 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 "scratchbestand"
 
-#: 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 commas (or it contains both)."
-msgstr "%s: Aangepaste munt-tekenreeks `%s' bevat niet exact drie punten of komma's (of het bevat beiden). "
+msgstr "%s: Aangepaste munt-tekenreeks `%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 "Variabelen-achtervoegsel te lang."
 
+#: src/data/sys-file-reader.c:213
+#, c-format
+msgid "Recoded variable name duplicates an existing `%s' within system file."
+msgstr "Recoded variabelennaam dupliceert een bestaande '%s' binnen systeembestand."
+
 #. 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:276 src/data/sys-file-writer.c:203
 msgid "system file"
 msgstr "systeembestand"
 
-#: src/data/sys-file-reader.c:205
+#: src/data/sys-file-reader.c:283
 #, 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 systeembestand: %s."
 
-#: src/data/sys-file-reader.c:244
+#: src/data/sys-file-reader.c:322 tests/dissect-sysfile.c:136
 msgid "Misplaced type 4 record."
-msgstr "Verkeerd geplaatst type 4 record. "
+msgstr "Verkeerd geplaatst type 4 record."
 
-#: src/data/sys-file-reader.c:255
+#: src/data/sys-file-reader.c:333 tests/dissect-sysfile.c:147
 #, c-format
 msgid "Unrecognized record type %d."
 msgstr "Niet-herkend recordtype %d."
 
-#: src/data/sys-file-reader.c:294
+#: src/data/sys-file-reader.c:374
 #, c-format
 msgid "File header claims %d variable positions but %d were read from file."
 msgstr "Bestandskop claimt %d variabelenposities maar er zijn er %d gelezen van het bestand."
 
-#: src/data/sys-file-reader.c:334
+#: src/data/sys-file-reader.c:414
 #, c-format
 msgid "Error closing system file \"%s\": %s."
 msgstr "Fout bij het sluiten van systeembestand \"%s\": %s."
 
-#: src/data/sys-file-reader.c:399 src/data/sys-file-reader.c:409
+#: src/data/sys-file-reader.c:479 src/data/sys-file-reader.c:489
+#: tests/dissect-sysfile.c:181 tests/dissect-sysfile.c:191
 msgid "This is not an SPSS system file."
 msgstr "Dit is geen SPSS-systeembestand."
 
-#: src/data/sys-file-reader.c:431
+#: src/data/sys-file-reader.c:511 tests/dissect-sysfile.c:204
 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:508
+#: src/data/sys-file-reader.c:588
 #, c-format
 msgid "Invalid variable name `%s'."
 msgstr "Ongeldige variabelennaam '%s'."
 
-#: src/data/sys-file-reader.c:512
-#, c-format
-msgid "Bad variable width %d."
-msgstr "Onjuiste variabelenbreedte %d."
-
-#: src/data/sys-file-reader.c:516
+#: src/data/sys-file-reader.c:596
 #, c-format
 msgid "Duplicate variable name `%s' within system file."
 msgstr "Dubbele variabelennaam '%s' binnen systeembestand."
 
-#: src/data/sys-file-reader.c:524
+#: src/data/sys-file-reader.c:604 tests/dissect-sysfile.c:328
 msgid "Variable label indicator field is not 0 or 1."
 msgstr "Variabelen-labelindicatorveld is niet 0 of 1."
 
-#: src/data/sys-file-reader.c:532
+#: src/data/sys-file-reader.c:612 tests/dissect-sysfile.c:337
 #, 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:551
+#: src/data/sys-file-reader.c:631 tests/dissect-sysfile.c:355
 msgid "Numeric missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr "Numeriek ontbrekende-waarde indicatorveld is niet -3, -2, 0, 1, 2, of 3."
 
-#: src/data/sys-file-reader.c:566
+#: src/data/sys-file-reader.c:649 tests/dissect-sysfile.c:370
 msgid "String missing value indicator field is not 0, 1, 2, or 3."
 msgstr "Tekenreeks ontbrekende-waarde indicatorveld is niet 0, 1, 2, of 3."
 
-#: src/data/sys-file-reader.c:569
-#, c-format
-msgid "Ignoring missing values on long string variable %s, which PSPP does not yet support."
-msgstr "Negeren van ontbrekende-waardes voor lange-tekenreeksvariabele %s, wat PSPP nog niet ondersteunt."
-
-#: src/data/sys-file-reader.c:598
+#: src/data/sys-file-reader.c:681
 msgid "Missing string continuation record."
 msgstr "Mis tekenreeks continuering record."
 
-#: src/data/sys-file-reader.c:632
+#: src/data/sys-file-reader.c:715
 #, c-format
 msgid "Unknown variable format %<PRIu8>."
 msgstr "Onbekend variabelen-format %<PRIu8>."
 
-#: src/data/sys-file-reader.c:650
+#: src/data/sys-file-reader.c:733
 #, c-format
 msgid "%s variable %s has invalid %s format %s."
 msgstr "%s variabele %s heeft ongeldig %s format %s."
 
-#: src/data/sys-file-reader.c:653
+#: src/data/sys-file-reader.c:736
 msgid "print"
 msgstr "afdrukken"
 
-#: src/data/sys-file-reader.c:653
+#: src/data/sys-file-reader.c:736
 msgid "write"
 msgstr "schrijf"
 
-#: src/data/sys-file-reader.c:657
+#: src/data/sys-file-reader.c:740
 msgid "Suppressing further invalid format warnings."
 msgstr "Onderdrukt verdere ongeldig format waarschuwingen."
 
-#: src/data/sys-file-reader.c:675
+#: src/data/sys-file-reader.c:758
 msgid "Weighting variable must be numeric."
 msgstr "Wegingvariabele moet numeriek zijn."
 
-#: src/data/sys-file-reader.c:689
+#: src/data/sys-file-reader.c:772
 msgid "Multiple type 6 (document) records."
 msgstr "Meerdere type 6 (document) records."
 
-#: src/data/sys-file-reader.c:693
+#: src/data/sys-file-reader.c:776
 #, c-format
 msgid "Number of document lines (%d) must be greater than 0."
 msgstr "Aantal documentregels (%d) moet groter dan 0 zijn."
 
-#: src/data/sys-file-reader.c:701
+#: src/data/sys-file-reader.c:784
 msgid "Document line contains null byte."
 msgstr "Documentregel bevat null byte."
 
-#: src/data/sys-file-reader.c:779
-msgid "Ignoring value labels for long string variables, which PSPP does not yet support."
-msgstr "Negeer waardelabels voor lange-tekenreeksvariabelen, die door PSPP nog niet ondersteund worden."
-
-#: src/data/sys-file-reader.c:784
+#: src/data/sys-file-reader.c:874
 #, 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:810
+#: src/data/sys-file-reader.c:901 tests/dissect-sysfile.c:550
 #, c-format
 msgid "Bad size (%zu) or count (%zu) field on record type 7, subtype 3."
 msgstr "Onjuiste lengte (%zu) of aantal (%zu) veld in recordtype 7, subtype 3."
 
-#: src/data/sys-file-reader.c:830
+#: src/data/sys-file-reader.c:921
 #, c-format
 msgid "Floating-point representation indicated by system file (%d) differs from expected (%d)."
 msgstr "Drijvende komma representatie aangegeven door systeembestand %d verschilt van verwachting (%d)."
 
-#: src/data/sys-file-reader.c:843
+#: src/data/sys-file-reader.c:934
 msgid "little-endian"
-msgstr ""
+msgstr "little-endian"
 
-#: src/data/sys-file-reader.c:843
+#: src/data/sys-file-reader.c:934
 msgid "big-endian"
-msgstr ""
+msgstr "big-endian"
 
-#: src/data/sys-file-reader.c:844
+#: src/data/sys-file-reader.c:935
 #, c-format
 msgid "Integer format indicated by system file (%s) differs from expected (%s)."
-msgstr "Integer format aangegeven door systeembestand (%s) verschilt van verwacht (%s). "
+msgstr "Integer format aangegeven door systeembestand (%s) verschilt van verwacht (%s)."
 
-#: src/data/sys-file-reader.c:860
+#: src/data/sys-file-reader.c:992 tests/dissect-sysfile.c:581
 #, c-format
 msgid "Bad size (%zu) or count (%zu) on extension 4."
 msgstr "Onjuiste lengte (%zu) of aantal (%zu) bij extensie 4."
 
-#: src/data/sys-file-reader.c:864
-#, c-format
-msgid "File specifies unexpected value %g as SYSMIS."
-msgstr "Bestand specificeert onverwachte waarde %g als SYSMIS."
-
-#: src/data/sys-file-reader.c:866
+#: src/data/sys-file-reader.c:996 src/data/sys-file-reader.c:1000
+#: src/data/sys-file-reader.c:1004 tests/dissect-sysfile.c:586
+#: tests/dissect-sysfile.c:591 tests/dissect-sysfile.c:596
 #, c-format
-msgid "File specifies unexpected value %g as HIGHEST."
-msgstr "Bestand specificeert onverwachte waarde %g als HIGHEST."
+msgid "File specifies unexpected value %g as %s."
+msgstr "Bestand specificeert onverwachte waarde %g als %s."
 
-#: src/data/sys-file-reader.c:868
-#, c-format
-msgid "File specifies unexpected value %g as LOWEST."
-msgstr "Bestand specificeert onverwachte waarde %g als LOWEST."
-
-#: src/data/sys-file-reader.c:884
+#: src/data/sys-file-reader.c:1021 tests/dissect-sysfile.c:611
 #, c-format
 msgid "Bad size %zu on extension 11."
 msgstr "Onjuiste lengte %zu voor extensie 11."
 
-#: src/data/sys-file-reader.c:896
+#: src/data/sys-file-reader.c:1033 tests/dissect-sysfile.c:623
 #, 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:917
+#: src/data/sys-file-reader.c:1054
 #, c-format
 msgid "Invalid variable display parameters for variable %zu (%s).  Default parameters substituted."
 msgstr "Ongeldige variabelen-toon-parameters voor variabele %zu (%s).  Verstek parameters ingevuld."
 
-#: src/data/sys-file-reader.c:963
+#: src/data/sys-file-reader.c:1098
 #, c-format
 msgid "Long variable mapping from %s to invalid variable name `%s'."
-msgstr "Lange variabele afbeelding van %s tot ongeldige naam '%s'. "
+msgstr "Lange variabele afbeelding van %s tot ongeldige naam '%s'."
 
-#: src/data/sys-file-reader.c:973
+#: src/data/sys-file-reader.c:1108
 #, c-format
 msgid "Duplicate long variable name `%s' within system file."
 msgstr "Dubbele lange variabelennaam `%s' binnen systeembestand."
 
-#: src/data/sys-file-reader.c:1028
+#: src/data/sys-file-reader.c:1161
 #, c-format
 msgid "%s listed as string of invalid length %s in very length string record."
 msgstr ""
 
-#: src/data/sys-file-reader.c:1038
+#: src/data/sys-file-reader.c:1171
 #, c-format
 msgid "%s listed in very long string record with width %s, which requires only one segment."
 msgstr "%s vermeld in erg lang tekenreeks-record met breedte %s, dat slechts een segment vereist."
 
-#: src/data/sys-file-reader.c:1044
+#: src/data/sys-file-reader.c:1177
 #, c-format
 msgid "Very long string %s overflows dictionary."
 msgstr "Erg lange-tekenreeks %s is te groot voor woordenboek."
 
-#: src/data/sys-file-reader.c:1058
+#: src/data/sys-file-reader.c:1191
 #, c-format
 msgid "Very long string with width %ld has segment %d of width %d (expected %d)"
 msgstr "Erg lange-tekenreeks met breedte %ld heeft segment %d van breedte %d (verwacht %d)"
 
-#: src/data/sys-file-reader.c:1103
+#: src/data/sys-file-reader.c:1237
 #, c-format
 msgid "Invalid number of labels: %d.  Ignoring labels."
 msgstr "Ongeldig aantal labels: %d. Labels worden genegeerd."
 
-#: src/data/sys-file-reader.c:1134
+#: src/data/sys-file-reader.c:1268 tests/dissect-sysfile.c:426
 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 waardelabel record (type 3) zoals het moet."
 
-#: src/data/sys-file-reader.c:1141
+#: src/data/sys-file-reader.c:1275
 #, 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 waardelabel (%d) is niet tussen 1 en het aantal variabelen (%zu)."
 
-#: src/data/sys-file-reader.c:1151
+#: src/data/sys-file-reader.c:1286
 #, c-format
-msgid "Value labels are not allowed on long string variables (%s)."
-msgstr "Waardelabels zijn niet toegestaan bij lange-tekenreeksvariabelen (%s)."
+msgid "Value labels may not be added to long string variables (e.g. %s) using records types 3 and 4."
+msgstr "Waardelabels mogen niet toegevoegd aan lange tekenreeks-variabelen (bv %s) bij het gebruik van records types 3 en 4."
 
-#: src/data/sys-file-reader.c:1158
+#: src/data/sys-file-reader.c:1295
 #, 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 waardelabel zijn niet allemaal van het identieke type.  Variabele %s is %s, maar variabele %s is %s."
 
-#: src/data/sys-file-reader.c:1191
+#: src/data/sys-file-reader.c:1329
 #, c-format
 msgid "Duplicate value label for %g on %s."
 msgstr "Dubbel waardelabel voor %g op %s."
 
-#: src/data/sys-file-reader.c:1194
+#: src/data/sys-file-reader.c:1332 src/data/sys-file-reader.c:1513
 #, c-format
 msgid "Duplicate value label for \"%.*s\" on %s."
 msgstr "Dubbel waardelabel voor \"%.*s\" on %s."
 
-#: src/data/sys-file-reader.c:1272
+#: src/data/sys-file-reader.c:1370
+#, c-format
+msgid "Error parsing attribute value %s[%d]"
+msgstr "Fout tijdens ontleden attribuut waarde %s[%d]"
+
+#: src/data/sys-file-reader.c:1384
+#, 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:1447 tests/dissect-sysfile.c:762
+#, c-format
+msgid "Variable name length in long string value label record (%d) exceeds %d-byte limit."
+msgstr ""
+
+#: src/data/sys-file-reader.c:1457
+#, c-format
+msgid "Ignoring long string value record for unknown variable %s."
+msgstr "Negeren lange tekenreeks waarde record voor onbekende variabele %s."
+
+#: src/data/sys-file-reader.c:1464
+#, c-format
+msgid "Ignoring long string value record for numeric variable %s."
+msgstr "Negeren lange tekenreeks waarde record voor numerieke variabele %s."
+
+#: src/data/sys-file-reader.c:1471
+#, 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:1493
+#, 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:1608
 msgid "File ends in partial case."
 msgstr "Bestand eindigt in gedeeltelijke case."
 
-#: src/data/sys-file-reader.c:1280
+#: src/data/sys-file-reader.c:1616
 #, c-format
 msgid "Error reading case from file %s."
 msgstr "Fout tijdens lezen case van bestand %s."
 
-#: src/data/sys-file-reader.c:1377 src/data/sys-file-reader.c:1413
+#: src/data/sys-file-reader.c:1713 src/data/sys-file-reader.c:1749
 msgid "Compressed data is corrupt."
 msgstr "Gecomprimeerde gegevens zijn beschadigd."
 
-#: src/data/sys-file-reader.c:1500
+#: src/data/sys-file-reader.c:1836
 #, c-format
 msgid "Variable index %d not in valid range 1...%d."
 msgstr "Variabele index %d niet in geldig bereik 1...%d."
 
-#: src/data/sys-file-reader.c:1505
+#: src/data/sys-file-reader.c:1841
 #, c-format
 msgid "Variable index %d refers to long string continuation."
 msgstr "Variabele index %d verwijst naar lange-tekenreeks voortzetting."
 
-#: src/data/sys-file-reader.c:1591
+#: src/data/sys-file-reader.c:1909
 #, c-format
-msgid "Suppressed %d additional variable map warnings."
-msgstr "Onderdrukt %d extra gerelateerde variabele map waarschuwingen."
+msgid "Suppressed %d additional related warnings."
+msgstr "Onderdrukt %d extra gerelateerde waarschuwingen."
 
-#: src/data/sys-file-reader.c:1604
+#: src/data/sys-file-reader.c:1950
 #, c-format
 msgid "Variable map refers to unknown variable %s."
 msgstr "Variabele afbeelding refereert aan onbekende variabele %s."
 
-#: src/data/sys-file-reader.c:1680
+#: src/data/sys-file-reader.c:2058 tests/dissect-sysfile.c:959
 #, c-format
 msgid "System error: %s."
 msgstr "Systeemfout: %s."
 
-#: src/data/sys-file-reader.c:1682
+#: src/data/sys-file-reader.c:2060 tests/dissect-sysfile.c:961
 msgid "Unexpected end of file."
 msgstr "Onverwacht einde-bestand."
 
-#: src/data/sys-file-writer.c:163
+#: src/data/sys-file-writer.c:176
 #, c-format
 msgid "Unknown system file version %d. Treating as version %d."
 msgstr "Onbekende systeembestand versie %d. Behandeld als versie %d."
 
-#: src/data/sys-file-writer.c:202
+#: src/data/sys-file-writer.c:215
 #, 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 systeembestand: %s."
 
-#: src/data/sys-file-writer.c:737
+#: src/data/sys-file-writer.c:923
 #, c-format
 msgid "An I/O error occurred writing system file \"%s\"."
 msgstr "Een I/O fout is opgetreden tijdens het schrijven van systeembestand \"%s\"."
 
-#: 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 "Karakter '%c' (in %s) mag niet als eerste karakter in een variabelennaam voorkomen. "
+msgstr "Karakter '%c' (in %s) mag niet als eerste karakter in een variabelennaam voorkomen."
 
-#: 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 "Karakter '%c' (in %s) mag niet in een variabelennaam voorkomen."
 
-#: src/data/variable.c:249
+#: src/data/variable.c:282
 msgid "Variable name cannot be empty string."
 msgstr "Variabelennaam kan geen lege tekenreeks zijn."
 
-#: src/data/variable.c:255
+#: src/data/variable.c:288
 #, c-format
 msgid "Variable name %s exceeds %d-character limit."
 msgstr "Variabelennaam %s overschrijdt de limiet van %d-karakters."
 
-#: 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 "'%s' mag niet gebruikt worden als variabelennaam omdat het een gereserveerd woord is."
 
-#: src/language/command.c:208
+#: src/language/syntax-file.c:88
+#, c-format
+msgid "opening \"%s\" as syntax file"
+msgstr "openen \"%s\" als syntaxbestand"
+
+#: 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/command.c:208 src/language/expressions/parse.c:1267
 #, c-format
-msgid "%s is unimplemented."
-msgstr "%s is niet geïmplementeerd."
+msgid "%s is not yet implemented."
+msgstr "%s is nog niet geïmplementeerd."
 
 #: src/language/command.c:214
 #, c-format
@@ -1056,14 +1132,14 @@ msgid "%s is allowed only before the active file has been defined, inside INPUT
 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
+#, fuzzy, c-format
 msgid "%s is not allowed inside INPUT PROGRAM."
-msgstr "%s is niet toegestaan binnen INPUT PROGRAM."
+msgstr "%s is alleen toegestaan binnen INPUT PROGRAM."
 
 #: src/language/command.c:694
-#, c-format
+#, fuzzy, c-format
 msgid "%s is not allowed inside FILE TYPE."
-msgstr "%s is niet toegestaan binnen FILE TYPE."
+msgstr "%s is alleen toegestaan binnen FILE TYPE."
 
 #: src/language/command.c:773 src/language/command.c:881
 #: src/language/utilities/permissions.c:98
@@ -1093,539 +1169,356 @@ msgstr "Opdracht-shell niet ondersteund op dit platform."
 msgid "Error executing command: %s."
 msgstr "Fout tijdens uitvoeren opdracht: %s."
 
-#: src/language/control/control-stack.c:27
+#: src/language/lexer/lexer.c:283
 #, c-format
-msgid "%s without %s."
-msgstr "%s zonder %s."
+msgid "%s does not form a valid number."
+msgstr "%s vormt geen geldig nummer."
 
-#: src/language/control/control-stack.c:55
+#: src/language/lexer/lexer.c:389
 #, 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."
+msgid "Bad character in input: `%s'."
+msgstr "Fout karakter in invoer: '%s'."
 
-#: src/language/control/control-stack.c:72
+#: src/language/lexer/lexer.c:426
 #, 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."
+msgid "Subcommand %s may only be specified once."
+msgstr "Subopdracht %s mag slechts een keer gespecificeerd worden."
 
-#: src/language/control/repeat.c:171
+#: src/language/lexer/lexer.c:434
 #, c-format
-msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
-msgstr "Dummy variabelennaam \"%s\" verbergt woordenboek variabele \"%s\"."
+msgid "missing required subcommand %s"
+msgstr "mis vereiste subopdracht %s"
 
-#: src/language/control/repeat.c:176
+#: src/language/lexer/lexer.c:463
 #, c-format
-msgid "Dummy variable name \"%s\" is given twice."
-msgstr "Dummy variabelennaam \"%s\"is 2 keer opgegeven."
+msgid "Syntax error %s at %s."
+msgstr "Syntaxfout %s op %s."
 
-#: src/language/control/repeat.c:222
+#: src/language/lexer/lexer.c:466
 #, c-format
-msgid "Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d were specified."
-msgstr "Dummy variabele \"%.*s\" heeft %d vervangingen, 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 compatibiliteitsmodus."
+msgid "Syntax error at %s."
+msgstr "Syntaxfout op %s."
 
-#: src/language/control/repeat.c:436
-msgid "Ranges may only have integer bounds"
-msgstr "Bereiken mogen alleen integer grenzen hebben"
+#: src/language/lexer/lexer.c:478 src/language/xforms/select-if.c:60
+#: src/language/stats/autorecode.c:154 src/language/data-io/print-space.c:73
+msgid "expecting end of command"
+msgstr "verwacht einde-van-opdracht "
 
-#: src/language/control/repeat.c:445
+#: src/language/lexer/lexer.c:600 src/language/lexer/lexer.c:617
 #, c-format
-msgid "%g TO %g is an invalid range."
-msgstr "%g TO %g is een ongeldig bereik."
-
-#: src/language/control/repeat.c:480
-msgid "String expected."
-msgstr "Tekenreeks verwacht."
+msgid "expecting `%s'"
+msgstr "verwacht '%s'"
 
-#: src/language/control/repeat.c:499
-msgid "No matching DO REPEAT."
-msgstr "Geen overeenkomende DO REPEAT."
+#: src/language/lexer/lexer.c:631
+msgid "expecting string"
+msgstr "tekenreeks verwacht"
 
-#: 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/lexer/lexer.c:645
+msgid "expecting integer"
+msgstr "verwacht integer"
 
-#: src/language/data-io/data-list.c:128
-msgid "The END subcommand may only be used within INPUT PROGRAM."
-msgstr "De END subopdracht mag alleen binnen INPUT PROGRAM gebruikt worden."
+#: src/language/lexer/lexer.c:658
+msgid "expecting number"
+msgstr "nummer verwacht"
 
-#: src/language/data-io/data-list.c:134
-msgid "The END subcommand may only be specified once."
-msgstr "De END subopdracht mag slechts 1 keer gespecificeerd worden."
+#: src/language/lexer/lexer.c:670
+msgid "expecting identifier"
+msgstr "verwacht herkenningsteken"
 
-#: src/language/data-io/data-list.c:172
-msgid "Only one of FIXED, FREE, or LIST may be specified."
-msgstr "Slechts 1 van FIXED, FREE of LIST mag gespecificeerd worden."
+#: src/language/lexer/lexer.c:1064
+msgid "binary"
+msgstr "binair"
 
-#: src/language/data-io/data-list.c:237
-msgid "The END subcommand may be used only with DATA LIST FIXED."
-msgstr "De END subopdracht mag alleen gebruikt worden met DATA LIST FIXED."
+#: src/language/lexer/lexer.c:1069
+msgid "octal"
+msgstr "octaal"
 
-#: src/language/data-io/data-list.c:252
-msgid "At least one variable must be specified."
-msgstr "Tenminste 1 variabele moet gespecificeerd worden."
+#: src/language/lexer/lexer.c:1074
+msgid "hex"
+msgstr "hexadecimaal"
 
-#: src/language/data-io/data-list.c:349 src/language/data-io/data-list.c:438
-#: src/language/data-io/get-data.c:528
+#: src/language/lexer/lexer.c:1084
 #, c-format
-msgid "%s is a duplicate variable name."
-msgstr "%s is een dubbele variabelennaam."
+msgid "String of %s digits has %zu characters, which is not a multiple of %d."
+msgstr "Tekenreeks van %s cijfers heeft %zu karakters, dat geen meervoud van %d is."
 
-#: src/language/data-io/data-list.c:356
+#: src/language/lexer/lexer.c:1113
 #, c-format
-msgid "There is already a variable %s of a different type."
-msgstr "Er is al een variabele %s van een ander type."
+msgid "`%c' is not a valid %s digit."
+msgstr "'%c' is geen geldig %s cijfer."
 
-#: src/language/data-io/data-list.c:363
-#, c-format
-msgid "There is already a string variable %s of a different width."
-msgstr "Er is al een tekenreeksvariabele %s van een andere breedte."
+#: src/language/lexer/lexer.c:1147
+msgid "Unterminated string constant."
+msgstr "Geen einde aan tekenreeksconstante."
+
+#: src/language/lexer/lexer.c:1201
+msgid "Unexpected end of file in string concatenation."
+msgstr "Onverwacht bestandseinde in tekenreeks samenvoeging."
+
+#: src/language/lexer/lexer.c:1209
+msgid "String expected following `+'."
+msgstr "Tekenreeks verwacht achter '+'."
 
-#: src/language/data-io/data-list.c:371
+#: src/language/lexer/lexer.c:1222
 #, 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."
+msgid "String exceeds 255 characters in length (%zu characters)."
+msgstr "Tekenreeks overschrijdt de lengte van 255 karakters (%zu karakters)."
 
-#: src/language/data-io/data-parser.c:455
-#: src/language/data-io/data-parser.c:464
-msgid "Quoted string extends beyond end of line."
-msgstr "Geciteerde tekenreeks loopt door na regeleinde."
+#: src/language/lexer/format-parser.c:88
+msgid "expecting valid format specifier"
+msgstr "verwacht geldige format-specificator"
 
-#: src/language/data-io/data-parser.c:520
+#: src/language/lexer/format-parser.c:107
+#: src/language/lexer/format-parser.c:126
+#: src/language/data-io/placement-parser.c:226
 #, c-format
-msgid "Partial case of %d of %d records discarded."
-msgstr "Gedeeltelijke case van %d van %d records genegeerd."
+msgid "Unknown format type \"%s\"."
+msgstr "Onbekend format-type \"%s\"."
 
-#: src/language/data-io/data-parser.c:566
+#: src/language/lexer/format-parser.c:121
+msgid "expecting format type"
+msgstr "verwacht format-type"
+
+#: src/language/lexer/value-parser.c:60
 #, c-format
-msgid "Partial case discarded.  The first variable missing was %s."
-msgstr "Gedeeltelijke case overgeslagen. De eerste gemiste variabele was %s."
+msgid "Low end of range (%g) is below high end (%g).  The range will be treated as reversed."
+msgstr "Ondergrens van bereik (%g) is lager dan bovengrens (%g). Het bereik wordt behandeld als omgekeerd."
 
-#: src/language/data-io/data-parser.c:603
+#: src/language/lexer/value-parser.c:68
 #, 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 "Ontbrekende-waarde(s) voor alle variabelen vanaf %s. Deze worden gevuld met de geschikte system-missing waarde of spatie."
+msgid "Ends of range are equal (%g)."
+msgstr "Eindes van bereik zijn gelijk (%g)."
 
-#: src/language/data-io/data-parser.c:622
-msgid "Record ends in data not part of any field."
-msgstr "Record eindigt in gegeven dat geen onderdeel is van een veld."
+#: 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 bereik zijn."
 
-#: src/language/data-io/data-parser.c:642
-#: src/language/data-io/data-parser.c:683 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
-msgid "Variable"
-msgstr "Variabele"
-
-#: src/language/data-io/data-parser.c:643 src/language/data-io/print.c:404
-msgid "Record"
-msgstr ""
+#: src/language/lexer/value-parser.c:109
+msgid "System-missing value is not valid here."
+msgstr "System-missing waarde is hier niet geldig."
 
-#: src/language/data-io/data-parser.c:644 src/language/data-io/print.c:405
-#: src/ui/gui/crosstabs.glade:92 src/ui/gui/psppire-var-sheet.c:107
-msgid "Columns"
-msgstr "Kolommen"
+#: src/language/lexer/value-parser.c:117
+msgid "expecting number or data string"
+msgstr "nummer of gegevenstekenreeks verwacht"
 
-#: src/language/data-io/data-parser.c:645
-#: src/language/data-io/data-parser.c:684 src/language/data-io/print.c:406
-msgid "Format"
-msgstr ""
+#: src/language/lexer/variable-parser.c:63
+msgid "expecting variable name"
+msgstr "variabelennaam verwacht"
 
-#: src/language/data-io/data-parser.c:664
+#: src/language/lexer/variable-parser.c:73
 #, 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."
+msgid "%s is not a variable name."
+msgstr "%s is geen variabelennaam."
 
-#: src/language/data-io/data-parser.c:700
+#: src/language/lexer/variable-parser.c:176
 #, c-format
-msgid "Reading free-form data from %s."
-msgstr "Lezen vrije-vorm gegeven 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 "gegevensbestand"
+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 variabelenlijst."
 
-#: src/language/data-io/data-reader.c:149
+#: src/language/lexer/variable-parser.c:179
 #, c-format
-msgid "Could not open \"%s\" for reading as a data file: %s."
-msgstr "Kon \"%s\" niet openen voor het lezen als gegevensbestand: %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 gegevens 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."
+msgid "%s is not a string variable.  It will not be included in the variable list."
+msgstr "%s is geen tekenreeksvariabele. Het wordt niet opgenomen in de variabelenlijst."
 
-#: src/language/data-io/data-reader.c:216
+#: src/language/lexer/variable-parser.c:183
 #, c-format
-msgid "Error reading file %s: %s."
-msgstr "Fout tijdens lezen bestand %s: %s."
+msgid "Scratch variables (such as %s) are not allowed here."
+msgstr "Scratch variabelen (zoals %s) zijn hier niet toegestaan."
 
-#: src/language/data-io/data-reader.c:219
+#: src/language/lexer/variable-parser.c:187
 #, c-format
-msgid "Unexpected end of file reading %s."
-msgstr "Onverwacht einde tijdens lezen van bestand %s."
+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 variabelenlijst dienen van hetzelfde type te zijn.  %s wordt overgeslagen voor de lijst."
 
-#: src/language/data-io/data-reader.c:228
+#: src/language/lexer/variable-parser.c:193
 #, c-format
-msgid "Unexpected end of file in partial record reading %s."
-msgstr ""
+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 variabelenlijst dienen dezelfde breedte te hebben.  %s wordt overgeslagen voor de lijst."
 
-#: src/language/data-io/data-reader.c:288
+#: src/language/lexer/variable-parser.c:198
 #, c-format
-msgid "Corrupt block descriptor word at offset 0x%lx in %s."
-msgstr ""
+msgid "Variable %s appears twice in variable list."
+msgstr "Variabele %s komt 2 keer in de variabelenlijst voor."
 
-#: src/language/data-io/data-reader.c:289
+#: src/language/lexer/variable-parser.c:311
 #, c-format
-msgid "Corrupt record descriptor word at offset 0x%lx in %s."
-msgstr ""
+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/data-io/data-reader.c:302
+#: src/language/lexer/variable-parser.c:319
 #, c-format
-msgid "Corrupt record size at offset 0x%lx in %s."
-msgstr ""
+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 variabelenwoordenboeken, of gewone- scratch- of systeemvariabelen zijn. %s is een %s variabele, terwijl %s %s. is."
 
-#: src/language/data-io/data-reader.c:444
-msgid "Record exceeds remaining block length."
-msgstr "Record overschrijdt resterend blok lengte."
+#: src/language/lexer/variable-parser.c:393
+msgid "incorrect use of TO convention"
+msgstr "foutief gebruik van TO conventie"
 
-#: 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/lexer/variable-parser.c:436
+msgid "Scratch variables not allowed here."
+msgstr "Scratch variabelen niet toegestaan hier."
 
-#: 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/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/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 invoerprogramma het inline-bestand niet benaderd."
+#: 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/data-io/data-writer.c:74
+#: src/language/xforms/compute.c:149 src/language/xforms/compute.c:203
 #, 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 gegevensbestand: %s."
+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/data-io/data-writer.c:191
+#: src/language/xforms/compute.c:153 src/language/xforms/compute.c:210
 #, c-format
-msgid "I/O error occurred writing data file \"%s\"."
-msgstr "I/O fout opgetreden tijdens schrijven gegevensbestand \"%s\"."
+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/data-io/file-handle.q:65
+#: src/language/xforms/compute.c:353
 #, c-format
-msgid "File handle %s is already defined.  Use CLOSE FILE HANDLE before redefining a file handle."
-msgstr "Bestands-handle %s is al gedefinieerd. Gebruik CLOSE FILE HANDLE voor het opnieuw definiëren van een bestands-handle."
+msgid "There is no vector named %s."
+msgstr "Er is geen vector genaamd %s."
 
-#: 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/xforms/count.c:123
+msgid "Destination cannot be a string variable."
+msgstr "Bestemming kan geen tekenreeksvariabele zijn."
 
-#: 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/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/data-io/file-handle.q:135
+#: src/language/xforms/sample.c:96
 #, 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."
+msgid "Cannot sample %d observations from a population of %d."
+msgstr "Kan niet %d observaties bemonsteren van een populatie van %d."
 
-#: src/language/data-io/file-handle.q:177
-msgid "file"
-msgstr "bestand"
+#: src/language/xforms/recode.c:248
+msgid "Inconsistent target variable types.  Target variables must be all numeric or all string."
+msgstr "Inconsistent doelvariabelen-types.  Doelvariabelen moeten allemaal numeriek of allemaal tekenreeks zijn."
 
-#: src/language/data-io/file-handle.q:179
-msgid "inline file"
-msgstr "inline-bestand"
+#: src/language/xforms/recode.c:269
+msgid "CONVERT requires string input values and numeric output values."
+msgstr "CONVERT vereist tekenreeks invoerwaardes en numerieke uitvoerwaardes."
 
-#: src/language/data-io/file-handle.q:205
-msgid "expecting a file name or handle name"
-msgstr "bestands- of handle-naam verwacht"
+#: src/language/xforms/recode.c:324
+msgid "THRU is not allowed with string variables."
+msgstr "THRU is niet toegestaan met tekenreeksvariabelen."
 
-#: 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/xforms/recode.c:403
+msgid "expecting output value"
+msgstr "verwacht uitvoerwaarde"
 
-#: src/language/data-io/get-data.c:62
+#: src/language/xforms/recode.c:460
 #, c-format
-msgid "Unsupported TYPE %s"
-msgstr "Niet ondersteund TYPE %s"
+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 doelvariabelen."
 
-#: src/language/data-io/get-data.c:258
+#: src/language/xforms/recode.c:475
 #, 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:313
-msgid "expecting FIXED or DELIMITED"
-msgstr "FIXED of DELIMITED verwacht"
-
-#: src/language/data-io/get-data.c:326
-msgid "Value of FIRSTCASE must be 1 or greater."
-msgstr "Waarde van FIRSTCASE moet 1 of groter zijn."
+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 tekenreeksvariabelen gespecificeerd bij INTO dienen al te bestaan.  Gebruik de STRING opdracht om een tekenreeks variabele aan te maken.)"
 
-#: src/language/data-io/get-data.c:351
-msgid "expecting LINE or VARIABLES"
-msgstr "LINE of VARIABLES verwacht"
+#: src/language/xforms/recode.c:491
+#, c-format
+msgid "INTO is required with %s input values and %s output values."
+msgstr "INTO is vereist met %s invoerwaardes en %s uitvoerwaardes."
 
-#: src/language/data-io/get-data.c:364
-msgid "Value of FIXCASE must be at least 1."
-msgstr "Waarde van FIXCASE moet tenminste 1 zijn."
+#: src/language/xforms/recode.c:504
+#, c-format
+msgid "Type mismatch.  Cannot store %s data in %s variable %s."
+msgstr "Type fout. Kan %s gegevens niet in %s variabele %s opslaan."
 
-#: src/language/data-io/get-data.c:384
-msgid "Value of FIRST must be at least 1."
-msgstr "Waarde van FIRST moet tenminste 1 zijn."
+#: src/language/xforms/select-if.c:100
+msgid "Syntax error expecting OFF or BY.  Turning off case filtering."
+msgstr "Syntaxfout verwacht OFF of BY. Schakelt case filtering uit."
 
-#: src/language/data-io/get-data.c:396
-msgid "Value of PERCENT must be between 1 and 100."
-msgstr "Waarde van PERCENT moet tussen 1 en 100 zijn."
+#: src/language/xforms/select-if.c:115
+msgid "The filter variable must be numeric."
+msgstr "De filtervariabele moet numeriek zijn."
 
-#: src/language/data-io/get-data.c:445
-msgid "In compatible syntax mode, the QUALIFIER string must contain exactly one character."
-msgstr "In compatible syntaxmodus, dient de QUALIFIER tekenreeks precies 1 karakter te bevatten."
+#: src/language/xforms/select-if.c:121
+msgid "The filter variable may not be scratch."
+msgstr "De filtervariabele mag niet scratch zijn."
 
-#: src/language/data-io/get-data.c:460
-msgid "expecting VARIABLES"
-msgstr "VARIABLES verwacht"
+#: src/language/control/control-stack.c:27
+#, c-format
+msgid "%s without %s."
+msgstr "%s zonder %s."
 
-#: src/language/data-io/get-data.c:482
-#: src/language/data-io/placement-parser.c:378
+#: src/language/control/control-stack.c:55
 #, 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 recordnummer, %ld, is op of voor het huidige record, %d. Gegevensvelden dienen opgegeven te worden in oplopende recordnummer volgorde."
+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/data-io/get-data.c:491
+#: src/language/control/control-stack.c:72
 #, c-format
-msgid "The record number specified, %ld, exceeds the number of records per case specified on FIXCASE, %d."
-msgstr "Het gespecificeerde recordnummer, %ld, overschrijdt het aantal records per case zoals gespecificeerd in FIXCASE, %d."
+msgid "This command cannot appear outside %s...%s."
+msgstr "Deze opdracht kan niet voorkomen buiten %s...%s."
 
-#: src/language/data-io/get.c:99
-msgid "expecting COMM or TAPE"
-msgstr "COMM of TAPE verwacht"
+#: 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/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 "%s of %s verwacht"
+#: src/language/control/loop.c:214
+msgid "Only one index clause may be specified."
+msgstr "Slechts een index clausule mag gespecificeerd worden."
 
-#: src/language/data-io/get.c:506 src/language/data-io/print.c:178
-msgid "expecting a valid subcommand"
-msgstr "een geldig subopdracht verwacht"
+#: 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/get.c:539
+#: src/language/control/repeat.c:171
 #, 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/get.c:565
-msgid "`=' expected after variable list."
-msgstr "'=' verwacht na variabelenlijst."
+msgid "Dummy variable name \"%s\" hides dictionary variable \"%s\"."
+msgstr "Dummy variabelennaam \"%s\" verbergt woordenboek variabele \"%s\"."
 
-#: src/language/data-io/get.c:572
+#: src/language/control/repeat.c:176
 #, 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."
+msgid "Dummy variable name \"%s\" is given twice."
+msgstr "Dummy variabelennaam \"%s\"is 2 keer opgegeven."
 
-#: src/language/data-io/get.c:585
+#: src/language/control/repeat.c:222
 #, c-format
-msgid "Requested renaming duplicates variable name %s."
-msgstr "Gevraagde hernoeming dupliceert variabelennaam %s."
+msgid "Dummy variable \"%.*s\" had %d substitutions, so \"%.*s\" must also, but %d were specified."
+msgstr "Dummy variabele \"%.*s\" heeft %d vervangingen, dus \"%.*s\" moet dat ook, maar %d zijn er gespecificeerd."
 
-#: src/language/data-io/get.c:615
-msgid "Cannot DROP all variables from dictionary."
-msgstr "Kan niet alle variabelen DROP-en uit woordenboek."
+#: src/language/control/repeat.c:334
+msgid "DO REPEAT may not nest in compatibility mode."
+msgstr "DO REPEAT mag niet nesten in compatibiliteitsmodus."
 
-#: src/language/data-io/get.c:788
-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/control/repeat.c:436
+msgid "Ranges may only have integer bounds"
+msgstr "Bereiken mogen alleen integer grenzen hebben"
 
-#: 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 "MATCH FILES mag niet gebruikt worden na TEMPORARY als het actieve bestand een invoer bron is. Tijdelijke transformaties zullen permanent worden."
-
-#: src/language/data-io/get.c:829
-msgid "Multiple IN subcommands for a single FILE or TABLE."
-msgstr "Meerdere IN subopdrachten voor een enkele FILE of TABLE."
-
-#: src/language/data-io/get.c:873
-#, c-format
-msgid "File %s lacks BY variable %s."
-msgstr "Bestand %s mist BY variabele %s."
-
-#: src/language/data-io/get.c:876
-#, c-format
-msgid "Active file lacks BY variable %s."
-msgstr "Actief bestand mist BY variabele %s."
-
-#: src/language/data-io/get.c:946
-msgid "BY is required when TABLE is specified."
-msgstr "BY is vereist als TABLE is gespecificeerd."
-
-#: src/language/data-io/get.c:951
-msgid "BY is required when IN is specified."
-msgstr "BY is noodzakelijk als IN is gespecificeerd."
-
-#: src/language/data-io/get.c:1056
-#, c-format
-msgid "Variable name %s specified on %s subcommand duplicates an existing variable name."
-msgstr "Variabelennaam %s gespecificeerd op %s subopdracht dupliceert een bestaande variabelennaam."
-
-#: 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 "Variabele %s in bestand %s (%s) heeft een ander type of breedte dan dezelfde variabele in eerder bestand (%s)."
-
-#: src/language/data-io/inpt-pgm.c:129
-msgid "Unexpected end-of-file within INPUT PROGRAM."
-msgstr "Onverwacht einde-bestand binnen INPUT PROGRAM."
-
-#: src/language/data-io/inpt-pgm.c:142
-msgid "Input program did not create any variables."
-msgstr "Invoerprogramma heeft geen variabelen gecreëerd."
-
-#: src/language/data-io/inpt-pgm.c:287
-msgid "COLUMN subcommand multiply specified."
-msgstr "COLUMN subopdracht meerdere keren gespecificeerd."
-
-#: src/language/data-io/inpt-pgm.c:337
-msgid "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
-msgstr "REREAD: Kolomnummers 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 op 1 gezet."
-
-#: 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 op 1 gezet."
-
-#: 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 op 1 gezet."
-
-#: src/language/data-io/list.q:211
-msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
-msgstr "'/FORMAT WEIGHT' gespecificeerd, maar weging staat niet aan."
-
-#: src/language/data-io/list.q:467
-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 formats (%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 format-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
+#: src/language/control/repeat.c:445
 #, c-format
-msgid "Unknown format type \"%s\"."
-msgstr "Onbekend format-type \"%s\"."
-
-#: src/language/data-io/placement-parser.c:305
-msgid "Column positions for fields must be positive."
-msgstr "Kolomposities voor velden moeten positief zijn."
-
-#: src/language/data-io/placement-parser.c:307
-msgid "Column positions for fields must not be negative."
-msgstr "Kolomposities 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 eindkolom van een veld moet groter zijn dan de startkolom."
-
-#: src/language/data-io/print-space.c:73 src/language/lexer/lexer.c:476
-#: 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."
+msgid "%g TO %g is an invalid range."
+msgstr "%g TO %g is een ongeldig bereik."
 
-#: 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/control/repeat.c:480
+msgid "String expected."
+msgstr "Tekenreeks verwacht."
 
-#: src/language/data-io/print.c:266
-#, c-format
-msgid "Output calls for %d records but %zu specified on RECORDS subcommand."
-msgstr "De uitvoer vraagt %d records maar %zu gespecificeerd bij RECORDS subopdracht. "
+#: src/language/control/repeat.c:499
+msgid "No matching DO REPEAT."
+msgstr "Geen overeenkomende DO REPEAT."
 
-#: src/language/data-io/print.c:436
-#, 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/dictionary/attributes.c:108
+msgid "Attribute array index must be between 1 and 65535."
+msgstr "Attribuut tabel index moet minimaal 1 en maximaal 65535 zijn."
 
-#: src/language/data-io/print.c:440
-#, 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/dictionary/attributes.c:189
+msgid "expecting ATTRIBUTE= or DELETE="
+msgstr "verwacht ATTRIBUTE= of DELETE="
 
 #: 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 doelbestand, maar %s in bronbestand."
 
-#: src/language/dictionary/apply-dictionary.c:99
-#, c-format
-msgid "Cannot add value labels from source file to long string variable %s."
-msgstr "Kan geen waardelabels van bronbestand toevoegen aan lange-tekenreeksvariabele %s."
-
-#: src/language/dictionary/apply-dictionary.c:113
-#, c-format
-msgid "Cannot apply missing values from source file to long string variable %s."
-msgstr "Kan ontbrekende-waardes uit bronbestand niet toepassen op lange-tekenreeksvariabele %s."
-
-#: 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 "Geen overeenkomende variabelen gevonden tussen het bron- en het doelbestand."
 
@@ -1646,7 +1539,7 @@ msgid "`)' expected after output format."
 msgstr "')' verwacht na uitvoer-format."
 
 #: src/language/dictionary/missing-values.c:56
-#: src/language/stats/aggregate.c:451
+#: src/language/stats/aggregate.c:458
 msgid "expecting `('"
 msgstr "'(' verwacht"
 
@@ -1655,12 +1548,12 @@ msgstr "'(' verwacht"
 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 tekenreeksvariabelen (b.v. %s) niet mixen binnen een enkele lijst."
 
-#: 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)."
-msgstr "Afkappen ontbrekende-waarde naar korte tekenreekslengte (%d karakters)."
+msgid "Truncating missing value to maximum acceptable length (%d bytes)."
+msgstr "Afkappen ontbrekende-waarde naar maximale acceptabele lengte (%d bytes)."
 
-#: 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 "De opgegeven ontbrekende-waarde zijn te lang om toe te kennen aan een variabele van breedte %d."
@@ -1670,32 +1563,36 @@ msgid "MODIFY VARS may not be used after TEMPORARY.  Temporary transformations w
 msgstr "MODIFY VARS mag niet gebruikt worden na TEMPORARY. Tijdelijke transformaties zullen permanent gemaakt worden."
 
 #: src/language/dictionary/modify-variables.c:114
+#, fuzzy
 msgid "REORDER subcommand may be given at most once."
-msgstr "REORDER subopdracht mag maximaal 1 keer gegeven worden."
+msgstr "%s 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
+#, fuzzy
 msgid "`(' expected on REORDER subcommand."
-msgstr "'(' verwacht bij REORDER subopdracht."
+msgstr "'(' verwacht bij %s subopdracht."
 
 #: src/language/dictionary/modify-variables.c:159
 msgid "`)' expected following variable names on REORDER subcommand."
 msgstr "')' verwacht achter variabelennamen bij REORDER subopdracht."
 
 #: src/language/dictionary/modify-variables.c:177
+#, fuzzy
 msgid "RENAME subcommand may be given at most once."
-msgstr "RENAME subopdracht mag maximaal 1 keer gegeven worden."
+msgstr "%s subopdracht mag maximaal 1 keer gegeven worden."
 
 #: src/language/dictionary/modify-variables.c:190
+#, fuzzy
 msgid "`(' expected on RENAME subcommand."
-msgstr "'(' verwacht bij RENAME subopdracht."
+msgstr "'(' verwacht bij %s 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 variabelennamen bij RENAME subopdracht. "
+msgstr "'=' verwacht tussen lijst van nieuwe en oude variabelennamen bij RENAME subopdracht."
 
 #: src/language/dictionary/modify-variables.c:208
 #: src/language/dictionary/rename-variables.c:76
@@ -1765,262 +1662,256 @@ msgid "Renaming would duplicate variable name %s."
 msgstr "Hernoemen zou variabelennaam %s dupliceren."
 
 #: src/language/dictionary/split-file.c:85
-#: src/language/dictionary/sys-file-info.c:563
-#: src/language/stats/crosstabs.q:1169 src/language/stats/crosstabs.q:1196
-#: src/language/stats/crosstabs.q:1216 src/language/stats/crosstabs.q:1238
-#: src/language/stats/examine.q:1215 src/language/stats/frequencies.q:1063
-#: src/language/stats/frequencies.q:1188
+#: src/language/dictionary/sys-file-info.c:486
+#: src/language/dictionary/sys-file-info.c:641
+#: src/language/stats/crosstabs.q:1235 src/language/stats/crosstabs.q:1262
+#: src/language/stats/crosstabs.q:1286 src/language/stats/crosstabs.q:1311
+#: src/language/stats/examine.q:1959 src/language/stats/frequencies.q:1051
+#: src/language/stats/frequencies.q:1176 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:564 src/ui/gui/crosstabs.glade:275
-#: src/ui/gui/psppire-var-sheet.c:104 src/ui/gui/psppire.glade:2099
+#: src/language/dictionary/sys-file-info.c:642
+#: src/ui/gui/psppire-var-sheet.c:537 src/ui/gui/psppire-var-store.c:836
+#: src/ui/gui/crosstabs.glade:275 src/ui/gui/psppire.glade:1974
 msgid "Label"
-msgstr ""
+msgstr "Label"
 
-#: src/language/dictionary/sys-file-info.c:110
+#: src/language/dictionary/sys-file-info.c:113
 msgid "File:"
 msgstr "Bestand:"
 
-#: 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 ""
+msgstr "Label:"
 
-#: src/language/dictionary/sys-file-info.c:116
+#: src/language/dictionary/sys-file-info.c:119
 msgid "No label."
 msgstr "Geen label."
 
-#: src/language/dictionary/sys-file-info.c:119
+#: src/language/dictionary/sys-file-info.c:122
 msgid "Created:"
 msgstr "Aangemaakt:"
 
-#: 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
+#, fuzzy
 msgid "Big Endian."
-msgstr ""
+msgstr "Big Endian"
 
-#: src/language/dictionary/sys-file-info.c:125
+#: src/language/dictionary/sys-file-info.c:128
+#, fuzzy
 msgid "Little Endian."
-msgstr ""
+msgstr "Little Endian"
 
-#: 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
+#, fuzzy
 msgid "Unknown."
-msgstr "Onbekend."
+msgstr "Onbekend"
 
-#: 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 ""
+msgstr "IEEE 754 LE."
 
-#: src/language/dictionary/sys-file-info.c:130
+#: src/language/dictionary/sys-file-info.c:133
 msgid "IEEE 754 BE."
-msgstr ""
+msgstr "IEEE 754 BE."
 
-#: src/language/dictionary/sys-file-info.c:131
+#: src/language/dictionary/sys-file-info.c:134
 msgid "VAX D."
-msgstr ""
+msgstr "VAX D."
 
-#: src/language/dictionary/sys-file-info.c:132
+#: src/language/dictionary/sys-file-info.c:135
 msgid "VAX G."
-msgstr ""
+msgstr "VAX G."
 
-#: src/language/dictionary/sys-file-info.c:133
+#: src/language/dictionary/sys-file-info.c:136
 msgid "IBM 390 Hex Long."
-msgstr ""
+msgstr "IBM 390 Hex Long."
 
-#: 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 "Variabelen:"
 
-#: src/language/dictionary/sys-file-info.c:137
+#: src/language/dictionary/sys-file-info.c:140
 msgid "Cases:"
-msgstr ""
+msgstr "Cases:"
 
-#: 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 "Onbekend"
 
-#: src/language/dictionary/sys-file-info.c:141
+#: src/language/dictionary/sys-file-info.c:144
 msgid "Type:"
-msgstr ""
+msgstr "Type:"
 
-#: src/language/dictionary/sys-file-info.c:142
+#: src/language/dictionary/sys-file-info.c:145
+#, fuzzy
 msgid "System File."
-msgstr "Systeembestand."
+msgstr "Systeembestand"
 
-#: src/language/dictionary/sys-file-info.c:143
+#: src/language/dictionary/sys-file-info.c:146
 msgid "Weight:"
 msgstr "Gewicht:"
 
-#: src/language/dictionary/sys-file-info.c:148
+#: src/language/dictionary/sys-file-info.c:151
 msgid "Not weighted."
 msgstr "Niet gewogen."
 
-#: src/language/dictionary/sys-file-info.c:150
+#: src/language/dictionary/sys-file-info.c:153
 msgid "Mode:"
 msgstr "Modus:"
 
-#: src/language/dictionary/sys-file-info.c:152
+#: src/language/dictionary/sys-file-info.c:155
 #, c-format
 msgid "Compression %s."
 msgstr "Compressie %s."
 
-#: src/language/dictionary/sys-file-info.c:152
+#: src/language/dictionary/sys-file-info.c:155
 msgid "on"
 msgstr "aan"
 
-#: src/language/dictionary/sys-file-info.c:152
+#: src/language/dictionary/sys-file-info.c:155
 msgid "off"
 msgstr "uit"
 
-#: 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 "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: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 "Positie"
 
-#: 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 "Het actieve bestand heeft geen bestandlabel."
 
-#: src/language/dictionary/sys-file-info.c:225
+#: src/language/dictionary/sys-file-info.c:223
 msgid "File label:"
 msgstr "Bestandlabel:"
 
-#: src/language/dictionary/sys-file-info.c:288
+#: 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:307
+#: src/language/dictionary/sys-file-info.c:313
 msgid "Macros not supported."
 msgstr "Macro's worden niet ondersteund."
 
-#: 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 "Het actieve bestandwoordenboek bevat geen documenten."
 
-#: src/language/dictionary/sys-file-info.c:325
+#: 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:477
+#: 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 ""
 
-#: src/language/dictionary/sys-file-info.c:485
+#: src/language/dictionary/sys-file-info.c:550
 #, c-format
 msgid "Print Format: %s"
 msgstr "Afdruk-format: %s"
 
-#: src/language/dictionary/sys-file-info.c:488
+#: src/language/dictionary/sys-file-info.c:554
 #, c-format
 msgid "Write Format: %s"
 msgstr "Schrijf-format: %s"
 
-#: src/language/dictionary/sys-file-info.c:494
+#: src/language/dictionary/sys-file-info.c:567
 #, c-format
 msgid "Measure: %s"
 msgstr "Meting: %s"
 
-#: 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 "Nominaal"
 
-#: 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 "Ordinaal"
 
-#: 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 "Schaal"
 
-#: src/language/dictionary/sys-file-info.c:500
+#: src/language/dictionary/sys-file-info.c:573
 #, c-format
 msgid "Display Alignment: %s"
 msgstr "Toongroepering: %s"
 
-#: 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 "Links"
 
-#: 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 "Centreer"
 
-#: 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 "Rechts"
 
-#: src/language/dictionary/sys-file-info.c:506
+#: src/language/dictionary/sys-file-info.c:579
 #, c-format
 msgid "Display Width: %d"
 msgstr "Toonbreedte: %d"
 
-#: src/language/dictionary/sys-file-info.c:517
+#: src/language/dictionary/sys-file-info.c:593
 msgid "Missing Values: "
-msgstr "Ontbrekende-Waardes:"
+msgstr "Ontbrekende Waardes: "
 
-#: src/language/dictionary/sys-file-info.c:611
+#: src/language/dictionary/sys-file-info.c:702
 msgid "No vectors defined."
 msgstr "Geen vectoren gedefinieerd."
 
-#: src/language/dictionary/sys-file-info.c:632
+#: src/language/dictionary/sys-file-info.c:723
 msgid "Vector"
-msgstr ""
+msgstr "Vector"
 
-#: src/language/dictionary/sys-file-info.c:635
+#: src/language/dictionary/sys-file-info.c:726
 msgid "Print Format"
 msgstr "Afdruk-Format"
 
-#: 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 "Het is niet mogelijk om waardelabels aan lange-tekenreeksvariabelen als %s toe te kennen."
-
-#: src/language/dictionary/value-labels.c:157 src/language/lexer/lexer.c:629
-msgid "expecting string"
-msgstr "tekenreeks verwacht"
-
-#: src/language/dictionary/value-labels.c:166 src/language/lexer/lexer.c:643
-msgid "expecting integer"
-msgstr "verwacht integer"
-
-#: 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 "Afkappen waardelabel tot 60 karakters."
 
-#: src/language/dictionary/variable-display.c:119
-msgid "Variable display width must be a positive integer."
-msgstr "Variabelen-toonbreedte moet een positieve integer zijn."
-
 #: src/language/dictionary/variable-label.c:51
 msgid "String expected for variable label."
 msgstr "Tekenreeks verwacht voor variabelenlabel."
@@ -2061,6 +1952,10 @@ msgstr "%s is te lang voor een variabelennaam."
 msgid "%s is an existing variable name."
 msgstr "%s is een bestaande variabelennaam."
 
+#: src/language/dictionary/variable-display.c:120
+msgid "Variable display width must be a positive integer."
+msgstr "Variabelen-toonbreedte moet een positieve integer zijn."
+
 #: src/language/dictionary/weight.c:49
 msgid "The weighting variable must be numeric."
 msgstr "De wegingvariabele moet numeriek zijn."
@@ -2069,382 +1964,139 @@ msgstr "De wegingvariabele moet numeriek zijn."
 msgid "The weighting variable may not be scratch."
 msgstr "De wegingvariabele mag geen scratch zijn."
 
-#: src/language/expressions/evaluate.c:154
-msgid "expecting number or string"
-msgstr "verwacht nummer of tekenreeks"
-
-#: src/language/expressions/evaluate.c:168
+#: src/language/tests/float-format.c:124
 #, c-format
-msgid "Duplicate variable name %s."
-msgstr "Dubbele variabelennaam %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."
+msgid "%zu-byte string needed but %zu-byte string supplied."
+msgstr "%zu-byte tekenreeks nodig maar %zu-byte tekenreeks gegeven."
 
-#: 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 het acceptabele bereik van 1 tot 53.  Het resultaat zal system-missing zijn."
+#: src/language/tests/float-format.c:136
+msgid "Hexadecimal floating constant too long."
+msgstr "Hexadecimale drijvende constante te lang."
 
-#: 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/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/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 het acceptabele bereik van 1 tot 366.  Het resultaat zal system-missing zijn."
+#: src/language/tests/float-format.c:247
+msgid "Too many values in single command."
+msgstr "Te veel waardes in enkele opdracht."
 
-#: 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/tests/moments-test.c:47
+msgid "expecting weight value"
+msgstr "verwacht wegingwaarde"
 
-#: src/language/expressions/helpers.c:182
+#: src/language/utilities/cd.c:41
 #, 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\"."
+msgid "Cannot change directory to %s:  %s "
+msgstr "Kan map niet veranderen in %s: %s "
 
-#: 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/utilities/date.c:32
+msgid "Only USE ALL is currently implemented."
+msgstr "Alleen USE ALL is op dit moment geïmplementeerd."
 
-#: src/language/expressions/parse.c:259
+#: src/language/utilities/title.c:68
 #, 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 vereist."
+msgid "%s: `.' expected after string."
+msgstr "%s: `.' verwacht na tekenreeks."
 
-#: src/language/expressions/parse.c:271
+#: src/language/utilities/title.c:108
 #, 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 tekenreeks waarde is hier vereist."
+msgid "   (Entered %s)"
+msgstr "   (Ingevoerd %s)"
 
-#: 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 systeemvariabele %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:509
-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 "Functieaanroep "
-
-#: 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: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 "%s is nog niet geïmplementeerd."
-
-#: 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 format-specificator"
-
-#: src/language/lexer/format-parser.c:121
-msgid "expecting format type"
-msgstr "verwacht format-type"
-
-#: src/language/lexer/lexer.c:282
-#, c-format
-msgid "%s does not form a valid number."
-msgstr "%s vormt geen geldig nummer."
-
-#: src/language/lexer/lexer.c:386
-#, c-format
-msgid "Bad character in input: `%c'."
-msgstr "Fout karakter in invoer: '%c'."
-
-#: src/language/lexer/lexer.c:388
-#, c-format
-msgid "Bad character in input: `\\%o'."
-msgstr "Fout karakter in invoer: '\\%o'."
-
-#: src/language/lexer/lexer.c:424
-#, c-format
-msgid "Subcommand %s may only be specified once."
-msgstr "Subopdracht %s mag slechts een keer gespecificeerd worden."
-
-#: src/language/lexer/lexer.c:432
-#, c-format
-msgid "missing required subcommand %s"
-msgstr "mis vereiste subopdracht %s"
-
-#: src/language/lexer/lexer.c:461
-#, c-format
-msgid "Syntax error %s at %s."
-msgstr "Syntaxfout %s op %s."
-
-#: src/language/lexer/lexer.c:464
-#, c-format
-msgid "Syntax error at %s."
-msgstr "Syntaxfout op %s."
-
-#: src/language/lexer/lexer.c:598 src/language/lexer/lexer.c:615
-#, c-format
-msgid "expecting `%s'"
-msgstr "verwacht '%s'"
-
-#: src/language/lexer/lexer.c:656
-msgid "expecting number"
-msgstr "nummer verwacht"
-
-#: src/language/lexer/lexer.c:668
-msgid "expecting identifier"
-msgstr "verwacht herkenningsteken"
-
-#: src/language/lexer/lexer.c:1062
-msgid "binary"
-msgstr "binair"
-
-#: src/language/lexer/lexer.c:1067
-msgid "octal"
-msgstr "octaal"
-
-#: src/language/lexer/lexer.c:1072
-msgid "hex"
-msgstr "hexadecimaal"
-
-#: src/language/lexer/lexer.c:1082
-#, c-format
-msgid "String of %s digits has %zu characters, which is not a multiple of %d."
-msgstr "Tekenreeks van %s cijfers heeft %zu karakters, dat geen meervoud van %d is."
-
-#: src/language/lexer/lexer.c:1111
-#, c-format
-msgid "`%c' is not a valid %s digit."
-msgstr "'%c' is geen geldig %s cijfer."
-
-#: src/language/lexer/lexer.c:1145
-msgid "Unterminated string constant."
-msgstr "Geen einde aan tekenreeksconstante."
-
-#: src/language/lexer/lexer.c:1199
-msgid "Unexpected end of file in string concatenation."
-msgstr "Onverwacht bestandseinde in tekenreeks samenvoeging."
-
-#: src/language/lexer/lexer.c:1207
-msgid "String expected following `+'."
-msgstr "Tekenreeks verwacht achter '+'."
-
-#: src/language/lexer/lexer.c:1220
-#, c-format
-msgid "String exceeds 255 characters in length (%zu characters)."
-msgstr "Tekenreeks overschrijdt de lengte van 255 karakters (%zu karakters)."
-
-#: src/language/lexer/range-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 bereik (%g) is lager dan bovengrens (%g). Het bereik wordt behandeld als omgekeerd."
-
-#: src/language/lexer/range-parser.c:68
-#, c-format
-msgid "Ends of range are equal (%g)."
-msgstr "Eindes van bereik zijn gelijk (%g)."
-
-#: src/language/lexer/range-parser.c:76
-msgid "LO or LOWEST must be part of a range."
-msgstr "LO of LOWEST moet een onderdeel van een bereik zijn."
-
-#: src/language/lexer/range-parser.c:108
-msgid "System-missing value is not valid here."
-msgstr "System-missing waarde is hier niet geldig."
-
-#: src/language/lexer/range-parser.c:116
-msgid "expecting number or data string"
-msgstr "nummer of gegevenstekenreeks verwacht"
-
-#: src/language/lexer/variable-parser.c:63
-msgid "expecting variable name"
-msgstr "variabelennaam verwacht"
+#: src/language/utilities/include.c:92
+msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
+msgstr "BATCH of INTERACTIVE verwacht na SYNTAX."
 
-#: src/language/lexer/variable-parser.c:73
-#, c-format
-msgid "%s is not a variable name."
-msgstr "%s is geen variabelennaam."
+#: src/language/utilities/include.c:109
+msgid "Expecting YES or NO after CD."
+msgstr "YES of NO verwacht na CD."
 
-#: 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 variabelenlijst."
+#: src/language/utilities/include.c:126
+msgid "Expecting CONTINUE or STOP after ERROR."
+msgstr "CONTINUE of STOP verwacht na ERROR."
 
-#: src/language/lexer/variable-parser.c:179
+#: src/language/utilities/include.c:133
 #, c-format
-msgid "%s is not a string variable.  It will not be included in the variable list."
-msgstr "%s is geen tekenreeksvariabele. Het wordt niet opgenomen in de variabelenlijst."
+msgid "Unexpected token: `%s'."
+msgstr "Onverwacht symbool: '%s'."
 
-#: 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/utilities/include.c:178
+msgid "expecting file name"
+msgstr "bestandsnaam verwacht"
 
-#: src/language/lexer/variable-parser.c:187
+#: src/language/utilities/include.c:190
 #, 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 variabelenlijst dienen van hetzelfde type te zijn.  %s wordt overgeslagen voor de lijst."
+msgid "Can't find `%s' in include file search path."
+msgstr "Kan '%s' niet vinden in include-bestand zoekpad."
 
-#: src/language/lexer/variable-parser.c:193
+#: src/language/utilities/include.c:198
 #, 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 variabelenlijst dienen dezelfde breedte te hebben.  %s wordt overgeslagen voor de lijst."
+msgid "Unable to open `%s': %s."
+msgstr "Onmogelijk om te openen '%s': %s."
 
-#: src/language/lexer/variable-parser.c:198
+#: src/language/utilities/permissions.c:73
 #, c-format
-msgid "Variable %s appears twice in variable list."
-msgstr "Variabele %s komt 2 keer in de variabelenlijst voor."
+msgid "Expecting %s or %s."
+msgstr "Verwacht %s of %s."
 
-#: src/language/lexer/variable-parser.c:311
+#: src/language/utilities/permissions.c:106
 #, 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."
+msgid "Cannot stat %s: %s"
+msgstr ""
 
-#: src/language/lexer/variable-parser.c:319
+#: src/language/utilities/permissions.c:119
 #, 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 variabelenwoordenboeken, of gewone- scratch- of systeemvariabelen 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."
+msgid "Cannot change mode of %s: %s"
+msgstr "Kan modus van %s niet veranderen: %s"
 
-#: src/language/stats/aggregate.c:209
+#: src/language/stats/aggregate.c:219
 msgid "while expecting COLUMNWISE"
 msgstr "terwijl COLUMNWISE verwacht werd"
 
-#: src/language/stats/aggregate.c:240
+#: src/language/stats/aggregate.c:247
 msgid "expecting BREAK"
 msgstr "BREAK verwacht"
 
-#: 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 "Als PRESORTED is gespecificeerd, heeft specificeren van sorteervolgorde met (A) of (D) geen effect. Uitvoergegevens zullen hetzelfde gesorteerd zijn als de invoergegevens."
 
-#: src/language/stats/aggregate.c:416
+#: src/language/stats/aggregate.c:423
 msgid "expecting aggregation function"
 msgstr "aggregatie-functie verwacht"
 
-#: src/language/stats/aggregate.c:434
+#: src/language/stats/aggregate.c:441
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr "Onbekende aggregatie functie %s."
 
-#: src/language/stats/aggregate.c:490
+#: src/language/stats/aggregate.c:497
 #, c-format
 msgid "Missing argument %zu to %s."
 msgstr "Mis argument %zu naar %s."
 
-#: 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 "Argumenten naar %s moeten van hetzelfde type zijn als bronvariabelen."
 
-#: src/language/stats/aggregate.c:521
+#: src/language/stats/aggregate.c:516 src/language/expressions/parse.c:885
+msgid "expecting `)'"
+msgstr "')' verwacht"
+
+#: src/language/stats/aggregate.c:528
 #, c-format
 msgid "Number of source variables (%zu) does not match number of target variables (%zu)."
 msgstr "Aantal bronvariabelen (%zu) komt niet overeen met aantal doelvariabelen (%zu)."
 
-#: 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 "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:607
+#: 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 "Variabelennaam %s is niet uniek binnen het aggregate-bestandwoordenboek, dat de aggregate- en break-variabelen bevat."
@@ -2464,1497 +2116,1068 @@ msgstr "Doelvariabele %s dupliceert bestaande variabele %s."
 msgid "Duplicate variable name %s among target variables."
 msgstr "Dubbele variabelennaam %s tussen doelvariabelen."
 
-#: src/language/stats/binomial.c:133
+#: 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:183
+#: src/language/stats/binomial.c:194
 msgid "Binomial Test"
-msgstr ""
+msgstr "Binomiaal Test"
 
-#: src/language/stats/binomial.c:207
+#: src/language/stats/binomial.c:224
 msgid "Group1"
 msgstr "Groep1 "
 
-#: src/language/stats/binomial.c:208
+#: src/language/stats/binomial.c:225
 msgid "Group2"
 msgstr "Groep2"
 
-#: src/language/stats/binomial.c:209 src/language/stats/chisquare.c:224
-#: src/language/stats/chisquare.c:284 src/language/stats/crosstabs.q:870
-#: src/language/stats/crosstabs.q:1076 src/language/stats/crosstabs.q:1799
-#: src/language/stats/examine.q:924 src/language/stats/frequencies.q:1140
-#: src/language/stats/oneway.q:307 src/language/stats/oneway.q:479
-#: 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/sign.c:94
+#: src/language/stats/wilcoxon.c:262 src/ui/gui/crosstabs-dialog.c:59
+#: src/language/stats/crosstabs.q:845 src/language/stats/crosstabs.q:1172
+#: src/language/stats/crosstabs.q:1596 src/language/stats/examine.q:1216
+#: src/language/stats/frequencies.q:1128 src/language/stats/oneway.q:305
+#: src/language/stats/oneway.q:476 src/language/stats/regression.q:309
+#: src/language/stats/reliability.q:718
 msgid "Total"
 msgstr "Totaal"
 
-#: src/language/stats/binomial.c:242 src/language/stats/chisquare.c:247
-#: src/language/stats/crosstabs.q:1194 src/language/stats/crosstabs.q:1235
+#: src/language/stats/binomial.c:259 src/language/stats/chisquare.c:225
+#: src/language/stats/crosstabs.q:1260 src/language/stats/crosstabs.q:1308
 msgid "Category"
 msgstr "Categorie"
 
-#: src/language/stats/binomial.c:243 src/language/stats/crosstabs.q:880
-#: src/language/stats/examine.q:999 src/language/stats/frequencies.q:1411
-#: src/language/stats/npar-summary.c:128 src/language/stats/oneway.q:394
-#: src/language/stats/t-test.q:700 src/language/stats/t-test.q:724
-#: src/language/stats/t-test.q:863 src/language/stats/t-test.q:1425
+#: src/language/stats/binomial.c:260 src/language/stats/npar-summary.c:123
+#: src/language/stats/sign.c:74 src/language/stats/wilcoxon.c:245
+#: src/language/stats/crosstabs.q:852 src/language/stats/examine.q:1289
+#: src/language/stats/frequencies.q:1399 src/language/stats/oneway.q:389
+#: src/language/stats/reliability.q:721 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
 msgid "N"
-msgstr ""
+msgstr "N"
 
-#: src/language/stats/binomial.c:244
+#: src/language/stats/binomial.c:261
 msgid "Observed Prop."
 msgstr ""
 
-#: src/language/stats/binomial.c:245
+#: src/language/stats/binomial.c:262
 msgid "Test Prop."
 msgstr ""
 
-#: src/language/stats/binomial.c:248
+#: src/language/stats/binomial.c:265
 #, c-format
 msgid "Exact Sig. (%d-tailed)"
 msgstr ""
 
-#: src/language/stats/chisquare.c:194
+#: 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:208 src/language/stats/chisquare.c:248
+#: src/language/stats/chisquare.c:186 src/language/stats/chisquare.c:226
 msgid "Observed N"
 msgstr "Waargenomen N"
 
-#: src/language/stats/chisquare.c:209 src/language/stats/chisquare.c:249
+#: src/language/stats/chisquare.c:187 src/language/stats/chisquare.c:227
 msgid "Expected N"
 msgstr "Verwacht N"
 
-#: src/language/stats/chisquare.c:210 src/language/stats/chisquare.c:250
-#: src/language/stats/regression.q:308 src/ui/gui/crosstabs-dialog.c:61
+#: src/language/stats/chisquare.c:188 src/language/stats/chisquare.c:228
+#: src/ui/gui/crosstabs-dialog.c:61 src/language/stats/regression.q:308
 msgid "Residual"
 msgstr "Overblijvend"
 
-#: src/language/stats/chisquare.c:243
+#: src/language/stats/chisquare.c:221 src/language/stats/sign.c:62
 msgid "Frequencies"
 msgstr "Frequenties"
 
-#: src/language/stats/chisquare.c:298
+#: src/language/stats/chisquare.c:276 src/language/stats/sign.c:115
+#: src/language/stats/wilcoxon.c:313
 msgid "Test Statistics"
-msgstr ""
+msgstr "Test Statistieken"
 
-#: src/language/stats/chisquare.c:312
+#: src/language/stats/chisquare.c:290
 msgid "Chi-Square"
-msgstr ""
+msgstr "Chi-Square"
 
-#: src/language/stats/chisquare.c:313 src/language/stats/crosstabs.q:1170
-#: src/language/stats/oneway.q:280 src/language/stats/oneway.q:695
-#: src/language/stats/regression.q:302 src/language/stats/t-test.q:1026
-#: src/language/stats/t-test.q:1219 src/language/stats/t-test.q:1316
+#: src/language/stats/chisquare.c:291 src/language/stats/crosstabs.q:1236
+#: 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 ""
+msgstr "df"
 
-#: src/language/stats/chisquare.c:314
+#: src/language/stats/chisquare.c:292
 msgid "Asymp. Sig."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:284
-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/descriptives.c:102 src/language/stats/npar-summary.c:126
+#: src/ui/gui/descriptives-dialog.c:39 src/ui/gui/frequencies-dialog.c:40
+#: src/language/stats/examine.q:1559 src/language/stats/frequencies.q:123
+#: 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
+msgid "Mean"
+msgstr ""
 
-#: src/language/stats/crosstabs.q:294
-msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
-msgstr "Write modus ALL niet toegestaan in algemene modus.  WRITE=CELLS aangenomen."
+#: src/language/stats/descriptives.c:103
+#, fuzzy
+msgid "S E Mean"
+msgstr "_Gemiddeld"
 
-#: src/language/stats/crosstabs.q:370
-msgid "Too many cross-tabulation variables or dimensions."
-msgstr "Te veel cross-tabulation variabelen of dimensies."
+#: src/language/stats/descriptives.c:104 src/language/stats/frequencies.q:127
+msgid "Std Dev"
+msgstr "Std Dev"
 
-#: src/language/stats/crosstabs.q:380
-msgid "expecting BY"
-msgstr "BY verwacht"
+#: src/language/stats/descriptives.c:105 src/ui/gui/descriptives-dialog.c:46
+#: src/ui/gui/frequencies-dialog.c:45 src/language/stats/examine.q:1589
+#: src/language/stats/frequencies.q:128
+msgid "Variance"
+msgstr "Variatie"
 
-#: src/language/stats/crosstabs.q:447
-msgid "VARIABLES must be specified before TABLES."
-msgstr "VARIABLES dient voor TABLES gespecificeerd te worden."
+#: src/language/stats/descriptives.c:106 src/ui/gui/descriptives-dialog.c:47
+#: src/ui/gui/frequencies-dialog.c:50 src/language/stats/examine.q:1625
+#: src/language/stats/frequencies.q:129
+msgid "Kurtosis"
+msgstr "Kurtosis"
 
-#: src/language/stats/crosstabs.q:485
-#, c-format
-msgid "Maximum value (%ld) less than minimum value (%ld)."
-msgstr "Maximumwaarde (%ld) is kleiner dan minimumwaarde (%ld)."
+#: src/language/stats/descriptives.c:107
+#, fuzzy
+msgid "S E Kurt"
+msgstr "S.E. Kurt"
 
-#: src/language/stats/crosstabs.q:865
-msgid "Summary."
-msgstr "Overzicht."
+#: src/language/stats/descriptives.c:108 src/ui/gui/descriptives-dialog.c:48
+#: src/ui/gui/frequencies-dialog.c:46 src/language/stats/examine.q:1620
+#: src/language/stats/frequencies.q:131
+msgid "Skewness"
+msgstr "Skewness"
 
-#: src/language/stats/crosstabs.q:867 src/language/stats/examine.q:987
-msgid "Cases"
-msgstr ""
+#: src/language/stats/descriptives.c:109
+#, fuzzy
+msgid "S E Skew"
+msgstr "S.E. Skew"
 
-#: src/language/stats/crosstabs.q:868 src/language/stats/examine.q:922
-#: src/language/stats/frequencies.q:1061 src/language/stats/frequencies.q:1412
-msgid "Valid"
-msgstr "Geldig"
+#: src/language/stats/descriptives.c:110 src/ui/gui/descriptives-dialog.c:43
+#: src/ui/gui/frequencies-dialog.c:48 src/language/stats/examine.q:1609
+#: src/language/stats/frequencies.q:133
+msgid "Range"
+msgstr "Bereik"
 
-#: src/language/stats/crosstabs.q:869 src/language/stats/examine.q:923
-#: src/language/stats/frequencies.q:1131 src/language/stats/frequencies.q:1413
-#: src/ui/gui/psppire-var-sheet.c:106
-msgid "Missing"
-msgstr "Ontbrekend"
+#: src/language/stats/descriptives.c:111 src/language/stats/npar-summary.c:132
+#: src/ui/gui/descriptives-dialog.c:41 src/ui/gui/frequencies-dialog.c:42
+#: src/language/stats/examine.q:1599 src/language/stats/frequencies.q:134
+#: src/language/stats/oneway.q:404
+msgid "Minimum"
+msgstr "Minimum"
 
-#: src/language/stats/crosstabs.q:881 src/language/stats/examine.q:1002
-#: src/language/stats/frequencies.q:1065 src/language/stats/frequencies.q:1066
-#: src/language/stats/frequencies.q:1067
-msgid "Percent"
-msgstr "Percentage"
+#: src/language/stats/descriptives.c:112 src/language/stats/npar-summary.c:135
+#: src/ui/gui/descriptives-dialog.c:42 src/ui/gui/frequencies-dialog.c:43
+#: src/language/stats/examine.q:1604 src/language/stats/frequencies.q:135
+#: src/language/stats/oneway.q:405
+msgid "Maximum"
+msgstr "Maximum"
 
-#: src/language/stats/crosstabs.q:1128
-msgid "count"
-msgstr "aantal"
+#: src/language/stats/descriptives.c:113 src/ui/gui/descriptives-dialog.c:44
+#: src/ui/gui/frequencies-dialog.c:53 src/language/stats/frequencies.q:136
+msgid "Sum"
+msgstr "Som"
 
-#: src/language/stats/crosstabs.q:1129
-msgid "row %"
-msgstr "rij %"
+#: src/language/stats/descriptives.c:344
+#, c-format
+msgid "Z-score variable name %s would be a duplicate variable name."
+msgstr "Z-score-variabelennaam %s zou een dubbele variabelennaam zijn."
 
-#: src/language/stats/crosstabs.q:1130
-msgid "column %"
-msgstr "kolom %"
+#: src/language/stats/descriptives.c:362 src/language/data-io/list.q:157
+msgid "No variables specified."
+msgstr "Geen variabelen gespecificeerd."
 
-#: src/language/stats/crosstabs.q:1131
-msgid "total %"
-msgstr "totaal %"
+#: src/language/stats/descriptives.c:451
+msgid "expecting statistic name: reverting to default"
+msgstr "statistische naam verwacht: teruggezet op verstek"
 
-#: src/language/stats/crosstabs.q:1132
-msgid "expected"
-msgstr "verwacht"
+#: 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/crosstabs.q:1133
-msgid "residual"
-msgstr "overblijvend"
+#: src/language/stats/descriptives.c:556
+msgid "Mapping of variables to corresponding Z-scores."
+msgstr "Mappen van variabelen naar corresponderende Z-scores."
 
-#: src/language/stats/crosstabs.q:1134
-msgid "std. resid."
-msgstr ""
+#: src/language/stats/descriptives.c:561
+msgid "Source"
+msgstr "Bron"
 
-#: src/language/stats/crosstabs.q:1135
-msgid "adj. resid."
-msgstr ""
+#: src/language/stats/descriptives.c:562
+msgid "Target"
+msgstr "Doel"
 
-#: src/language/stats/crosstabs.q:1165
-msgid "Chi-square tests."
-msgstr ""
+#: src/language/stats/descriptives.c:673
+#, c-format
+msgid "Z-score of %s"
+msgstr "Z-score van %s"
 
-#: src/language/stats/crosstabs.q:1168 src/language/stats/crosstabs.q:1195
-#: src/language/stats/crosstabs.q:1215 src/language/stats/crosstabs.q:1236
-#: src/language/stats/examine.q:1462 src/ui/gui/checkbox-treeview.c:94
-msgid "Statistic"
-msgstr "Statistiek"
+#: src/language/stats/descriptives.c:888
+msgid "Valid N"
+msgstr "Geldige N"
 
-#: src/language/stats/crosstabs.q:1172
-msgid "Asymp. Sig. (2-sided)"
-msgstr ""
+#: src/language/stats/descriptives.c:889
+msgid "Missing N"
+msgstr "Missende N"
 
-#: src/language/stats/crosstabs.q:1174
-msgid "Exact. Sig. (2-sided)"
-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 ontbrekende-waarde(s) = %g."
 
-#: src/language/stats/crosstabs.q:1176
-msgid "Exact. Sig. (1-sided)"
-msgstr ""
+#: src/language/stats/sort-cases.c:64
+msgid "Buffer limit must be at least 2."
+msgstr "Bufferlimiet moet tenminste 2 zijn."
 
-#: src/language/stats/crosstabs.q:1191
-msgid "Symmetric measures."
-msgstr "Symmetrische metingen."
+#: src/language/stats/sort-criteria.c:74
+msgid "`A' or `D' expected inside parentheses."
+msgstr "'A' of 'D' verwacht binnen haakjes."
 
-#: src/language/stats/crosstabs.q:1197 src/language/stats/crosstabs.q:1239
-msgid "Asymp. Std. Error"
-msgstr ""
+#: src/language/stats/sort-criteria.c:79
+msgid "`)' expected."
+msgstr "')' verwacht."
 
-#: src/language/stats/crosstabs.q:1198 src/language/stats/crosstabs.q:1240
-msgid "Approx. T"
-msgstr ""
+#: src/language/stats/sort-criteria.c:92
+#, c-format
+msgid "Variable %s specified twice in sort criteria."
+msgstr "Variabele %s 2 keer opgegeven in sorteer criteria."
 
-#: src/language/stats/crosstabs.q:1199 src/language/stats/crosstabs.q:1241
-msgid "Approx. Sig."
-msgstr ""
+#: src/language/stats/flip.c:98
+msgid "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
+msgstr "FLIP negeert TEMPORARY. Tijdelijke transformaties worden permanent gemaakt."
 
-#: src/language/stats/crosstabs.q:1210
-msgid "Risk estimate."
-msgstr ""
+#: src/language/stats/flip.c:150
+msgid "Could not create temporary file for FLIP."
+msgstr "Kon geen tijdelijk bestand voor FLIP aanmaken."
 
-#: src/language/stats/crosstabs.q:1214
+#: src/language/stats/flip.c:327
 #, c-format
-msgid "95%% Confidence Interval"
-msgstr ""
-
-#: src/language/stats/crosstabs.q:1217 src/language/stats/t-test.q:1030
-#: src/language/stats/t-test.q:1216 src/language/stats/t-test.q:1319
-msgid "Lower"
-msgstr "Lager"
+msgid "Error rewinding FLIP file: %s."
+msgstr "Fout tijdens terugdraaien FLIP bestand: %s."
 
-#: src/language/stats/crosstabs.q:1218 src/language/stats/t-test.q:1031
-#: src/language/stats/t-test.q:1217 src/language/stats/t-test.q:1320
-msgid "Upper"
-msgstr "Hoger"
+#: src/language/stats/flip.c:334
+msgid "Error creating FLIP source file."
+msgstr "Fout tijdens het creëren van FLIP bronbestand."
 
-#: src/language/stats/crosstabs.q:1232
-msgid "Directional measures."
-msgstr "Directionele metingen."
+#: src/language/stats/flip.c:347
+#, c-format
+msgid "Error reading FLIP file: %s."
+msgstr "Fout tijdens lezen FLIP bestand: %s."
 
-#: src/language/stats/crosstabs.q:1237 src/ui/gui/psppire-var-sheet.c:101
-#: src/ui/gui/psppire.glade:2223
-msgid "Type"
-msgstr ""
+#: src/language/stats/flip.c:349
+msgid "Unexpected end of file reading FLIP file."
+msgstr "Onverwacht einde-bestand tijdens lezen FLIP bestand."
 
-#: src/language/stats/crosstabs.q:1993
-msgid "Pearson Chi-Square"
-msgstr ""
+#: src/language/stats/flip.c:365
+#, c-format
+msgid "Error seeking FLIP source file: %s."
+msgstr "Fout tijdens zoeken FLIP bronbestand: %s."
 
-#: src/language/stats/crosstabs.q:1994
-msgid "Likelihood Ratio"
-msgstr ""
+#: src/language/stats/flip.c:373
+#, c-format
+msgid "Error writing FLIP source file: %s."
+msgstr "Fout tijdens schrijven FLIP bronbestand: %s."
 
-#: src/language/stats/crosstabs.q:1995
-msgid "Fisher's Exact Test"
-msgstr ""
+#: src/language/stats/flip.c:384
+#, c-format
+msgid "Error closing FLIP source file: %s."
+msgstr "Fout tijdens sluiten FLIP bronbestand: %s."
 
-#: src/language/stats/crosstabs.q:1996
-msgid "Continuity Correction"
-msgstr ""
+#: src/language/stats/flip.c:392
+#, c-format
+msgid "Error rewinding FLIP source file: %s."
+msgstr "Fout tijdens terugdraaien FLIP bronbestand: %s."
 
-#: src/language/stats/crosstabs.q:1997
-msgid "Linear-by-Linear Association"
-msgstr ""
+#: src/language/stats/flip.c:426
+#, c-format
+msgid "Error reading FLIP temporary file: %s."
+msgstr "Fout tijdens lezen FLIP tijdelijk bestand: %s."
 
-#: src/language/stats/crosstabs.q:2034 src/language/stats/crosstabs.q:2107
-#: src/language/stats/crosstabs.q:2169
-msgid "N of Valid Cases"
-msgstr ""
+#: src/language/stats/flip.c:429
+msgid "Unexpected end of file reading FLIP temporary file."
+msgstr "Onverwacht einde-bestand tijdens lezen FLIP tijdelijk bestand."
 
-#: src/language/stats/crosstabs.q:2053 src/language/stats/crosstabs.q:2185
-msgid "Nominal by Nominal"
-msgstr ""
+#: src/language/stats/npar-summary.c:109
+msgid "Descriptive Statistics"
+msgstr "Descriptieve Statistieken"
 
-#: src/language/stats/crosstabs.q:2054 src/language/stats/crosstabs.q:2186
-msgid "Ordinal by Ordinal"
-msgstr ""
+#: src/language/stats/npar-summary.c:129 src/language/stats/examine.q:1594
+#: 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 "Std. Deviatie"
 
-#: src/language/stats/crosstabs.q:2055
-msgid "Interval by Interval"
-msgstr ""
+#: src/language/stats/npar-summary.c:142 src/ui/gui/examine.glade:333
+#: src/language/stats/examine.q:2117 src/language/stats/examine.q:2134
+#: src/language/stats/frequencies.q:1410
+msgid "Percentiles"
+msgstr "Percentiles"
 
-#: src/language/stats/crosstabs.q:2056
-msgid "Measure of Agreement"
-msgstr ""
+#: src/language/stats/npar-summary.c:146
+msgid "25th"
+msgstr "25ste"
 
-#: src/language/stats/crosstabs.q:2061 src/ui/gui/crosstabs-dialog.c:41
-msgid "Phi"
-msgstr ""
+#: src/language/stats/npar-summary.c:149
+msgid "50th (Median)"
+msgstr "50ste (Mediaan)"
 
-#: src/language/stats/crosstabs.q:2062
-msgid "Cramer's V"
-msgstr ""
+#: src/language/stats/npar-summary.c:152
+msgid "75th"
+msgstr "75ste"
 
-#: src/language/stats/crosstabs.q:2063
-msgid "Contingency Coefficient"
+#: src/language/stats/roc.c:938
+msgid "Area Under the Curve"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2064
-msgid "Kendall's tau-b"
+#: src/language/stats/roc.c:940
+#, c-format
+msgid "Area Under the Curve (%s)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2065
-msgid "Kendall's tau-c"
+#: src/language/stats/roc.c:946
+msgid "Area"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2066 src/ui/gui/crosstabs-dialog.c:48
-msgid "Gamma"
-msgstr ""
+#: src/language/stats/roc.c:959 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 "Std. Fout"
 
-#: src/language/stats/crosstabs.q:2067
-msgid "Spearman Correlation"
+#: src/language/stats/roc.c:960
+msgid "Asymptotic Sig."
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2068
-msgid "Pearson's R"
-msgstr ""
+#: src/language/stats/roc.c:962 src/language/stats/examine.q:1570
+#: src/language/stats/oneway.q:401
+msgid "Lower Bound"
+msgstr "Benedengrens"
 
-#: src/language/stats/crosstabs.q:2069 src/ui/gui/crosstabs-dialog.c:50
-msgid "Kappa"
-msgstr ""
+#: src/language/stats/roc.c:963 src/language/stats/examine.q:1575
+#: src/language/stats/oneway.q:402
+msgid "Upper Bound"
+msgstr "Bovengrens"
 
-#: src/language/stats/crosstabs.q:2142
+#: src/language/stats/roc.c:967
 #, c-format
-msgid "Odds Ratio for %s (%g / %g)"
-msgstr ""
+msgid "Asymp. %g%% Confidence Interval"
+msgstr "Asymp. %g%% Betrouwbaarheidsinterval"
 
-#: src/language/stats/crosstabs.q:2145
-#, c-format
-msgid "Odds Ratio for %s (%.*s / %.*s)"
+#: src/language/stats/roc.c:973
+msgid "Variable under test"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2153
-#, c-format
-msgid "For cohort %s = %g"
-msgstr "Voor cohort %s = %g"
+#: src/language/stats/roc.c:1032
+msgid "Case Summary"
+msgstr "Case Overzicht"
 
-#: src/language/stats/crosstabs.q:2156
-#, c-format
-msgid "For cohort %s = %.*s"
-msgstr "Voor cohort %s = %.*s"
+#: src/language/stats/roc.c:1054
+msgid "Unweighted"
+msgstr "Niet gewogen"
 
-#: src/language/stats/crosstabs.q:2187
-msgid "Nominal by Interval"
-msgstr ""
+#: src/language/stats/roc.c:1055
+msgid "Weighted"
+msgstr "Gewicht"
 
-#: src/language/stats/crosstabs.q:2192 src/ui/gui/crosstabs-dialog.c:43
-msgid "Lambda"
+#: src/language/stats/roc.c:1059
+msgid "Valid N (listwise)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2193
-msgid "Goodman and Kruskal tau"
-msgstr ""
+#: src/language/stats/roc.c:1062
+msgid "Positive"
+msgstr "Positief"
 
-#: src/language/stats/crosstabs.q:2194
-msgid "Uncertainty Coefficient"
-msgstr ""
+#: src/language/stats/roc.c:1063
+msgid "Negative"
+msgstr "Negatief"
 
-#: src/language/stats/crosstabs.q:2195
-msgid "Somers' d"
+#: src/language/stats/roc.c:1091
+msgid "Coordinates of the Curve"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2196 src/ui/gui/crosstabs-dialog.c:51
-msgid "Eta"
+#: src/language/stats/roc.c:1093
+#, c-format
+msgid "Coordinates of the Curve (%s)"
 msgstr ""
 
-#: src/language/stats/crosstabs.q:2201
-msgid "Symmetric"
-msgstr ""
+#: src/language/stats/roc.c:1103
+msgid "Test variable"
+msgstr "Testvariabele(n)"
 
-#: src/language/stats/crosstabs.q:2202 src/language/stats/crosstabs.q:2203
-#, c-format
-msgid "%s Dependent"
-msgstr ""
+#: src/language/stats/roc.c:1105
+msgid "Positive if greater than or equal to"
+msgstr "Positief als grote dan of gelijk aan"
 
-#: src/language/stats/descriptives.c:102 src/language/stats/examine.q:1579
-#: src/language/stats/frequencies.q:123 src/language/stats/npar-summary.c:131
-#: src/language/stats/oneway.q:395 src/language/stats/t-test.q:701
-#: src/language/stats/t-test.q:725 src/language/stats/t-test.q:862
-#: src/language/stats/t-test.q:1213 src/ui/gui/descriptives-dialog.c:39
-#: src/ui/gui/frequencies-dialog.c:40
-msgid "Mean"
-msgstr "Gemiddeld"
+#: src/language/stats/roc.c:1106 src/language/stats/roc.c:1171
+msgid "Sensitivity"
+msgstr "Gevoeligheid"
 
-#: src/language/stats/descriptives.c:103
-msgid "S E Mean"
+#: src/language/stats/roc.c:1107 src/language/stats/roc.c:1170
+msgid "1 - Specificity"
 msgstr ""
 
-#: src/language/stats/descriptives.c:104 src/language/stats/frequencies.q:127
-msgid "Std Dev"
-msgstr ""
+#: src/language/stats/roc.c:1169
+msgid "ROC Curve"
+msgstr "ROC Curve"
 
-#: src/language/stats/descriptives.c:105 src/language/stats/examine.q:1659
-#: 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/sign.c:91
+msgid "Negative Differences"
+msgstr "Negatieve Verschillen"
 
-#: src/language/stats/descriptives.c:106 src/language/stats/examine.q:1760
-#: src/language/stats/frequencies.q:129 src/ui/gui/descriptives-dialog.c:47
-#: src/ui/gui/frequencies-dialog.c:50
-msgid "Kurtosis"
+#: 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/descriptives.c:107
-msgid "S E Kurt"
+#: src/language/stats/sign.c:134 src/language/stats/wilcoxon.c:331
+msgid "Exact Sig. (2-tailed)"
 msgstr ""
 
-#: src/language/stats/descriptives.c:108 src/language/stats/examine.q:1741
-#: src/language/stats/frequencies.q:131 src/ui/gui/descriptives-dialog.c:48
-#: src/ui/gui/frequencies-dialog.c:46
-msgid "Skewness"
+#: src/language/stats/sign.c:137 src/language/stats/wilcoxon.c:332
+msgid "Exact Sig. (1-tailed)"
 msgstr ""
 
-#: src/language/stats/descriptives.c:109
-msgid "S E Skew"
+#: src/language/stats/sign.c:140 src/language/stats/wilcoxon.c:335
+msgid "Point Probability"
 msgstr ""
 
-#: src/language/stats/descriptives.c:110 src/language/stats/examine.q:1704
-#: src/language/stats/frequencies.q:133 src/ui/gui/descriptives-dialog.c:43
-#: src/ui/gui/frequencies-dialog.c:48
-msgid "Range"
-msgstr "Bereik"
+#: src/language/stats/wilcoxon.c:232
+msgid "Ranks"
+msgstr "Rangschikking"
 
-#: src/language/stats/descriptives.c:111 src/language/stats/examine.q:1684
-#: src/language/stats/frequencies.q:134 src/language/stats/npar-summary.c:137
-#: src/language/stats/oneway.q:407 src/ui/gui/descriptives-dialog.c:41
-#: src/ui/gui/frequencies-dialog.c:42
-msgid "Minimum"
+#: src/language/stats/wilcoxon.c:246
+msgid "Mean Rank"
 msgstr ""
 
-#: src/language/stats/descriptives.c:112 src/language/stats/examine.q:1694
-#: src/language/stats/frequencies.q:135 src/language/stats/npar-summary.c:140
-#: src/language/stats/oneway.q:408 src/ui/gui/descriptives-dialog.c:42
-#: src/ui/gui/frequencies-dialog.c:43
-msgid "Maximum"
-msgstr ""
+#: src/language/stats/wilcoxon.c:247
+msgid "Sum of Ranks"
+msgstr "Som van Rangen"
 
-#: 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/wilcoxon.c:259
+msgid "Negative Ranks"
+msgstr "Negatieve Rangen"
 
-#: src/language/stats/descriptives.c:344
-#, c-format
-msgid "Z-score variable name %s would be a duplicate variable name."
-msgstr "Z-score-variabelennaam %s zou een dubbele variabelennaam zijn."
+#: src/language/stats/wilcoxon.c:260
+msgid "Positive Ranks"
+msgstr "Positieve Rangen"
 
-#: src/language/stats/descriptives.c:451
-msgid "expecting statistic name: reverting to default"
-msgstr "statistische naam verwacht: teruggezet op verstek"
+#: src/language/stats/wilcoxon.c:326
+msgid "Z"
+msgstr "Z"
 
-#: 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/wilcoxon.c:327
+msgid "Asymp. Sig. (2-tailed)"
+msgstr ""
 
-#: src/language/stats/descriptives.c:556
-msgid "Mapping of variables to corresponding Z-scores."
-msgstr "Mappen van variabelen naar corresponderende Z-scores."
+#: 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/stats/descriptives.c:561
-msgid "Source"
-msgstr "Bron"
+#: 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/stats/descriptives.c:562
-msgid "Target"
-msgstr "Doel"
+#: 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/stats/descriptives.c:672
+#: src/language/data-io/combine-files.c:302
 #, c-format
-msgid "Z-score of %s"
-msgstr "Z-score van %s"
+msgid "File %s lacks BY variable %s."
+msgstr "Bestand %s mist BY variabele %s."
 
-#: src/language/stats/descriptives.c:886
-msgid "Valid N"
-msgstr ""
+#: 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/stats/descriptives.c:887
-msgid "Missing N"
-msgstr ""
+#: src/language/data-io/combine-files.c:376
+msgid "The BY subcommand is required."
+msgstr "De BY subopdracht is verplicht."
+
+#: src/language/data-io/combine-files.c:381
+#, fuzzy
+msgid "BY is required when TABLE is specified."
+msgstr "BY is noodzakelijk als %s is gespecificeerd."
+
+#: src/language/data-io/combine-files.c:386
+#, fuzzy
+msgid "BY is required when SORT is specified."
+msgstr "BY is noodzakelijk als %s is gespecificeerd."
 
-#: src/language/stats/descriptives.c:915
+#: src/language/data-io/combine-files.c:513
+msgid "Combining files with incompatible encodings. String data may not be represented correctly."
+msgstr "Combineren van bestanden met incompatibele codering. Tekenreeks gegevens worden mogelijk niet correct weergegeven."
+
+#: src/language/data-io/combine-files.c:545
 #, c-format
-msgid "Valid cases = %g; cases with missing value(s) = %g."
-msgstr "Geldige cases = %g; cases met ontbrekende-waarde(s) = %g."
+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 breedte dan dezelfde variabele in eerder bestand."
 
-#: src/language/stats/examine.q:290 src/language/stats/examine.q:293
+#: src/language/data-io/combine-files.c:551
 #, c-format
-msgid "%s is not currently supported."
-msgstr "%s is nog niet geïmplementeerd."
+msgid "In file %s, %s is numeric."
+msgstr "In bestand %s, %s is numeriek."
 
-#: src/language/stats/examine.q:503 src/language/stats/examine.q:516
+#: src/language/data-io/combine-files.c:554
 #, c-format
-msgid "%s and %s are mutually exclusive"
-msgstr "%s en %s zijn wederzijds exclusief"
+msgid "In file %s, %s is a string variable with width %d."
+msgstr "In bestand %s, %s is een tekenreeksvariabele met breedte %d."
 
-#: src/language/stats/examine.q:982
-msgid "Case Processing Summary"
-msgstr "Case Bewerkingsoverzicht"
+#: src/language/data-io/combine-files.c:559
+#, c-format
+msgid "In an earlier file, %s was numeric."
+msgstr "In een eerder bestand, %s was numeriek."
 
-#: src/language/stats/examine.q:1200
-msgid "Extreme Values"
-msgstr "Extreme Waardes"
+#: 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 tekenreeks met breedte %d."
 
-#: src/language/stats/examine.q:1216
-msgid "Case Number"
-msgstr "Case Nummer"
+#: src/language/data-io/combine-files.c:601
+#, c-format
+msgid "Variable name %s specified on %s subcommand duplicates an existing variable name."
+msgstr "Variabelennaam %s gespecificeerd op %s subopdracht dupliceert een bestaande variabelennaam."
 
-#: src/language/stats/examine.q:1317
-msgid "Highest"
-msgstr "Hoogste"
+#: src/language/data-io/combine-files.c:762
+#, c-format
+msgid "Encountered %zu sets of duplicate cases in the master file."
+msgstr "Gevonden% zu sets van dubbele cases in het master-bestand."
 
-#: src/language/stats/examine.q:1322
-msgid "Lowest"
-msgstr "Laagste"
+#: 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/stats/examine.q:1463 src/language/stats/oneway.q:397
-#: src/language/stats/oneway.q:693 src/language/stats/regression.q:203
-msgid "Std. Error"
-msgstr ""
+#: 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/stats/examine.q:1465 src/language/stats/oneway.q:411
-#: src/ui/gui/examine.glade:307
-msgid "Descriptives"
-msgstr ""
+#: 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/stats/examine.q:1597 src/language/stats/oneway.q:402
-#, c-format
-msgid "%g%% Confidence Interval for Mean"
-msgstr ""
+#: src/language/data-io/data-list.c:243
+msgid "Encoding should not be specified for inline data. It will be ignored."
+msgstr "Codering dient niet opgegeven te worden voor inline-gegevens. Het wordt genegeerd."
 
-#: src/language/stats/examine.q:1603 src/language/stats/oneway.q:404
-msgid "Lower Bound"
-msgstr "Benedengrens"
+#: 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 alleen gebruikt worden met DATA LIST FIXED."
 
-#: src/language/stats/examine.q:1614 src/language/stats/oneway.q:405
-msgid "Upper Bound"
-msgstr "Bovengrens"
+#: 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/stats/examine.q:1626
+#: 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 "5%% Trimmed Mean"
-msgstr ""
-
-#: src/language/stats/examine.q:1637 src/language/stats/frequencies.q:125
-#: src/ui/gui/frequencies-dialog.c:52
-msgid "Median"
-msgstr ""
-
-#: src/language/stats/examine.q:1671 src/language/stats/npar-summary.c:134
-#: src/language/stats/oneway.q:396 src/language/stats/t-test.q:702
-#: src/language/stats/t-test.q:726 src/language/stats/t-test.q:864
-#: src/language/stats/t-test.q:1214
-msgid "Std. Deviation"
-msgstr ""
-
-#: src/language/stats/examine.q:1716
-msgid "Interquartile Range"
-msgstr ""
+msgid "%s is a duplicate variable name."
+msgstr "%s is een dubbele variabelennaam."
 
-#: src/language/stats/examine.q:1870
+#: src/language/data-io/data-list.c:375
 #, c-format
-msgid "Boxplot of %s vs. %s"
-msgstr ""
+msgid "There is already a variable %s of a different type."
+msgstr "Er is al een variabele %s van een ander type."
 
-#: src/language/stats/examine.q:1897
-msgid "Boxplot"
-msgstr ""
+#: 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 tekenreeksvariabele %s van een andere breedte."
 
-#: src/language/stats/examine.q:1939
+#: src/language/data-io/data-list.c:390
 #, c-format
-msgid "Normal Q-Q Plot of %s"
-msgstr "Normal Q-Q Plot van %s"
+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/stats/examine.q:1940 src/language/stats/examine.q:1946
-msgid "Observed Value"
-msgstr "Waargenomen Waarde"
+#: src/language/data-io/data-parser.c:460
+#: src/language/data-io/data-parser.c:469
+msgid "Quoted string extends beyond end of line."
+msgstr "Geciteerde tekenreeks loopt door na regeleinde."
 
-#: src/language/stats/examine.q:1941
-msgid "Expected Normal"
-msgstr ""
+#: src/language/data-io/data-parser.c:525
+#, c-format
+msgid "Partial case of %d of %d records discarded."
+msgstr "Gedeeltelijke case van %d van %d records genegeerd."
 
-#: src/language/stats/examine.q:1944
+#: src/language/data-io/data-parser.c:572
 #, c-format
-msgid "Detrended Normal Q-Q Plot of %s"
-msgstr "Detrended Normal Q-Q Plot van %s"
+msgid "Partial case discarded.  The first variable missing was %s."
+msgstr "Gedeeltelijke case overgeslagen. De eerste gemiste variabele was %s."
 
-#: src/language/stats/examine.q:1947
-msgid "Dev from Normal"
-msgstr ""
+#: src/language/data-io/data-parser.c:610
+#, 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 "Ontbrekende-waarde(s) voor alle variabelen vanaf %s. Deze worden gevuld met de geschikte system-missing waarde of spatie."
 
-#: src/language/stats/examine.q:2066 src/language/stats/examine.q:2088
-#: src/language/stats/frequencies.q:1422 src/language/stats/npar-summary.c:147
-#: src/ui/gui/examine.glade:328
-msgid "Percentiles"
-msgstr ""
+#: src/language/data-io/data-parser.c:630
+msgid "Record ends in data not part of any field."
+msgstr "Record eindigt in gegeven dat geen onderdeel is van een veld."
 
-#: src/language/stats/examine.q:2221
-msgid "Tukey's Hinges"
-msgstr ""
+#: src/language/data-io/data-parser.c:651 src/language/data-io/print.c:405
+msgid "Record"
+msgstr "Record"
 
-#: 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/data-io/data-parser.c:652 src/language/data-io/print.c:406
+#: src/ui/gui/psppire-var-sheet.c:540 src/ui/gui/psppire-var-store.c:839
+#: src/ui/gui/crosstabs.glade:92
+msgid "Columns"
+msgstr "Kolommen"
 
-#: src/language/stats/flip.c:151
-msgid "Could not create temporary file for FLIP."
-msgstr "Kon geen tijdelijk bestand voor FLIP aanmaken."
+#: src/language/data-io/data-parser.c:653
+#: src/language/data-io/data-parser.c:692 src/language/data-io/print.c:407
+msgid "Format"
+msgstr ""
 
-#: src/language/stats/flip.c:162
+#: src/language/data-io/data-parser.c:672
 #, c-format
-msgid "Error writing FLIP file: %s."
-msgstr "Fout tijdens het schrijven van FLIP bestand: %s."
+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/stats/flip.c:262
+#: src/language/data-io/data-parser.c:708
 #, c-format
-msgid "Could not create acceptable variant for variable %s."
-msgstr "Kon geen acceptabele variant voor variabele %s creëren."
+msgid "Reading free-form data from %s."
+msgstr "Lezen vrije-vorm gegeven van %s."
 
-#: src/language/stats/flip.c:278
-msgid "Cannot create more than 99999 variable names."
-msgstr "Kan niet meer dan 99999 variabelennamen creëren."
+#. 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 "gegevensbestand"
 
-#: src/language/stats/flip.c:394
+#: src/language/data-io/data-reader.c:149
 #, c-format
-msgid "Error rewinding FLIP file: %s."
-msgstr "Fout tijdens terugdraaien FLIP bestand: %s."
+msgid "Could not open \"%s\" for reading as a data file: %s."
+msgstr "Kon \"%s\" niet openen voor het lezen als gegevensbestand: %s."
 
-#: src/language/stats/flip.c:401
-msgid "Error creating FLIP source file."
-msgstr "Fout tijdens het creëren van FLIP bronbestand."
+#: 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 gegevens 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/stats/flip.c:414
+#: src/language/data-io/data-reader.c:216
 #, c-format
-msgid "Error reading FLIP file: %s."
-msgstr "Fout tijdens lezen FLIP bestand: %s."
-
-#: src/language/stats/flip.c:416
-msgid "Unexpected end of file reading FLIP file."
-msgstr "Onverwacht einde-bestand tijdens lezen FLIP bestand."
+msgid "Error reading file %s: %s."
+msgstr "Fout tijdens lezen bestand %s: %s."
 
-#: src/language/stats/flip.c:432
+#: src/language/data-io/data-reader.c:219
 #, c-format
-msgid "Error seeking FLIP source file: %s."
-msgstr "Fout tijdens zoeken FLIP bronbestand: %s."
+msgid "Unexpected end of file reading %s."
+msgstr "Onverwacht einde tijdens lezen van bestand %s."
 
-#: src/language/stats/flip.c:440
+#: src/language/data-io/data-reader.c:228
 #, c-format
-msgid "Error writing FLIP source file: %s."
-msgstr "Fout tijdens schrijven FLIP bronbestand: %s."
+msgid "Unexpected end of file in partial record reading %s."
+msgstr ""
 
-#: src/language/stats/flip.c:451
+#: src/language/data-io/data-reader.c:288
 #, c-format
-msgid "Error closing FLIP source file: %s."
-msgstr "Fout tijdens sluiten FLIP bronbestand: %s."
+msgid "Corrupt block descriptor word at offset 0x%lx in %s."
+msgstr ""
 
-#: src/language/stats/flip.c:459
+#: src/language/data-io/data-reader.c:289
 #, c-format
-msgid "Error rewinding FLIP source file: %s."
-msgstr "Fout tijdens terugdraaien FLIP bronbestand: %s."
+msgid "Corrupt record descriptor word at offset 0x%lx in %s."
+msgstr ""
 
-#: src/language/stats/flip.c:488
+#: src/language/data-io/data-reader.c:302
 #, c-format
-msgid "Error reading FLIP temporary file: %s."
-msgstr "Fout tijdens lezen FLIP tijdelijk bestand: %s."
-
-#: src/language/stats/flip.c:491
-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"
+msgid "Corrupt record size at offset 0x%lx in %s."
 msgstr ""
 
-#: src/language/stats/frequencies.q:126 src/ui/gui/frequencies-dialog.c:49
-msgid "Mode"
-msgstr "Modus"
+#: src/language/data-io/data-reader.c:444
+msgid "Record exceeds remaining block length."
+msgstr "Record overschrijdt resterend blok lengte."
 
-#: src/language/stats/frequencies.q:130
-msgid "S.E. Kurt"
-msgstr ""
+#: 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/stats/frequencies.q:132
-msgid "S.E. Skew"
-msgstr ""
+#: 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/stats/frequencies.q:409
-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 "Maximaal een van BARCHART, HISTOGRAM, of HBAR mag opgegeven worden. HBAR wordt aangenomen.  Argument waardes zullen gebruikt worden in opgegeven volgorde."
+#: 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 invoerprogramma het inline-bestand niet benaderd."
 
-#: src/language/stats/frequencies.q:492
+#: src/language/data-io/data-writer.c:74
 #, 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."
+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 gegevensbestand: %s."
 
-#: src/language/stats/frequencies.q:761
+#: src/language/data-io/data-writer.c:191
 #, 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:824
-msgid "`)' expected after GROUPED interval list."
-msgstr "')' verwacht na GROUPED interval lijst."
+msgid "I/O error occurred writing data file \"%s\"."
+msgstr "I/O fout opgetreden tijdens schrijven gegevensbestand \"%s\"."
 
-#: src/language/stats/frequencies.q:836
+#: src/language/data-io/get-data.c:64
 #, c-format
-msgid "Variables %s specified on GROUPED but not on VARIABLES."
-msgstr "Variabele %s gespecificeerd bij GROUPED maar niet bij VARIABLES."
+msgid "Unsupported TYPE %s"
+msgstr "Niet ondersteund TYPE %s"
 
-#: src/language/stats/frequencies.q:843
+#: src/language/data-io/get-data.c:260
 #, 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:1062 src/language/stats/frequencies.q:1155
-#: src/language/stats/frequencies.q:1156 src/language/stats/frequencies.q:1191
-msgid "Cum"
-msgstr "Cum."
+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/stats/frequencies.q:1064 src/output/charts/plot-hist.c:125
-msgid "Frequency"
-msgstr "Frequenties"
+#: src/language/data-io/get-data.c:315
+msgid "expecting FIXED or DELIMITED"
+msgstr "FIXED of DELIMITED verwacht"
 
-#: src/language/stats/frequencies.q:1085
-msgid "Value Label"
-msgstr "WaardeLabel"
+#: 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/stats/frequencies.q:1189
-msgid "Freq"
-msgstr "Freq."
-
-#: src/language/stats/frequencies.q:1190 src/language/stats/frequencies.q:1192
-msgid "Pct"
-msgstr ""
-
-#: src/language/stats/frequencies.q:1385
-#, c-format
-msgid "No valid data for variable %s; statistics not displayed."
-msgstr "Geen geldige gegevens voor variabele %s; statistieken worden niet getoond."
-
-#: src/language/stats/frequencies.q:1426
-msgid "50 (Median)"
-msgstr ""
-
-#: src/language/stats/glm.q:148
-msgid "Multivariate GLM not yet supported"
-msgstr ""
-
-#: src/language/stats/glm.q:356 src/language/stats/regression.q:1026
-msgid "No valid data found. This command was skipped."
-msgstr "Geen geldige gegevens gevonden. Deze opdracht is overgeslagen."
-
-#: src/language/stats/means.q:100
-msgid "Missing required subcommand TABLES."
-msgstr "Mis vereiste 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-summary.c:114
-msgid "Descriptive Statistics"
-msgstr "Descriptieve Statistieken"
-
-#: src/language/stats/npar-summary.c:151
-msgid "25th"
-msgstr ""
-
-#: src/language/stats/npar-summary.c:154
-msgid "50th (Median)"
-msgstr ""
-
-#: src/language/stats/npar-summary.c:157
-msgid "75th"
-msgstr ""
-
-#: src/language/stats/npar.q:98
-msgid "NPAR subcommand not currently implemented."
-msgstr ""
-
-#: src/language/stats/npar.q:236
-#, 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:291
-#, 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 het opgegeven bereik (%d-%d) vereist precies %d waardes."
-
-#: src/language/stats/npar.q:425 src/language/stats/t-test.q:501
-#, 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/oneway.q:170
-msgid "Number of contrast coefficients must equal the number of groups"
-msgstr ""
-
-#: src/language/stats/oneway.q:179
-#, c-format
-msgid "Coefficients for contrast %zu do not total zero"
-msgstr ""
-
-#: src/language/stats/oneway.q:245
-#, c-format
-msgid "`%s' is not a variable name"
-msgstr "'%s' is geen variabelennaam"
-
-#: src/language/stats/oneway.q:279 src/language/stats/regression.q:301
-msgid "Sum of Squares"
-msgstr ""
-
-#: src/language/stats/oneway.q:281 src/language/stats/regression.q:303
-msgid "Mean Square"
-msgstr ""
-
-#: src/language/stats/oneway.q:282 src/language/stats/regression.q:304
-#: src/language/stats/t-test.q:1023
-msgid "F"
-msgstr ""
-
-#: src/language/stats/oneway.q:283 src/language/stats/oneway.q:543
-#: src/language/stats/regression.q:206 src/language/stats/regression.q:305
-msgid "Significance"
-msgstr "Significantie "
-
-#: src/language/stats/oneway.q:305
-msgid "Between Groups"
-msgstr "Tussen Groepen"
-
-#: src/language/stats/oneway.q:306
-msgid "Within Groups"
-msgstr "Binnen Groepen"
-
-#: src/language/stats/oneway.q:353 src/language/stats/regression.q:330
-msgid "ANOVA"
-msgstr ""
-
-#: src/language/stats/oneway.q:540
-msgid "Levene Statistic"
-msgstr ""
-
-#: src/language/stats/oneway.q:541
-msgid "df1"
-msgstr ""
-
-#: src/language/stats/oneway.q:542
-msgid "df2"
-msgstr ""
-
-#: src/language/stats/oneway.q:546
-msgid "Test of Homogeneity of Variances"
-msgstr ""
-
-#: src/language/stats/oneway.q:614
-msgid "Contrast Coefficients"
-msgstr ""
-
-#: src/language/stats/oneway.q:616 src/language/stats/oneway.q:691
-msgid "Contrast"
-msgstr ""
-
-#: src/language/stats/oneway.q:689
-msgid "Contrast Tests"
-msgstr ""
-
-#: src/language/stats/oneway.q:692
-msgid "Value of Contrast"
-msgstr ""
-
-#: src/language/stats/oneway.q:694 src/language/stats/regression.q:205
-#: src/language/stats/t-test.q:1025 src/language/stats/t-test.q:1218
-#: src/language/stats/t-test.q:1315
-msgid "t"
-msgstr ""
-
-#: src/language/stats/oneway.q:696 src/language/stats/t-test.q:1027
-#: src/language/stats/t-test.q:1220 src/language/stats/t-test.q:1317
-msgid "Sig. (2-tailed)"
-msgstr ""
-
-#: src/language/stats/oneway.q:740
-msgid "Assume equal variances"
-msgstr "Veronderstelt gelijke variantie"
-
-#: src/language/stats/oneway.q:744
-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:602
-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:695
-msgid "Variables Created By RANK"
-msgstr "Variabelen gecreëerd door RANK"
-
-#: src/language/stats/rank.q:719
-#, 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:730
-#, 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:744
-#, 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:754
-#, c-format
-msgid "%s into %s(%s of %s)"
-msgstr "%s in %s(%s van %s)"
-
-#: src/language/stats/rank.q:767
-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 zijn niet gevraagd. De FRACTION subopdracht wordt genegeerd."
-
-#: src/language/stats/rank.q:860
-#, c-format
-msgid "Variable %s already exists."
-msgstr "Variabele %s bestaat al."
-
-#: src/language/stats/rank.q:865
-msgid "Too many variables in INTO clause."
-msgstr "Te veel variabelen in INTO clausule."
-
-#: src/language/stats/regression.q:159 src/ui/gui/regression-dialog.c:41
-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:807
-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
-msgid "Dependent variable must be numeric."
-msgstr "Afhankelijke variabele moet numeriek zijn."
-
-#: src/language/stats/sort-cases.c:64
-msgid "Buffer limit must be at least 2."
-msgstr "Bufferlimiet moet tenminste 2 zijn."
-
-#: src/language/stats/sort-criteria.c:69
-msgid "`A' or `D' expected inside parentheses."
-msgstr "'A' of 'D' verwacht binnen haakjes."
-
-#: src/language/stats/sort-criteria.c:74
-msgid "`)' expected."
-msgstr "')' verwacht."
-
-#: src/language/stats/sort-criteria.c:85
-#, c-format
-msgid "Variable %s specified twice in sort criteria."
-msgstr "Variabele %s 2 keer opgegeven in sorteer criteria."
-
-#: src/language/stats/t-test.q:280
-msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
-msgstr "TESTVAL, GROUPS en PAIRS subopdracht zijn wederzijds uitsluitend."
-
-#: src/language/stats/t-test.q:298
-msgid "VARIABLES subcommand is not appropriate with PAIRS"
-msgstr "VARIABLES subcommando is niet geschikt met PAIRS"
-
-#: src/language/stats/t-test.q:336
-msgid "One or more VARIABLES must be specified."
-msgstr "Een of meer VARIABLES moeten gespecificeerd zijn."
-
-#: src/language/stats/t-test.q:386
-#, c-format
-msgid "Long string variable %s is not valid here."
-msgstr "Lange-tekenreeksvariabele %s is niet geldig hier."
-
-#: src/language/stats/t-test.q:406 src/language/stats/t-test.q:420
-msgid "When applying GROUPS to a string variable, two values must be specified."
-msgstr "Bij het toepassen van GROUPS op een tekenreeksvariabele moeten twee waardes opgegeven zijn."
-
-#: src/language/stats/t-test.q:518
-msgid "At least two variables must be specified on PAIRS."
-msgstr "Tenminste 2 variabelen moeten opgegeven worden bij PAIRS."
-
-#: src/language/stats/t-test.q:698
-msgid "One-Sample Statistics"
-msgstr ""
-
-#: src/language/stats/t-test.q:703 src/language/stats/t-test.q:727
-#: src/language/stats/t-test.q:865
-msgid "SE. Mean"
-msgstr ""
-
-#: src/language/stats/t-test.q:722
-msgid "Group Statistics"
-msgstr ""
-
-#: src/language/stats/t-test.q:859
-msgid "Paired Sample Statistics"
-msgstr ""
-
-#: src/language/stats/t-test.q:885 src/language/stats/t-test.q:1243
-#: src/language/stats/t-test.q:1442
-#, c-format
-msgid "Pair %d"
-msgstr ""
-
-#: src/language/stats/t-test.q:1011
-msgid "Independent Samples Test"
-msgstr ""
-
-#: src/language/stats/t-test.q:1019
-msgid "Levene's Test for Equality of Variances"
-msgstr ""
-
-#: src/language/stats/t-test.q:1021
-msgid "t-test for Equality of Means"
-msgstr ""
-
-#: src/language/stats/t-test.q:1024 src/language/stats/t-test.q:1427
-msgid "Sig."
-msgstr ""
-
-#: src/language/stats/t-test.q:1028 src/language/stats/t-test.q:1318
-msgid "Mean Difference"
-msgstr "Gemiddelde Verschil"
-
-#: src/language/stats/t-test.q:1029
-msgid "Std. Error Difference"
-msgstr ""
-
-#: src/language/stats/t-test.q:1034 src/language/stats/t-test.q:1210
-#: src/language/stats/t-test.q:1310
-#, c-format
-msgid "%g%% Confidence Interval of the Difference"
-msgstr ""
-
-#: src/language/stats/t-test.q:1090
-msgid "Equal variances assumed"
-msgstr ""
-
-#: src/language/stats/t-test.q:1142
-msgid "Equal variances not assumed"
-msgstr ""
-
-#: src/language/stats/t-test.q:1200
-msgid "Paired Samples Test"
-msgstr ""
-
-#: src/language/stats/t-test.q:1203
-msgid "Paired Differences"
-msgstr ""
-
-#: src/language/stats/t-test.q:1215
-msgid "Std. Error Mean"
-msgstr ""
-
-#: src/language/stats/t-test.q:1299
-msgid "One-Sample Test"
-msgstr ""
-
-#: src/language/stats/t-test.q:1304
-#, c-format
-msgid "Test Value = %f"
-msgstr ""
+#: src/language/data-io/get-data.c:353
+msgid "expecting LINE or VARIABLES"
+msgstr "LINE of VARIABLES verwacht"
 
-#: src/language/stats/t-test.q:1422
-msgid "Paired Samples Correlations"
-msgstr ""
+#: 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/stats/t-test.q:1426
-msgid "Correlation"
-msgstr "Correlatie"
+#: 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/stats/t-test.q:1445
-#, c-format
-msgid "%s & %s"
-msgstr ""
+#: 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/syntax-file.c:88
-#, c-format
-msgid "opening \"%s\" as syntax file"
-msgstr "openen \"%s\" als syntaxbestand"
+#: src/language/data-io/get-data.c:447
+msgid "In compatible syntax mode, the QUALIFIER string must contain exactly one character."
+msgstr "In compatible syntaxmodus, dient de QUALIFIER tekenreeks precies 1 karakter te bevatten."
 
-#: src/language/syntax-file.c:93
-#, c-format
-msgid "Opening `%s': %s."
-msgstr "Openen '%s': %s."
+#: src/language/data-io/get-data.c:462
+msgid "expecting VARIABLES"
+msgstr "VARIABLES verwacht"
 
-#: src/language/syntax-file.c:106
+#: src/language/data-io/get-data.c:484
+#: src/language/data-io/placement-parser.c:378
 #, c-format
-msgid "Reading `%s': %s."
-msgstr "Lezen '%s': %s."
+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 recordnummer, %ld, is op of voor het huidige record, %d. Gegevensvelden dienen opgegeven te worden in oplopende recordnummer volgorde."
 
-#: src/language/syntax-file.c:126
+#: src/language/data-io/get-data.c:493
 #, c-format
-msgid "Closing `%s': %s."
-msgstr "Sluiten '%s': %s."
-
-#: src/language/tests/check-model.q:138
-msgid "PATH and SEARCH subcommands are mutually exclusive.  Ignoring PATH."
-msgstr "PATH en SEARCH subopdrachten zijn wederzijds uitsluitend. PATH genegeerd. "
+msgid "The record number specified, %ld, exceeds the number of records per case specified on FIXCASE, %d."
+msgstr "Het gespecificeerde recordnummer, %ld, overschrijdt het aantal records per case zoals gespecificeerd in FIXCASE, %d."
 
-#: src/language/tests/check-model.q:156
-msgid "At least one value must be specified on PATH."
-msgstr "Tenminste 1 waarde dient bij PATH opgegeven te zijn."
+#: src/language/data-io/get.c:99
+msgid "expecting COMM or TAPE"
+msgstr "COMM of TAPE verwacht"
 
-#: src/language/tests/check-model.q:167
-#, c-format
-msgid "Hash bits adjusted to %d."
-msgstr "Hash bits aangepast naar %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/tests/check-model.q:208
-#, c-format
-msgid "error opening \"%s\" for writing"
-msgstr "fout bij openen \"%s\" voor schrijven"
+#: src/language/data-io/inpt-pgm.c:143
+msgid "Input program did not create any variables."
+msgstr "Invoerprogramma heeft geen variabelen gecreëerd."
 
-#: src/language/tests/float-format.c:124
-#, c-format
-msgid "%zu-byte string needed but %zu-byte string supplied."
-msgstr "%zu-byte tekenreeks nodig maar %zu-byte tekenreeks gegeven."
+#: src/language/data-io/inpt-pgm.c:288
+msgid "COLUMN subcommand multiply specified."
+msgstr "COLUMN subopdracht meerdere keren gespecificeerd."
 
-#: src/language/tests/float-format.c:136
-msgid "Hexadecimal floating constant too long."
-msgstr ""
+#: src/language/data-io/inpt-pgm.c:338
+msgid "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
+msgstr "REREAD: Kolomnummers moeten positieve eindige nummers zijn. Kolom is op 1 gezet."
 
-#: src/language/tests/float-format.c:201
+#: src/language/data-io/placement-parser.c:87
 #, 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."
+msgid "Number of variables specified (%zu) differs from number of variable formats (%zu)."
+msgstr "Aantal gespecificeerde variabelen (%zu) verschilt van aantal variabele formats (%zu)."
 
-#: src/language/tests/moments-test.c:47
-msgid "expecting weight value"
-msgstr "verwacht wegingwaarde"
+#: 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 format-specificatie verwacht na variabele naam."
 
-#: src/language/utilities/cd.c:41
+#: src/language/data-io/placement-parser.c:119
 #, c-format
-msgid "Cannot change directory to %s:  %s "
-msgstr "Kan map niet veranderen in %s: %s "
+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/utilities/date.c:32
-msgid "Only USE ALL is currently implemented."
-msgstr "Alleen USE ALL is op dit moment geïmplementeerd."
+#: src/language/data-io/placement-parser.c:305
+msgid "Column positions for fields must be positive."
+msgstr "Kolomposities voor velden moeten positief zijn."
 
-#: src/language/utilities/include.c:93
-msgid "Expecting BATCH or INTERACTIVE after SYNTAX."
-msgstr "BATCH of INTERACTIVE verwacht na SYNTAX."
+#: src/language/data-io/placement-parser.c:307
+msgid "Column positions for fields must not be negative."
+msgstr "Kolomposities voor velden mogen niet negatief zijn."
 
-#: src/language/utilities/include.c:110
-msgid "Expecting YES or NO after CD."
-msgstr "YES of NO verwacht na CD."
+#: src/language/data-io/placement-parser.c:344
+msgid "The ending column for a field must be greater than the starting column."
+msgstr "De eindkolom van een veld moet groter zijn dan de startkolom."
 
-#: src/language/utilities/include.c:127
-msgid "Expecting CONTINUE or STOP after ERROR."
-msgstr "CONTINUE of STOP verwacht na ERROR."
+#: 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/utilities/include.c:134
+#: src/language/data-io/print-space.c:119
 #, c-format
-msgid "Unexpected token: `%s'."
-msgstr "Onverwacht symbool: '%s'."
+msgid "The expression on PRINT SPACE evaluated to %g."
+msgstr "De expressie bij PRINT SPACE evalueerde tot %g."
 
-#: src/language/utilities/include.c:179
-msgid "expecting file name"
-msgstr "bestandsnaam verwacht"
+#: src/language/data-io/print.c:179 src/language/data-io/trim.c:54
+msgid "expecting a valid subcommand"
+msgstr "een geldig subopdracht verwacht"
 
-#: src/language/utilities/include.c:191
+#: src/language/data-io/print.c:267
 #, c-format
-msgid "Can't find `%s' in include file search path."
-msgstr "Kan '%s' niet vinden in include-bestand zoekpad."
+msgid "Output calls for %d records but %zu specified on RECORDS subcommand."
+msgstr "De uitvoer vraagt %d records maar %zu gespecificeerd bij RECORDS subopdracht."
 
-#: src/language/utilities/include.c:199
-#, c-format
-msgid "Unable to open `%s': %s."
-msgstr "Onmogelijk om te openen '%s': %s."
+#: src/language/data-io/print.c:438
+#, fuzzy, c-format
+msgid "Writing %d record to %s."
+msgid_plural "Writing %d records to %s."
+msgstr[0] "Schrijven van %zu record naar %s."
+msgstr[1] "Schrijven van %zu records naar %s."
 
-#: src/language/utilities/permissions.c:73
-#, c-format
-msgid "Expecting %s or %s."
-msgstr "Verwacht %s of %s."
+#: src/language/data-io/print.c:442
+#, fuzzy, c-format
+msgid "Writing %d record."
+msgid_plural "Writing %d records."
+msgstr[0] "Schrijven van %zu record."
+msgstr[1] "Schrijven van %zu records."
 
-#: src/language/utilities/permissions.c:106
+#: src/language/data-io/save.c:223 src/language/data-io/save.c:238
+#: src/language/data-io/save.c:266
 #, c-format
-msgid "Cannot stat %s: %s"
-msgstr ""
+msgid "expecting %s or %s"
+msgstr "%s of %s verwacht"
 
-#: src/language/utilities/permissions.c:119
+#: src/language/data-io/trim.c:88
 #, c-format
-msgid "Cannot change mode of %s: %s"
-msgstr "Kan modus van %s niet veranderen: %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 "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/utilities/set.q:200
-msgid "WORKSPACE must be at least 1MB"
-msgstr "WORKSPACE moet minstens 1MB zijn"
+#: src/language/data-io/trim.c:114
+msgid "`=' expected after variable list."
+msgstr "'=' verwacht na variabelenlijst."
 
-#: 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/data-io/trim.c:121
 #, c-format
-msgid "%s is obsolete."
-msgstr "%s is verouderd."
+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/utilities/set.q:225
+#: src/language/data-io/trim.c:134
 #, c-format
-msgid "%s is not implemented."
-msgstr "%s is niet geïmplementeerd."
+msgid "Requested renaming duplicates variable name %s."
+msgstr "Gevraagde hernoeming dupliceert variabelennaam %s."
 
-#: src/language/utilities/set.q:228
-msgid "Active file compression is not implemented."
-msgstr "Actief bestand compressie is niet geïmplementeerd."
+#: 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/utilities/set.q:323
-msgid "EPOCH must be 1500 or later."
-msgstr "EPOCH moet 1500 of later zijn."
+#: src/language/expressions/evaluate.c:155
+msgid "expecting number or string"
+msgstr "verwacht nummer of tekenreeks"
 
-#: src/language/utilities/set.q:330
-msgid "expecting AUTOMATIC or year"
-msgstr "AUTOMATIC of jaar verwacht"
+#: src/language/expressions/evaluate.c:169
+#, c-format
+msgid "Duplicate variable name %s."
+msgstr "Dubbele variabelennaam %s."
 
-#: src/language/utilities/set.q:351
-msgid "LENGTH must be at least 1."
-msgstr "LENGTH moet tenminste 1 zijn."
+#: 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/utilities/set.q:395
-msgid "WIDTH must be at least 40."
-msgstr "WIDTH moet tenminste 40 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/utilities/set.q:418
-#, c-format
-msgid "FORMAT requires numeric output format as an argument.  Specified format %s is of type string."
-msgstr "FORMAT vereist numeriek uitvoer-format als een argument. Opgegeven format %s is van het type tekenreeks."
+#: 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 het acceptabele bereik van 1 tot 53.  Het resultaat zal system-missing zijn."
 
-#: src/language/utilities/set.q:485
-msgid "BLANKS is SYSMIS."
-msgstr ""
+#: 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/utilities/set.q:487
-#, c-format
-msgid "BLANKS is %g."
-msgstr ""
+#: 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 het acceptabele bereik van 1 tot 366.  Het resultaat zal system-missing zijn."
 
-#: src/language/utilities/set.q:522
-#, c-format
-msgid "%s is \"%s\"."
-msgstr ""
+#: 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/utilities/set.q:558
+#: src/language/expressions/helpers.c:182
 #, c-format
-msgid "DECIMAL is \"%c\"."
-msgstr ""
+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/utilities/set.q:564
-#, c-format
-msgid "ENDCMD is \"%c\"."
-msgstr ""
+#: 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/utilities/set.q:572
+#: src/language/expressions/parse.c:259
 #, c-format
-msgid "ERRORS is \"%s\"."
-msgstr ""
+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 vereist."
 
-#: src/language/utilities/set.q:583
+#: src/language/expressions/parse.c:271
 #, c-format
-msgid "FORMAT is %s."
-msgstr ""
+msgid "Type mismatch: expression has %s type, but a string value is required here."
+msgstr "Type ongelijk: expressie heeft type %s, maar een tekenreeks waarde is hier vereist."
 
-#: src/language/utilities/set.q:589
+#: src/language/expressions/parse.c:427
 #, c-format
-msgid "LENGTH is %d."
-msgstr ""
+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/utilities/set.q:595
-#, c-format
-msgid "MXERRS is %d."
+#: 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/utilities/set.q:601
-#, c-format
-msgid "MXLOOPS is %d."
+#: 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/utilities/set.q:607
+#: src/language/expressions/parse.c:809
 #, c-format
-msgid "MXWARNS is %d."
-msgstr ""
+msgid "Unknown system variable %s."
+msgstr "Onbekende systeemvariabele %s."
 
-#: src/language/utilities/set.q:614 src/language/utilities/set.q:665
+#: src/language/expressions/parse.c:857
 #, c-format
-msgid "%s is %s (%s)."
-msgstr ""
-
-#: src/language/utilities/set.q:686
-msgid "SCOMPRESSION is ON."
-msgstr ""
-
-#: src/language/utilities/set.q:688
-msgid "SCOMPRESSION is OFF."
-msgstr ""
-
-#: src/language/utilities/set.q:695
-msgid "UNDEFINED is WARN."
-msgstr ""
-
-#: src/language/utilities/set.q:697
-msgid "UNDEFINED is NOWARN."
-msgstr ""
+msgid "Unknown identifier %s."
+msgstr "Onbekende herkenningsteken %s."
 
-#: src/language/utilities/set.q:705
-msgid "WEIGHT is off."
-msgstr "WEGING is uit."
+#: src/language/expressions/parse.c:892
+msgid "in expression"
+msgstr "in expressie"
 
-#: src/language/utilities/set.q:707
+#: src/language/expressions/parse.c:1073
 #, c-format
-msgid "WEIGHT is variable %s."
-msgstr "WEGING is variabele %s."
+msgid "%s must have at least %d arguments in list."
+msgstr "%s heeft tenminste %d argumenten nodig in lijst."
 
-#: src/language/utilities/set.q:725
+#: src/language/expressions/parse.c:1082
 #, c-format
-msgid "WIDTH is %d."
-msgstr "BREEDTE is %d."
+msgid "%s must have even number of arguments in list."
+msgstr "%s heeft een even aantal argumenten in lijst nodig."
 
-#: src/language/utilities/title.c:68
+#: src/language/expressions/parse.c:1085
 #, c-format
-msgid "%s: `.' expected after string."
-msgstr "%s: `.' verwacht na tekenreeks."
+msgid "%s must have multiple of %d arguments in list."
+msgstr "%s heeft meerdere %d argumenten in lijst nodig."
 
-#: src/language/utilities/title.c:108
+#: src/language/expressions/parse.c:1095
 #, c-format
-msgid "   (Entered %s)"
-msgstr "   (Ingevoerd %s)"
+msgid "%s function does not accept a minimum valid argument count."
+msgstr "%s functie accepteert geen minimaal geldige argumenten teller."
 
-#: src/language/xforms/compute.c:146 src/language/xforms/compute.c:194
+#: src/language/expressions/parse.c:1104
 #, 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."
+msgid "%s requires at least %d valid arguments in list."
+msgstr "%s vereist tenminste %d geldige argumenten in lijst."
 
-#: src/language/xforms/compute.c:150 src/language/xforms/compute.c:201
+#: src/language/expressions/parse.c:1110
 #, 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."
+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/xforms/compute.c:344
+#: src/language/expressions/parse.c:1164
 #, 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 tekenreeksvariabele zijn."
-
-#: src/language/xforms/recode.c:246
-msgid "Inconsistent target variable types.  Target variables must be all numeric or all string."
-msgstr "Inconsistent doelvariabelen-types.  Doelvariabelen moeten allemaal numeriek of allemaal tekenreeks zijn. "
-
-#: src/language/xforms/recode.c:267
-msgid "CONVERT requires string input values and numeric output values."
-msgstr "CONVERT vereist tekenreeks invoerwaardes en numerieke uitvoerwaardes."
+msgid "Type mismatch invoking %s as "
+msgstr "Type ongelijk bij aanroep %s als "
 
-#: src/language/xforms/recode.c:317
-msgid "THRU is not allowed with string variables."
-msgstr "THRU is niet toegestaan met tekenreeksvariabelen."
+#: src/language/expressions/parse.c:1169
+msgid "Function invocation "
+msgstr "Functieaanroep "
 
-#: src/language/xforms/recode.c:391
-msgid "expecting output value"
-msgstr "verwacht uitvoerwaarde"
+#: 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/xforms/recode.c:440
+#: src/language/expressions/parse.c:1201
 #, 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 doelvariabelen."
+msgid "No function or vector named %s."
+msgstr "Geen functie of vector genaamd %s."
 
-#: src/language/xforms/recode.c:455
+#: src/language/expressions/parse.c:1244
 #, 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 tekenreeksvariabelen gespecificeerd bij INTO dienen al te bestaan.  Gebruik de STRING opdracht om een tekenreeks variabele aan te maken.)"
+msgid "expecting `,' or `)' invoking %s function"
+msgstr "verwacht ',' of ')' bij aanroep %s functie"
 
-#: src/language/xforms/recode.c:470
+#: src/language/expressions/parse.c:1264
 #, c-format
-msgid "INTO is required with %s input values and %s output values."
-msgstr "INTO is vereist met %s invoerwaardes en %s uitvoerwaardes."
+msgid "%s is a PSPP extension."
+msgstr "%s is een PSPP extensie."
 
-#: src/language/xforms/recode.c:483
+#: src/language/expressions/parse.c:1273
 #, c-format
-msgid "Type mismatch.  Cannot store %s data in %s variable %s."
-msgstr "Type fout. Kan %s gegevens 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."
+msgid "%s may not appear after TEMPORARY."
+msgstr "%s mag niet voorkomen na TEMPORARY."
 
-#: src/language/xforms/sample.c:96
+#: src/libpspp/hash.c:545
 #, c-format
-msgid "Cannot sample %d observations from a population of %d."
-msgstr "Kan niet %d observaties bemonsteren van een populatie van %d."
+msgid "hash table:"
+msgstr "hash tabel:"
 
-#: src/language/xforms/select-if.c:100
-msgid "Syntax error expecting OFF or BY.  Turning off case filtering."
-msgstr "Syntaxfout verwacht OFF of BY. Schakelt case filtering uit. "
+#: src/libpspp/tmpfile.c:55
+msgid "failed to create temporary file"
+msgstr "aanmaken van tijdelijk bestand is mislukt"
 
-#: src/language/xforms/select-if.c:115
-msgid "The filter variable must be numeric."
-msgstr "De filtervariabele moet numeriek zijn."
+#: src/libpspp/tmpfile.c:96
+msgid "seeking in temporary file"
+msgstr "zoeken in tijdelijk bestand"
 
-#: src/language/xforms/select-if.c:121
-msgid "The filter variable may not be scratch."
-msgstr "De filtervariabele mag niet scratch zijn."
+#: src/libpspp/tmpfile.c:115
+msgid "reading temporary file"
+msgstr "lezen tijdelijk bestand"
 
-#: src/libpspp/hash.c:614
-#, c-format
-msgid "hash table:"
-msgstr "hash tabel:"
+#: src/libpspp/tmpfile.c:117
+msgid "unexpected end of file reading temporary file"
+msgstr "onverwacht einde-bestand bij het lezen van tijdelijk bestand"
+
+#: src/libpspp/tmpfile.c:136
+msgid "writing to temporary file"
+msgstr "schrijven naar tijdelijk bestand"
 
-#: src/math/percentiles.c:41
+#: src/math/percentiles.c:35
 msgid "HAverage"
 msgstr ""
 
-#: src/math/percentiles.c:42
+#: src/math/percentiles.c:36
 msgid "Weighted Average"
-msgstr "Gewogen Gemiddelde"
+msgstr "Gewogengemiddelde"
 
-#: src/math/percentiles.c:43
+#: src/math/percentiles.c:37
 msgid "Rounded"
 msgstr "Afgerond"
 
-#: src/math/percentiles.c:44
+#: src/math/percentiles.c:38
 msgid "Empirical"
-msgstr ""
+msgstr "Empirisch"
 
-#: src/math/percentiles.c:45
+#: src/math/percentiles.c:39
 msgid "Empirical with averaging"
-msgstr ""
+msgstr "Empirisch met gemiddelde"
+
+#: src/output/charts/plot-hist.c:138
+msgid "HISTOGRAM"
+msgstr "HISTOGRAM"
+
+#: src/output/charts/plot-hist.c:140 src/language/stats/frequencies.q:1052
+msgid "Frequency"
+msgstr "Frequenties"
 
 #: src/output/afm.c:149
 #, c-format
@@ -4044,7 +3267,6 @@ 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 schermapparaten mogen 'auto' lengte of breedte hebben"
 
@@ -4054,7 +3276,6 @@ 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"
 
@@ -4069,7 +3290,6 @@ 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"
 
@@ -4093,15 +3313,6 @@ msgstr "%s - Pagina %d"
 msgid "ascii: closing output file \"%s\""
 msgstr "ascii: sluiten uitvoerbestand \"%s\""
 
-#: src/output/chart.c:145
-#, c-format
-msgid "creating \"%s\""
-msgstr "aanmaken \"%s\""
-
-#: src/output/charts/plot-hist.c:123
-msgid "HISTOGRAM"
-msgstr ""
-
 #: src/output/html.c:71
 #, c-format
 msgid "opening HTML output file: %s"
@@ -4137,12 +3348,10 @@ msgid "output driver `%s' referenced but never defined"
 msgstr "uitvoerstuurprogramma '%s' gerefereerd maar nooit gedefinieerd"
 
 #: src/output/output.c:261
-#, c-format
 msgid "using default output driver configuration"
 msgstr "gebruik verstek uitvoerstuurprogramma-configuratie"
 
 #: src/output/output.c:290
-#, c-format
 msgid "cannot find output initialization file (use `-vv' to view search path)"
 msgstr "kan uitvoer-initialisatiebestand niet vinden (gebruik -vv om zoekpad te zien)"
 
@@ -4156,8 +3365,7 @@ msgstr "kan \"%s\" niet openen"
 msgid "reading \"%s\""
 msgstr "lezen \"%s\""
 
-#: src/output/output.c:332 src/ui/gui/message-dialog.c:97
-#, c-format
+#: src/output/output.c:332 src/ui/gui/message-dialog.c:99
 msgid "syntax error"
 msgstr "syntaxfout"
 
@@ -4167,12 +3375,10 @@ msgid "error closing \"%s\""
 msgstr "fout bij sluiten \"%s\""
 
 #: src/output/output.c:349
-#, c-format
 msgid "no active output drivers"
 msgstr "geen actieve uitvoerstuurprogramma's"
 
 #: src/output/output.c:352
-#, c-format
 msgid "error reading device definition file"
 msgstr "fout tijdens lezen apparaat-definitiebestand"
 
@@ -4221,7 +3427,6 @@ 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 "stuurprogramma definitieregel mist stuurprogramma- of klasse-naam"
 
@@ -4331,7 +3536,6 @@ msgid "cannot open font encoding file \"%s\""
 msgstr "kan fontcodering-bestand \"%s\" niet openen"
 
 #: src/output/postscript.c:1399
-#, c-format
 msgid "invalid numeric format"
 msgstr "ongeldig numeriek-format"
 
@@ -4340,16 +3544,149 @@ msgstr "ongeldig numeriek-format"
 msgid "closing Postscript encoding \"%s\""
 msgstr "sluiten Postscript codering \"%s\""
 
-#: src/output/table.c:236
+#: src/output/table.c:237
 #, 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
+#: src/output/table.c:308
 #, 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/output/chart.c:154
+#, c-format
+msgid "creating \"%s\""
+msgstr "aanmaken \"%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 u wilt dat de output door defecte algoritmes wordt berekend "
+
+#: src/ui/source-init-opts.c:43
+msgid "Append DIR to include path"
+msgstr ""
+
+#: src/ui/source-init-opts.c:44
+msgid "Clear include path"
+msgstr ""
+
+#: src/ui/source-init-opts.c:45
+msgid "Disable execution of .pspp/rc at startup"
+msgstr "Schakel executie van .pspp/rc tijdens opstarten uit"
+
+#: src/ui/source-init-opts.c:46
+msgid "Set configuration directory to DIR"
+msgstr "Zet configuratie folder op DIR"
+
+#: src/ui/source-init-opts.c:47
+msgid "Don't allow some unsafe operations"
+msgstr "Sta geen onveilige operaties 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 u alleen SPSS compatibele syntax wilt accepteren"
+
+#: src/ui/source-init-opts.c:83
+msgid "Algorithm must be either \"compatible\" or \"enhanced\"."
+msgstr "Algoritme moet of \"compatible\" of \"enhanced\" zijn."
+
+#: src/ui/source-init-opts.c:124
+msgid "Syntax must be either \"compatible\" or \"enhanced\"."
+msgstr "Syntax moet of \"compatible\" of \"enhanced\" zijn."
+
+#: 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 "BESTAND1, BESTAND2 ... BESTANDn"
+
+#: 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 "Diagnostische opties:"
+
+#: src/ui/terminal/main.c:125 src/ui/gui/main.c:177
+msgid "Options affecting syntax and behavior:"
+msgstr "Opties die syntax en gedrag beïnvloeden:"
+
+#: src/ui/terminal/main.c:156
+msgid "Stopping syntax file processing here to avoid a cascade of dependent command failures."
+msgstr "Stop syntaxbestand uitvoering hier om een cascade van afhankelijke opdrachtfouten 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 syntaxbestand af vanwege fout."
+
+#: src/ui/terminal/msg-ui.c:96
+#, c-format
+msgid "Errors (%d) exceeds limit (%d)."
+msgstr "Fouten (%d) overschrijden limiet (%d)."
+
+#: src/ui/terminal/msg-ui.c:99
+#, c-format
+msgid "Warnings (%d) exceed limit (%d)."
+msgstr "Waarschuwingen (%d) overschrijden 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 diagnostische breedsprakigheid niveau"
+
+#: src/ui/terminal/terminal-opts.c:68
+msgid "Send error messages to FILE (appended)"
+msgstr "Zend foutmeldingen naar BESTAND (toevoegen)"
+
+#: 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 "Start een interactieve sessie"
+
+#: src/ui/gui/about.c:64
+msgid "A program for the analysis of sampled data"
+msgstr "Een programma voor de analyse van bemonsterde gegevens."
+
+#. TRANSLATORS: Use this string to list the people who have helped with
+#. translation to your language.
+#: src/ui/gui/about.c:74
+msgid "translator-credits"
+msgstr "vertaler-credits"
+
+#: src/ui/gui/checkbox-treeview.c:92 src/language/stats/crosstabs.q:1234
+#: src/language/stats/crosstabs.q:1261 src/language/stats/crosstabs.q:1285
+#: src/language/stats/crosstabs.q:1309 src/language/stats/examine.q:1753
+msgid "Statistic"
+msgstr "Statistiek"
+
 #: src/ui/gui/comments-dialog.c:58
 #, c-format
 msgid "Column Number: %d"
@@ -4357,835 +3694,1195 @@ msgstr "Kolomnummer: %d"
 
 #: src/ui/gui/crosstabs-dialog.c:40
 msgid "Chisq"
-msgstr ""
+msgstr "Chisq"
+
+#: src/ui/gui/crosstabs-dialog.c:41 src/language/stats/crosstabs.q:1842
+msgid "Phi"
+msgstr "Phi"
 
 #: src/ui/gui/crosstabs-dialog.c:42
 msgid "CC"
-msgstr ""
+msgstr "CC"
+
+#: src/ui/gui/crosstabs-dialog.c:43 src/language/stats/crosstabs.q:1980
+msgid "Lambda"
+msgstr "Lambda"
 
 #: src/ui/gui/crosstabs-dialog.c:44
 msgid "UC"
-msgstr ""
+msgstr "UC"
 
 #: src/ui/gui/crosstabs-dialog.c:45
 msgid "BTau"
+msgstr "BTau"
+
+#: src/ui/gui/crosstabs-dialog.c:46
+msgid "CTau"
+msgstr "CTau"
+
+#: src/ui/gui/crosstabs-dialog.c:47
+msgid "Risk"
+msgstr ""
+
+#: src/ui/gui/crosstabs-dialog.c:48 src/language/stats/crosstabs.q:1847
+msgid "Gamma"
+msgstr "Gamma"
+
+#: src/ui/gui/crosstabs-dialog.c:49
+msgid "D"
+msgstr "D"
+
+#: src/ui/gui/crosstabs-dialog.c:50 src/language/stats/crosstabs.q:1850
+msgid "Kappa"
+msgstr "Kappa"
+
+#: src/ui/gui/crosstabs-dialog.c:51 src/language/stats/crosstabs.q:1984
+msgid "Eta"
+msgstr "Eta"
+
+#: src/ui/gui/crosstabs-dialog.c:52
+msgid "Corr"
+msgstr "Corr"
+
+#: 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:612 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/customentry.c:334
+msgid "Style of bevel around the custom entry button"
+msgstr ""
+
+#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
+msgid "Standard deviation"
+msgstr "Standaarddeviatie"
+
+#: src/ui/gui/descriptives-dialog.c:45
+msgid "Standard error"
+msgstr "Standaardfout"
+
+#: src/ui/gui/find-dialog.c:652
+#, c-format
+msgid "Bad regular expression: %s"
+msgstr "Onjuiste regulaire expressie: %s"
+
+#: 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:49 src/language/stats/frequencies.q:126
+msgid "Mode"
+msgstr "Modus"
+
+#: src/ui/gui/frequencies-dialog.c:51
+msgid "Standard error of the kurtosis"
+msgstr ""
+
+#: src/ui/gui/frequencies-dialog.c:52 src/language/stats/examine.q:1584
+#: src/language/stats/frequencies.q:125
+msgid "Median"
+msgstr "Mediaan"
+
+#: src/ui/gui/helper.c:197
+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:242
+#, 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/message-dialog.c:103
+msgid "data file error"
+msgstr "gegevensbestand 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 "gegevensbestand 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 "gegevensbestand-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 hieronder getoond."
+msgstr[1] "%d van deze meldingen worden hieronder getoond."
+
+#: src/ui/gui/missing-val-dialog.c:113 src/ui/gui/missing-val-dialog.c:167
+msgid "Incorrect value for variable type"
+msgstr "Onjuiste waarde voor variabelentype"
+
+#: src/ui/gui/missing-val-dialog.c:134 src/ui/gui/missing-val-dialog.c:143
+msgid "Incorrect range specification"
+msgstr "Onjuiste bereikspecificatie"
+
+#: src/ui/gui/oneway-anova-dialog.c:331
+#, c-format
+msgid "Contrast %d of %d"
 msgstr ""
 
-#: src/ui/gui/crosstabs-dialog.c:46
-msgid "CTau"
-msgstr ""
+#: src/ui/gui/psppire.c:247
+msgid "_Reset"
+msgstr "_Herstel"
+
+#: src/ui/gui/psppire.c:248
+msgid "_Select"
+msgstr "_Selecteer"
+
+#: src/ui/gui/psppire-data-editor.c:951
+msgid "Data View"
+msgstr "Gegevensweergave"
+
+#: src/ui/gui/psppire-data-editor.c:954
+msgid "Variable View"
+msgstr "Variabelenweergave"
+
+#: src/ui/gui/psppire-data-store.c:744
+msgid "var"
+msgstr "var"
 
-#: src/ui/gui/crosstabs-dialog.c:47
-msgid "Risk"
-msgstr ""
+#: src/ui/gui/psppire-data-store.c:755 src/ui/gui/psppire-var-store.c:699
+#: src/ui/gui/psppire-var-store.c:709 src/ui/gui/psppire-var-store.c:719
+#: src/ui/gui/psppire-var-store.c:825
+#, c-format
+msgid "%d"
+msgstr "%d"
 
-#: src/ui/gui/crosstabs-dialog.c:49
-msgid "D"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:213
+msgid "Transformations Pending"
+msgstr "Transformaties Uitstaand"
 
-#: src/ui/gui/crosstabs-dialog.c:52
-msgid "Corr"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:229
+msgid "Filter off"
+msgstr "Filter uit"
 
-#: 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
-msgid "None"
-msgstr "Geen"
+#: src/ui/gui/psppire-data-window.c:243
+#, c-format
+msgid "Filter by %s"
+msgstr "Filter op %s"
 
-#: src/ui/gui/crosstabs-dialog.c:56
-msgid "Count"
-msgstr "Aantal"
+#: src/ui/gui/psppire-data-window.c:264
+msgid "No Split"
+msgstr "Geen Splits"
 
-#: src/ui/gui/crosstabs-dialog.c:57
-msgid "Row"
-msgstr "Rij"
+#: src/ui/gui/psppire-data-window.c:273
+msgid "Split by "
+msgstr "Splits op "
 
-#: src/ui/gui/crosstabs-dialog.c:58
-msgid "Column"
-msgstr "Kolom"
+#: src/ui/gui/psppire-data-window.c:301
+msgid "Weights off"
+msgstr "Weging uit"
 
-#: src/ui/gui/crosstabs-dialog.c:60
-msgid "Expected"
-msgstr "Verwacht"
+#: src/ui/gui/psppire-data-window.c:315
+#, c-format
+msgid "Weight by %s"
+msgstr "Weeg op %s"
 
-#: src/ui/gui/crosstabs-dialog.c:62
-msgid "Std. Residual"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:383 src/ui/gui/data-editor.glade:702
+msgid "Open"
+msgstr "Open"
 
-#: src/ui/gui/crosstabs-dialog.c:63
-msgid "Adjusted Std. Residual"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:391 src/ui/gui/psppire-data-window.c:593
+msgid "System Files (*.sav)"
+msgstr "Systeembestand (*.sav)"
 
-#: src/ui/gui/crosstabs.glade:50
-msgid "Rows"
-msgstr "Rijen"
+#: src/ui/gui/psppire-data-window.c:397 src/ui/gui/psppire-data-window.c:599
+msgid "Portable Files (*.por) "
+msgstr "Overdraagbaar (Portable) Bestand (*.por)"
 
-#: src/ui/gui/crosstabs.glade:131 src/ui/gui/frequencies.glade:185
-msgid "Format..."
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:403 src/ui/gui/psppire-data-window.c:605
+#: 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/crosstabs.glade:138 src/ui/gui/examine.glade:246
-#: src/ui/gui/regression.glade:31
-msgid "Statistics..."
-msgstr "Statistieken..."
+#: src/ui/gui/psppire-data-window.c:585 src/ui/gui/data-editor.glade:712
+msgid "Save"
+msgstr "Opslaan"
 
-#: src/ui/gui/crosstabs.glade:148
-msgid "Cells..."
-msgstr "Cellen..."
+#: src/ui/gui/psppire-data-window.c:613
+msgid "System File"
+msgstr "Systeembestand"
 
-#: src/ui/gui/crosstabs.glade:230
-msgid "Print tables"
-msgstr "Print tabellen"
+#: src/ui/gui/psppire-data-window.c:618
+msgid "Portable File"
+msgstr "Overdraagbaar Bestand"
 
-#: src/ui/gui/crosstabs.glade:240
-msgid "Pivot"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:768
+msgid "Font Selection"
+msgstr "Font Selectie"
 
-#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:778
-msgid "Ascending"
-msgstr "Oplopend"
+#: src/ui/gui/psppire-data-window.c:836
+msgid "Sort Ascending"
+msgstr "Sorteer oplopend"
 
-#: src/ui/gui/crosstabs.glade:283
-msgid "No label"
-msgstr "Geen label"
+#: src/ui/gui/psppire-data-window.c:842
+msgid "Sort Descending"
+msgstr "Sorteer aflopend"
 
-#: src/ui/gui/crosstabs.glade:295
-msgid "Suppress value labels"
-msgstr "Onderdruk waardelabels"
+#: src/ui/gui/psppire-data-window.c:847 src/ui/gui/psppire-data-window.c:937
+#: src/ui/gui/data-editor.glade:174 src/ui/gui/data-editor.glade:843
+msgid "Insert Variable"
+msgstr "Invoegen Variabele"
 
-#: src/ui/gui/crosstabs.glade:311
-msgid "Labeling"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:850 src/ui/gui/psppire-data-window.c:904
+#: src/ui/gui/psppire-data-window.c:940 src/ui/gui/psppire-data-window.c:1307
+#: src/ui/gui/psppire-data-window.c:1325
+msgid "Clear"
+msgstr "Ruimop"
 
-#: src/ui/gui/crosstabs.glade:378
-msgid "Cell Display"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:901 src/ui/gui/data-editor.glade:831
+msgid "Insert Case"
+msgstr "Invoegen Case"
 
-#: 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/psppire-data-window.c:1185
+msgid "Open a data file"
+msgstr "Open een gegevensbestand"
 
-#: src/ui/gui/customentry.c:334
-msgid "Style of bevel around the custom entry button"
-msgstr ""
+#: src/ui/gui/psppire-data-window.c:1203
+msgid "New data file"
+msgstr "Nieuw gegevensbestand"
 
-#: src/ui/gui/data-editor.c:150
-msgid "Transformations Pending"
-msgstr "Transformaties Uitstaand"
+#: src/ui/gui/psppire-data-window.c:1218
+msgid "Import text data file"
+msgstr "Importeer tekstgegevensbestand"
 
-#: src/ui/gui/data-editor.c:323
-msgid "_Labels"
-msgstr "_Labels"
+#: src/ui/gui/psppire-data-window.c:1234 src/ui/gui/psppire-data-window.c:1250
+msgid "Save data to file"
+msgstr "Gegevens opslaan als bestand"
 
-#: src/ui/gui/data-editor.c:324
+#: src/ui/gui/psppire-data-window.c:1288
 msgid "Show/hide value labels"
 msgstr "Toon/verberg waardelabels"
 
-#: src/ui/gui/data-editor.c:342 src/ui/gui/data-editor.c:361
-#: src/ui/gui/data-editor.c:1512 src/ui/gui/data-editor.c:1566
-msgid "Clear"
-msgstr "Ruimop"
-
-#: src/ui/gui/data-editor.c:343
+#: src/ui/gui/psppire-data-window.c:1308
 msgid "Delete the cases at the selected position(s)"
 msgstr "Verwijder de cases op de geselecteerde positie(s)"
 
-#: src/ui/gui/data-editor.c:362
+#: src/ui/gui/psppire-data-window.c:1326
 msgid "Delete the variables at the selected position(s)"
 msgstr "Verwijder de variabele op de geselecteerde positie(s)"
 
-#: src/ui/gui/data-editor.c:377
-msgid "Insert _Variable"
-msgstr "Invoegen _Variabele"
-
-#: src/ui/gui/data-editor.c:378
+#: src/ui/gui/psppire-data-window.c:1344
 msgid "Create a new variable at the current position"
 msgstr "Creëer een nieuwe variabele op de huidige positie"
 
-#: src/ui/gui/data-editor.c:396
-msgid "Insert Ca_se"
-msgstr "Invoegen Ca_se"
-
-#: src/ui/gui/data-editor.c:397
+#: src/ui/gui/psppire-data-window.c:1359
 msgid "Create a new case at the current position"
 msgstr "Creëer een nieuwe case op de huidige positie"
 
-#: src/ui/gui/data-editor.c:417
-msgid "_Goto Case"
-msgstr "_Ga Naar Case"
-
-#: src/ui/gui/data-editor.c:418
+#: src/ui/gui/psppire-data-window.c:1375
 msgid "Jump to a Case in the Data Sheet"
 msgstr "Spring naar een Case in het Gegevensblad"
 
-#: src/ui/gui/data-editor.c:437
-msgid "_Weights"
-msgstr "Ge_wicht:"
-
-#: src/ui/gui/data-editor.c:438
+#: src/ui/gui/psppire-data-window.c:1391
 msgid "Weight cases by variable"
 msgstr "Weeg cases per variabele"
 
-#: src/ui/gui/data-editor.c:447 src/ui/gui/data-editor.glade:319
-msgid "_Transpose"
-msgstr "_Herschik"
-
-#: src/ui/gui/data-editor.c:448
+#: src/ui/gui/psppire-data-window.c:1405
 msgid "Transpose the cases with the variables"
 msgstr "Herschik de cases met de variabelen"
 
-#: src/ui/gui/data-editor.c:459
-msgid "S_plit"
-msgstr "S_plits"
-
-#: src/ui/gui/data-editor.c:460
+#: src/ui/gui/psppire-data-window.c:1419
 msgid "Split the active file"
 msgstr "Splits het actieve bestand"
 
-#: src/ui/gui/data-editor.c:470
-msgid "_Sort"
-msgstr "_Sorteer"
-
-#: src/ui/gui/data-editor.c:471
+#: src/ui/gui/psppire-data-window.c:1434
 msgid "Sort cases in the active file"
 msgstr "Sorteer cases in het actieve bestand"
 
-#: src/ui/gui/data-editor.c:479 src/ui/gui/data-editor.glade:340
-msgid "Select _Cases"
-msgstr "Selecteer _Cases"
-
-#: src/ui/gui/data-editor.c:480
+#: src/ui/gui/psppire-data-window.c:1448
 msgid "Select cases from the active file"
 msgstr "Selecteer cases van het actieve bestand"
 
-#: src/ui/gui/data-editor.c:489 src/ui/gui/data-editor.glade:369
-msgid "_Compute"
-msgstr "_Bereken"
-
-#: src/ui/gui/data-editor.c:490
+#: src/ui/gui/psppire-data-window.c:1462
 msgid "Compute new values for a variable"
 msgstr "Bereken nieuwe waardes voor een variabele"
 
-#: src/ui/gui/data-editor.c:498
-msgid "Oneway _ANOVA"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:499
+#: src/ui/gui/psppire-data-window.c:1476
 msgid "Perform one way analysis of variance"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:507 src/ui/gui/data-editor.glade:496
-msgid "_Independent Samples T Test"
-msgstr ""
-
-#: src/ui/gui/data-editor.c:508
+#: src/ui/gui/psppire-data-window.c:1491
 msgid "Calculate T Test for samples from independent groups"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:517 src/ui/gui/data-editor.glade:504
-msgid "_Paired Samples T Test"
+#: src/ui/gui/psppire-data-window.c:1505
+msgid "Calculate T Test for paired samples"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:518
-msgid "Calculate T Test for paired samples"
+#: src/ui/gui/psppire-data-window.c:1519
+msgid "Calculate T Test for sample from a single distribution"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:527
-msgid "One _Sample T Test"
+#: src/ui/gui/psppire-data-window.c:1534
+msgid "Commentary text for the data file"
+msgstr "Commentaar tekst voor het gegevensbestand"
+
+#: src/ui/gui/psppire-data-window.c:1560
+msgid "Rank Cases"
+msgstr "Rangschik Cases"
+
+#: src/ui/gui/psppire-data-window.c:1574
+msgid "Recode values into the same variables"
+msgstr "Hercodeer waardes in dezelfde variabelen"
+
+#: src/ui/gui/psppire-data-window.c:1588
+msgid "Recode values into different variables"
+msgstr "Hercodeer waardes in andere variabelen"
+
+#: src/ui/gui/psppire-data-window.c:1602
+msgid "Jump to variable"
+msgstr "Spring naar variabele"
+
+#: src/ui/gui/psppire-data-window.c:1615
+msgid "Calculate descriptive statistics (mean, variance, ...)"
+msgstr "Bereken descriptieve statistieken (mean, variance, ...)"
+
+#: src/ui/gui/psppire-data-window.c:1629
+msgid "Generate frequency statistics"
+msgstr "Genereer frequentiestatistieken"
+
+#: src/ui/gui/psppire-data-window.c:1643
+msgid "Generate crosstabulations"
+msgstr "Genereer kruistabellen"
+
+#: src/ui/gui/psppire-data-window.c:1658
+msgid "Examine Data by Factors"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:528
-msgid "Calculate T Test for sample from a single distribution"
+#: src/ui/gui/psppire-data-window.c:1672
+msgid "Estimate parameters of the linear model"
 msgstr ""
 
-#: src/ui/gui/data-editor.c:537 src/ui/gui/data-editor.glade:593
-msgid "Data File _Comments"
-msgstr "Gegevensbestand _Commentaren"
+#: src/ui/gui/psppire-data-window.c:1686 src/ui/gui/reliability.glade:7
+msgid "Reliability Analysis"
+msgstr "Betrouwbaarheids Analyses"
+
+#: src/ui/gui/psppire-data-window.c:1849
+msgid "Split the window vertically and horizontally"
+msgstr "Splits het scherm verticaal en horizontaal"
+
+#: src/ui/gui/psppire-data-window.c:1891
+msgid "Data Editor"
+msgstr "PSPP Gegevensbewerker"
+
+#: 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 "Opslaan 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 "Syntaxbestand (*.sps)"
+
+#: src/ui/gui/psppire-syntax-window.c:371
+msgid "Open Syntax"
+msgstr "Open Syntax"
+
+#: src/ui/gui/psppire-syntax-window.c:551
+msgid "Syntax Editor"
+msgstr "Syntaxbewerker"
+
+#: src/ui/gui/psppire-syntax-window.c:565
+#, c-format
+msgid "Cannot load syntax file '%s'"
+msgstr "Kan syntaxbestand '%s' niet laden"
+
+#: src/ui/gui/psppire-var-sheet.c:533 src/ui/gui/psppire-var-store.c:832
+msgid "Name"
+msgstr "Naam"
+
+#: src/ui/gui/psppire-var-sheet.c:534 src/ui/gui/psppire-var-store.c:833
+#: src/ui/gui/psppire.glade:2099 src/language/stats/crosstabs.q:1310
+msgid "Type"
+msgstr "Type"
+
+#: src/ui/gui/psppire-var-sheet.c:535 src/ui/gui/psppire-var-store.c:834
+#: src/ui/gui/psppire.glade:2020
+msgid "Width"
+msgstr "Breedte"
+
+#: src/ui/gui/psppire-var-sheet.c:536 src/ui/gui/psppire-var-store.c:835
+msgid "Decimals"
+msgstr "Decimalen"
+
+#: src/ui/gui/psppire-var-sheet.c:538 src/ui/gui/psppire-var-store.c:837
+msgid "Values"
+msgstr "Waardes"
+
+#: src/ui/gui/psppire-var-sheet.c:539 src/ui/gui/psppire-var-store.c:838
+#: src/language/stats/crosstabs.q:844 src/language/stats/examine.q:1215
+#: src/language/stats/frequencies.q:1119 src/language/stats/frequencies.q:1401
+msgid "Missing"
+msgstr "Ontbrekend"
+
+#: src/ui/gui/psppire-var-sheet.c:541 src/ui/gui/psppire-var-store.c:840
+msgid "Align"
+msgstr "Uitlijnen"
+
+#: src/ui/gui/psppire-var-sheet.c:542 src/ui/gui/psppire-var-store.c:841
+msgid "Measure"
+msgstr "Meting"
+
+#: src/ui/gui/psppire-var-store.c:622 src/ui/gui/var-sheet-dialogs.glade:43
+msgid "Comma"
+msgstr "Komma"
+
+#: src/ui/gui/psppire-var-store.c:623 src/ui/gui/var-sheet-dialogs.glade:59
+msgid "Dot"
+msgstr "Punt"
+
+#: src/ui/gui/psppire-var-store.c:624
+msgid "Scientific"
+msgstr "Wetenschappelijk"
+
+#: src/ui/gui/psppire-var-store.c:625 src/ui/gui/var-sheet-dialogs.glade:91
+msgid "Date"
+msgstr "Datum"
+
+#: src/ui/gui/psppire-var-store.c:626 src/ui/gui/var-sheet-dialogs.glade:107
+msgid "Dollar"
+msgstr "Euro"
+
+#: src/ui/gui/psppire-var-store.c:627
+msgid "Custom"
+msgstr "Aangepast"
+
+#: src/ui/gui/psppire-window.c:97
+#, c-format
+msgid "%s %s PSPPIRE %s"
+msgstr "%s %s PSPPIRE %s"
+
+#: src/ui/gui/psppire-window.c:480
+#, c-format
+msgid "Save the changes to \"%s\" before closing?"
+msgstr "Opslaan van de aanpassingen in \"%s\" voor het 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 u niet opslaat zullen de aanpassingen van de laatste %ld seconden permanent verloren gaan."
+
+#: src/ui/gui/psppire-window.c:491
+msgid "Close _without saving"
+msgstr "%s zonder %s"
+
+#: 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/regression-dialog.c:41
+msgid "Coeff"
+msgstr "Coeff"
 
-#: src/ui/gui/data-editor.c:538
-msgid "Commentary text for the data file"
-msgstr "Commentaar tekst voor het gegevensbestand"
+#: src/ui/gui/regression-dialog.c:42 src/language/stats/regression.q:159
+msgid "R"
+msgstr "R"
 
-#: src/ui/gui/data-editor.c:546 src/ui/gui/data-editor.glade:228
-msgid "_Find"
-msgstr "V_ind"
+#: src/ui/gui/regression-dialog.c:43
+msgid "Anova"
+msgstr "Anova"
 
-#: src/ui/gui/data-editor.c:547
-msgid "Find Case"
-msgstr "Vind Case"
+#: src/ui/gui/regression-dialog.c:44
+msgid "Bcov"
+msgstr "Bcov"
 
-#: src/ui/gui/data-editor.c:556 src/ui/gui/data-editor.glade:377
-msgid "Ran_k Cases"
-msgstr "Rangschi_k Cases"
+#: 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/data-editor.c:557
-msgid "Rank Cases"
-msgstr "Rangschik 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/data-editor.c:566 src/ui/gui/data-editor.glade:389
-msgid "Recode into _Same Variables"
-msgstr "Hercodeer in _Zelfde Variabelen"
+#: src/ui/gui/select-cases-dialog.c:223
+#, c-format
+msgid "%d thru %d"
+msgstr "%d tot %d"
 
-#: src/ui/gui/data-editor.c:567
-msgid "Recode values into the same Variables"
-msgstr "Hercodeer waardes in dezelfde Variabelen"
+#: 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/data-editor.c:576 src/ui/gui/data-editor.glade:396
-msgid "Recode into _Different Variables"
-msgstr "Hercodeer in _Andere Variabelen"
+#: 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/data-editor.c:577
-msgid "Recode values into different Variables"
-msgstr "Hercodeer waardes in andere Variabelen"
+#: 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 tekstbestand."
 
-#: src/ui/gui/data-editor.c:586 src/ui/gui/data-editor.glade:293
-#: src/ui/gui/data-editor.glade:584
-msgid "_Variables"
-msgstr "_Variabelen"
+#: src/ui/gui/text-data-import-dialog.c:494
+#, c-format
+msgid "\"%s\" is empty."
+msgstr "\"%s\" is leeg."
 
-#: src/ui/gui/data-editor.c:587
-msgid "Jump to Variable"
-msgstr "Spring naar Variabele"
+#: src/ui/gui/text-data-import-dialog.c:539
+msgid "Import Delimited Text Data"
+msgstr "Importeer Gescheiden Tekstgegevens"
 
-#: src/ui/gui/data-editor.c:595 src/ui/gui/data-editor.glade:450
-#: src/ui/gui/oneway.glade:179
-msgid "_Descriptives"
-msgstr ""
+#: src/ui/gui/text-data-import-dialog.c:590
+msgid "Importing Delimited Text Data"
+msgstr "Importeren Gescheiden Tekstgegevens"
 
-#: src/ui/gui/data-editor.c:596
-msgid "Calculate descriptive statistics (mean, variance, ...)"
-msgstr "Bereken descriptive statistieken (mean, variance, ...)"
+#: 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 gegevens in PSPP van een tekstbestand met 1 regel per case, waarin velden zijn gescheiden door tabs, komma's of andere scheidingstekens.\n"
 
-#: src/ui/gui/data-editor.c:605 src/ui/gui/data-editor.glade:442
-msgid "_Frequencies"
-msgstr "_Frequenties"
+#: 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 tekst.  "
+msgstr[1] "Het geselecteerde bestand bevat %zu regels tekst. "
 
-#: src/ui/gui/data-editor.c:606
-msgid "Generate frequency statistics"
-msgstr "Genereer frequentiestatistieken"
+#: 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 tekst. "
+msgstr[1] "Het geselecteerde bestand bevat ongeveer %lu regels tekst. "
 
-#: src/ui/gui/data-editor.c:614 src/ui/gui/data-editor.glade:466
-msgid "_Crosstabs"
-msgstr ""
+#: 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 voorbeeld doeleinden in de volgende schermen.  "
+msgstr[1] "Alleen de eerste %zu regels van het bestand worden getoond voor voorbeeld doeleinden in de volgende schermen.  "
 
-#: src/ui/gui/data-editor.c:615
-msgid "Generate crosstabulations"
-msgstr ""
+#: 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/data-editor.c:624 src/ui/gui/data-editor.glade:458
-msgid "_Explore"
-msgstr "_Exploreer"
+#: src/ui/gui/text-data-import-dialog.c:1523
+#: src/ui/gui/text-data-import-dialog.c:1768
+msgid "This input line has too few separators to fill in this field."
+msgstr "Deze invoerregel heeft te weinig scheidingstekens om dit veld te vullen."
 
-#: src/ui/gui/data-editor.c:625
-msgid "Examine Data by Factors"
-msgstr ""
+#: src/ui/gui/text-data-import-dialog.c:1759
+#, c-format
+msgid "Field content \"%.*s\" cannot be parsed in format %s."
+msgstr "Veldinhoud \"%.*s\" kan niet ontleed worden in format %s."
 
-#: src/ui/gui/data-editor.c:634 src/ui/gui/data-editor.glade:532
-msgid "Linear _Regression"
-msgstr ""
+#: src/ui/gui/t-test-options.c:60
+#, c-format
+msgid "Confidence Interval: %2d %%"
+msgstr "Betrouwbaarheidsinterval: %2d %%"
 
-#: src/ui/gui/data-editor.c:635
-msgid "Estimate parameters of the linear model"
-msgstr ""
+#: src/ui/gui/t-test-paired-samples.c:226
+msgid "Var 1"
+msgstr "Var 1"
 
-#: src/ui/gui/data-editor.c:1027
-msgid "Font Selection"
-msgstr "Font Selectie"
+#: src/ui/gui/t-test-paired-samples.c:227
+msgid "Var 2"
+msgstr "Var 2"
 
-#: src/ui/gui/data-editor.c:1099
-msgid "No Split"
-msgstr "Geen Splits"
+#: src/ui/gui/variable-info-dialog.c:76
+#, c-format
+msgid "Label: %s\n"
+msgstr "Label: %s\n"
 
-#: src/ui/gui/data-editor.c:1108
-msgid "Split by "
-msgstr "Splits op "
+#: src/ui/gui/variable-info-dialog.c:83
+#, c-format
+msgid "Type: %s\n"
+msgstr "Type: %s\n"
 
-#: src/ui/gui/data-editor.c:1133
-msgid "Filter off"
-msgstr "Filter uit"
+#: src/ui/gui/variable-info-dialog.c:87
+#, c-format
+msgid "Missing Values: %s\n"
+msgstr "Ontbrekende-Waardes: %s\n"
 
-#: src/ui/gui/data-editor.c:1145
+#: src/ui/gui/variable-info-dialog.c:92
 #, c-format
-msgid "Filter by %s"
-msgstr "Filter op %s"
+msgid "Measurement Level: %s\n"
+msgstr "Meetniveau: %s\n"
 
-#: src/ui/gui/data-editor.c:1163
-msgid "Weights off"
-msgstr "Weging uit"
+#: src/ui/gui/variable-info-dialog.c:107
+msgid "Value Labels:\n"
+msgstr "Waardelabels:\n"
 
-#: src/ui/gui/data-editor.c:1175
+#: src/ui/gui/variable-info-dialog.c:117
 #, c-format
-msgid "Weight by %s"
-msgstr "Weeg op %s"
+msgid "%s %s\n"
+msgstr "%s %s\n"
 
-#: src/ui/gui/data-editor.c:1198 src/ui/gui/data-editor.c:1445
-#: src/ui/gui/data-editor.glade:660
-msgid "Open"
-msgstr ""
+#: src/ui/gui/weight-cases-dialog.c:79 src/ui/gui/psppire.glade:47
+#: src/ui/gui/psppire.glade:130
+msgid "Do not weight cases"
+msgstr "Weeg cases niet"
 
-#: src/ui/gui/data-editor.c:1199
-msgid "Open a data file"
-msgstr "Open een gegevensbestand"
+#: src/ui/gui/weight-cases-dialog.c:85
+#, c-format
+msgid "Weight cases by %s"
+msgstr "Weeg cases per %s"
 
-#: src/ui/gui/data-editor.c:1207 src/ui/gui/data-editor.c:1323
-#: src/ui/gui/data-editor.glade:670
-msgid "Save"
-msgstr "Opslaan"
+#: src/ui/gui/crosstabs.glade:50
+msgid "Rows"
+msgstr "Rijen"
 
-#: src/ui/gui/data-editor.c:1208 src/ui/gui/data-editor.c:1218
-msgid "Save data to file"
-msgstr "Gegevens opslaan als bestand"
+#: src/ui/gui/crosstabs.glade:131 src/ui/gui/frequencies.glade:185
+msgid "Format..."
+msgstr ""
 
-#: src/ui/gui/data-editor.c:1217
-msgid "Save As"
-msgstr "Opslaan Als"
+#: 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/data-editor.c:1226 src/ui/gui/recode-dialog.c:928
-#: src/ui/gui/recode-dialog.c:1023
-msgid "New"
-msgstr "Nieuw"
+#: src/ui/gui/crosstabs.glade:148
+msgid "Cells..."
+msgstr "Cellen..."
 
-#: src/ui/gui/data-editor.c:1227
-msgid "New data file"
-msgstr "Nieuw gegevensbestand"
+#: src/ui/gui/crosstabs.glade:230
+msgid "Print tables"
+msgstr "Print tabellen"
 
-#: src/ui/gui/data-editor.c:1235
-msgid "_Import Text Data"
-msgstr "_Importeer Gescheiden Tekstgegevens"
+#: src/ui/gui/crosstabs.glade:240
+msgid "Pivot"
+msgstr ""
 
-#: src/ui/gui/data-editor.c:1236
-msgid "Import text data file"
-msgstr "Importeer tekstgegevensbestand"
+#: src/ui/gui/crosstabs.glade:253 src/ui/gui/psppire.glade:756
+msgid "Ascending"
+msgstr "Oplopend"
 
-#: src/ui/gui/data-editor.c:1331 src/ui/gui/data-editor.c:1453
-msgid "System Files (*.sav)"
-msgstr "Systeembestand (*.sav)"
+#: src/ui/gui/crosstabs.glade:283
+msgid "No label"
+msgstr "Geen label"
 
-#: src/ui/gui/data-editor.c:1337 src/ui/gui/data-editor.c:1459
-msgid "Portable Files (*.por) "
-msgstr "Overdraagbaar (Portable) Bestand (*.por)"
+#: src/ui/gui/crosstabs.glade:295
+msgid "Suppress value labels"
+msgstr "Onderdruk waardelabels"
 
-#: src/ui/gui/data-editor.c:1343 src/ui/gui/data-editor.c:1465
-#: src/ui/gui/syntax-editor.c:138 src/ui/gui/syntax-editor.c:522
-msgid "All Files"
-msgstr "Alle bestanden"
+#: src/ui/gui/crosstabs.glade:311
+msgid "Labeling"
+msgstr "labellen"
 
-#: src/ui/gui/data-editor.c:1351
-msgid "System File"
-msgstr "Systeembestand"
+#: src/ui/gui/crosstabs.glade:378
+msgid "Cell Display"
+msgstr ""
 
-#: src/ui/gui/data-editor.c:1356
-msgid "Portable File"
-msgstr "Overdraagbaar Bestand"
+#: 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/data-editor.c:1503
-msgid "Sort Ascending"
-msgstr "Sorteer oplopend"
+#: src/ui/gui/descriptives-dialog.glade:122 src/ui/gui/frequencies.glade:139
+msgid "Statistics:"
+msgstr "Statistieken:"
 
-#: src/ui/gui/data-editor.c:1506
-msgid "Sort Descending"
-msgstr "Sorteer aflopend"
+#: 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/data-editor.c:1509 src/ui/gui/data-editor.glade:150
-#: src/ui/gui/data-editor.glade:801
-msgid "Insert Variable"
-msgstr "Invoegen Variabele"
+#: src/ui/gui/descriptives-dialog.glade:194
+msgid "Include user-missing data in analysis"
+msgstr "Inclusief user-missing gegevens in analyse"
 
-#: src/ui/gui/data-editor.c:1563 src/ui/gui/data-editor.glade:789
-msgid "Insert Case"
-msgstr "Invoegen Case"
+#: 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/data-editor.glade:18 src/ui/gui/output-viewer.glade:22
-#: src/ui/gui/syntax-editor.glade:39
+#: 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:33 src/ui/gui/data-editor.glade:59
-#: src/ui/gui/syntax-editor.glade:57 src/ui/gui/syntax-editor.glade:87
+#: 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 ""
+msgstr "_Syntax"
 
-#: 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
+#: 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 "_Gegevens"
 
-#: src/ui/gui/data-editor.glade:78
+#: src/ui/gui/data-editor.glade:70
 msgid "_Import Delimited Text Data"
 msgstr "_Importeer Gescheiden Tekstgegevens"
 
-#: src/ui/gui/data-editor.glade:111
+#: src/ui/gui/data-editor.glade:103
+msgid "D_isplay Data File Information"
+msgstr "Toon Gegevensbestand _Informatie"
+
+#: src/ui/gui/data-editor.glade:112
+msgid "Working File"
+msgstr "Werkbestand"
+
+#: 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 Ge_gevens"
 
-#: src/ui/gui/data-editor.glade:118
+#: src/ui/gui/data-editor.glade:142
 msgid "Recently Used _Files"
 msgstr "Recent Gebruikte _Bestanden"
 
-#: src/ui/gui/data-editor.glade:142 src/ui/gui/output-viewer.glade:55
-#: src/ui/gui/syntax-editor.glade:143
+#: src/ui/gui/data-editor.glade:166 src/ui/gui/output-viewer.glade:55
+#: src/ui/gui/syntax-editor.glade:118
 msgid "_Edit"
 msgstr "B_ewerk"
 
-#: src/ui/gui/data-editor.glade:158
+#: src/ui/gui/data-editor.glade:182
 msgid "Insert Cases"
 msgstr "Invoegen Cases"
 
-#: src/ui/gui/data-editor.glade:166 src/ui/gui/data-editor.glade:738
+#: 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:207
+#: src/ui/gui/data-editor.glade:231
 msgid "Cl_ear Variables"
 msgstr "V_erwijder Variabelen"
 
-#: src/ui/gui/data-editor.glade:215
+#: src/ui/gui/data-editor.glade:239
 msgid "_Clear Cases"
 msgstr "Verwijder _Cases"
 
-#: src/ui/gui/data-editor.glade:239
+#: src/ui/gui/data-editor.glade:252
+msgid "gtk-find"
+msgstr "gtk-find"
+
+#: src/ui/gui/data-editor.glade:264
 msgid "_View"
 msgstr "Bee_ld"
 
-#: src/ui/gui/data-editor.glade:246
+#: src/ui/gui/data-editor.glade:271
 msgid "_Status Bar"
 msgstr "_Status Balk"
 
-#: src/ui/gui/data-editor.glade:259
+#: src/ui/gui/data-editor.glade:284
 msgid "_Fonts"
-msgstr ""
+msgstr "_Fonts"
 
-#: src/ui/gui/data-editor.glade:266
+#: src/ui/gui/data-editor.glade:291
 msgid "_Grid Lines"
 msgstr "_Grid Lijnen"
 
-#: src/ui/gui/data-editor.glade:274
+#: src/ui/gui/data-editor.glade:299
 msgid "Value _Labels"
 msgstr "Waarde_labels"
 
-#: src/ui/gui/data-editor.glade:311
+#: src/ui/gui/data-editor.glade:318 src/ui/gui/data-editor.glade:613
+msgid "_Variables"
+msgstr "_Variabelen"
+
+#: src/ui/gui/data-editor.glade:336
 msgid "_Sort Cases"
 msgstr "_Sorteer Cases"
 
-#: src/ui/gui/data-editor.glade:332
+#: 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:347
+#: 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:359
+#: src/ui/gui/data-editor.glade:390
 msgid "_Transform"
 msgstr "_Transformeer"
 
-#: src/ui/gui/data-editor.glade:409
+#: 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 "_uitvoeren uitstaande Transformaties"
 
-#: src/ui/gui/data-editor.glade:422
+#: src/ui/gui/data-editor.glade:453
 msgid "_Analyze"
 msgstr "_Analyseer"
 
-#: src/ui/gui/data-editor.glade:432
+#: src/ui/gui/data-editor.glade:463
 msgid "_Descriptive Statistics"
 msgstr "_Descriptieve Statistieken"
 
-#: src/ui/gui/data-editor.glade:478
+#: 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 "_Descriptieven"
+
+#: src/ui/gui/data-editor.glade:489
+msgid "_Explore"
+msgstr "_Exploreer"
+
+#: src/ui/gui/data-editor.glade:497
+msgid "_Crosstabs"
+msgstr "_Kruistabellen"
+
+#: src/ui/gui/data-editor.glade:509
+#, fuzzy
 msgid "Compare _Means"
-msgstr "_Vergelijk Gemiddelde"
+msgstr "Vergelijk groepen."
 
-#: src/ui/gui/data-editor.glade:488
+#: src/ui/gui/data-editor.glade:519
+#, fuzzy
 msgid "_One Sample T Test"
-msgstr ""
+msgstr "One-Sample Test"
+
+#: src/ui/gui/data-editor.glade:527
+#, fuzzy
+msgid "_Independent Samples T Test"
+msgstr "One-Sample Test"
+
+#: src/ui/gui/data-editor.glade:535
+#, fuzzy
+msgid "_Paired Samples T Test"
+msgstr "One-Sample Test"
 
-#: src/ui/gui/data-editor.glade:512
+#: src/ui/gui/data-editor.glade:543
 msgid "One Way _ANOVA"
 msgstr ""
 
-#: src/ui/gui/data-editor.glade:524
-msgid "Bivariate _Correlation"
-msgstr ""
+#: src/ui/gui/data-editor.glade:554
+msgid "Re_liability"
+msgstr "Betrouwbaarheid"
+
+#: src/ui/gui/data-editor.glade:562
+msgid "Linear _Regression"
+msgstr "Lineare _Regressie"
 
-#: src/ui/gui/data-editor.glade:540
+#: src/ui/gui/data-editor.glade:569
+#, fuzzy
 msgid "_Non-Parametric Statistics"
-msgstr ""
+msgstr "Test Statistieken"
 
-#: src/ui/gui/data-editor.glade:550
+#: src/ui/gui/data-editor.glade:579
 msgid "_Chi-Square"
-msgstr ""
+msgstr "_Chi-Square"
 
-#: src/ui/gui/data-editor.glade:558
+#: src/ui/gui/data-editor.glade:587
 msgid "_Binomial"
-msgstr ""
+msgstr "_Binomiaal"
 
-#: src/ui/gui/data-editor.glade:574
+#: src/ui/gui/data-editor.glade:603
 msgid "_Utilities"
 msgstr "E_xtra"
 
-#: src/ui/gui/data-editor.glade:604 src/ui/gui/output-viewer.glade:78
-#: src/ui/gui/syntax-editor.glade:234
+#: src/ui/gui/data-editor.glade:622
+msgid "Data File _Comments"
+msgstr "Gegevensbestand _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:611 src/ui/gui/output-viewer.glade:88
-#: src/ui/gui/syntax-editor.glade:243
+#: 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 "_Minimaliseer Alle Vensters"
 
-#: src/ui/gui/data-editor.glade:622 src/ui/gui/output-viewer.glade:99
-#: src/ui/gui/syntax-editor.glade:254
+#: 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 ""
+msgstr "_Help"
 
-#: src/ui/gui/data-editor.glade:629 src/ui/gui/output-viewer.glade:106
-#: src/ui/gui/syntax-editor.glade:262
+#: 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:636 src/ui/gui/output-viewer.glade:113
-#: src/ui/gui/syntax-editor.glade:269
+#: 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:680
+#: src/ui/gui/data-editor.glade:722
 msgid "Print"
 msgstr "Afdrukken"
 
-#: src/ui/gui/data-editor.glade:690
+#: src/ui/gui/data-editor.glade:732
 msgid "Recall"
 msgstr "Opnieuw"
 
-#: src/ui/gui/data-editor.glade:708
+#: src/ui/gui/data-editor.glade:750
 msgid "Undo"
 msgstr "Ongedaan maken"
 
-#: src/ui/gui/data-editor.glade:718
+#: src/ui/gui/data-editor.glade:760
 msgid "Redo"
 msgstr "Herstellen"
 
-#: src/ui/gui/data-editor.glade:748
+#: src/ui/gui/data-editor.glade:790
 msgid "Variables"
 msgstr "Variabelen"
 
-#: src/ui/gui/data-editor.glade:769
+#: src/ui/gui/data-editor.glade:811
 msgid "Find"
 msgstr "V_ind"
 
-#: src/ui/gui/data-editor.glade:821
+#: src/ui/gui/data-editor.glade:863
 msgid "Split File"
 msgstr "Splits Bestand"
 
-#: src/ui/gui/data-editor.glade:832
+#: src/ui/gui/data-editor.glade:874
 msgid "Weight Cases"
 msgstr "Weeg Cases"
 
-#: src/ui/gui/data-editor.glade:844
+#: src/ui/gui/data-editor.glade:886
 msgid "Select Cases"
 msgstr "Selecteer Cases"
 
-#: src/ui/gui/data-editor.glade:864 src/ui/gui/data-editor.glade:1452
-#: src/ui/gui/data-editor.glade:1633
+#: 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 "Waardelabels"
 
-#: src/ui/gui/data-editor.glade:875
+#: src/ui/gui/data-editor.glade:917
 msgid "Use Sets"
 msgstr "Gebruik Sets"
 
-#: src/ui/gui/data-editor.glade:907
+#: src/ui/gui/data-editor.glade:938
 msgid "Information Area"
 msgstr "Informatiegebied"
 
-#: src/ui/gui/data-editor.glade:926
+#: src/ui/gui/data-editor.glade:957
 msgid "Processor Area"
 msgstr "Processorgebied"
 
-#: src/ui/gui/data-editor.glade:951
+#: src/ui/gui/data-editor.glade:982
 msgid "Case Counter Area"
 msgstr "Case-Tellergebied"
 
-#: src/ui/gui/data-editor.glade:976
+#: src/ui/gui/data-editor.glade:1007
 msgid "Filter Use Status Area"
 msgstr "Filtergebruik Statusgebied"
 
-#: src/ui/gui/data-editor.glade:1002
+#: src/ui/gui/data-editor.glade:1033
 msgid "Weight Status Area"
 msgstr "Weging Statusgebied"
 
-#: src/ui/gui/data-editor.glade:1028
+#: src/ui/gui/data-editor.glade:1059
 msgid "Split File Status Area"
 msgstr "Splitsbestand Statusgebied"
 
-#: src/ui/gui/data-editor.glade:1058
-msgid "Variable Type"
-msgstr "Variabelentype"
-
-#: src/ui/gui/data-editor.glade:1094 src/ui/gui/psppire-var-store.c:599
-msgid "Comma"
-msgstr "Komma"
-
-#: src/ui/gui/data-editor.glade:1110 src/ui/gui/psppire-var-store.c:600
-msgid "Dot"
-msgstr "Punt"
-
-#: src/ui/gui/data-editor.glade:1126
-msgid "Scientific notation"
-msgstr "Wetenschappelijke notatie"
-
-#: src/ui/gui/data-editor.glade:1142 src/ui/gui/psppire-var-store.c:602
-msgid "Date"
-msgstr "Datum"
-
-#: src/ui/gui/data-editor.glade:1158 src/ui/gui/psppire-var-store.c:603
-msgid "Dollar"
-msgstr "Euro"
-
-#: src/ui/gui/data-editor.glade:1174
-msgid "Custom currency"
-msgstr "Aangepaste waarde"
-
-#: src/ui/gui/data-editor.glade:1268
-msgid "positive"
-msgstr "positief"
-
-#: src/ui/gui/data-editor.glade:1274
-msgid "negative"
-msgstr "negatief"
-
-#: src/ui/gui/data-editor.glade:1287
-msgid "Sample"
-msgstr "Steekproef"
-
-#: src/ui/gui/data-editor.glade:1337
-msgid "Width:"
-msgstr "Breedte:"
-
-#: src/ui/gui/data-editor.glade:1381
-msgid "Decimal Places:"
-msgstr "Decimalen:"
-
-#: src/ui/gui/data-editor.glade:1550
-msgid "Value Label:"
-msgstr "Waardelabel:"
-
-#: src/ui/gui/data-editor.glade:1563 src/ui/gui/psppire.glade:2544
-#: src/ui/gui/recode.glade:185
-msgid "Value:"
-msgstr "Waarde:"
-
-#: src/ui/gui/data-editor.glade:1700 src/ui/gui/examine.glade:423
-#: src/ui/gui/t-test.glade:460
-msgid "Missing Values"
-msgstr "Ontbrekende-Waardes"
-
-#: src/ui/gui/data-editor.glade:1718
-msgid "_Range plus one optional discrete missing value"
-msgstr ""
-
-#: src/ui/gui/data-editor.glade:1743
-msgid "_Low:"
-msgstr "_Laag:"
-
-#: src/ui/gui/data-editor.glade:1772
-msgid "_High:"
-msgstr "_Hoog:"
-
-#: src/ui/gui/data-editor.glade:1813
-msgid "Di_screte value:"
-msgstr ""
-
-#: src/ui/gui/data-editor.glade:1860
-msgid "_No missing values"
-msgstr ""
-
-#: src/ui/gui/data-editor.glade:1878
-msgid "_Discrete missing values"
-msgstr ""
-
-#: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
-msgid "Standard deviation"
-msgstr "Standaarddeviatie"
-
-#: src/ui/gui/descriptives-dialog.c:45
-msgid "Standard error"
-msgstr "Standaardfout"
-
-#: 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 gegevens 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:132
-msgid "Dependent List:"
-msgstr ""
+#: src/ui/gui/examine.glade:49
+msgid "Label Cases by:"
+msgstr "Label Cases per:"
 
-#: src/ui/gui/examine.glade:180
+#: src/ui/gui/examine.glade:100
 msgid "Factor List:"
-msgstr ""
+msgstr "Factor Lijst:"
 
-#: src/ui/gui/examine.glade:218
-msgid "Label Cases by:"
-msgstr ""
+#: src/ui/gui/examine.glade:150
+msgid "Dependent List:"
+msgstr "Afhankelijkenlijst:"
 
-#: src/ui/gui/examine.glade:255 src/ui/gui/t-test.glade:69
+#: 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:316
+#: src/ui/gui/examine.glade:310 src/language/stats/examine.q:1750
+#: src/language/stats/oneway.q:408
+msgid "Descriptives"
+msgstr "Descriptieve"
+
+#: src/ui/gui/examine.glade:320
 msgid "Extremes"
-msgstr ""
+msgstr "Extremen"
 
-#: src/ui/gui/examine.glade:382
+#: src/ui/gui/examine.glade:388
 msgid "Exclude cases listwise"
 msgstr ""
 
-#: src/ui/gui/examine.glade:392
+#: src/ui/gui/examine.glade:399
 msgid "Exclude cases pairwise"
 msgstr ""
 
-#: src/ui/gui/examine.glade:406
+#: src/ui/gui/examine.glade:414
 msgid "Repeat values"
 msgstr "Herhaal waardes"
 
-#: src/ui/gui/find-dialog.c:659
-#, c-format
-msgid "Bad regular expression: %s"
-msgstr "Onjuiste regulaire expressie: %s"
+#: 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/frequencies-dialog.c:44
-msgid "Standard error of the mean"
-msgstr ""
+#: src/ui/gui/find.glade:80
+msgid "Variable:"
+msgstr "Variabele:"
 
-#: src/ui/gui/frequencies-dialog.c:47
-msgid "Standard error of the skewness"
-msgstr ""
+#: 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/frequencies-dialog.c:51
-msgid "Standard error of the kurtosis"
+#: src/ui/gui/find.glade:137
+msgid "Search value labels"
+msgstr "Zoek waardelabels"
+
+#: src/ui/gui/find.glade:161
+msgid "Regular expression Match"
 msgstr ""
 
-#: src/ui/gui/frequencies.glade:98 src/ui/gui/psppire.glade:265
-#: src/ui/gui/rank.glade:67
+#: src/ui/gui/find.glade:172
+msgid "Search substrings"
+msgstr "Zoek subtekenreeksen"
+
+#: 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.glade:98 src/ui/gui/psppire.glade:252
+#: src/ui/gui/rank.glade:103
 msgid "Variable(s):"
 msgstr "Variabele(n):"
 
@@ -5221,103 +4918,194 @@ msgstr "Onderdruk tabellen met meer dan N categorieën"
 msgid "Maximum no of categories"
 msgstr "Maximaal aantal categorieën"
 
-#: src/ui/gui/helper.c:139
-msgid "Sorry. The help system hasn't yet been implemented."
-msgstr "Sorry. Het help systeem is nog niet geïmplementeerd."
+#: src/ui/gui/message-dialog.glade:10
+msgid "Messages Reported"
+msgstr "Meldingen Gerapporteerd"
 
-#: src/ui/gui/helper.c:165
-#, c-format
-msgid "Cannot open reference manual: %s"
-msgstr "Kan de handleiding niet openen: %s"
+#: 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 hieronder getoond:"
 
-#: src/ui/gui/message-dialog.c:101
-msgid "data file error"
-msgstr "gegevensbestand fout"
+#: src/ui/gui/message-dialog.glade:101
+msgid "gtk-close"
+msgstr "gtk-sluit"
 
-#: src/ui/gui/message-dialog.c:106
-msgid "PSPP error"
-msgstr "PSPP fout"
+#: src/ui/gui/psppire.glade:58
+msgid "Weight cases by"
+msgstr "Weeg cases op"
 
-#: src/ui/gui/message-dialog.c:114
-msgid "syntax warning"
-msgstr "syntax waarschuwing"
+#: src/ui/gui/psppire.glade:83
+msgid "Frequency Variable"
+msgstr "Frequentievariabele"
 
-#: src/ui/gui/message-dialog.c:118
-msgid "data file warning"
-msgstr "gegevensbestand waarschuwing"
+#: src/ui/gui/psppire.glade:123
+msgid "Current Status: "
+msgstr "Huidige Status: "
 
-#: src/ui/gui/message-dialog.c:123
-msgid "PSPP warning"
-msgstr "PSPP waarschuwing"
+#: src/ui/gui/psppire.glade:219
+msgid "Name Variable:"
+msgstr "Naam Variabele:"
 
-#: src/ui/gui/message-dialog.c:132
-msgid "syntax information"
-msgstr "syntax informatie"
+#: 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/message-dialog.c:136
-msgid "data file information"
-msgstr "gegevensbestand-informatie"
+#: src/ui/gui/psppire.glade:415
+msgid "Compare groups."
+msgstr "Vergelijk groepen."
 
-#: src/ui/gui/message-dialog.c:141
-msgid "PSPP information"
-msgstr "PSPP informatie"
+#: 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 groeperingsvariabelen."
+
+#: 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 "Sorteervolgorde"
+
+#: src/ui/gui/psppire.glade:853
+msgid "Target Variable:"
+msgstr "Doelvariabele:"
+
+#: src/ui/gui/psppire.glade:884
+msgid "Type & Label"
+msgstr "Type & Label"
+
+#: 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 filtervariabele"
+
+#: src/ui/gui/psppire.glade:1373
+msgid "Based on time or case range"
+msgstr "Gebaseerd op tijd of case bereik"
+
+#: src/ui/gui/psppire.glade:1386
+msgid "Range..."
+msgstr "Bereik..."
+
+#: src/ui/gui/psppire.glade:1425
+msgid "Random sample of cases"
+msgstr "Willekeurige 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/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] "De PSPP processor rapporteert de volgende melding:"
-msgstr[1] "De PSPP processor rapporteert de volgende meldingen:"
+#: src/ui/gui/psppire.glade:1664
+msgid "Comments:"
+msgstr "Commentaren:"
 
-#: 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] "De PSPP processor rapporteert %d melding."
-msgstr[1] "De PSPP processor rapporteert %d meldingen."
+#: src/ui/gui/psppire.glade:1706
+msgid "Display comments in output"
+msgstr "Toon commentaren in uitvoer"
 
-#: 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] "%d van deze meldingen worden hieronder getoond."
-msgstr[1] "%d van deze meldingen worden hieronder getoond."
+#: src/ui/gui/psppire.glade:1721
+msgid "Column Number: 0"
+msgstr "Kolomnummer: 0"
 
-#: src/ui/gui/message-dialog.glade:8
-msgid "Messages Reported"
-msgstr "Meldingen Gerapporteerd"
+#: src/ui/gui/psppire.glade:1804
+msgid "First case"
+msgstr "Eerste case"
 
-#: src/ui/gui/message-dialog.glade:42
-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 hieronder getoond:"
+#: src/ui/gui/psppire.glade:1817
+msgid "Last case"
+msgstr "Laatste case"
 
-#: src/ui/gui/message-dialog.glade:94
-msgid "gtk-close"
-msgstr ""
+#: src/ui/gui/psppire.glade:1830
+msgid "Observation"
+msgstr "Observatie"
 
-#: src/ui/gui/missing-val-dialog.c:115 src/ui/gui/missing-val-dialog.c:160
-msgid "Incorrect value for variable type"
-msgstr "Onjuiste waarde voor variabelentype"
+#: src/ui/gui/psppire.glade:1894
+msgid "Use expression as label"
+msgstr "Gebruik expressie als label"
 
-#: src/ui/gui/missing-val-dialog.c:136 src/ui/gui/missing-val-dialog.c:143
-msgid "Incorrect range specification"
-msgstr "Onjuiste bereikspecificatie"
+#: src/ui/gui/psppire.glade:2150
+msgid "Goto Case Number:"
+msgstr "Ga naar Case Nummer:"
 
-#: src/ui/gui/oneway-anova-dialog.c:335
-#, c-format
-msgid "Contrast %d of %d"
-msgstr ""
+#: src/ui/gui/psppire.glade:2287
+msgid "Sample Size"
+msgstr "Steekproef Grootte"
 
 #: src/ui/gui/oneway.glade:30
 msgid "_Factor:"
-msgstr ""
+msgstr "_Factor:"
 
 #: src/ui/gui/oneway.glade:66
 msgid "Dependent _Variable(s):"
-msgstr ""
+msgstr "Afhankelijke _Variabel(en):"
 
 #: src/ui/gui/oneway.glade:190
 msgid "_Homogeneity"
-msgstr ""
+msgstr "_Homogeniteit"
 
 #: src/ui/gui/oneway.glade:226
 msgid "_Contrasts..."
@@ -5325,19 +5113,19 @@ msgstr ""
 
 #: src/ui/gui/oneway.glade:309
 msgid "gtk-go-back"
-msgstr ""
+msgstr "gtk-ga-terug"
 
 #: src/ui/gui/oneway.glade:320
 msgid "gtk-go-forward"
-msgstr ""
+msgstr "gtk-ga-vooruit"
 
 #: src/ui/gui/oneway.glade:343
 msgid "_Coefficients:"
-msgstr ""
+msgstr "_Coëfficiënten:"
 
 #: src/ui/gui/oneway.glade:389
 msgid "Coefficient Total: "
-msgstr ""
+msgstr "Coëfficiënt Totaal: "
 
 #: src/ui/gui/oneway.glade:422
 msgid "Contrast 1 of 1"
@@ -5345,1252 +5133,1484 @@ msgstr ""
 
 #: src/ui/gui/output-viewer.glade:32
 msgid "gtk-save"
-msgstr ""
+msgstr "gtk-opslaan"
 
 #: src/ui/gui/output-viewer.glade:41
 msgid "gtk-save-as"
-msgstr ""
+msgstr "gtk-opslaan-als"
 
 #: src/ui/gui/output-viewer.glade:65
 msgid "gtk-copy"
+msgstr "gtk-kopie"
+
+#: 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/psppire-buttonbox.c:143
-msgid "Buttons"
-msgstr "Knoppen"
+#: src/ui/gui/rank.glade:339
+msgid "Sum of case weights"
+msgstr "Totaal van case gewichten"
 
-#: 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/rank.glade:355
+msgid "Fractional rank as %"
+msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:273 src/ui/gui/psppire-buttonbox.c:429
-msgid "Continue"
-msgstr "Verder"
+#: src/ui/gui/rank.glade:369
+msgid "Fractional rank"
+msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:427
-msgid "OK"
+#: src/ui/gui/rank.glade:383
+msgid "Savage score"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:428
-msgid "Go To"
-msgstr "Ga Naar"
+#: src/ui/gui/rank.glade:397
+msgid "Rank"
+msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:430
-msgid "Cancel"
-msgstr "Afbreken"
+#: src/ui/gui/rank.glade:411
+msgid "Ntiles"
+msgstr "Ntiles"
 
-#: src/ui/gui/psppire-buttonbox.c:431
-msgid "Help"
+#: src/ui/gui/rank.glade:450
+msgid "Proportion Estimates"
 msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:432
-msgid "Reset"
-msgstr "Herstel"
+#: src/ui/gui/rank.glade:460
+msgid "Normal Scores"
+msgstr ""
 
-#: src/ui/gui/psppire-buttonbox.c:433
-msgid "Paste"
-msgstr "Plak"
+#: src/ui/gui/rank.glade:494
+msgid "Blom"
+msgstr "Blom"
 
-#: src/ui/gui/psppire-data-editor.c:617
-msgid "Data View"
-msgstr "Gegevensweergave"
+#: src/ui/gui/rank.glade:505
+msgid "Tukey"
+msgstr "Tukey"
 
-#: src/ui/gui/psppire-data-editor.c:620
-msgid "Variable View"
-msgstr "Variabelenweergave"
+#: src/ui/gui/rank.glade:519
+msgid "Rankit"
+msgstr "Rankit"
 
-#: src/ui/gui/psppire-data-store.c:828
-msgid "var"
+#: src/ui/gui/rank.glade:533
+msgid "Van der Wärden"
+msgstr "Van der Wärden"
+
+#: src/ui/gui/rank.glade:550
+msgid "Proportion Estimation Formula"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:949 src/ui/gui/psppire-var-store.c:840
-#, c-format
-msgid "%ld"
+#: 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 "_Sequentiële rangen naar unieke waardes"
+
+#: src/ui/gui/rank.glade:678
+msgid "Rank Assigned to Ties"
 msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:100
-msgid "Name"
-msgstr "Naam"
+#: src/ui/gui/recode.glade:197
+#, fuzzy
+msgid "System-Missing"
+msgstr "Ontbrekend"
 
-#: src/ui/gui/psppire-var-sheet.c:102 src/ui/gui/psppire.glade:2187
-msgid "Width"
-msgstr "Breedte"
+#: src/ui/gui/recode.glade:211
+msgid "System-or user-missing"
+msgstr ""
 
-#: src/ui/gui/psppire-var-sheet.c:103
-msgid "Decimals"
-msgstr "Decimalen"
+#: src/ui/gui/recode.glade:245
+msgid "through"
+msgstr "tot"
 
-#: src/ui/gui/psppire-var-sheet.c:105
-msgid "Values"
-msgstr "Waardes"
+#: src/ui/gui/recode.glade:283
+msgid "Range, LOWEST thru value"
+msgstr "Bereik, LAAGSTE tot waarde"
 
-#: src/ui/gui/psppire-var-sheet.c:108
-msgid "Align"
-msgstr "Uitlijnen"
+#: src/ui/gui/recode.glade:297
+msgid "Range, value thru HIGHEST"
+msgstr "Bereik, waarde tot HOOGSTE"
 
-#: src/ui/gui/psppire-var-sheet.c:109
-msgid "Measure"
-msgstr "Meting"
+#: src/ui/gui/recode.glade:327
+msgid "All other values"
+msgstr "Alle andere waardes"
 
-#: src/ui/gui/psppire-var-store.c:601
-msgid "Scientific"
-msgstr "Wetenschappelijk"
+#: src/ui/gui/recode.glade:363
+msgid "Range:"
+msgstr "Bereik:"
 
-#: src/ui/gui/psppire-var-store.c:604
-msgid "Custom"
-msgstr "Aangepast"
+#: src/ui/gui/recode.glade:380
+msgid "Old Value"
+msgstr "Oude Waarde"
 
-#: src/ui/gui/psppire-var-store.c:675 src/ui/gui/psppire-var-store.c:685
-#: src/ui/gui/psppire-var-store.c:695
-#, c-format
-msgid "%d"
+#: 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 tekenreeksen naar nummers ('5' -> 5)"
+
+#: src/ui/gui/recode.glade:608
+msgid "Output variables are strings"
+msgstr "Uitvoervariabelen zijn tekenreeksen"
+
+#: 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 "Uitvoervariabele"
+
+#: src/ui/gui/recode.glade:965
+msgid "Old and New Values"
+msgstr "Oude en Nieuwe Waardes"
+
+#: 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 "Model:\t"
+
+#: 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 splitsing:"
+
+#: src/ui/gui/syntax-editor.glade:163
+msgid "_Run"
+msgstr "Uitvoe_ren"
+
+#: src/ui/gui/syntax-editor.glade:172
+msgid "All"
+msgstr "Alles"
 
-#: src/ui/gui/psppire.c:206
-msgid "_Reset"
-msgstr "_Herstel"
+#: src/ui/gui/syntax-editor.glade:180
+msgid "Selection"
+msgstr "Selectie"
 
-#: src/ui/gui/psppire.c:207
-msgid "_Select"
-msgstr "_Selecteer"
+#: src/ui/gui/syntax-editor.glade:188
+msgid "Current Line"
+msgstr "Huidige Regel"
 
-#: src/ui/gui/psppire.glade:11
-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"
+#: src/ui/gui/syntax-editor.glade:197
+msgid "To End"
+msgstr "Naar Einde"
 
-#: src/ui/gui/psppire.glade:73 src/ui/gui/psppire.glade:154
-#: src/ui/gui/weight-cases-dialog.c:80
-msgid "Do not weight cases"
-msgstr "Weeg cases niet"
+#: src/ui/gui/text-data-import.glade:8
+msgid "Importing Textual Data"
+msgstr "Importeren Textuele Gegevens"
 
-#: src/ui/gui/psppire.glade:83
-msgid "Weight cases by"
-msgstr "Weeg cases op"
+#: 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 assistent zal je assisteren bij het proces van het importeren van gegevens in PSPP vanuit een tekstbestand met een regel per case en velden gescheiden met tabs, komma's of andere scheiders.\n"
+" \n"
+"Het geselecteerde bestand bevat N regels tekst.  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/psppire.glade:107
-msgid "Frequency Variable"
-msgstr "Frequentievariabele"
+#: src/ui/gui/text-data-import.glade:50
+msgid "All cases"
+msgstr "Alle cases"
 
-#: src/ui/gui/psppire.glade:147
-msgid "Current Status: "
-msgstr "Huidige Status:"
+#: 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/psppire.glade:314
-msgid "Name Variable:"
-msgstr "Naam Variabele:"
+#: src/ui/gui/text-data-import.glade:97
+msgid " cases"
+msgstr " cases"
 
-#: src/ui/gui/psppire.glade:429
-msgid "Analyze all cases.  Do not create groups."
-msgstr "Analyseer alle cases.  Creëer geen groepen."
+#: src/ui/gui/text-data-import.glade:152
+msgid "% of file (approximately)"
+msgstr "% van bestand (ongeveer)"
 
-#: src/ui/gui/psppire.glade:439
-msgid "Compare groups."
-msgstr "Vergelijk groepen."
+#: src/ui/gui/text-data-import.glade:173
+msgid "<b>Amount to Import</b>"
+msgstr "<b>Aantal te Importeren</b>"
 
-#: src/ui/gui/psppire.glade:452
-msgid "Organize output by groups."
-msgstr "Organiseer uitvoer per groepen."
+#: src/ui/gui/text-data-import.glade:195
+msgid "Select Data to Import"
+msgstr "Selecteer Gegevens om te Importeren"
 
-#: src/ui/gui/psppire.glade:499
-msgid "Groups based on:"
-msgstr "Groepen gebaseerd op:"
+#: 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 gegevensbestand die gegevens bevat."
 
-#: src/ui/gui/psppire.glade:563
-msgid "Sort the file by grouping variables."
-msgstr "Sorteer bestand op groeperingsvariabelen."
+#: src/ui/gui/text-data-import.glade:236
+msgid "Line above selected line contains variable names"
+msgstr "De regel boven de geselecteerde gegevensregel bevat de variabelennamen"
 
-#: src/ui/gui/psppire.glade:574
-msgid "File is already sorted."
-msgstr "Bestand is al gesorteerd."
+#: src/ui/gui/text-data-import.glade:251
+msgid "Choose Separators"
+msgstr "Kies scheidingstekens"
 
-#: src/ui/gui/psppire.glade:618
-msgid "Current Status : "
-msgstr "Huidige Status : "
+#: src/ui/gui/text-data-import.glade:299
+msgid "C_ustom"
+msgstr ""
 
-#: src/ui/gui/psppire.glade:626
-msgid "Analysis by groups is off"
-msgstr "Analyseer per groep is uit"
+#: src/ui/gui/text-data-import.glade:314
+msgid "Slas_h (/)"
+msgstr "Sc_huine streep (/)"
 
-#: src/ui/gui/psppire.glade:725
-msgid "Sort by:"
-msgstr "Sorteer op:"
+#: src/ui/gui/text-data-import.glade:331
+msgid "Semicolo_n (;)"
+msgstr "Pu_ntkomma(;)"
 
-#: src/ui/gui/psppire.glade:788
-msgid "Descending"
-msgstr "Aflopend"
+#: src/ui/gui/text-data-import.glade:348
+msgid "P_ipe (|)"
+msgstr "P_ijp (|)"
 
-#: src/ui/gui/psppire.glade:804
-msgid "Sort Order"
-msgstr "Sorteervolgorde"
+#: src/ui/gui/text-data-import.glade:363
+msgid "H_yphen (-)"
+msgstr "Streep (-)"
 
-#: src/ui/gui/psppire.glade:873
-msgid "Target Variable:"
-msgstr "Doelvariabele:"
+#: src/ui/gui/text-data-import.glade:380
+msgid "Co_mma (,)"
+msgstr "Ko_mma (,)"
 
-#: src/ui/gui/psppire.glade:904
-msgid "Type & Label"
-msgstr ""
+#: src/ui/gui/text-data-import.glade:397
+msgid "_Colon (:)"
+msgstr "_Dubbelepunt (:)"
 
-#: src/ui/gui/psppire.glade:943
-msgid "="
-msgstr ""
+#: src/ui/gui/text-data-import.glade:412
+msgid "Ban_g (!)"
+msgstr "Uitroepteken(!)"
 
-#: src/ui/gui/psppire.glade:989
-msgid "Numeric Expressions:"
-msgstr "Numerieke Expressies:"
+#: src/ui/gui/text-data-import.glade:427
+msgid "Ta_b"
+msgstr "Ta_b"
 
-#: src/ui/gui/psppire.glade:1043
-msgid "Functions:"
-msgstr "Functies:"
+#: src/ui/gui/text-data-import.glade:442
+msgid "_Space"
+msgstr "_Spatie"
 
-#: src/ui/gui/psppire.glade:1107 src/ui/gui/psppire.glade:1253
-#: src/ui/gui/recode.glade:731
-msgid "If..."
-msgstr "Als..."
+#: src/ui/gui/text-data-import.glade:456
+msgid "<b>Separators</b>"
+msgstr "<b>Scheiders</b>"
 
-#: src/ui/gui/psppire.glade:1223
-msgid "All Cases"
-msgstr "Alle Cases"
+#: src/ui/gui/text-data-import.glade:489
+msgid "Doubled quote mark treated as escape"
+msgstr ""
 
-#: src/ui/gui/psppire.glade:1239
-msgid "If condition is satisfied"
-msgstr "Aan If conditie is voldaan"
+#: src/ui/gui/text-data-import.glade:526
+msgid "Quote separator characters with"
+msgstr "Citeer scheidingstekens met"
 
-#: src/ui/gui/psppire.glade:1291
-msgid "Random sample of cases"
-msgstr "Willekeurige steekproef van cases"
+#: src/ui/gui/text-data-import.glade:543
+msgid "<b>Quoting</b>"
+msgstr "<b>Citeren</b>"
 
-#: src/ui/gui/psppire.glade:1305
-msgid "Sample..."
-msgstr "Steekproef..."
+#: src/ui/gui/text-data-import.glade:594
+msgid "<b>Fields Preview</b>"
+msgstr "<b>Veldenvoorbeeld</b>"
 
-#: src/ui/gui/psppire.glade:1343
-msgid "Based on time or case range"
-msgstr "Gebaseerd op tijd of case bereik"
+#: src/ui/gui/text-data-import.glade:612
+msgid "Adjust Variable Formats"
+msgstr "Pas Variabelen-formats aan"
 
-#: src/ui/gui/psppire.glade:1356
-msgid "Range..."
-msgstr "Bereik..."
+#: 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 gegevens-formats hieronder en verbeter degene die foutief zijn. Je mag andere variabelenopties nu of later zetten."
 
-#: src/ui/gui/psppire.glade:1394
-msgid "Use filter variable"
-msgstr "Gebruik filtervariabele"
+#: src/ui/gui/text-data-import.glade:665
+msgid "<b>Variables</b>"
+msgstr "<b>Variabelen</b>"
 
-#: src/ui/gui/psppire.glade:1556
-msgid "Select"
-msgstr "Selecteer"
+#: src/ui/gui/text-data-import.glade:712
+msgid "<b>Data Preview</b>"
+msgstr "<b>Gegevensvoorbeeld</b>"
 
-#: src/ui/gui/psppire.glade:1586
-msgid "Filtered"
-msgstr "Gefilterd"
+#: src/ui/gui/t-test.glade:56 src/ui/gui/t-test.glade:165
+msgid "Define Groups"
+msgstr "Definieer Groepen"
 
-#: src/ui/gui/psppire.glade:1596
-msgid "Deleted"
-msgstr "Verwijderd"
+#: 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 "Testvariabele(n):"
 
-#: src/ui/gui/psppire.glade:1613
-msgid "Unselected Cases Are"
-msgstr "Niet geselecteerde Cases zijn"
+#: src/ui/gui/t-test.glade:258
+msgid "Group_2 value:"
+msgstr "Groep_2 waarde:"
 
-#: src/ui/gui/psppire.glade:1678
-msgid "Comments:"
-msgstr "Commentaren:"
+#: src/ui/gui/t-test.glade:271
+msgid "Group_1 value:"
+msgstr "Groep_1 waarde:"
 
-#: src/ui/gui/psppire.glade:1720
-msgid "Display comments in output"
-msgstr "Toon commentaren in uitvoer"
+#: src/ui/gui/t-test.glade:320
+msgid "_Cut point:"
+msgstr "_Knippunt:"
 
-#: src/ui/gui/psppire.glade:1734
-msgid "Column Number: 0"
-msgstr "Kolomnummer: 0"
+#: src/ui/gui/t-test.glade:349
+msgid "_Use specified values:"
+msgstr "_Gebruik gespecificeerde waardes:"
 
-#: src/ui/gui/psppire.glade:1810
-msgid "Variable Information:"
-msgstr "Variabeleninformatie:"
+#: src/ui/gui/t-test.glade:431
+msgid "Exclude cases _analysis by analysis"
+msgstr "Sluit cases _analysis by analysis uit"
 
-#: src/ui/gui/psppire.glade:1836
-msgid ""
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-msgstr ""
+#: src/ui/gui/t-test.glade:442
+msgid "Exclude cases _listwise"
+msgstr "Sluit cases _listwise uit"
 
-#: src/ui/gui/psppire.glade:1900
-msgid "Observation"
-msgstr "Observatie"
+#: src/ui/gui/t-test.glade:594
+msgid "Test Value: "
+msgstr "Testwaarde: "
 
-#: src/ui/gui/psppire.glade:1910
-msgid "Last case"
-msgstr "Laatste case"
+#: src/ui/gui/var-sheet-dialogs.glade:7
+msgid "Variable Type"
+msgstr "Variabelentype"
 
-#: src/ui/gui/psppire.glade:1923
-msgid "First case"
-msgstr "Eerste case"
+#: src/ui/gui/var-sheet-dialogs.glade:75
+msgid "Scientific notation"
+msgstr "Wetenschappelijke notatie"
 
-#: src/ui/gui/psppire.glade:2081
-msgid "Use expression as label"
-msgstr "Gebruik expressie als label"
+#: src/ui/gui/var-sheet-dialogs.glade:123
+msgid "Custom currency"
+msgstr "Aangepaste waarde"
 
-#: src/ui/gui/psppire.glade:2274
-msgid "Goto Case Number:"
-msgstr "Ga naar Case Nummer:"
+#: src/ui/gui/var-sheet-dialogs.glade:217
+msgid "positive"
+msgstr "positief"
 
-#: src/ui/gui/psppire.glade:2410
-msgid "Sample Size"
-msgstr "Steekproef Grootte"
+#: src/ui/gui/var-sheet-dialogs.glade:223
+msgid "negative"
+msgstr "negatief"
 
-#: src/ui/gui/psppire.glade:2513
-msgid "Variable:"
-msgstr "Variabele:"
+#: src/ui/gui/var-sheet-dialogs.glade:236
+msgid "Sample"
+msgstr "Steekproef"
 
-#: src/ui/gui/psppire.glade:2570
-msgid "Search value labels"
-msgstr "Zoek waardelabels"
+#: src/ui/gui/var-sheet-dialogs.glade:286
+msgid "Width:"
+msgstr "Breedte:"
 
-#: src/ui/gui/psppire.glade:2593
-msgid "Regular expression Match"
-msgstr ""
+#: src/ui/gui/var-sheet-dialogs.glade:330
+msgid "Decimal Places:"
+msgstr "Decimalen:"
 
-#: src/ui/gui/psppire.glade:2603
-msgid "Search substrings"
-msgstr "Zoek subtekenreeksen"
+#: src/ui/gui/var-sheet-dialogs.glade:499
+msgid "Value Label:"
+msgstr "Waardelabel:"
 
-#: src/ui/gui/psppire.glade:2615
-msgid "Wrap around"
-msgstr "Tekstterugloop"
+#: src/ui/gui/var-sheet-dialogs.glade:677
+msgid "_No missing values"
+msgstr "Geen missende waardes"
 
-#: src/ui/gui/psppire.glade:2627
-msgid "Search backward"
-msgstr "Zoek achterwaarts"
+#: src/ui/gui/var-sheet-dialogs.glade:747
+msgid "_Discrete missing values"
+msgstr "_Discrete missende waardes"
 
-#: src/ui/gui/rank.glade:111
-msgid "By:"
-msgstr "Per:"
+#: src/ui/gui/var-sheet-dialogs.glade:782
+msgid "_Low:"
+msgstr "_Laag:"
 
-#: src/ui/gui/rank.glade:197
-msgid "_Smallest Value"
-msgstr "_Kleinste Waarde"
+#: src/ui/gui/var-sheet-dialogs.glade:801
+msgid "_High:"
+msgstr "_Hoog:"
 
-#: src/ui/gui/rank.glade:209
-msgid "_Largest Value"
-msgstr "_Grootste Waarde"
+#: src/ui/gui/var-sheet-dialogs.glade:826
+msgid "Di_screte value:"
+msgstr "Di_screte waarde:"
 
-#: src/ui/gui/rank.glade:228
-msgid "Assign rank 1 to:"
-msgstr "Ken rang 1 toe aan:"
+#: src/ui/gui/var-sheet-dialogs.glade:856
+msgid "_Range plus one optional discrete missing value"
+msgstr ""
 
-#: src/ui/gui/rank.glade:246
-msgid "_Display summary tables"
-msgstr "_Toon totalen tabellen"
+#: src/ui/gui/variable-info-dialog.glade:49
+msgid "Variable Information:"
+msgstr "Variabeleninformatie:"
 
-#: src/ui/gui/rank.glade:262
-msgid "Rank T_ypes"
-msgstr "Rangschik T_ypes"
+#: tests/dissect-sysfile.c:528
+#, c-format
+msgid "Unrecognized record type 7, subtype %d."
+msgstr "Niet-herkend recordtype 7, subtype %d."
+
+#: tests/dissect-sysfile.c:701
+#, c-format
+msgid "%s: Error parsing attribute value %s[%d]"
+msgstr "%s: Fout bij het ontleden van attribuut waarde %s[%d]"
 
-#: src/ui/gui/rank.glade:273
-msgid "_Ties..."
-msgstr ""
+#: tests/dissect-sysfile.c:707
+#, c-format
+msgid "%s: Attribute value %s[%d] is not quoted: %s"
+msgstr "%s: Attribuut waarde %s[%d] is niet geciteerd: %s"
 
-#: src/ui/gui/rank.glade:343
-msgid "Ntiles"
-msgstr ""
+#: src/language/utilities/set.q:202
+msgid "WORKSPACE must be at least 1MB"
+msgstr "WORKSPACE moet minstens 1MB zijn"
 
-#: src/ui/gui/rank.glade:376
-msgid "Rank"
-msgstr ""
+#: 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/ui/gui/rank.glade:386
-msgid "Savage score"
-msgstr ""
+#: src/language/utilities/set.q:227
+#, fuzzy, c-format
+msgid "%s is not implemented."
+msgstr "%s is nog niet geïmplementeerd."
 
-#: src/ui/gui/rank.glade:400
-msgid "Fractional rank"
-msgstr ""
+#: src/language/utilities/set.q:230
+msgid "Active file compression is not implemented."
+msgstr "Actief bestand compressie is niet geïmplementeerd."
 
-#: src/ui/gui/rank.glade:414
-msgid "Fractional rank as %"
-msgstr ""
+#: src/language/utilities/set.q:325
+msgid "EPOCH must be 1500 or later."
+msgstr "EPOCH moet 1500 of later zijn."
 
-#: src/ui/gui/rank.glade:428
-msgid "Sum of case weights"
-msgstr "Totaal van case gewichten"
+#: src/language/utilities/set.q:332
+msgid "expecting AUTOMATIC or year"
+msgstr "AUTOMATIC of jaar verwacht"
 
-#: src/ui/gui/rank.glade:450
-msgid "Proportion Estimates"
-msgstr ""
+#: src/language/utilities/set.q:353
+msgid "LENGTH must be at least 1."
+msgstr "LENGTH moet tenminste 1 zijn."
 
-#: src/ui/gui/rank.glade:460
-msgid "Normal Scores"
-msgstr ""
+#: 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/ui/gui/rank.glade:495
-msgid "Blom"
-msgstr ""
+#: src/language/utilities/set.q:432
+msgid "WIDTH must be at least 40."
+msgstr "WIDTH moet tenminste 40 zijn."
 
-#: src/ui/gui/rank.glade:506
-msgid "Tukey"
-msgstr ""
+#: 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-format als een argument. Opgegeven format %s is van het type tekenreeks."
 
-#: src/ui/gui/rank.glade:520
-msgid "Rankit"
-msgstr ""
+#: src/language/utilities/set.q:668
+msgid "ISL (32-bit IEEE 754 single, little-endian)"
+msgstr "ISL (32-bit IEEE 754 single, little-endian)"
 
-#: src/ui/gui/rank.glade:534
-msgid "Van der Wärden"
-msgstr ""
+#: src/language/utilities/set.q:671
+msgid "ISB (32-bit IEEE 754 single, big-endian)"
+msgstr "ISB (32-bit IEEE 754 single, big-endian)"
 
-#: src/ui/gui/rank.glade:551
-msgid "Proportion Estimation Formula"
-msgstr ""
+#: src/language/utilities/set.q:674
+msgid "IDL (64-bit IEEE 754 double, little-endian)"
+msgstr "IDL (64-bit IEEE 754 double, little-endian)"
 
-#: src/ui/gui/rank.glade:614
-msgid "_Mean"
-msgstr "_Gemiddeld"
+#: src/language/utilities/set.q:677
+msgid "IDB (64-bit IEEE 754 double, big-endian)"
+msgstr "IDB (64-bit IEEE 754 double, big-endian)"
 
-#: src/ui/gui/rank.glade:626
-msgid "_Low"
-msgstr "_Laag"
+#: src/language/utilities/set.q:681
+msgid "VF (32-bit VAX F, VAX-endian)"
+msgstr "VF (32-bit VAX F, VAX-endian)"
 
-#: src/ui/gui/rank.glade:642
-msgid "_High"
-msgstr "_Hoog"
+#: src/language/utilities/set.q:684
+msgid "VD (64-bit VAX D, VAX-endian)"
+msgstr "VD (64-bit VAX D, VAX-endian)"
 
-#: src/ui/gui/rank.glade:660
-msgid "_Sequential ranks to unique values"
-msgstr "_Sequentiële rangen naar unieke waardes"
+#: src/language/utilities/set.q:687
+msgid "VG (64-bit VAX G, VAX-endian)"
+msgstr "VG (64-bit VAX G, VAX-endian)"
 
-#: src/ui/gui/rank.glade:680
-msgid "Rank Assigned to Ties"
-msgstr ""
+#: src/language/utilities/set.q:691
+msgid "ZS (32-bit IBM Z hexadecimal short, big-endian)"
+msgstr "ZS (32-bit IBM Z hexadecimal short, big-endian)"
 
-#: src/ui/gui/recode-dialog.c:879
-msgid "Recode into Different Variables"
-msgstr "Hercodeer in Andere Variabelen"
+#: src/language/utilities/set.q:694
+msgid "ZL (64-bit IBM Z hexadecimal long, big-endian)"
+msgstr "ZL (64-bit IBM Z hexadecimal long, big-endian)"
 
-#: src/ui/gui/recode-dialog.c:882
-msgid "Recode into Same Variables"
-msgstr "Hercodeer in Zelfde Variabelen"
+#: src/language/utilities/set.q:793
+#, c-format
+msgid "%s is %s."
+msgstr "%s van %s."
 
-#: src/ui/gui/recode-dialog.c:913 src/ui/gui/recode-dialog.c:1015
-msgid "Old"
-msgstr "Oud"
+#: src/language/stats/crosstabs.q:327
+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/ui/gui/recode-dialog.c:1274
-msgid "Recode into Different Variables: Old and New Values "
-msgstr "Hercodeer in Andere Variabelen: Oude en Nieuwe Waardes "
+#: src/language/stats/crosstabs.q:416
+msgid "Too many cross-tabulation variables or dimensions."
+msgstr "Te veel cross-tabulation variabelen of dimensies."
 
-#: src/ui/gui/recode-dialog.c:1275
-msgid "Recode into Same Variables: Old and New Values"
-msgstr "Hercodeer in Zelfde Variabelen: Oude en Nieuwe Waardes "
+#: src/language/stats/crosstabs.q:426
+msgid "expecting BY"
+msgstr "BY verwacht"
 
-#: src/ui/gui/recode.glade:197
-msgid "System-Missing"
-msgstr ""
+#: src/language/stats/crosstabs.q:486
+msgid "VARIABLES must be specified before TABLES."
+msgstr "VARIABLES dient voor TABLES gespecificeerd te worden."
 
-#: src/ui/gui/recode.glade:211
-msgid "System-or user-missing"
-msgstr ""
+#: src/language/stats/crosstabs.q:524
+#, c-format
+msgid "Maximum value (%ld) less than minimum value (%ld)."
+msgstr "Maximumwaarde (%ld) is kleiner dan minimumwaarde (%ld)."
 
-#: src/ui/gui/recode.glade:245
-msgid "through"
-msgstr "tot"
+#: src/language/stats/crosstabs.q:840
+msgid "Summary."
+msgstr "Overzicht."
 
-#: src/ui/gui/recode.glade:283
-msgid "Range, LOWEST thru value"
-msgstr "Bereik, LAAGSTE tot waarde"
+#: src/language/stats/crosstabs.q:842 src/language/stats/examine.q:1277
+#: src/language/stats/reliability.q:709
+msgid "Cases"
+msgstr "Cases"
 
-#: src/ui/gui/recode.glade:297
-msgid "Range, value thru HIGHEST"
-msgstr "Bereik, waarde tot HOOGSTE"
+#: src/language/stats/crosstabs.q:843 src/language/stats/examine.q:1214
+#: src/language/stats/frequencies.q:1049 src/language/stats/frequencies.q:1400
+#: src/language/stats/reliability.q:712
+msgid "Valid"
+msgstr "Geldig"
 
-#: src/ui/gui/recode.glade:327
-msgid "All other values"
-msgstr "Alle andere waardes"
+#: src/language/stats/crosstabs.q:853 src/language/stats/examine.q:1292
+#: src/language/stats/frequencies.q:1053 src/language/stats/frequencies.q:1054
+#: src/language/stats/frequencies.q:1055
+msgid "Percent"
+msgstr "Percentage"
 
-#: src/ui/gui/recode.glade:363
-msgid "Range:"
-msgstr "Bereik:"
+#: src/language/stats/crosstabs.q:1133
+msgid "count"
+msgstr "aantal"
 
-#: src/ui/gui/recode.glade:380
-msgid "Old Value"
-msgstr "Oude Waarde"
+#: src/language/stats/crosstabs.q:1134
+msgid "row %"
+msgstr "rij %"
 
-#: src/ui/gui/recode.glade:462
-msgid "System Missing"
-msgstr ""
+#: src/language/stats/crosstabs.q:1135
+msgid "column %"
+msgstr "kolom %"
 
-#: src/ui/gui/recode.glade:476
-msgid "Copy old values"
-msgstr "Kopieer oude waardes"
+#: src/language/stats/crosstabs.q:1136
+msgid "total %"
+msgstr "totaal %"
 
-#: src/ui/gui/recode.glade:500
-msgid "Value: "
-msgstr "Waarde: "
+#: src/language/stats/crosstabs.q:1137
+msgid "expected"
+msgstr "verwacht"
 
-#: src/ui/gui/recode.glade:530
-msgid "New Value"
-msgstr "Nieuwe Waarde"
+#: src/language/stats/crosstabs.q:1138
+msgid "residual"
+msgstr "overblijvend"
 
-#: src/ui/gui/recode.glade:590
-msgid "Convert numeric strings to numbers ('5' -> 5)"
-msgstr "Converteer numerieke tekenreeksen naar nummers ('5' -> 5)"
+#: src/language/stats/crosstabs.q:1139
+msgid "std. resid."
+msgstr ""
 
-#: src/ui/gui/recode.glade:608
-msgid "Output variables are strings"
-msgstr "Uitvoervariabelen zijn tekenreeksen"
+#: src/language/stats/crosstabs.q:1140
+msgid "adj. resid."
+msgstr ""
 
-#: src/ui/gui/recode.glade:620
-msgid "Width: "
-msgstr "Breedte: "
+#: src/language/stats/crosstabs.q:1231
+msgid "Chi-square tests."
+msgstr "Chi-square tests."
 
-#: src/ui/gui/recode.glade:743
-msgid "(optional case selection condition)"
-msgstr "(optionele case selectie conditie)"
+#: src/language/stats/crosstabs.q:1238
+msgid "Asymp. Sig. (2-sided)"
+msgstr ""
 
-#: src/ui/gui/recode.glade:823
-msgid "Name:"
-msgstr "Naam:"
+#: src/language/stats/crosstabs.q:1240
+msgid "Exact Sig. (2-sided)"
+msgstr ""
 
-#: src/ui/gui/recode.glade:867
-msgid "Change"
-msgstr "Wijzig"
+#: src/language/stats/crosstabs.q:1242
+msgid "Exact Sig. (1-sided)"
+msgstr ""
 
-#: src/ui/gui/recode.glade:885
-msgid "Output Variable"
-msgstr "Uitvoervariabele"
+#: src/language/stats/crosstabs.q:1257
+msgid "Symmetric measures."
+msgstr "Symmetrische metingen."
 
-#: src/ui/gui/recode.glade:965
-msgid "Old and New Values"
-msgstr "Oude en Nieuwe Waardes"
+#: src/language/stats/crosstabs.q:1263 src/language/stats/crosstabs.q:1312
+msgid "Asymp. Std. Error"
+msgstr ""
 
-#: src/ui/gui/regression-dialog.c:40
-msgid "Coeff"
+#: src/language/stats/crosstabs.q:1264 src/language/stats/crosstabs.q:1313
+msgid "Approx. T"
 msgstr ""
 
-#: src/ui/gui/regression-dialog.c:42
-msgid "Anova"
+#: src/language/stats/crosstabs.q:1265 src/language/stats/crosstabs.q:1314
+msgid "Approx. Sig."
 msgstr ""
 
-#: src/ui/gui/regression-dialog.c:43
-msgid "Bcov"
+#: src/language/stats/crosstabs.q:1280
+msgid "Risk estimate."
 msgstr ""
 
-#: src/ui/gui/regression.glade:40
-msgid "Save..."
-msgstr "Opslaan..."
+#: src/language/stats/crosstabs.q:1284
+#, c-format
+msgid "95%% Confidence Interval"
+msgstr "95%% Betrouwbaarheidsinterval"
 
-#: src/ui/gui/regression.glade:145
-msgid "Dependent"
-msgstr "Afhankelijk"
+#: src/language/stats/crosstabs.q:1287 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/ui/gui/regression.glade:193
-msgid "Independent"
-msgstr "Onafhankelijk"
+#: src/language/stats/crosstabs.q:1288 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/ui/gui/regression.glade:243
-msgid "Predicted values"
-msgstr "Voorspelde waardes"
+#: src/language/stats/crosstabs.q:1305
+msgid "Directional measures."
+msgstr "Directionele metingen."
 
-#: src/ui/gui/regression.glade:252
-msgid "Residuals"
-msgstr "Restant"
+#: src/language/stats/crosstabs.q:1776
+msgid "Pearson Chi-Square"
+msgstr "Pearson Chi-Square"
 
-#: src/ui/gui/select-cases-dialog.c:81
-#, c-format
-msgid "Approximately %3d%% of all cases."
-msgstr "Ongeveer %3d%% van alle cases."
+#: src/language/stats/crosstabs.q:1777
+msgid "Likelihood Ratio"
+msgstr "Waarschijnlijkheidsratio"
 
-#: src/ui/gui/select-cases-dialog.c:82
-#, c-format
-msgid "Exactly %3d cases from the first %3d cases."
-msgstr "Precies %3d cases van de eerste %3d cases."
+#: src/language/stats/crosstabs.q:1778
+msgid "Fisher's Exact Test"
+msgstr "Fisher's Exact Test"
 
-#: src/ui/gui/select-cases-dialog.c:222
-#, c-format
-msgid "%d thru %d"
-msgstr "%d tot %d"
+#: src/language/stats/crosstabs.q:1779
+msgid "Continuity Correction"
+msgstr "Continuïteitscorrectie"
 
-#: src/ui/gui/syntax-editor.c:77
-#, c-format
-msgid "Save contents of syntax editor to %s?"
+#: src/language/stats/crosstabs.q:1780
+msgid "Linear-by-Linear Association"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.c:124
-msgid "Save Syntax"
-msgstr "Sla Syntax op"
+#: src/language/stats/crosstabs.q:1815 src/language/stats/crosstabs.q:1890
+#: src/language/stats/crosstabs.q:1955
+msgid "N of Valid Cases"
+msgstr ""
 
-#: src/ui/gui/syntax-editor.c:132 src/ui/gui/syntax-editor.c:516
-msgid "Syntax Files (*.sps) "
-msgstr "Syntaxbestand (*.sps)"
+#: src/language/stats/crosstabs.q:1834 src/language/stats/crosstabs.q:1973
+msgid "Nominal by Nominal"
+msgstr ""
 
-#: src/ui/gui/syntax-editor.c:508
-msgid "Open Syntax"
+#: src/language/stats/crosstabs.q:1835 src/language/stats/crosstabs.q:1974
+msgid "Ordinal by Ordinal"
 msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:10
-msgid "Psppire Syntax Editor"
-msgstr "Psppire Syntaxbewerker"
+#: src/language/stats/crosstabs.q:1836
+msgid "Interval by Interval"
+msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:188
-msgid "_Run"
-msgstr "Uitvoe_ren"
+#: src/language/stats/crosstabs.q:1837
+msgid "Measure of Agreement"
+msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:197
-msgid "All"
-msgstr "Alles"
+#: src/language/stats/crosstabs.q:1843
+msgid "Cramer's V"
+msgstr "Cramer's V"
 
-#: src/ui/gui/syntax-editor.glade:205
-msgid "Selection"
-msgstr "Selectie"
+#: src/language/stats/crosstabs.q:1844
+msgid "Contingency Coefficient"
+msgstr ""
 
-#: src/ui/gui/syntax-editor.glade:213
-msgid "Current Line"
-msgstr "Huidige Regel"
+#: src/language/stats/crosstabs.q:1845
+msgid "Kendall's tau-b"
+msgstr "Kendall's tau-b"
+
+#: src/language/stats/crosstabs.q:1846
+msgid "Kendall's tau-c"
+msgstr "Kendall's tau-c"
 
-#: src/ui/gui/syntax-editor.glade:222
-msgid "To End"
-msgstr "Naar Einde"
+#: src/language/stats/crosstabs.q:1848
+msgid "Spearman Correlation"
+msgstr "Spearman Correlatie"
 
-#: src/ui/gui/t-test-options.c:60
+#: src/language/stats/crosstabs.q:1849
+msgid "Pearson's R"
+msgstr "Pearson's R"
+
+#: src/language/stats/crosstabs.q:1928
 #, c-format
-msgid "Confidence Interval: %2d %%"
+msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
-#: src/ui/gui/t-test-paired-samples.c:228
-msgid "Var 1"
+#: src/language/stats/crosstabs.q:1931
+#, c-format
+msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
-#: src/ui/gui/t-test-paired-samples.c:229
-msgid "Var 2"
-msgstr ""
+#: src/language/stats/crosstabs.q:1939
+#, c-format
+msgid "For cohort %s = %g"
+msgstr "Voor cohort %s = %g"
 
-#: src/ui/gui/t-test.glade:56 src/ui/gui/t-test.glade:165
-msgid "Define Groups"
-msgstr "Definieer Groepen"
+#: src/language/stats/crosstabs.q:1942
+#, c-format
+msgid "For cohort %s = %.*s"
+msgstr "Voor cohort %s = %.*s"
 
-#: 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 "Testvariabele(n):"
+#: src/language/stats/crosstabs.q:1975
+msgid "Nominal by Interval"
+msgstr ""
 
-#: src/ui/gui/t-test.glade:258
-msgid "Group_2 value:"
-msgstr "Groep_2 waarde:"
+#: src/language/stats/crosstabs.q:1981
+msgid "Goodman and Kruskal tau"
+msgstr "Goodman and Kruskal tau"
 
-#: src/ui/gui/t-test.glade:271
-msgid "Group_1 value:"
-msgstr "Groep_1 waarde:"
+#: src/language/stats/crosstabs.q:1982
+msgid "Uncertainty Coefficient"
+msgstr "Onzekerheidscoëfficiënt"
 
-#: src/ui/gui/t-test.glade:320
-msgid "_Cut point:"
-msgstr "_Knippunt:"
+#: src/language/stats/crosstabs.q:1983
+msgid "Somers' d"
+msgstr "Somers' d"
 
-#: src/ui/gui/t-test.glade:349
-msgid "_Use specified values:"
-msgstr "_Gebruik gespecificeerde waardes:"
+#: src/language/stats/crosstabs.q:1989
+msgid "Symmetric"
+msgstr ""
 
-#: src/ui/gui/t-test.glade:431
-msgid "Exclude cases _analysis by analysis"
-msgstr "Sluit cases _analysis by analysis uit"
+#: src/language/stats/crosstabs.q:1990 src/language/stats/crosstabs.q:1991
+#, c-format
+msgid "%s Dependent"
+msgstr "%s Afhankelijk"
 
-#: src/ui/gui/t-test.glade:442
-msgid "Exclude cases _listwise"
-msgstr "Sluit cases _listwise uit"
+#: 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 "Niet aanmaken van plot omdat gegevens set leeg is."
 
-#: src/ui/gui/t-test.glade:594
-msgid "Test Value: "
-msgstr "Testwaarde:"
+#: 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/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."
+#: src/language/stats/examine.q:358
+msgid "Expected Normal"
 msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:488
+#: src/language/stats/examine.q:360
 #, c-format
-msgid "Could not open \"%s\": %s"
-msgstr "Kon \"%s\": %s niet openen"
+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/ui/gui/text-data-import-dialog.c:504
+#: src/language/stats/examine.q:516
 #, c-format
-msgid "Error reading \"%s\": %s"
-msgstr "Fout bij lezen \"%s\": %s"
+msgid "Boxplot of %s vs. %s"
+msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:507
+#: src/language/stats/examine.q:520
 #, 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 tekstbestand."
+msgid "Boxplot of %s"
+msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:521
+#: src/language/stats/examine.q:756 src/language/stats/examine.q:769
 #, c-format
-msgid "\"%s\" is empty."
-msgstr "\"%s\" is leeg."
+msgid "%s and %s are mutually exclusive"
+msgstr "%s en %s zijn wederzijds exclusief"
 
-#: src/ui/gui/text-data-import-dialog.c:566
-msgid "Import Delimited Text Data"
-msgstr "Importeer Gescheiden Tekstgegevens"
+#: src/language/stats/examine.q:1272 src/language/stats/reliability.q:686
+msgid "Case Processing Summary"
+msgstr "Case Bewerkingsoverzicht"
 
-#: src/ui/gui/text-data-import-dialog.c:617
-msgid "Importing Delimited Text Data"
-msgstr "Importeren Gescheiden Tekstgegevens"
+#: src/language/stats/examine.q:1564 src/language/stats/oneway.q:398
+#, c-format
+msgid "%g%% Confidence Interval for Mean"
+msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:768
-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 gegevens in PSPP van een tekstbestand met 1 regel per case, waarin velden zijn gescheiden door tabs, komma's of andere scheidingstekens.\n"
+#: src/language/stats/examine.q:1579
+msgid "5% Trimmed Mean"
+msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:774
-#, 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 tekst.  "
-msgstr[1] "Het geselecteerde bestand bevat %zu regels tekst. "
+#: src/language/stats/examine.q:1614
+msgid "Interquartile Range"
+msgstr ""
 
-#: src/ui/gui/text-data-import-dialog.c:782
-#, 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 tekst. "
-msgstr[1] "Het geselecteerde bestand bevat ongeveer %lu regels tekst. "
+#: src/language/stats/examine.q:1939
+msgid "Highest"
+msgstr "Hoogste"
 
-#: src/ui/gui/text-data-import-dialog.c:788
-#, 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 voorbeeld doeleinden in de volgende schermen."
-msgstr[1] "Alleen de eerste %zu regels van het bestand worden getoond voor voorbeeld doeleinden in de volgende schermen."
+#: src/language/stats/examine.q:1944
+msgid "Lowest"
+msgstr "Laagste"
 
-#: src/ui/gui/text-data-import-dialog.c:795
-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/language/stats/examine.q:1951
+msgid "Extreme Values"
+msgstr "Extreme Waardes"
 
-#: src/ui/gui/text-data-import-dialog.c:1515
-#: src/ui/gui/text-data-import-dialog.c:1759
-msgid "This input line has too few separators to fill in this field."
-msgstr "Deze invoerregel heeft te weinig scheidingstekens om dit veld te vullen."
+#: src/language/stats/examine.q:1955
+msgid "Case Number"
+msgstr "Case Nummer"
 
-#: src/ui/gui/text-data-import-dialog.c:1750
+#: src/language/stats/examine.q:2077
+msgid "Tukey's Hinges"
+msgstr "Tukey's Hinges"
+
+#: src/language/stats/examine.q:2124
 #, c-format
-msgid "Field content \"%.*s\" cannot be parsed in format %s."
-msgstr "Veldinhoud \"%.*s\" kan niet ontleed worden in format %s."
+msgid "%g"
+msgstr "%g"
 
-#: src/ui/gui/text-data-import.glade:7
-msgid "Importing Textual Data"
+#: src/language/stats/frequencies.q:124
+msgid "S.E. Mean"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:16
-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 assistent zal je assisteren bij het proces van het importeren van gegevens in PSPP vanuit een tekstbestand met een regel per case en velden gescheiden met tabs, komma's of andere scheiders.\n"
-" \n"
-"Het geselecteerde bestand bevat N regels tekst.  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/language/stats/frequencies.q:130
+msgid "S.E. Kurt"
+msgstr "S.E. Kurt"
 
-#: src/ui/gui/text-data-import.glade:47
-msgid "All cases"
-msgstr "Alle cases"
+#: src/language/stats/frequencies.q:132
+msgid "S.E. Skew"
+msgstr "S.E. Skew"
 
-#: src/ui/gui/text-data-import.glade:62 src/ui/gui/text-data-import.glade:117
-msgid "Only first "
-msgstr "Alleen eerste "
+#: src/language/stats/frequencies.q:407
+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 "Maximaal een van BARCHART, HISTOGRAM, of HBAR mag opgegeven worden. HBAR wordt aangenomen.  Argument waardes zullen gebruikt worden in opgegeven volgorde."
 
-#: src/ui/gui/text-data-import.glade:93
-msgid " cases"
-msgstr ""
+#: src/language/stats/frequencies.q:490
+#, 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/ui/gui/text-data-import.glade:147
-msgid "% of file (approximately)"
-msgstr "% van bestand (ongeveer)"
+#: src/language/stats/frequencies.q:754
+#, c-format
+msgid "Variable %s specified multiple times on VARIABLES subcommand."
+msgstr "Variabele %s is meerdere keren opgegeven bij VARIABLES subopdracht."
 
-#: src/ui/gui/text-data-import.glade:168
-msgid "<b>Amount to Import</b>"
-msgstr "<b>Aantal te Importeren</b>"
+#: src/language/stats/frequencies.q:812
+msgid "`)' expected after GROUPED interval list."
+msgstr "')' verwacht na GROUPED interval lijst."
 
-#: src/ui/gui/text-data-import.glade:189
-msgid "Select Data to Import"
-msgstr "Selecteer Gegevens om te Importeren"
+#: src/language/stats/frequencies.q:824
+#, c-format
+msgid "Variables %s specified on GROUPED but not on VARIABLES."
+msgstr "Variabele %s gespecificeerd bij GROUPED maar niet bij VARIABLES."
 
-#: src/ui/gui/text-data-import.glade:198
-msgid "Select the first line of the data file that contains data."
-msgstr "Selecteer de eerste regel van het gegevensbestand die gegevens bevat."
+#: src/language/stats/frequencies.q:831
+#, c-format
+msgid "Variables %s specified multiple times on GROUPED subcommand."
+msgstr "Variabele %s is meerdere keren gespecificeerd bij GROUPED subopdracht."
 
-#: src/ui/gui/text-data-import.glade:209
-msgid "Line above selected line contains variable names"
-msgstr "De regel boven de geselecteerde gegevensregel bevat de variabelennamen"
+#: src/language/stats/frequencies.q:1050 src/language/stats/frequencies.q:1143
+#: src/language/stats/frequencies.q:1144 src/language/stats/frequencies.q:1179
+msgid "Cum"
+msgstr "Cum"
 
-#: src/ui/gui/text-data-import.glade:243
-msgid "Choose Separators"
-msgstr "Kies scheidingstekens"
+#: src/language/stats/frequencies.q:1073
+msgid "Value Label"
+msgstr "Waardelabel"
 
-#: src/ui/gui/text-data-import.glade:274
-msgid "_Space"
-msgstr "_Spatie"
+#: src/language/stats/frequencies.q:1177
+msgid "Freq"
+msgstr "Freq"
 
-#: src/ui/gui/text-data-import.glade:285
-msgid "Ta_b"
-msgstr ""
+#: src/language/stats/frequencies.q:1178 src/language/stats/frequencies.q:1180
+msgid "Pct"
+msgstr "Pct"
 
-#: src/ui/gui/text-data-import.glade:300
-msgid "Ban_g (!)"
-msgstr "Uitroepteken(!)"
+#: src/language/stats/frequencies.q:1373
+#, c-format
+msgid "No valid data for variable %s; statistics not displayed."
+msgstr "Geen geldige gegevens voor variabele %s; statistieken worden niet getoond."
 
-#: src/ui/gui/text-data-import.glade:315
-msgid "_Colon (:)"
-msgstr "_Dubbelepunt (:)"
+#: src/language/stats/frequencies.q:1414
+msgid "50 (Median)"
+msgstr "50 (Mediaan)"
 
-#: src/ui/gui/text-data-import.glade:330
-msgid "Co_mma (,)"
-msgstr "Ko_mma (,)"
+#: src/language/stats/glm.q:196
+msgid "Multivariate GLM not yet supported"
+msgstr "Mutivariatie GLm nog niet gesupport"
 
-#: src/ui/gui/text-data-import.glade:347
-msgid "H_yphen (-)"
-msgstr "Streep (-)"
+#: src/language/stats/glm.q:320 src/language/stats/regression.q:1000
+msgid "No valid data found. This command was skipped."
+msgstr "Geen geldige gegevens gevonden. Deze opdracht is overgeslagen."
 
-#: src/ui/gui/text-data-import.glade:364
-msgid "P_ipe (|)"
-msgstr ""
+#: src/language/stats/means.q:100
+msgid "Missing required subcommand TABLES."
+msgstr "Mis vereiste subopdracht TABLES."
 
-#: src/ui/gui/text-data-import.glade:379
-msgid "Semicolo_n (;)"
-msgstr "Pu_ntkomma(;)"
+#: 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/ui/gui/text-data-import.glade:396
-msgid "Slas_h (/)"
+#: src/language/stats/npar.q:109
+msgid "NPAR subcommand not currently implemented."
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:413
-msgid "C_ustom"
+#: 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 het opgegeven bereik (%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/oneway.q:171
+msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:444
-msgid "<b>Separators</b>"
-msgstr "<b>Scheiders</b>"
+#: src/language/stats/oneway.q:180
+#, c-format
+msgid "Coefficients for contrast %zu do not total zero"
+msgstr ""
 
-#: src/ui/gui/text-data-import.glade:475
-msgid "Quote separator characters with"
-msgstr "Citeer scheidingstekens met"
+#: src/language/stats/oneway.q:243
+#, c-format
+msgid "`%s' is not a variable name"
+msgstr "'%s' is geen variabelennaam"
 
-#: src/ui/gui/text-data-import.glade:489
-msgid ""
-"\"'\n"
-"\"\n"
-"'\n"
+#: src/language/stats/oneway.q:277 src/language/stats/regression.q:301
+msgid "Sum of Squares"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:514
-msgid "Doubled quote mark treated as escape"
+#: src/language/stats/oneway.q:279 src/language/stats/regression.q:303
+msgid "Mean Square"
 msgstr ""
 
-#: src/ui/gui/text-data-import.glade:533
-msgid "<b>Quoting</b>"
-msgstr "<b>Citeren</b>"
+#: src/language/stats/oneway.q:280 src/language/stats/regression.q:304
+#: src/language/stats/t-test.q:750
+msgid "F"
+msgstr "F"
 
-#: src/ui/gui/text-data-import.glade:584
-msgid "<b>Fields Preview</b>"
-msgstr "<b>Veldenvoorbeeld</b>"
+#: 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/ui/gui/text-data-import.glade:601
-msgid "Adjust Variable Formats"
-msgstr "Pas Variabelen-formats aan"
+#: src/language/stats/oneway.q:348 src/language/stats/regression.q:330
+msgid "ANOVA"
+msgstr "ANOVA"
 
-#: src/ui/gui/text-data-import.glade:610
-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 gegevens-formats hieronder en verbeter degene die foutief zijn. Je mag andere variabelenopties nu of later zetten."
+#: src/language/stats/oneway.q:536
+msgid "Levene Statistic"
+msgstr ""
 
-#: src/ui/gui/text-data-import.glade:653
-msgid "<b>Variables</b>"
-msgstr "<b>Variabelen</b>"
+#: src/language/stats/oneway.q:537
+msgid "df1"
+msgstr "df1"
 
-#: src/ui/gui/text-data-import.glade:700
-msgid "<b>Data Preview</b>"
-msgstr "<b>Gegevensvoorbeeld</b>"
+#: src/language/stats/oneway.q:538
+msgid "df2"
+msgstr "df2"
 
-#: src/ui/gui/variable-info-dialog.c:89
-#, c-format
-msgid "Label: %s\n"
+#: src/language/stats/oneway.q:541
+msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:98
-#, c-format
-msgid "Type: %s\n"
+#: src/language/stats/oneway.q:608
+msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:102
-#, c-format
-msgid "Missing Values: %s\n"
-msgstr "Ontbrekende-Waardes: %s\n"
-
-#: src/ui/gui/variable-info-dialog.c:107
-#, c-format
-msgid "Measurement Level: %s\n"
-msgstr "Meetniveau: %s\n"
+#: src/language/stats/oneway.q:610 src/language/stats/oneway.q:687
+msgid "Contrast"
+msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:121
-msgid "Value Labels:\n"
-msgstr "Waardelabels:\n"
+#: src/language/stats/oneway.q:685
+msgid "Contrast Tests"
+msgstr ""
 
-#: src/ui/gui/variable-info-dialog.c:133
-#, c-format
-msgid "%s %s\n"
+#: src/language/stats/oneway.q:688
+msgid "Value of Contrast"
 msgstr ""
 
-#: src/ui/gui/weight-cases-dialog.c:86
-#, c-format
-msgid "Weight cases by %s"
-msgstr "Weeg cases per %s"
+#: 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 "t"
 
-#: src/ui/gui/window-manager.c:142
-#, c-format
-msgid "Syntax%d"
+#: 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/ui/gui/window-manager.c:143 src/ui/gui/window-manager.c:178
-#, c-format
-msgid "%s --- PSPP Syntax Editor"
-msgstr "%s --- PSPP Syntaxbewerker"
+#: src/language/stats/oneway.q:736
+msgid "Assume equal variances"
+msgstr "Veronderstelt gelijke variantie"
 
-#: src/ui/gui/window-manager.c:146
-#, c-format
-msgid "Untitled%d"
-msgstr ""
+#: src/language/stats/oneway.q:740
+msgid "Does not assume equal"
+msgstr "Veronderstelt niet gelijk"
 
-#: src/ui/gui/window-manager.c:147 src/ui/gui/window-manager.c:181
+#: src/language/stats/rank.q:221
 #, c-format
-msgid "%s --- PSPP Data Editor"
-msgstr "%s --- PSPP Gegevensbewerker"
+msgid "%s of %s by %s"
+msgstr "%s van %s per %s"
 
-#: src/ui/gui/window-manager.c:150
+#: src/language/stats/rank.q:226
 #, c-format
-msgid "Output%d"
-msgstr "Uitvoer%d"
+msgid "%s of %s"
+msgstr "%s van %s"
 
-#: src/ui/gui/window-manager.c:151
-#, c-format
-msgid "%s --- PSPP Output"
-msgstr "%s --- PSPP-uitvoer"
+#: 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/ui/terminal/command-line.c:230
+#: 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 ""
-"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"
-msgstr ""
+msgid "%s into %s(%s of %s using %s BY %s)"
+msgstr "%s in %s(%s van %s gebruikt %s PER %s)"
 
-#: src/ui/terminal/command-line.c:265
+#: src/language/stats/rank.q:728
 #, c-format
-msgid ""
-"\n"
-"Report bugs to <%s>.\n"
-msgstr ""
+msgid "%s into %s(%s of %s BY %s)"
+msgstr "%s in %s(%s van %s PER %s)"
 
-#: src/ui/terminal/main.c:131
-msgid "Stopping syntax file processing here to avoid a cascade of dependent command failures."
-msgstr "Stop syntaxbestand uitvoering hier om een cascade van afhankelijke opdrachtfouten te voorkomen."
+#: 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/ui/terminal/msg-ui.c:67
+#: src/language/stats/rank.q:750
 #, 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"
+msgid "%s into %s(%s of %s)"
+msgstr "%s in %s(%s van %s)"
 
-#: src/ui/terminal/msg-ui.c:94
-msgid "Terminating execution of syntax file due to error."
-msgstr "Breek uitvoering van syntaxbestand af vanwege fout."
+#: 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 zijn niet gevraagd. De FRACTION subopdracht wordt genegeerd."
 
-#: src/ui/terminal/msg-ui.c:96
+#: src/language/stats/rank.q:853
 #, c-format
-msgid "Errors (%d) exceeds limit (%d)."
-msgstr "Fouten (%d) overschrijden limiet (%d)."
+msgid "Variable %s already exists."
+msgstr "Variabele %s bestaat al."
 
-#: src/ui/terminal/msg-ui.c:99
-#, c-format
-msgid "Warnings (%d) exceed limit (%d)."
-msgstr "Waarschuwingen (%d) overschrijden limiet (%d)."
+#: src/language/stats/rank.q:858
+msgid "Too many variables in INTO clause."
+msgstr "Te veel variabelen in INTO clausule."
 
-#: src/ui/terminal/msg-ui.c:150
-msgid "error"
-msgstr "fout"
+#: src/language/stats/regression.q:160
+msgid "R Square"
+msgstr ""
 
-#: src/ui/terminal/msg-ui.c:151
-msgid "warning"
-msgstr "waarschuwing"
+#: src/language/stats/regression.q:161
+msgid "Adjusted R Square"
+msgstr ""
 
-#: 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/language/stats/regression.q:162
+msgid "Std. Error of the Estimate"
+msgstr ""
+
+#: src/language/stats/regression.q:167
+msgid "Model Summary"
+msgstr ""
 
-#~ msgid "Error parsing attribute value %s[%d]"
-#~ msgstr "Fout bij het ontleden van attribuut waarde %s[%d]"
+#: src/language/stats/regression.q:202
+msgid "B"
+msgstr "B"
 
-#~ msgid "Attribute value %s[%d] is not quoted: %s"
-#~ msgstr "Attribuut waarde %s[%d] is niet geciteerd: %s"
+#: src/language/stats/regression.q:204
+msgid "Beta"
+msgstr "Beta"
 
-#~ msgid "Suppressed %d additional related warnings."
-#~ msgstr "Onderdrukt %d extra gerelateerde waarschuwingen."
+#: src/language/stats/regression.q:207
+msgid "(Constant)"
+msgstr "(Constante)"
 
-#~ 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 invoerbron is. Tijdelijke transformaties zullen permanent worden."
+#: src/language/stats/regression.q:271
+msgid "Coefficients"
+msgstr "Coëfficiënten"
 
-#~ msgid "The BY subcommand is required."
-#~ msgstr "De BY subopdracht is vereist."
+#: src/language/stats/regression.q:307
+msgid "Regression"
+msgstr "Regressie"
 
-#~ msgid "BY is required when SORT is specified."
-#~ msgstr "BY is vereist als SORT is gespecificeerd."
+#: src/language/stats/regression.q:389
+msgid "Model"
+msgstr "Model"
 
-#~ msgid "Combining files with incompatible encodings. String data may not be represented correctly."
-#~ msgstr "Combineren bestanden met niet overeenkomstige codering. Tekenreeks gegeven wordt misschien niet correct weergegeven."
+#: src/language/stats/regression.q:390
+msgid "Covariances"
+msgstr "Covarianties"
 
-#~ 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 breedte dan dezelfde variabele in eerder bestand."
+#: src/language/stats/regression.q:405
+msgid "Coefficient Correlations"
+msgstr ""
 
-#~ msgid "In file %s, %s is numeric."
-#~ msgstr "In bestand %s, %s is numeriek."
+#: 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 ""
 
-#~ msgid "In file %s, %s is a string variable with width %d."
-#~ msgstr "In bestand %s, %s is een tekenreeksvariabele met breedte %d."
+#: src/language/stats/regression.q:904
+msgid "Dependent variable must be numeric."
+msgstr "Afhankelijke variabele moet numeriek zijn."
 
-#~ msgid "In an earlier file, %s was numeric."
-#~ msgstr "In eerder bestand, %s was numeriek."
+#: src/language/stats/reliability.q:433
+msgid "Reliability Statistics"
+msgstr "Betrouwbaarheids Statistieken"
 
-#~ msgid "In an earlier file, %s was a string variable with width %d."
-#~ msgstr "In een eerder bestand, %s was een tekenreeksvariabele met breedte %d."
+#: src/language/stats/reliability.q:476
+msgid "Item-Total Statistics"
+msgstr "Item-Totaal Statistieken"
 
-#~ msgid "Encountered %zu sets of duplicate cases in the master file."
-#~ msgstr "Ontmoet %zu sets van dubbele cases in het master-bestand. "
+#: src/language/stats/reliability.q:498
+msgid "Scale Mean if Item Deleted"
+msgstr ""
 
-#~ msgid "Encoding should not be specified for inline data. It will be ignored."
-#~ msgstr "Coderen dient niet opgegeven te worden voor inline-gegeven. Het wordt genegeerd."
+#: src/language/stats/reliability.q:501
+msgid "Scale Variance if Item Deleted"
+msgstr ""
 
-#~ msgid "Attribute array index must be between 1 and 65535."
-#~ msgstr "Attribuuttabel index moet tussen 1 en 65535 liggen."
+#: src/language/stats/reliability.q:504
+msgid "Corrected Item-Total Correlation"
+msgstr ""
 
-#~ msgid "expecting ATTRIBUTE= or DELETE="
-#~ msgstr "ATTRIBUTE= of DELETE= verwacht"
+#: src/language/stats/reliability.q:507
+msgid "Cronbach's Alpha if Item Deleted"
+msgstr ""
 
-#~ msgid "Charset:"
-#~ msgstr "Karakterset:"
+#: src/language/stats/reliability.q:557 src/language/stats/reliability.q:576
+msgid "Cronbach's Alpha"
+msgstr "Cronbach's Alpha"
 
-#~ msgid "Attribute"
-#~ msgstr "Attribuut"
+#: src/language/stats/reliability.q:560
+msgid "N of items"
+msgstr ""
 
-#~ msgid "Not creating plot because data set is empty."
-#~ msgstr "Er wordt geen plot aangemaakt omdat de gegevensset leeg is."
+#: src/language/stats/reliability.q:579
+msgid "Part 1"
+msgstr "Deel 1"
 
-#~ msgid "Part 1"
-#~ msgstr "Deel 1"
+#: src/language/stats/reliability.q:585 src/language/stats/reliability.q:596
+msgid "N of Items"
+msgstr ""
 
-#~ msgid "Part 2"
-#~ msgstr "Deel 2"
+#: src/language/stats/reliability.q:590
+msgid "Part 2"
+msgstr "Deel 2"
 
-#~ msgid "Equal Length"
-#~ msgstr "Gelijke Lengte"
+#: src/language/stats/reliability.q:601
+msgid "Total N of Items"
+msgstr ""
 
-#~ msgid "Unequal Length"
-#~ msgstr "Ongelijke Lengte"
+#: src/language/stats/reliability.q:604
+msgid "Correlation Between Forms"
+msgstr "Correlatie Tussen Formulieren"
 
-#~ msgid "Excluded"
-#~ msgstr "Uitgesloten"
+#: src/language/stats/reliability.q:608
+msgid "Spearman-Brown Coefficient"
+msgstr "Spearman-Brown Coefficient"
 
-#~ msgid "Negative Differences"
-#~ msgstr "Negatieve Verschillen"
+#: src/language/stats/reliability.q:611
+msgid "Equal Length"
+msgstr "Gelijke Lengte"
 
-#~ msgid "Positive Differences"
-#~ msgstr "Positieve Verschillen"
+#: src/language/stats/reliability.q:614
+msgid "Unequal Length"
+msgstr "Ongelijke Lengte"
 
-#~ msgid "Ranks"
-#~ msgstr "Rangen"
+#: src/language/stats/reliability.q:618
+msgid "Guttman Split-Half Coefficient"
+msgstr "Guttman Split-Half Coëfficiënt"
 
-#~ msgid "Mean Rank"
-#~ msgstr "Gemiddelde Rang"
+#: src/language/stats/reliability.q:715
+msgid "Excluded"
+msgstr "Uitgesloten"
 
-#~ msgid "Sum of Ranks"
-#~ msgstr "Totaal van de Rangen"
+#: src/language/stats/reliability.q:723
+msgid "%"
+msgstr "%"
 
-#~ msgid "Negative Ranks"
-#~ msgstr "Negatieve Rangen"
+#: src/language/stats/t-test.q:189
+msgid "Exactly one of TESTVAL, GROUPS and PAIRS subcommands must be specified."
+msgstr "Precies 1 van de TESTVAL, GROUPS en PAIRS subopdrachten moet zijn gespecificeerd."
 
-#~ msgid "Positive Ranks"
-#~ msgstr "Positieve Rangen"
+#: src/language/stats/t-test.q:210
+msgid "VARIABLES subcommand may not be used with PAIRS."
+msgstr "VARIABLES subcommando mag niet gebruikt worden met PAIRS."
 
-#~ msgid "%s is not a recognised encoding or locale name"
-#~ msgstr "%s is geen herkende codering of lokale naam"
+#: src/language/stats/t-test.q:229
+msgid "One or more VARIABLES must be specified."
+msgstr "Een of meer VARIABLES moeten gespecificeerd zijn."
 
-#~ msgid "D_isplay Data File Information"
-#~ msgstr "_Toon Gegevensbestand-informatie"
+#: src/language/stats/t-test.q:323
+msgid "When applying GROUPS to a string variable, two values must be specified."
+msgstr "Bij het toepassen van GROUPS op een tekenreeksvariabele moeten twee waardes opgegeven zijn."
 
-#~ msgid "Working File"
-#~ msgstr "Werkbestand"
+#: src/language/stats/t-test.q:394
+msgid "At least two variables must be specified on PAIRS."
+msgstr "Tenminste 2 variabelen moeten opgegeven worden bij PAIRS."
 
-#~ msgid "External File"
-#~ msgstr "Extern bestand"
+#: src/language/stats/t-test.q:504
+msgid "One-Sample Statistics"
+msgstr ""
 
-#~ msgid "_Split"
-#~ msgstr "_Splits"
+#: src/language/stats/t-test.q:509 src/language/stats/t-test.q:529
+#: src/language/stats/t-test.q:628
+#, fuzzy
+msgid "SE. Mean"
+msgstr "_Gemiddeld"
 
-#~ msgid "Don't show the splash screen"
-#~ msgstr "Toon het opstartscherm niet"
+#: src/language/stats/t-test.q:523
+msgid "Group Statistics"
+msgstr ""
 
-#~ msgid "PSPPIRE --- A user interface for PSPP"
-#~ msgstr "PSPPIRE --- Een gebruikers interface voor PSPP"
+#: src/language/stats/t-test.q:622
+msgid "Paired Sample Statistics"
+msgstr ""
 
-#~ msgid "Miscellaneous options:"
-#~ msgstr "Diverse opties:"
+#: 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 ""
 
-#~ msgid "Options affecting syntax and behavior:"
-#~ msgstr "Opties die de syntax en het gedrag beïnvloeden:"
+#: src/language/stats/t-test.q:738
+msgid "Independent Samples Test"
+msgstr ""
 
-#~ msgid "Recode values into the same variables"
-#~ msgstr "Hercodeer waardes in dezelfde Variabelen"
+#: src/language/stats/t-test.q:746
+msgid "Levene's Test for Equality of Variances"
+msgstr ""
 
-#~ msgid "Recode values into different variables"
-#~ msgstr "Hercodeer waardes in andere Variabelen"
+#: src/language/stats/t-test.q:748
+msgid "t-test for Equality of Means"
+msgstr ""
 
-#~ msgid "Jump to variable"
-#~ msgstr "Spring naar Variabele"
+#: src/language/stats/t-test.q:751 src/language/stats/t-test.q:1107
+msgid "Sig."
+msgstr ""
 
-#~ msgid "Split the window vertically and horizontally"
-#~ msgstr "Splits het venster verticaal en horizontaal"
+#: src/language/stats/t-test.q:755 src/language/stats/t-test.q:1013
+msgid "Mean Difference"
+msgstr ""
 
-#~ msgid "Data Editor"
-#~ msgstr "Gegevensbewerker"
+#: src/language/stats/t-test.q:756
+msgid "Std. Error Difference"
+msgstr ""
 
-#~ msgid "How many things can be selected"
-#~ msgstr "Hoeveel dingen kunnen worden geselecteerd"
+#: 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 ""
 
-#~ msgid "Prefer variable labels"
-#~ msgstr "Prefereer variabelenlabels"
+#: src/language/stats/t-test.q:815
+msgid "Equal variances assumed"
+msgstr ""
 
-#~ msgid "Output Viewer"
-#~ msgstr "Uitvoer-Viewer"
+#: src/language/stats/t-test.q:861
+msgid "Equal variances not assumed"
+msgstr ""
 
-#~ msgid "Saved file \"%s\""
-#~ msgstr "Opgeslagen bestand \"%s\""
+#: src/language/stats/t-test.q:905
+msgid "Paired Samples Test"
+msgstr ""
 
-#~ msgid "Syntax Editor"
-#~ msgstr "Syntaxbewerker"
+#: src/language/stats/t-test.q:908
+msgid "Paired Differences"
+msgstr ""
 
-#~ msgid "Cannot load syntax file '%s'"
-#~ msgstr "Kan syntaxbestand \"%s\" niet laden"
+#: src/language/stats/t-test.q:920
+msgid "Std. Error Mean"
+msgstr ""
 
-#~ msgid "Save the changes to \"%s\" before closing?"
-#~ msgstr "De veranderingen van \"%s\" opslaan voor afsluiten?"
+#: src/language/stats/t-test.q:994
+msgid "One-Sample Test"
+msgstr "One-Sample Test"
 
-#~ 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/language/stats/t-test.q:999
+#, c-format
+msgid "Test Value = %f"
+msgstr "Testwaarde = %f"
 
-#~ msgid "Close _without saving"
-#~ msgstr "Sluit _zonder opslaan"
+#: src/language/stats/t-test.q:1102
+msgid "Paired Samples Correlations"
+msgstr ""
 
-#~ msgid ""
-#~ "Alpha\n"
-#~ "Split"
-#~ msgstr ""
-#~ "Alpha\n"
-#~ "Splits"
+#: src/language/stats/t-test.q:1106
+msgid "Correlation"
+msgstr "Correlatie"
 
-#~ msgid "Variables in first split:"
-#~ msgstr "Variabelen in eerste splits:"
+#: src/language/stats/t-test.q:1121
+#, c-format
+msgid "%s & %s"
+msgstr "%s & %s"
 
-#~ msgid "set to `compatible' if you want output calculated from broken algorithms"
-#~ msgstr "zet op 'compatible' als je uitvoer wilt die door gebroken algoritmen wordt berekend"
+#: 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 "Bestands-handle %s is al gedefinieerd. Gebruik CLOSE FILE HANDLE voor het opnieuw definiëren van een bestands-handle."
 
-#~ msgid "Append DIR to include path"
-#~ msgstr "Voeg DIR toe aan include-pad"
+#: src/language/data-io/file-handle.q:120
+msgid "RECFORM must be specified with MODE=360."
+msgstr "RECFORM moet opgegeven worden met MODE=360."
 
-#~ msgid "Clear include path"
-#~ msgstr "Maak include-pad leeg"
+#: src/language/data-io/file-handle.q:131
+#, fuzzy, c-format
+msgid "The specified file mode requires LRECL.  Assuming %d-character records."
+msgstr "De gespecificeerd bestandsmodus vereist LRECL. %zu-karakter records veronderstelt."
 
-#~ msgid "Disable execution of .pspp/rc at startup"
-#~ msgstr "Schakel uitvoeren van .pspp/rc bij het opstarten uit"
+#: 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."
 
-#~ msgid "Set configuration directory to DIR"
-#~ msgstr "Zet configuratiedirectory op DIR"
+#: src/language/data-io/file-handle.q:177
+msgid "file"
+msgstr "bestand"
 
-#~ msgid "Don't allow some unsafe operations"
-#~ msgstr "Sta sommige onveilige operaties niet toe"
+#: src/language/data-io/file-handle.q:179
+msgid "inline file"
+msgstr "inline-bestand"
 
-#~ 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/language/data-io/file-handle.q:205
+msgid "expecting a file name or handle name"
+msgstr "bestands- of handle-naam verwacht"
 
-#~ msgid "Algorithm must be either \"compatible\" or \"enhanced\"."
-#~ msgstr "Algoritme moet zijn \"compatible\" of \"enhanced\"."
+#: 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."
 
-#~ msgid "Syntax must be either \"compatible\" or \"enhanced\"."
-#~ msgstr "Syntax moet zijn \"compatible\" of \"enhanced\"."
+#: 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."
 
-#~ msgid "PSPP --- A program for statistical analysis"
-#~ msgstr "PSPP -- Een programma voor statistische analyse"
+#: 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 op 1 gezet."
 
-#~ msgid "Options affecting input and output locations:"
-#~ msgstr "Opties die invoer- en uitvoerlocaties beïnvloeden:"
+#: 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 op 1 gezet."
 
-#~ msgid "Diagnositic options:"
-#~ msgstr "Diagnostische opties:"
+#: 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 op 1 gezet."
 
-#~ msgid "Increase diagnostic verbosity level"
-#~ msgstr "Verhoog diagnose-zichtbaarheidsniveau"
+#: src/language/data-io/list.q:211
+msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
+msgstr "'/FORMAT WEIGHT' gespecificeerd, maar weging staat niet aan."
 
-#~ msgid "Send error messages to FILE (appended)"
-#~ msgstr "Stuur foutmeldingen naar FILE (aanvullend)"
+#: src/language/data-io/list.q:468
+msgid "Line"
+msgstr "Regel"
 
-#~ msgid "Select output driver DEVICE and disable defaults"
-#~ msgstr "Selecteer uitvoerstuurprogramma DEVICE en schakel verstekwaardes uit"
+#~ msgid "%s is not allowed inside %s."
+#~ msgstr "%s is niet toegestaan binnen %s."
 
-#~ msgid "Print a list of known driver classes, then exit"
-#~ msgstr "Print een lijst van bekende stuurprogramma-klassen en eindig daarna"
+#~ msgid "Correlations"
+#~ msgstr "Correlatie"
 
-#~ msgid "Start an interactive session"
-#~ msgstr "Start een interactieve sessie"
+#~ msgid "Pearson Correlation"
+#~ msgstr "Pearson Correlatie"
 
-#~ msgid "Diagnostic options:"
-#~ msgstr "Diagnoseopties:"
+#~ msgid "Covariance"
+#~ msgstr "Covariantie"
 
 #~ msgid "Valid Percent"
 #~ msgstr "Geldig Percentage"
+
+#~ msgid "Cum Percent"
+#~ msgstr "Cum Percentage"
index b30050d905dcfe33359b2b8383644d3cd3d1f186..9cfe8f1b7c27ce51fff6a80ad8bb852800293e41 100644 (file)
@@ -7,7 +7,7 @@ msgstr ""
 "Project-Id-Version: pspp-0.6.2-pre4\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
 "POT-Creation-Date: 2009-07-08 20:24-0700\n"
-"PO-Revision-Date: 2009-09-10 01:15-0300\n"
+"PO-Revision-Date: 2009-09-07 02:30-0300\n"
 "Last-Translator: Michel Almada de Castro Boaventura <michel@cecaps.ufmg.br>\n"
 "Language-Team: Brazilian Portuguese <ldp-br@bazar.conectiva.com.br>\n"
 "MIME-Version: 1.0\n"
@@ -1756,7 +1756,7 @@ msgstr "Valor"
 #: src/language/dictionary/sys-file-info.c:564 src/ui/gui/crosstabs.glade:275
 #: src/ui/gui/psppire-var-sheet.c:104 src/ui/gui/psppire.glade:2099
 msgid "Label"
-msgstr "Rótulo"
+msgstr "Label"
 
 #: src/language/dictionary/sys-file-info.c:110
 msgid "File:"
@@ -1765,7 +1765,7 @@ msgstr "Arquivo:"
 #: src/language/dictionary/sys-file-info.c:112 src/ui/gui/psppire.glade:2052
 #: src/ui/gui/recode.glade:841
 msgid "Label:"
-msgstr "Rótulo:"
+msgstr "Label:"
 
 #: src/language/dictionary/sys-file-info.c:116
 msgid "No label."
@@ -1839,7 +1839,7 @@ msgstr "Arquivo de sistema."
 
 #: src/language/dictionary/sys-file-info.c:143
 msgid "Weight:"
-msgstr "Ponderação:"
+msgstr "Peso:"
 
 #: src/language/dictionary/sys-file-info.c:148
 msgid "Not weighted."
@@ -1959,7 +1959,7 @@ msgstr ""
 
 #: src/language/dictionary/sys-file-info.c:517
 msgid "Missing Values: "
-msgstr "Valores ausentes:"
+msgstr "Missing Values:"
 
 #: src/language/dictionary/sys-file-info.c:611
 msgid "No vectors defined."
@@ -2553,12 +2553,12 @@ msgstr "esperando BY"
 
 #: src/language/stats/crosstabs.q:447
 msgid "VARIABLES must be specified before TABLES."
-msgstr "VARIABLES deve ser especificado antes de TABLES."
+msgstr ""
 
 #: src/language/stats/crosstabs.q:485
 #, c-format
 msgid "Maximum value (%ld) less than minimum value (%ld)."
-msgstr "O valor máximo (%ld) é menor que o mínimo (%ld)."
+msgstr ""
 
 #: src/language/stats/crosstabs.q:865
 msgid "Summary."
@@ -2629,15 +2629,15 @@ msgstr "Estatística"
 
 #: src/language/stats/crosstabs.q:1172
 msgid "Asymp. Sig. (2-sided)"
-msgstr "Sig. assintótica (bi-caudal)"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1174
 msgid "Exact. Sig. (2-sided)"
-msgstr "Sig. exata (bi-caudal)"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1176
 msgid "Exact. Sig. (1-sided)"
-msgstr "Sig. exata (uni-caudal)"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1191
 msgid "Symmetric measures."
@@ -2645,15 +2645,15 @@ msgstr "Medidas simétricas."
 
 #: src/language/stats/crosstabs.q:1197 src/language/stats/crosstabs.q:1239
 msgid "Asymp. Std. Error"
-msgstr "Erro padrão assintótico"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1198 src/language/stats/crosstabs.q:1240
 msgid "Approx. T"
-msgstr "T aproximado"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1199 src/language/stats/crosstabs.q:1241
 msgid "Approx. Sig."
-msgstr "Sig. aproximada"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1210
 msgid "Risk estimate."
@@ -2667,12 +2667,12 @@ msgstr "Intervalo de confiança de 95%%"
 #: src/language/stats/crosstabs.q:1217 src/language/stats/t-test.q:1030
 #: src/language/stats/t-test.q:1216 src/language/stats/t-test.q:1319
 msgid "Lower"
-msgstr "Mínimo"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1218 src/language/stats/t-test.q:1031
 #: src/language/stats/t-test.q:1217 src/language/stats/t-test.q:1320
 msgid "Upper"
-msgstr "Máximo"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1232
 msgid "Directional measures."
@@ -2693,7 +2693,7 @@ msgstr ""
 
 #: src/language/stats/crosstabs.q:1995
 msgid "Fisher's Exact Test"
-msgstr "Teste exato de Fisher"
+msgstr ""
 
 #: src/language/stats/crosstabs.q:1996
 msgid "Continuity Correction"
@@ -2917,7 +2917,7 @@ msgstr "N válidos"
 
 #: src/language/stats/descriptives.c:887
 msgid "Missing N"
-msgstr "Missing N"
+msgstr ""
 
 #: src/language/stats/descriptives.c:915
 #, c-format
@@ -3163,7 +3163,7 @@ msgstr "Frequência"
 
 #: src/language/stats/frequencies.q:1085
 msgid "Value Label"
-msgstr "Rótulo de valor"
+msgstr ""
 
 #: src/language/stats/frequencies.q:1189
 msgid "Freq"
@@ -3192,7 +3192,7 @@ msgstr ""
 
 #: src/language/stats/means.q:100
 msgid "Missing required subcommand TABLES."
-msgstr "Faltando subcomando necessário TABLES."
+msgstr ""
 
 #: src/language/stats/means.q:134
 msgid "TABLES subcommand may not appear more than once."
@@ -4453,7 +4453,7 @@ msgstr "Transformação Pendente"
 
 #: src/ui/gui/data-editor.c:323
 msgid "_Labels"
-msgstr "_Rótulos"
+msgstr "_Label"
 
 #: src/ui/gui/data-editor.c:324
 msgid "Show/hide value labels"
@@ -4689,12 +4689,12 @@ msgstr "Filtrar por %s"
 
 #: src/ui/gui/data-editor.c:1163
 msgid "Weights off"
-msgstr "Sem ponderação"
+msgstr "Sem Pesos"
 
 #: src/ui/gui/data-editor.c:1175
 #, c-format
 msgid "Weight by %s"
-msgstr "Ponderar por %s"
+msgstr "Peso por %s"
 
 #: src/ui/gui/data-editor.c:1198 src/ui/gui/data-editor.c:1445
 #: src/ui/gui/data-editor.glade:660
@@ -4840,7 +4840,7 @@ msgstr "_Linhas da Grade"
 
 #: src/ui/gui/data-editor.glade:274
 msgid "Value _Labels"
-msgstr "_Rótulos de Valores"
+msgstr "_Labels"
 
 #: src/ui/gui/data-editor.glade:311
 msgid "_Sort Cases"
@@ -4852,7 +4852,7 @@ msgstr "_Dividir Arquivo"
 
 #: src/ui/gui/data-editor.glade:347
 msgid "_Weight Cases"
-msgstr "_Ponderar Casos"
+msgstr "Dar _Peso aos Casos"
 
 #: src/ui/gui/data-editor.glade:359
 msgid "_Transform"
@@ -4864,7 +4864,7 @@ msgstr "_Executar Transformações Pendentes"
 
 #: src/ui/gui/data-editor.glade:422
 msgid "_Analyze"
-msgstr "_Analisar"
+msgstr "A_nalisar"
 
 #: src/ui/gui/data-editor.glade:432
 msgid "_Descriptive Statistics"
@@ -4957,7 +4957,7 @@ msgstr "Dividir Arquivo"
 
 #: src/ui/gui/data-editor.glade:832
 msgid "Weight Cases"
-msgstr "Ponderar Casos"
+msgstr "Dar Peso Aos Casos"
 
 #: src/ui/gui/data-editor.glade:844
 msgid "Select Cases"
@@ -4966,7 +4966,7 @@ msgstr "Selecionar Casos"
 #: src/ui/gui/data-editor.glade:864 src/ui/gui/data-editor.glade:1452
 #: src/ui/gui/data-editor.glade:1633
 msgid "Value Labels"
-msgstr "Rótulos de valores"
+msgstr "Value Labels"
 
 #: src/ui/gui/data-editor.glade:875
 msgid "Use Sets"
@@ -4990,7 +4990,7 @@ msgstr "Área de Status do Uso de Filtros"
 
 #: src/ui/gui/data-editor.glade:1002
 msgid "Weight Status Area"
-msgstr "Área de status da ponderação"
+msgstr "Área de Status dos Pesos"
 
 #: src/ui/gui/data-editor.glade:1028
 msgid "Split File Status Area"
@@ -5046,7 +5046,7 @@ msgstr "Casas Decimais:"
 
 #: src/ui/gui/data-editor.glade:1550
 msgid "Value Label:"
-msgstr "Rótulo de valor:"
+msgstr "Value Label:"
 
 #: src/ui/gui/data-editor.glade:1563 src/ui/gui/psppire.glade:2544
 #: src/ui/gui/recode.glade:185
@@ -5056,7 +5056,7 @@ msgstr "Valor:"
 #: src/ui/gui/data-editor.glade:1700 src/ui/gui/examine.glade:423
 #: src/ui/gui/t-test.glade:460
 msgid "Missing Values"
-msgstr "Valores Missing"
+msgstr "Missing Values"
 
 #: src/ui/gui/data-editor.glade:1718
 msgid "_Range plus one optional discrete missing value"
@@ -5072,15 +5072,15 @@ msgstr "Ma_ior:"
 
 #: src/ui/gui/data-editor.glade:1813
 msgid "Di_screte value:"
-msgstr "Valor _discreto:"
+msgstr "Valor Di_screto:"
 
 #: src/ui/gui/data-editor.glade:1860
 msgid "_No missing values"
-msgstr "Sem valores _missing"
+msgstr "Sem missi_ng values"
 
 #: src/ui/gui/data-editor.glade:1878
 msgid "_Discrete missing values"
-msgstr "Valores missing _discretos"
+msgstr "Missing values _discretos"
 
 #: src/ui/gui/descriptives-dialog.c:40 src/ui/gui/frequencies-dialog.c:41
 msgid "Standard deviation"
@@ -5120,7 +5120,7 @@ msgstr "Lista de Fator:"
 
 #: src/ui/gui/examine.glade:218
 msgid "Label Cases by:"
-msgstr "Rotular casos por:"
+msgstr "Inserir Label nos Casos por:"
 
 #: 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
@@ -5505,7 +5505,7 @@ msgstr "Variável alvo:"
 
 #: src/ui/gui/psppire.glade:904
 msgid "Type & Label"
-msgstr "Tipo & Rótulo"
+msgstr "Tipo & Label"
 
 #: src/ui/gui/psppire.glade:943
 msgid "="
@@ -5697,7 +5697,7 @@ msgstr ""
 # lala
 #: src/ui/gui/rank.glade:428
 msgid "Sum of case weights"
-msgstr "Soma das ponderações dos casos"
+msgstr "Soma dos pesos dos casos"
 
 #: src/ui/gui/rank.glade:450
 msgid "Proportion Estimates"
@@ -5769,11 +5769,11 @@ msgstr "Transformar Para a Mesma Variável: Antigos e Novos Valores"
 
 #: src/ui/gui/recode.glade:197
 msgid "System-Missing"
-msgstr "Missing do sistema"
+msgstr "System-Missing"
 
 #: src/ui/gui/recode.glade:211
 msgid "System-or user-missing"
-msgstr "Missing do sistema ou usuário"
+msgstr "System ou user-missing"
 
 #: src/ui/gui/recode.glade:245
 msgid "through"
@@ -5801,7 +5801,7 @@ msgstr "Valores Antigos"
 
 #: src/ui/gui/recode.glade:462
 msgid "System Missing"
-msgstr "Missing do sistema"
+msgstr "System Missing"
 
 #: src/ui/gui/recode.glade:476
 msgid "Copy old values"
@@ -6194,7 +6194,7 @@ msgstr "<b>Visualização de dados</b>"
 #: src/ui/gui/variable-info-dialog.c:89
 #, c-format
 msgid "Label: %s\n"
-msgstr "Rótulo: %s\n"
+msgstr "Label: %s\n"
 
 #: src/ui/gui/variable-info-dialog.c:98
 #, c-format
@@ -6213,7 +6213,7 @@ msgstr "Nível de medida: %s\n"
 
 #: src/ui/gui/variable-info-dialog.c:121
 msgid "Value Labels:\n"
-msgstr "Rótulos de valores:\n"
+msgstr "Value Labels:\n"
 
 #: src/ui/gui/variable-info-dialog.c:133
 #, c-format
index 1ffbdf3e24ae70d8673cff1887b6d3fe2222c6ab..46e0ef73403f1cbd7dd097c0361b692c6aeaaff9 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..49297d1349f5c50661e904408f6135684786e710 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)
+           value_copy (case_data_rw_idx (dst, dst_idx), case_data_idx (src, src_idx), caseproto_get_width (map->proto, dst_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..ab653d874193a46d1f9a83a03f1e15ac4e09e7a6 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
@@ -76,7 +119,7 @@ dict_dump (const struct dictionary *d)
     {
       const struct variable *v =
        d->var[i];
-      printf ("Name: %s;\tdict_idx: %d; case_idx: %d\n",
+      printf ("Name: %s;\tdict_idx: %zu; case_idx: %zu\n",
              var_get_name (v),
              var_get_dict_index (v),
              var_get_case_index (v));
@@ -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 6be5ef0afa468b2705de8b95e88e328d8afca5c5..91229595924c7bfe7fe11d1486970ae598950f5c 100644 (file)
@@ -448,9 +448,9 @@ 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;
 }
 
@@ -476,7 +476,6 @@ default_output_path (void)
          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);
@@ -494,7 +493,6 @@ default_output_path (void)
       else
         path = xstrdup (home_dir);
 
-
       for(i = 0; i < strlen (path); i++)
        if (path[i] == '\\') path[i] = '/';
     }
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 8d973e4dbc103d09860e4c14cc12a006fda0bfcd..f63a122fe83b96776b632cef57b51e49c83c4bd1 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. */
@@ -99,9 +101,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;
@@ -113,15 +117,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
@@ -152,7 +164,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 *,
@@ -164,7 +178,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.
@@ -284,6 +362,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);
 
@@ -303,11 +383,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);
 
@@ -511,7 +591,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,
@@ -545,7 +625,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
@@ -564,21 +644,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. */
@@ -723,7 +806,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:
@@ -766,21 +849,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"),
@@ -794,7 +884,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);
@@ -803,7 +894,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;
@@ -842,12 +933,53 @@ read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count,
     NOT_REACHED ();
   if (integer_representation != expected_integer_format)
     {
-      static const char *const endian[] = {N_("little-endian"), N_("big-endian")};
+      static const char *const endian[] = {N_("Little Endian"), N_("Big Endian")};
       sys_warn (r, _("Integer format indicated by system file (%s) "
                      "differs from expected (%s)."),
                 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. */
@@ -863,11 +995,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
@@ -946,14 +1083,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;
@@ -999,7 +1134,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;
 }
 
@@ -1009,14 +1144,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;
@@ -1064,7 +1197,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);
 }
 
@@ -1078,7 +1211,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. */
     };
@@ -1088,6 +1221,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;
 
@@ -1146,12 +1280,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. */
@@ -1170,9 +1307,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);
     }
@@ -1194,7 +1332,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));
             }
        }
@@ -1202,6 +1340,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. */
 
@@ -1211,31 +1546,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++)
@@ -1243,28 +1578,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. */
@@ -1311,7 +1647,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;
@@ -1324,7 +1660,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)
@@ -1402,7 +1738,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)
 {
   int opcode = read_opcode (r);
   switch (opcode)
@@ -1450,7 +1786,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)
@@ -1478,7 +1814,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);
 }
@@ -1563,82 +1899,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 001b78f162a7d1724d73afba520f9066facbcfa4..5daea89dc5a908ac385a5f19dc8c2c62db7c0e20 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);
@@ -435,27 +461,24 @@ write_variable (struct sfm_writer *w, const struct variable *v)
   /* Value label. */
   if (var_has_label (v))
     {
-      const char *label = var_get_label (v);
+      char *label = recode_string (dict_get_encoding (dict), UTF8, var_get_label (v), -1);
       size_t label_len = MIN (strlen (label), 255);
       size_t padded_len = ROUND_UP (label_len, 4);
       write_int (w, label_len);
       write_string (w, label, padded_len);
+      free (label);
     }
 
   /* 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);
 
@@ -475,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. */
@@ -521,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,
@@ -584,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)
@@ -595,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. */
@@ -672,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;
     }
 
@@ -683,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. */
@@ -761,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;
 
@@ -769,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);
         }
     }
@@ -782,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;
 
@@ -790,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)
@@ -814,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);
@@ -937,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..32dba0b4f7cd0ca7760f18315e13ae0b845069a0 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;
     }
@@ -689,9 +689,9 @@ report_state_mismatch (const struct command *command, enum cmd_state state)
         }
     }
   else if (state == CMD_STATE_INPUT_PROGRAM)
-    msg (SE, _("%s is not allowed inside INPUT PROGRAM."), command->name);
+    msg (SE, _("%s is not allowed inside %s."), command->name, "INPUT PROGRAM" );
   else if (state == CMD_STATE_FILE_TYPE)
-    msg (SE, _("%s is not allowed inside FILE TYPE."), command->name);
+    msg (SE, _("%s is not allowed inside %s."), command->name, "FILE TYPE");
 
   return false;
 }
index ccf4c5560c1450a3109f66ec51083c335a2454ec..066ecf92c4c7a28a483a7a5f0bdae136006b5b02 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)
@@ -95,6 +100,7 @@ DEF_CMD (S_DATA, 0, "AUTORECODE", cmd_autorecode)
 DEF_CMD (S_DATA, F_KEEP_FINAL_TOKEN, "BEGIN DATA", cmd_begin_data)
 DEF_CMD (S_DATA, 0, "COUNT", cmd_count)
 DEF_CMD (S_DATA, 0, "CROSSTABS", cmd_crosstabs)
+DEF_CMD (S_DATA, 0, "CORRELATIONS", cmd_correlation)
 DEF_CMD (S_DATA, 0, "DELETE VARIABLES", cmd_delete_variables)
 DEF_CMD (S_DATA, 0, "DESCRIPTIVES", cmd_descriptives)
 DEF_CMD (S_DATA, 0, "EXAMINE", cmd_examine)
@@ -104,14 +110,16 @@ 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, "PEARSON CORRELATIONS", cmd_correlation)
 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 +135,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 +146,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")
@@ -151,7 +157,6 @@ UNIMPL_CMD ("CCF", "Time series cross correlation")
 UNIMPL_CMD ("CLEAR TRANSFORMATIONS", "Clears transformations from active file")
 UNIMPL_CMD ("CLUSTER", "Hierachial clustering")
 UNIMPL_CMD ("CONJOINT", "Analyse full concept data")
-UNIMPL_CMD ("CORRELATIONS", "Correlation coefficients")
 UNIMPL_CMD ("CORRESPONDENCE", "Show correspondence")
 UNIMPL_CMD ("COXREG", "Cox proportional hazards regression")
 UNIMPL_CMD ("CREATE", "Create time series data")
@@ -163,7 +168,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")
@@ -213,7 +217,6 @@ UNIMPL_CMD ("ORTHOPLAN", "Orthogonal effects design")
 UNIMPL_CMD ("OVERALS", "Nonlinear canonical correlation")
 UNIMPL_CMD ("PACF", "Partial autocorrelation")
 UNIMPL_CMD ("PARTIAL CORR", "Partial correlation")
-UNIMPL_CMD ("PEARSON CORRELATIONS", "Correlation coefficients")
 UNIMPL_CMD ("PLANCARDS", "Conjoint analysis planning")
 UNIMPL_CMD ("PLUM", "Estimate ordinal regression models")
 UNIMPL_CMD ("POINT", "Marker in keyed file")
@@ -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..d4b9bf5
--- /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 %s is specified."), "TABLE");
+          goto error;
+        }
+      if (saw_sort)
+        {
+          msg (SE, _("BY is required when %s is specified."), "SORT");
+          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..33aa1d168a5c4efce423688b3d8998193b8c7abc 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)
@@ -129,7 +129,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds)
         {
           if (cmd.n_lrecl[0] == LONG_MIN)
             msg (SE, _("The specified file mode requires LRECL.  "
-                       "Assuming %d-character records."),
+                       "Assuming %zu-character records."),
                  properties.record_width);
           else if (cmd.n_lrecl[0] < 1 || cmd.n_lrecl[0] >= (1UL << 31))
             msg (SE, _("Record length (%ld) must be between 1 and %lu bytes.  "
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..2cfa0177c77950f26495aa9a5bd421ab8df73488 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,19 +428,19 @@ 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++;
     }
 
   if (fh != NULL)
-    tab_title (t, ngettext ("Writing %d record to %s.",
-                            "Writing %d records to %s.", trns->record_cnt),
+    tab_title (t, ngettext ("Writing %zu record to %s.",
+                            "Writing %zu records to %s.", trns->record_cnt),
                trns->record_cnt, fh_get_name (fh));
   else
-    tab_title (t, ngettext ("Writing %d record.",
-                            "Writing %d records.", trns->record_cnt),
+    tab_title (t, ngettext ("Writing %zu record.",
+                            "Writing %zu records.", trns->record_cnt),
                trns->record_cnt);
   tab_submit (t);
 }
@@ -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 cd5016910d61242c0c6afa8698a8f43553d38308..64204b78ec374cd0ef3477e0157b9a3883e994bf 100644 (file)
@@ -111,7 +111,7 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
 
          if (already_encountered & 1)
            {
-             msg (SE, _("REORDER subcommand may be given at most once."));
+             msg (SE, _("%s subcommand may be given at most once."), "REORDER");
              goto done;
            }
          already_encountered |= 1;
@@ -144,7 +144,7 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
                {
                  if (!lex_match (lexer, '('))
                    {
-                     msg (SE, _("`(' expected on REORDER subcommand."));
+                     msg (SE, _("`(' expected on %s subcommand."), "REORDER");
                      free (v);
                      goto done;
                    }
@@ -174,7 +174,7 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
        {
          if (already_encountered & 2)
            {
-             msg (SE, _("RENAME subcommand may be given at most once."));
+             msg (SE, _("%s subcommand may be given at most once."), "RENAME");
              goto done;
            }
          already_encountered |= 2;
@@ -187,7 +187,7 @@ cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
 
              if (!lex_match (lexer, '('))
                {
-                 msg (SE, _("`(' expected on RENAME subcommand."));
+                 msg (SE, _("`(' expected on %s subcommand."), "RENAME");
                  goto done;
                }
              if (!parse_variables (lexer, dataset_dict (ds),
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..f68a830b83810ba6eec7d4e73b42ab40b041ca91 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,13 +120,13 @@ 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.")
-            : info.integer_format == INTEGER_LSB_FIRST ? _("Little Endian.")
-            : _("Unknown."));
+            info.integer_format == INTEGER_MSB_FIRST ? _("Big Endian")
+            : info.integer_format == INTEGER_LSB_FIRST ? _("Little Endian")
+            : _("Unknown"));
   tab_text (t, 0, 4, TAB_LEFT, _("Real Format:"));
   tab_text (t, 1, 4, TAB_LEFT,
             info.float_format == FLOAT_IEEE_DOUBLE_LE ? _("IEEE 754 LE.")
@@ -131,15 +134,15 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
             : info.float_format == FLOAT_VAX_D ? _("VAX D.")
             : info.float_format == FLOAT_VAX_G ? _("VAX G.")
             : info.float_format == FLOAT_Z_LONG ? _("IBM 390 Hex Long.")
-            : _("Unknown."));
+            : _("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, 1, 7, TAB_LEFT, _("System File"));
   tab_text (t, 0, 8, TAB_LEFT, _("Weight:"));
   {
     struct variable *weight_var = dict_get_weight (d);
@@ -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 bc4e830d7ffefc010cf3283d6f237fb8dc58a880..11eb8e0bf35645fe422c82d4932cb4132a4bfb4d 100644 (file)
@@ -23,19 +23,19 @@ helpers = src/language/expressions/generate.pl \
        src/language/expressions/operations.def
 
 $(expressions_built_sources): $(helpers)
-EXTRA_DIST += $(helpers) $(expressions_built_sources:=.pl)
+EXTRA_DIST += $(helpers) $(expressions_built_sources:=pl)
 AM_CPPFLAGS += -I$(top_builddir)/src/language/expressions \
        -I$(top_srcdir)/src/language/expressions
 
-SUFFIXES = .h.pl .inc.pl
+SUFFIXES += .h .hpl .inc .incpl
 
 generate_from_pl = $(MKDIR_P) `dirname $@` && \
        $(PERL) -I $(top_srcdir)/src/language/expressions $< -o $@ -i $(top_srcdir)/src/language/expressions/operations.def
 
-.h.pl.h:
+.hpl.h:
        $(generate_from_pl)
 
-.inc.pl.inc:
+.incpl.inc:
        $(generate_from_pl)
 
 EXTRA_DIST += src/language/expressions/OChangeLog
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;
 }
diff --git a/src/language/expressions/evaluate.h.pl b/src/language/expressions/evaluate.h.pl
deleted file mode 100644 (file)
index c26a7cc..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-use strict;
-use warnings 'all';
-
-do 'generate.pl';
-
-our (%ops);
-our (@order);
-sub generate_output {
-    print "#include \"helpers.h\"\n\n";
-
-    for my $opname (@order) {
-       my ($op) = $ops{$opname};
-       next if $op->{UNIMPLEMENTED};
-
-       my (@args);
-       for my $arg (@{$op->{ARGS}}) {
-           if (!defined $arg->{IDX}) {
-               push (@args, c_type ($arg->{TYPE}) . $arg->{NAME});
-           } else {
-               push (@args, c_type ($arg->{TYPE}) . "$arg->{NAME}" . "[]");
-               push (@args, "size_t $arg->{IDX}");
-           }
-       }
-       for my $aux (@{$op->{AUX}}) {
-           push (@args, c_type ($aux->{TYPE}) . $aux->{NAME});
-       }
-       push (@args, "void") if !@args;
-
-       my ($statements) = $op->{BLOCK} || "  return $op->{EXPRESSION};\n";
-
-       print "static inline ", c_type ($op->{RETURNS}), "\n";
-       print "eval_$opname (", join (', ', @args), ")\n";
-       print "{\n";
-       print "$statements";
-       print "}\n\n";
-    }
-}
diff --git a/src/language/expressions/evaluate.hpl b/src/language/expressions/evaluate.hpl
new file mode 100644 (file)
index 0000000..c26a7cc
--- /dev/null
@@ -0,0 +1,37 @@
+use strict;
+use warnings 'all';
+
+do 'generate.pl';
+
+our (%ops);
+our (@order);
+sub generate_output {
+    print "#include \"helpers.h\"\n\n";
+
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+       next if $op->{UNIMPLEMENTED};
+
+       my (@args);
+       for my $arg (@{$op->{ARGS}}) {
+           if (!defined $arg->{IDX}) {
+               push (@args, c_type ($arg->{TYPE}) . $arg->{NAME});
+           } else {
+               push (@args, c_type ($arg->{TYPE}) . "$arg->{NAME}" . "[]");
+               push (@args, "size_t $arg->{IDX}");
+           }
+       }
+       for my $aux (@{$op->{AUX}}) {
+           push (@args, c_type ($aux->{TYPE}) . $aux->{NAME});
+       }
+       push (@args, "void") if !@args;
+
+       my ($statements) = $op->{BLOCK} || "  return $op->{EXPRESSION};\n";
+
+       print "static inline ", c_type ($op->{RETURNS}), "\n";
+       print "eval_$opname (", join (', ', @args), ")\n";
+       print "{\n";
+       print "$statements";
+       print "}\n\n";
+    }
+}
diff --git a/src/language/expressions/evaluate.inc.pl b/src/language/expressions/evaluate.inc.pl
deleted file mode 100644 (file)
index e2fccab..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-use strict;
-use warnings 'all';
-
-do 'generate.pl';
-
-our (@order);
-our (%ops);
-
-sub generate_output {
-    for my $opname (@order) {
-       my ($op) = $ops{$opname};
-
-       if ($op->{UNIMPLEMENTED}) {
-           print "case $opname:\n";
-           print "  NOT_REACHED ();\n\n";
-           next;
-       }
-
-       my (@decls);
-       my (@args);
-       for my $arg (@{$op->{ARGS}}) {
-           my ($name) = $arg->{NAME};
-           my ($type) = $arg->{TYPE};
-           my ($c_type) = c_type ($type);
-           my ($idx) = $arg->{IDX};
-           push (@args, "arg_$arg->{NAME}");
-           if (!defined ($idx)) {
-               my ($decl) = "${c_type}arg_$name";
-               if ($type->{ROLE} eq 'any') {
-                   unshift (@decls, "$decl = *--$type->{STACK}");
-               } elsif ($type->{ROLE} eq 'leaf') {
-                   push (@decls, "$decl = op++->$type->{ATOM}");
-               } else {
-                   die;
-               }
-           } else {
-               my ($stack) = $type->{STACK};
-               defined $stack or die;
-               unshift (@decls,
-                        "$c_type*arg_$arg->{NAME} = $stack -= arg_$idx");
-               unshift (@decls, "size_t arg_$arg->{IDX} = op++->integer");
-
-               my ($idx) = "arg_$idx";
-               if ($arg->{TIMES} != 1) {
-                   $idx .= " / $arg->{TIMES}";
-               }
-               push (@args, $idx);
-           }
-       }
-       for my $aux (@{$op->{AUX}}) {
-           my ($type) = $aux->{TYPE};
-           my ($name) = $aux->{NAME};
-           if ($type->{ROLE} eq 'leaf') {
-               my ($c_type) = c_type ($type);
-               push (@decls, "${c_type}aux_$name = op++->$type->{ATOM}");
-               push (@args, "aux_$name");
-           } elsif ($type->{ROLE} eq 'fixed') {
-               push (@args, $type->{FIXED_VALUE});
-           }
-       }
-
-       my ($sysmis_cond) = make_sysmis_decl ($op, "op++->integer");
-       push (@decls, $sysmis_cond) if defined $sysmis_cond;
-
-       my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")";
-
-       my ($stack) = $op->{RETURNS}{STACK};
-
-       print "case $opname:\n";
-       if (@decls) {
-           print "  {\n";
-           print "    $_;\n" foreach @decls;
-           if (defined $sysmis_cond) {
-               my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE};
-               print "    *$stack++ = force_sysmis ? $miss_ret : $result;\n";
-           } else {
-               print "    *$stack++ = $result;\n";
-           }
-           print "  }\n";
-       } else {
-           print "  *$stack++ = $result;\n";
-       }
-       print "  break;\n\n";
-    }
-}
diff --git a/src/language/expressions/evaluate.incpl b/src/language/expressions/evaluate.incpl
new file mode 100644 (file)
index 0000000..e2fccab
--- /dev/null
@@ -0,0 +1,85 @@
+use strict;
+use warnings 'all';
+
+do 'generate.pl';
+
+our (@order);
+our (%ops);
+
+sub generate_output {
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+
+       if ($op->{UNIMPLEMENTED}) {
+           print "case $opname:\n";
+           print "  NOT_REACHED ();\n\n";
+           next;
+       }
+
+       my (@decls);
+       my (@args);
+       for my $arg (@{$op->{ARGS}}) {
+           my ($name) = $arg->{NAME};
+           my ($type) = $arg->{TYPE};
+           my ($c_type) = c_type ($type);
+           my ($idx) = $arg->{IDX};
+           push (@args, "arg_$arg->{NAME}");
+           if (!defined ($idx)) {
+               my ($decl) = "${c_type}arg_$name";
+               if ($type->{ROLE} eq 'any') {
+                   unshift (@decls, "$decl = *--$type->{STACK}");
+               } elsif ($type->{ROLE} eq 'leaf') {
+                   push (@decls, "$decl = op++->$type->{ATOM}");
+               } else {
+                   die;
+               }
+           } else {
+               my ($stack) = $type->{STACK};
+               defined $stack or die;
+               unshift (@decls,
+                        "$c_type*arg_$arg->{NAME} = $stack -= arg_$idx");
+               unshift (@decls, "size_t arg_$arg->{IDX} = op++->integer");
+
+               my ($idx) = "arg_$idx";
+               if ($arg->{TIMES} != 1) {
+                   $idx .= " / $arg->{TIMES}";
+               }
+               push (@args, $idx);
+           }
+       }
+       for my $aux (@{$op->{AUX}}) {
+           my ($type) = $aux->{TYPE};
+           my ($name) = $aux->{NAME};
+           if ($type->{ROLE} eq 'leaf') {
+               my ($c_type) = c_type ($type);
+               push (@decls, "${c_type}aux_$name = op++->$type->{ATOM}");
+               push (@args, "aux_$name");
+           } elsif ($type->{ROLE} eq 'fixed') {
+               push (@args, $type->{FIXED_VALUE});
+           }
+       }
+
+       my ($sysmis_cond) = make_sysmis_decl ($op, "op++->integer");
+       push (@decls, $sysmis_cond) if defined $sysmis_cond;
+
+       my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")";
+
+       my ($stack) = $op->{RETURNS}{STACK};
+
+       print "case $opname:\n";
+       if (@decls) {
+           print "  {\n";
+           print "    $_;\n" foreach @decls;
+           if (defined $sysmis_cond) {
+               my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE};
+               print "    *$stack++ = force_sysmis ? $miss_ret : $result;\n";
+           } else {
+               print "    *$stack++ = $result;\n";
+           }
+           print "  }\n";
+       } else {
+           print "  *$stack++ = $result;\n";
+       }
+       print "  break;\n\n";
+    }
+}
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
diff --git a/src/language/expressions/operations.h.pl b/src/language/expressions/operations.h.pl
deleted file mode 100644 (file)
index 8e6e120..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-use strict;
-use warnings 'all';
-
-do 'generate.pl';
-our (@types, @funcs, @opers);
-
-sub generate_output {
-    print "#include <stdlib.h>\n";
-    print "#include <stdbool.h>\n\n";
-
-    print "typedef enum";
-    print "  {\n";
-    my (@atoms);
-    foreach my $type (@types) {
-       next if $type->{ROLE} eq 'fixed';
-       push (@atoms, "OP_$type->{NAME}");
-    }
-    print_operations ('atom', 1, \@atoms);
-    print_operations ('function', "OP_atom_last + 1", \@funcs);
-    print_operations ('operator', "OP_function_last + 1", \@opers);
-    print_range ("OP_composite", "OP_function_first", "OP_operator_last");
-    print ",\n\n";
-    print_range ("OP", "OP_atom_first", "OP_composite_last");
-    print "\n  }\n";
-    print "operation_type, atom_type;\n";
-
-    print_predicate ('is_operation', 'OP');
-    print_predicate ("is_$_", "OP_$_")
-       foreach qw (atom composite function operator);
-}
-
-sub print_operations {
-    my ($type, $first, $names) = @_;
-    print "    /* \u$type types. */\n";
-    print "    $names->[0] = $first,\n";
-    print "    $_,\n" foreach @$names[1...$#{$names}];
-    print_range ("OP_$type", $names->[0], $names->[$#{$names}]);
-    print ",\n\n";
-}
-
-sub print_range {
-    my ($prefix, $first, $last) = @_;
-    print "    ${prefix}_first = $first,\n";
-    print "    ${prefix}_last = $last,\n";
-    print "    ${prefix}_cnt = ${prefix}_last - ${prefix}_first + 1";
-}
-
-sub print_predicate {
-    my ($function, $category) = @_;
-    my ($assertion) = "";
-
-    print "\nstatic inline bool\n";
-    print "$function (operation_type op)\n";
-    print "{\n";
-    print "  assert (is_operation (op));\n" if $function ne 'is_operation';
-    print "  return op >= ${category}_first && op <= ${category}_last;\n";
-    print "}\n";
-}
diff --git a/src/language/expressions/operations.hpl b/src/language/expressions/operations.hpl
new file mode 100644 (file)
index 0000000..8e6e120
--- /dev/null
@@ -0,0 +1,58 @@
+use strict;
+use warnings 'all';
+
+do 'generate.pl';
+our (@types, @funcs, @opers);
+
+sub generate_output {
+    print "#include <stdlib.h>\n";
+    print "#include <stdbool.h>\n\n";
+
+    print "typedef enum";
+    print "  {\n";
+    my (@atoms);
+    foreach my $type (@types) {
+       next if $type->{ROLE} eq 'fixed';
+       push (@atoms, "OP_$type->{NAME}");
+    }
+    print_operations ('atom', 1, \@atoms);
+    print_operations ('function', "OP_atom_last + 1", \@funcs);
+    print_operations ('operator', "OP_function_last + 1", \@opers);
+    print_range ("OP_composite", "OP_function_first", "OP_operator_last");
+    print ",\n\n";
+    print_range ("OP", "OP_atom_first", "OP_composite_last");
+    print "\n  }\n";
+    print "operation_type, atom_type;\n";
+
+    print_predicate ('is_operation', 'OP');
+    print_predicate ("is_$_", "OP_$_")
+       foreach qw (atom composite function operator);
+}
+
+sub print_operations {
+    my ($type, $first, $names) = @_;
+    print "    /* \u$type types. */\n";
+    print "    $names->[0] = $first,\n";
+    print "    $_,\n" foreach @$names[1...$#{$names}];
+    print_range ("OP_$type", $names->[0], $names->[$#{$names}]);
+    print ",\n\n";
+}
+
+sub print_range {
+    my ($prefix, $first, $last) = @_;
+    print "    ${prefix}_first = $first,\n";
+    print "    ${prefix}_last = $last,\n";
+    print "    ${prefix}_cnt = ${prefix}_last - ${prefix}_first + 1";
+}
+
+sub print_predicate {
+    my ($function, $category) = @_;
+    my ($assertion) = "";
+
+    print "\nstatic inline bool\n";
+    print "$function (operation_type op)\n";
+    print "{\n";
+    print "  assert (is_operation (op));\n" if $function ne 'is_operation';
+    print "  return op >= ${category}_first && op <= ${category}_last;\n";
+    print "}\n";
+}
diff --git a/src/language/expressions/optimize.inc.pl b/src/language/expressions/optimize.inc.pl
deleted file mode 100644 (file)
index c441774..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-use strict;
-use warnings 'all';
-
-do 'generate.pl';
-our (@order, %ops);
-
-sub generate_output {
-    for my $opname (@order) {
-       my ($op) = $ops{$opname};
-
-       if (!$op->{OPTIMIZABLE} || $op->{UNIMPLEMENTED}) {
-           print "case $opname:\n";
-           print "  NOT_REACHED ();\n\n";
-           next;
-       }
-
-       my (@decls);
-       my ($arg_idx) = 0;
-       for my $arg (@{$op->{ARGS}}) {
-           my ($decl);
-           my ($name) = $arg->{NAME};
-           my ($type) = $arg->{TYPE};
-           my ($ctype) = c_type ($type);
-           my ($idx) = $arg->{IDX};
-           if (!defined ($idx)) {
-               my ($func) = "get_$type->{ATOM}_arg";
-               push (@decls, "${ctype}arg_$name = $func (node, $arg_idx)");
-           } else {
-               my ($decl) = "size_t arg_$idx = node->arg_cnt";
-               $decl .= " - $arg_idx" if $arg_idx;
-               push (@decls, $decl);
-
-               push (@decls, "${ctype}*arg_$name = "
-                     . "get_$type->{ATOM}_args "
-                     . " (node, $arg_idx, arg_$idx, e)");
-           }
-           $arg_idx++;
-       }
-
-       my ($sysmis_cond) = make_sysmis_decl ($op, "node->min_valid");
-       push (@decls, $sysmis_cond) if defined $sysmis_cond;
-
-       my (@args);
-       for my $arg (@{$op->{ARGS}}) {
-           push (@args, "arg_$arg->{NAME}");
-           if (defined $arg->{IDX}) {
-               my ($idx) = "arg_$arg->{IDX}";
-               $idx .= " / $arg->{TIMES}" if $arg->{TIMES} != 1;
-               push (@args, $idx);
-           }
-       }
-       for my $aux (@{$op->{AUX}}) {
-           my ($type) = $aux->{TYPE};
-           if ($type->{ROLE} eq 'leaf') {
-               my ($func) = "get_$type->{ATOM}_arg";
-               push (@args, "$func (node, $arg_idx)");
-               $arg_idx++;
-           } elsif ($type->{ROLE} eq 'fixed') {
-               push (@args, $type->{FIXED_VALUE});
-           } else {
-               die;
-           }
-       }
-
-       my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")";
-       if (@decls && defined ($sysmis_cond)) {
-           my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE};
-           push (@decls, c_type ($op->{RETURNS}) . "result = "
-                 . "force_sysmis ? $miss_ret : $result");
-           $result = "result";
-       }
-
-       print "case $opname:\n";
-       my ($alloc_func) = "expr_allocate_$op->{RETURNS}{NAME}";
-       if (@decls) {
-           print "  {\n";
-           print "    $_;\n" foreach @decls;
-           print "    return $alloc_func (e, $result);\n";
-           print "  }\n";
-       } else {
-           print "  return $alloc_func (e, $result);\n";
-       }
-       print "\n";
-    }
-}
diff --git a/src/language/expressions/optimize.incpl b/src/language/expressions/optimize.incpl
new file mode 100644 (file)
index 0000000..c441774
--- /dev/null
@@ -0,0 +1,85 @@
+use strict;
+use warnings 'all';
+
+do 'generate.pl';
+our (@order, %ops);
+
+sub generate_output {
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+
+       if (!$op->{OPTIMIZABLE} || $op->{UNIMPLEMENTED}) {
+           print "case $opname:\n";
+           print "  NOT_REACHED ();\n\n";
+           next;
+       }
+
+       my (@decls);
+       my ($arg_idx) = 0;
+       for my $arg (@{$op->{ARGS}}) {
+           my ($decl);
+           my ($name) = $arg->{NAME};
+           my ($type) = $arg->{TYPE};
+           my ($ctype) = c_type ($type);
+           my ($idx) = $arg->{IDX};
+           if (!defined ($idx)) {
+               my ($func) = "get_$type->{ATOM}_arg";
+               push (@decls, "${ctype}arg_$name = $func (node, $arg_idx)");
+           } else {
+               my ($decl) = "size_t arg_$idx = node->arg_cnt";
+               $decl .= " - $arg_idx" if $arg_idx;
+               push (@decls, $decl);
+
+               push (@decls, "${ctype}*arg_$name = "
+                     . "get_$type->{ATOM}_args "
+                     . " (node, $arg_idx, arg_$idx, e)");
+           }
+           $arg_idx++;
+       }
+
+       my ($sysmis_cond) = make_sysmis_decl ($op, "node->min_valid");
+       push (@decls, $sysmis_cond) if defined $sysmis_cond;
+
+       my (@args);
+       for my $arg (@{$op->{ARGS}}) {
+           push (@args, "arg_$arg->{NAME}");
+           if (defined $arg->{IDX}) {
+               my ($idx) = "arg_$arg->{IDX}";
+               $idx .= " / $arg->{TIMES}" if $arg->{TIMES} != 1;
+               push (@args, $idx);
+           }
+       }
+       for my $aux (@{$op->{AUX}}) {
+           my ($type) = $aux->{TYPE};
+           if ($type->{ROLE} eq 'leaf') {
+               my ($func) = "get_$type->{ATOM}_arg";
+               push (@args, "$func (node, $arg_idx)");
+               $arg_idx++;
+           } elsif ($type->{ROLE} eq 'fixed') {
+               push (@args, $type->{FIXED_VALUE});
+           } else {
+               die;
+           }
+       }
+
+       my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")";
+       if (@decls && defined ($sysmis_cond)) {
+           my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE};
+           push (@decls, c_type ($op->{RETURNS}) . "result = "
+                 . "force_sysmis ? $miss_ret : $result");
+           $result = "result";
+       }
+
+       print "case $opname:\n";
+       my ($alloc_func) = "expr_allocate_$op->{RETURNS}{NAME}";
+       if (@decls) {
+           print "  {\n";
+           print "    $_;\n" foreach @decls;
+           print "    return $alloc_func (e, $result);\n";
+           print "  }\n";
+       } else {
+           print "  return $alloc_func (e, $result);\n";
+       }
+       print "\n";
+    }
+}
diff --git a/src/language/expressions/parse.inc.pl b/src/language/expressions/parse.inc.pl
deleted file mode 100644 (file)
index 4e7243c..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-use strict;
-use warnings 'all';
-
-do 'generate.pl';
-our (@types, @order, %ops);
-
-sub generate_output {
-    my (@members) = ("\"\"", "\"\"", 0, 0, 0, "{}", 0, 0);
-    print "{", join (', ', @members), "},\n";
-
-    for my $type (@types) {
-       next if $type->{ROLE} eq 'fixed';
-
-       my ($human_name) = $type->{HUMAN_NAME};
-       $human_name = "" if !defined $human_name;
-       
-       my (@members) = ("\"$type->{NAME}\"", "\"\"",
-                        0, "OP_$type->{NAME}", 0, "{}", 0, 0);
-       print "{", join (', ', @members), "},\n";
-    }
-
-    for my $opname (@order) {
-       my ($op) = $ops{$opname};
-
-       my (@members);
-
-       push (@members, "\"$op->{NAME}\"");
-
-       if ($op->{CATEGORY} eq 'function') {
-           my (@args, @opt_args);
-           for my $arg (@{$op->{ARGS}}) {
-               push (@args, $arg->{TYPE}{HUMAN_NAME}) if !defined $arg->{IDX};
-           }
-
-           if (my ($array) = array_arg ($op)) {
-               if (!defined $op->{MIN_VALID}) {
-                   my (@array_args);
-                   for (my $i = 0; $i < $array->{TIMES}; $i++) {
-                       push (@array_args, $array->{TYPE}{HUMAN_NAME});
-                   }
-                   push (@args, @array_args);
-                   @opt_args = @array_args;
-               } else {
-                   for (my $i = 0; $i < $op->{MIN_VALID}; $i++) {
-                       push (@args, $array->{TYPE}{HUMAN_NAME});
-                   }
-                   push (@opt_args, $array->{TYPE}{HUMAN_NAME});
-               }
-           }
-           my ($human) = "$op->{NAME}(" . join (', ', @args);
-           $human .= '[, ' . join (', ', @opt_args) . ']...' if @opt_args;
-           $human .= ')';
-           push (@members, "\"$human\"");
-       } else {
-           push (@members, "NULL");
-       }
-
-       my (@flags);
-       push (@flags, "OPF_ABSORB_MISS") if defined $op->{ABSORB_MISS};
-       push (@flags, "OPF_ARRAY_OPERAND") if array_arg ($op);
-       push (@flags, "OPF_MIN_VALID") if defined $op->{MIN_VALID};
-       push (@flags, "OPF_NONOPTIMIZABLE") if !$op->{OPTIMIZABLE};
-       push (@flags, "OPF_EXTENSION") if $op->{EXTENSION};
-       push (@flags, "OPF_UNIMPLEMENTED") if $op->{UNIMPLEMENTED};
-       push (@flags, "OPF_PERM_ONLY") if $op->{PERM_ONLY};
-       push (@flags, "OPF_NO_ABBREV") if $op->{NO_ABBREV};
-       push (@members, @flags ? join (' | ', @flags) : 0);
-
-       push (@members, "OP_$op->{RETURNS}{NAME}");
-
-       push (@members, scalar (@{$op->{ARGS}}));
-
-       my (@arg_types) = map ("OP_$_->{TYPE}{NAME}", @{$op->{ARGS}});
-       push (@members, "{" . join (', ', @arg_types) . "}");
-
-       push (@members, $op->{MIN_VALID} || 0);
-
-       push (@members, array_arg ($op) ? ${array_arg ($op)}{TIMES} : 0);
-
-       print "{", join (', ', @members), "},\n";
-    }
-}
diff --git a/src/language/expressions/parse.incpl b/src/language/expressions/parse.incpl
new file mode 100644 (file)
index 0000000..4e7243c
--- /dev/null
@@ -0,0 +1,82 @@
+use strict;
+use warnings 'all';
+
+do 'generate.pl';
+our (@types, @order, %ops);
+
+sub generate_output {
+    my (@members) = ("\"\"", "\"\"", 0, 0, 0, "{}", 0, 0);
+    print "{", join (', ', @members), "},\n";
+
+    for my $type (@types) {
+       next if $type->{ROLE} eq 'fixed';
+
+       my ($human_name) = $type->{HUMAN_NAME};
+       $human_name = "" if !defined $human_name;
+       
+       my (@members) = ("\"$type->{NAME}\"", "\"\"",
+                        0, "OP_$type->{NAME}", 0, "{}", 0, 0);
+       print "{", join (', ', @members), "},\n";
+    }
+
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+
+       my (@members);
+
+       push (@members, "\"$op->{NAME}\"");
+
+       if ($op->{CATEGORY} eq 'function') {
+           my (@args, @opt_args);
+           for my $arg (@{$op->{ARGS}}) {
+               push (@args, $arg->{TYPE}{HUMAN_NAME}) if !defined $arg->{IDX};
+           }
+
+           if (my ($array) = array_arg ($op)) {
+               if (!defined $op->{MIN_VALID}) {
+                   my (@array_args);
+                   for (my $i = 0; $i < $array->{TIMES}; $i++) {
+                       push (@array_args, $array->{TYPE}{HUMAN_NAME});
+                   }
+                   push (@args, @array_args);
+                   @opt_args = @array_args;
+               } else {
+                   for (my $i = 0; $i < $op->{MIN_VALID}; $i++) {
+                       push (@args, $array->{TYPE}{HUMAN_NAME});
+                   }
+                   push (@opt_args, $array->{TYPE}{HUMAN_NAME});
+               }
+           }
+           my ($human) = "$op->{NAME}(" . join (', ', @args);
+           $human .= '[, ' . join (', ', @opt_args) . ']...' if @opt_args;
+           $human .= ')';
+           push (@members, "\"$human\"");
+       } else {
+           push (@members, "NULL");
+       }
+
+       my (@flags);
+       push (@flags, "OPF_ABSORB_MISS") if defined $op->{ABSORB_MISS};
+       push (@flags, "OPF_ARRAY_OPERAND") if array_arg ($op);
+       push (@flags, "OPF_MIN_VALID") if defined $op->{MIN_VALID};
+       push (@flags, "OPF_NONOPTIMIZABLE") if !$op->{OPTIMIZABLE};
+       push (@flags, "OPF_EXTENSION") if $op->{EXTENSION};
+       push (@flags, "OPF_UNIMPLEMENTED") if $op->{UNIMPLEMENTED};
+       push (@flags, "OPF_PERM_ONLY") if $op->{PERM_ONLY};
+       push (@flags, "OPF_NO_ABBREV") if $op->{NO_ABBREV};
+       push (@members, @flags ? join (' | ', @flags) : 0);
+
+       push (@members, "OP_$op->{RETURNS}{NAME}");
+
+       push (@members, scalar (@{$op->{ARGS}}));
+
+       my (@arg_types) = map ("OP_$_->{TYPE}{NAME}", @{$op->{ARGS}});
+       push (@members, "{" . join (', ', @arg_types) . "}");
+
+       push (@members, $op->{MIN_VALID} || 0);
+
+       push (@members, array_arg ($op) ? ${array_arg ($op)}{TIMES} : 0);
+
+       print "{", join (', ', @members), "},\n";
+    }
+}
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..84bcaba8950d2fc2f3ffc62236329c500f870af3 100644 (file)
@@ -1,10 +1,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..05e8bcf9440060a73537a7a2ccc4af8bd684ed37 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 = \
@@ -22,6 +22,7 @@ language_stats_sources = \
        src/language/stats/binomial.h \
        src/language/stats/chisquare.c \
        src/language/stats/chisquare.h \
+       src/language/stats/correlations.c \
        src/language/stats/descriptives.c \
        src/language/stats/npar.h \
        src/language/stats/sort-cases.c \
@@ -31,7 +32,12 @@ 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/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.c b/src/language/stats/correlations.c
new file mode 100644 (file)
index 0000000..605609a
--- /dev/null
@@ -0,0 +1,502 @@
+/* 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/assertion.h>
+#include <math/covariance.h>
+#include <math/correlation.h>
+#include <math/design-matrix.h>
+#include <gsl/gsl_matrix.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 <language/dictionary/split-file.h>
+#include <language/lexer/lexer.h>
+#include <language/lexer/variable-parser.h>
+#include <output/manager.h>
+#include <output/table.h>
+#include <libpspp/message.h>
+#include <data/format.h>
+#include <math/moments.h>
+
+#include <math.h>
+#include "xalloc.h"
+#include "minmax.h"
+#include <libpspp/misc.h>
+#include <gsl/gsl_cdf.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+struct corr
+{
+  size_t n_vars_total;
+  size_t n_vars1;
+
+  const struct variable **vars;
+};
+
+
+/* Handling of missing values. */
+enum corr_missing_type
+  {
+    CORR_PAIRWISE,       /* Handle missing values on a per-variable-pair basis. */
+    CORR_LISTWISE        /* Discard entire case if any variable is missing. */
+  };
+
+enum stats_opts
+  {
+    STATS_DESCRIPTIVES = 0x01,
+    STATS_XPROD = 0x02,
+    STATS_ALL = STATS_XPROD | STATS_DESCRIPTIVES
+  };
+
+struct corr_opts
+{
+  enum corr_missing_type missing_type;
+  enum mv_class exclude;      /* Classes of missing values to exclude. */
+
+  bool sig;   /* Flag significant values or not */
+  int tails;  /* Report significance with how many tails ? */
+  enum stats_opts statistics;
+
+  const struct variable *wv;  /* The weight variable (if any) */
+};
+
+
+static void
+output_descriptives (const struct corr *corr, const gsl_matrix *means,
+                    const gsl_matrix *vars, const gsl_matrix *ns)
+{
+  const int nr = corr->n_vars_total + 1;
+  const int nc = 4;
+  int c, r;
+
+  const int heading_columns = 1;
+  const int heading_rows = 1;
+
+  struct tab_table *t = tab_create (nc, nr, 0);
+  tab_title (t, _("Descriptive Statistics"));
+  tab_dim (t, tab_natural_dimensions, NULL);
+
+  tab_headers (t, heading_columns, 0, heading_rows, 0);
+
+  /* Outline the box */
+  tab_box (t,
+          TAL_2, TAL_2,
+          -1, -1,
+          0, 0,
+          nc - 1, nr - 1);
+
+  /* Vertical lines */
+  tab_box (t,
+          -1, -1,
+          -1, TAL_1,
+          heading_columns, 0,
+          nc - 1, nr - 1);
+
+  tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
+  tab_hline (t, TAL_1, 0, nc - 1, heading_rows);
+
+  tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Mean"));
+  tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
+  tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("N"));
+
+  for (r = 0 ; r < corr->n_vars_total ; ++r)
+    {
+      const struct variable *v = corr->vars[r];
+      tab_text (t, 0, r + heading_rows, TAB_LEFT | TAT_TITLE, var_to_string (v));
+
+      for (c = 1 ; c < nc ; ++c)
+       {
+         double x ;
+         double n;
+         switch (c)
+           {
+           case 1:
+             x = gsl_matrix_get (means, r, 0);
+             break;
+           case 2:
+             x = gsl_matrix_get (vars, r, 0);
+
+             /* Here we want to display the non-biased estimator */
+             n = gsl_matrix_get (ns, r, 0);
+             x *= n / (n -1);
+
+             x = sqrt (x);
+             break;
+           case 3:
+             x = gsl_matrix_get (ns, r, 0);
+             break;
+           default: 
+             NOT_REACHED ();
+           };
+         
+         tab_double (t, c, r + heading_rows, 0, x, NULL);
+       }
+    }
+
+  tab_submit (t);
+}
+
+static void
+output_correlation (const struct corr *corr, const struct corr_opts *opts,
+                   const gsl_matrix *cm, const gsl_matrix *samples,
+                   const gsl_matrix *cv)
+{
+  int r, c;
+  struct tab_table *t;
+  int matrix_cols;
+  int nr = corr->n_vars1;
+  int nc = matrix_cols = corr->n_vars_total > corr->n_vars1 ?
+    corr->n_vars_total - corr->n_vars1 : corr->n_vars1;
+
+  const struct fmt_spec *wfmt = opts->wv ? var_get_print_format (opts->wv) : & F_8_0;
+
+  const int heading_columns = 2;
+  const int heading_rows = 1;
+
+  int rows_per_variable = opts->missing_type == CORR_LISTWISE ? 2 : 3;
+
+  if (opts->statistics & STATS_XPROD)
+    rows_per_variable += 2;
+
+  /* Two header columns */
+  nc += heading_columns;
+
+  /* Three data per variable */
+  nr *= rows_per_variable;
+
+  /* One header row */
+  nr += heading_rows;
+
+  t = tab_create (nc, nr, 0);
+  tab_title (t, _("Correlations"));
+  tab_dim (t, tab_natural_dimensions, NULL);
+
+  tab_headers (t, heading_columns, 0, heading_rows, 0);
+
+  /* Outline the box */
+  tab_box (t,
+          TAL_2, TAL_2,
+          -1, -1,
+          0, 0,
+          nc - 1, nr - 1);
+
+  /* Vertical lines */
+  tab_box (t,
+          -1, -1,
+          -1, TAL_1,
+          heading_columns, 0,
+          nc - 1, nr - 1);
+
+  tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
+  tab_vline (t, TAL_1, 1, heading_rows, nr - 1);
+
+  for (r = 0 ; r < corr->n_vars1 ; ++r)
+    {
+      tab_text (t, 0, 1 + r * rows_per_variable, TAB_LEFT | TAT_TITLE, 
+               var_to_string (corr->vars[r]));
+
+      tab_text (t, 1, 1 + r * rows_per_variable, TAB_LEFT | TAT_TITLE, _("Pearson Correlation"));
+      tab_text (t, 1, 2 + r * rows_per_variable, TAB_LEFT | TAT_TITLE, 
+               (opts->tails == 2) ? _("Sig. (2-tailed)") : _("Sig. (1-tailed)"));
+
+      if (opts->statistics & STATS_XPROD)
+       {
+         tab_text (t, 1, 3 + r * rows_per_variable, TAB_LEFT | TAT_TITLE, _("Cross-products"));
+         tab_text (t, 1, 4 + r * rows_per_variable, TAB_LEFT | TAT_TITLE, _("Covariance"));
+       }
+
+      if ( opts->missing_type != CORR_LISTWISE )
+       tab_text (t, 1, rows_per_variable + r * rows_per_variable, TAB_LEFT | TAT_TITLE, _("N"));
+
+      tab_hline (t, TAL_1, 0, nc - 1, r * rows_per_variable + 1);
+    }
+
+  for (c = 0 ; c < matrix_cols ; ++c)
+    {
+      const struct variable *v = corr->n_vars_total > corr->n_vars1 ? corr->vars[corr->n_vars_total - corr->n_vars1 + c] : corr->vars[c];
+      tab_text (t, heading_columns + c, 0, TAB_LEFT | TAT_TITLE, var_to_string (v));      
+    }
+
+  for (r = 0 ; r < corr->n_vars1 ; ++r)
+    {
+      const int row = r * rows_per_variable + heading_rows;
+      for (c = 0 ; c < matrix_cols ; ++c)
+       {
+         unsigned char flags = 0; 
+         const int col_index = corr->n_vars_total - corr->n_vars1 + c;
+         double pearson = gsl_matrix_get (cm, r, col_index);
+         double w = gsl_matrix_get (samples, r, col_index);
+         double sig = opts->tails * significance_of_correlation (pearson, w);
+
+         if ( opts->missing_type != CORR_LISTWISE )
+           tab_double (t, c + heading_columns, row + rows_per_variable - 1, 0, w, wfmt);
+
+         if ( c != r)
+           tab_double (t, c + heading_columns, row + 1, 0,  sig, NULL);
+
+         if ( opts->sig && c != r && sig < 0.05)
+           flags = TAB_EMPH;
+         
+         tab_double (t, c + heading_columns, row, flags, pearson, NULL);
+
+         if (opts->statistics & STATS_XPROD)
+           {
+             double cov = gsl_matrix_get (cv, r, col_index);
+             const double xprod_dev = cov * w;
+             cov *= w / (w - 1.0);
+
+             tab_double (t, c + heading_columns, row + 2, 0, xprod_dev, NULL);
+             tab_double (t, c + heading_columns, row + 3, 0, cov, NULL);
+           }
+       }
+    }
+
+  tab_submit (t);
+}
+
+
+static void
+run_corr (struct casereader *r, const struct corr_opts *opts, const struct corr *corr)
+{
+  struct ccase *c;
+  const gsl_matrix *var_matrix,  *samples_matrix, *mean_matrix;
+  const gsl_matrix *cov_matrix;
+  gsl_matrix *corr_matrix;
+  struct covariance *cov = covariance_create (corr->n_vars_total, corr->vars,
+                                             opts->wv, opts->exclude);
+
+  for ( ; (c = casereader_read (r) ); case_unref (c))
+    {
+      covariance_accumulate (cov, c);
+    }
+
+  cov_matrix = covariance_calculate (cov);
+
+  samples_matrix = covariance_moments (cov, MOMENT_NONE);
+  var_matrix = covariance_moments (cov, MOMENT_VARIANCE);
+  mean_matrix = covariance_moments (cov, MOMENT_MEAN);
+
+  corr_matrix = correlation_from_covariance (cov_matrix, var_matrix);
+
+  if ( opts->statistics & STATS_DESCRIPTIVES) 
+    output_descriptives (corr, mean_matrix, var_matrix, samples_matrix);
+
+  output_correlation (corr, opts,
+                     corr_matrix,
+                     samples_matrix,
+                     cov_matrix);
+
+  covariance_destroy (cov);
+  gsl_matrix_free (corr_matrix);
+}
+
+int
+cmd_correlation (struct lexer *lexer, struct dataset *ds)
+{
+  int i;
+  int n_all_vars = 0; /* Total number of variables involved in this command */
+  const struct variable **all_vars ;
+  const struct dictionary *dict = dataset_dict (ds);
+  bool ok = true;
+
+  struct casegrouper *grouper;
+  struct casereader *group;
+
+  struct corr *corr = NULL;
+  size_t n_corrs = 0;
+
+  struct corr_opts opts;
+  opts.missing_type = CORR_PAIRWISE;
+  opts.wv = dict_get_weight (dict);
+  opts.tails = 2;
+  opts.sig = false;
+  opts.exclude = MV_ANY;
+  opts.statistics = 0;
+
+  /* Parse CORRELATIONS. */
+  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, "PAIRWISE"))
+                opts.missing_type = CORR_PAIRWISE;
+              else if (lex_match_id (lexer, "LISTWISE"))
+                opts.missing_type = CORR_LISTWISE;
+
+              else if (lex_match_id (lexer, "INCLUDE"))
+                opts.exclude = MV_SYSTEM;
+              else if (lex_match_id (lexer, "EXCLUDE"))
+               opts.exclude = MV_ANY;
+              else
+                {
+                  lex_error (lexer, NULL);
+                  goto error;
+                }
+              lex_match (lexer, ',');
+            }
+        }
+      else if (lex_match_id (lexer, "PRINT"))
+       {
+          lex_match (lexer, '=');
+          while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+           {
+             if ( lex_match_id (lexer, "TWOTAIL"))
+               opts.tails = 2;
+             else if (lex_match_id (lexer, "ONETAIL"))
+               opts.tails = 1;
+             else if (lex_match_id (lexer, "SIG"))
+               opts.sig = false;
+             else if (lex_match_id (lexer, "NOSIG"))
+               opts.sig = true;
+             else
+               {
+                 lex_error (lexer, NULL);
+                 goto error;
+               }
+
+              lex_match (lexer, ',');
+           }
+       }
+      else if (lex_match_id (lexer, "STATISTICS"))
+       {
+         lex_match (lexer, '=');
+          while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
+           {
+             if ( lex_match_id (lexer, "DESCRIPTIVES"))
+               opts.statistics = STATS_DESCRIPTIVES;
+             else if (lex_match_id (lexer, "XPROD"))
+               opts.statistics = STATS_XPROD;
+             else if (lex_token (lexer) == T_ALL)
+               {
+                 opts.statistics = STATS_ALL;
+                 lex_get (lexer);
+               }
+             else 
+               {
+                 lex_error (lexer, NULL);
+                 goto error;
+               }
+
+              lex_match (lexer, ',');
+           }
+       }
+      else
+       {
+         if (lex_match_id (lexer, "VARIABLES"))
+           {
+             lex_match (lexer, '=');
+           }
+
+         corr = xrealloc (corr, sizeof (*corr) * (n_corrs + 1));
+         corr[n_corrs].n_vars_total = corr[n_corrs].n_vars1 = 0;
+      
+         if ( ! parse_variables_const (lexer, dict, &corr[n_corrs].vars, 
+                                       &corr[n_corrs].n_vars_total,
+                                       PV_NUMERIC))
+           {
+             ok = false;
+             break;
+           }
+
+
+         corr[n_corrs].n_vars1 = corr[n_corrs].n_vars_total;
+
+         if ( lex_match (lexer, T_WITH))
+           {
+             if ( ! parse_variables_const (lexer, dict,
+                                           &corr[n_corrs].vars, &corr[n_corrs].n_vars_total,
+                                           PV_NUMERIC | PV_APPEND))
+               {
+                 ok = false;
+                 break;
+               }
+           }
+
+         n_all_vars += corr[n_corrs].n_vars_total;
+
+         n_corrs++;
+       }
+    }
+
+  if (n_corrs == 0)
+    {
+      msg (SE, _("No variables specified."));
+      goto error;
+    }
+
+
+  all_vars = xmalloc (sizeof (*all_vars) * n_all_vars);
+
+  {
+    /* FIXME:  Using a hash here would make more sense */
+    const struct variable **vv = all_vars;
+
+    for (i = 0 ; i < n_corrs; ++i)
+      {
+       int v;
+       const struct corr *c = &corr[i];
+       for (v = 0 ; v < c->n_vars_total; ++v)
+         *vv++ = c->vars[v];
+      }
+  }
+
+  grouper = casegrouper_create_splits (proc_open (ds), dict);
+
+  while (casegrouper_get_next_group (grouper, &group))
+    {
+      for (i = 0 ; i < n_corrs; ++i)
+       {
+         /* FIXME: No need to iterate the data multiple times */
+         struct casereader *r = casereader_clone (group);
+
+         if ( opts.missing_type == CORR_LISTWISE)
+           r = casereader_create_filter_missing (r, all_vars, n_all_vars,
+                                                 opts.exclude, NULL, NULL);
+
+
+         run_corr (r, &opts,  &corr[i]);
+         casereader_destroy (r);
+       }
+      casereader_destroy (group);
+    }
+
+  ok = casegrouper_destroy (grouper);
+  ok = proc_commit (ds) && ok;
+
+  free (all_vars);
+
+
+  /* Done. */
+  free (corr);
+  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
+
+ error:
+  free (corr);
+  return CMD_FAILURE;
+}
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 60194ad7badbd64008b827dc2dfa44787c59b216..99fa41d9c056a039ef9d92fdc9090758539c9d15 100644 (file)
@@ -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,199 +175,198 @@ get_var_range (const struct variable *v)
   return var_get_aux (v);
 }
 
-static int
-get_var_trimmed_width (const struct variable *v)
-{
-  int width = var_get_width (v);
-  return MIN (width, MAX_SHORT_STRING);
-}
-
-/* Indexes into crosstab.v. */
-enum
-  {
-    ROW_VAR = 0,
-    COL_VAR = 1
-  };
-
-/* 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
+struct crosstabs_proc
   {
-    INTEGER,
-    GENERAL
+    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. */
   };
-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;
-
-      precalc (group, ds);
+      struct ccase *c;
 
-      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;
@@ -335,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")
@@ -352,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);
@@ -363,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]))
         {
@@ -385,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;
@@ -452,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) != '(')
@@ -495,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) == '/')
@@ -511,357 +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)
+  HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
     {
-      gen_tab = hsh_create (512, compare_table_entry, hash_table_entry,
-                           NULL, NULL);
-    }
-  else
-    {
-      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;
+      for (j = 0; j < pt->n_vars; j++)
+        if ((int) case_num (c, pt->vars[j]) != (int) te->values[j].f)
+          goto no_match;
 
-             {
-                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 (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);
-
-  /* Case weight. */
-  double weight = dict_get_case_weight (dataset_dict (ds), c, NULL);
+  struct table_entry *te;
+  size_t hash;
+  int j;
 
-  /* 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 = get_var_trimmed_width (x->vars[j]);
-               memcpy (te->values[j].s, case_str (c, x->vars[j]), n);
-
-               /* Necessary in order to simplify comparisons. */
-               memset (&te->values[j].s[n], 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
-        {
-          int width = get_var_trimmed_width (x->vars[i]);
-          const int diffstr = strncmp (a->values[i].s, b->values[i].s, width);
-          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));
+      const struct table_entry *a = pt->entries[row];
+      const struct table_entry *b = pt->entries[row - 1];
+      int col;
 
-  /* 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;
-
-       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"));
@@ -872,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"));
-      }
+  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);
 
-      tab_hline (table, TAL_1, 0, nvar + n_cols - 1, 2);
-      tab_vline (table, TAL_1, nvar + n_cols - 1, 0, 1);
-
-      /* 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;
 
@@ -1517,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. */
@@ -1571,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 = get_var_trimmed_width (v);
-      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);
     }
 }
 
@@ -1714,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;
@@ -1727,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 == ' ')
     {
@@ -1741,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
@@ -1761,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--;
@@ -1786,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"),
@@ -1999,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);
        }
@@ -2032,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"),
@@ -2076,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++)
     {
@@ -2105,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]),
-                    get_var_trimmed_width (x->vars[COL_VAR]), c[0].s,
-                    get_var_trimmed_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]),
-                    get_var_trimmed_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[] =
     {
@@ -2243,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++)
     {
@@ -2270,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);
                }
            }
       }
@@ -2295,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;
 }
@@ -2333,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);
@@ -2348,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;
@@ -2369,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;
@@ -2398,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;
 
@@ -2406,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)
@@ -2416,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
@@ -2442,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;
@@ -2465,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;
@@ -2519,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. */
       {
@@ -2608,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
@@ -2655,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;
                  }
 
@@ -2683,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.;
@@ -2768,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);
          }
       }
 
@@ -2785,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
@@ -2862,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;
@@ -2874,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)
@@ -2891,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);
@@ -2922,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])
 {
   {
@@ -2933,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;
              }
 
@@ -2962,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;
              }
 
@@ -2979,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. */
@@ -3063,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);
@@ -3086,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);
       }
@@ -3212,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);
       }
@@ -3238,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..6b57fd297d8475ad63bcecc320a8c0c1b2d9201f 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,13 +100,13 @@ struct dsc_statistic_info
 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
   {
     {"MEAN", N_("Mean"), MOMENT_MEAN},
-    {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
+    {"SEMEAN", N_("S.E. Mean"), MOMENT_VARIANCE},
     {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
     {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
     {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
-    {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
+    {"SEKURTOSIS", N_("S.E. Kurt"), MOMENT_NONE},
     {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
-    {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
+    {"SESKEWNESS", N_("S.E. Skew"), MOMENT_NONE},
     {"RANGE", N_("Range"), MOMENT_NONE},
     {"MINIMUM", N_("Minimum"), MOMENT_NONE},
     {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
@@ -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 592bb56b13fa18d02279a6c50082f448e51849d5..08077942340234031939cded549e87886d5119ee 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,1288 +1316,883 @@ 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);
+             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);
+           }
 
-                 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)
-                           );
+         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;
 
-                 ds_destroy (&vstr);
+             while (weight-- > 0 && e < cmd.st_n)
+               {
+                 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;
                }
 
-             prev = (*fs)->id[0];
+             min_ll = ll_next (min_ll);
+           }
 
-             if (fctr->indep_var[1] && count > 0 )
-               tab_hline (tbl, TAL_1, 2, n_cols - 1, row);
+         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;
 
-             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 + 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;
                }
 
-             populate_descriptives (tbl, heading_columns - 2,
-                                    row,
-                                    dependent_var[i],
-                                    & (*fs)->m[i]);
-
-             count++ ;
-             fs++;
+             max_ll = ll_next (max_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 ();
-      if (ch == NULL)
-        break;
-
-      ds_init_empty (&str);
-      factor_to_string (fctr, *fs, 0, &str );
-
-      chart_write_title (ch, "%s", 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 ();
-      if (ch == NULL)
-        break;
-
-      boxplot_draw_yscale (ch, totals[i].max, totals[i].min);
-
-      if ( fctr )
-       {
-         int n_factors = 0;
-         int f=0;
-         for ( fs = fctr->fs ; *fs ; ++fs )
-           ++n_factors;
-
-         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..c8cb03b92d7f75aff63dc5fe3ca549d8a4d2b365 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,7 +40,8 @@
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 #include <libpspp/str.h>
-
+#include <data/data-in.h>
+#include <data/data-out.h>
 #include "intprops.h"
 #include "minmax.h"
 #include "xalloc.h"
 #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 dictionary *dict;     /* Dictionary of the 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 +80,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,54 +99,51 @@ 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;
+  flip->dict = dict;
 
   lex_match (lexer, '/');
   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 +151,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
+            {
+              name = data_out_pool (value, dict_get_encoding (flip->dict), var_get_write_format (flip->new_names_var),
+                flip->pool);
+       
+            }
+          var_names_add (flip->pool, &flip->new_names, name);
+        }
+      case_unref (c);
     }
   ok = casereader_destroy (input);
   ok = proc_commit (ds) && ok;
@@ -186,15 +204,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%03zu", 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 +236,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 +249,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 +263,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);
+          strncpy (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 +290,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 +319,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 +335,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 +350,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 +397,31 @@ 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));
+  data_in (ss_cstr (flip->old_names.names[flip->cases_read]), dict_get_encoding (flip->dict), 
+       FMT_A, 0,
+       0, 0,
+       flip->dict, 
+       case_data_rw_idx (c, 0), 8);
+       
+  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 +430,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 +459,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..5a704d0d019f1f0d723aac1c8b81b455f02c0f7a 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,34 +1030,18 @@ 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;
+  int r, x;
   double cum_total = 0.0;
   double cum_freq = 0.0;
 
-  struct init
-    {
-      int c, r;
-      const char *s;
-    };
-
-  const struct init *p;
-
-  static const struct init vec[] =
-  {
-    {4, 0, N_("Valid")},
-    {5, 0, N_("Cum")},
-    {1, 1, N_("Value")},
-    {2, 1, N_("Frequency")},
-    {3, 1, N_("Percent")},
-    {4, 1, N_("Percent")},
-    {5, 1, N_("Percent")},
-    {0, 0, NULL},
-    {1, 0, NULL},
-    {2, 0, NULL},
-    {3, 0, NULL},
-    {-1, -1, NULL},
+  static const char *headings[] = {
+    N_("Value"),
+    N_("Frequency"),
+    N_("Percent"),
+    N_("Valid Percent"),
+    N_("Cum Percent")
   };
 
   const bool lab = (cmd.labels == FRQ_LABELS);
@@ -1077,18 +1049,17 @@ dump_full (const struct variable *v, const struct variable *wv)
   vf = get_var_freqs (v);
   ft = &vf->tab;
   n_categories = ft->n_valid + ft->n_missing;
-  t = tab_create (5 + lab, n_categories + 3, 0);
-  tab_headers (t, 0, 0, 2, 0);
-  tab_dim (t, full_dim);
+  t = tab_create (5 + lab, n_categories + 2, 0);
+  tab_headers (t, 0, 0, 1, 0);
+  tab_dim (t, full_dim, NULL);
 
   if (lab)
-    tab_text (t, 0, 1, TAB_CENTER | TAT_TITLE, _("Value Label"));
+    tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Value Label"));
 
-  for (p = vec; p->s; p++)
-    tab_text (t, lab ? p->c : p->c - 1, p->r,
-                 TAB_CENTER | TAT_TITLE, gettext (p->s));
+  for (x = 0; x < 5; x++)
+    tab_text (t, lab + x, 0, TAB_CENTER | TAT_TITLE, gettext (headings[x]));
 
-  r = 2;
+  r = 1;
   for (f = ft->valid; f < ft->missing; f++)
     {
       double percent, valid_percent;
@@ -1101,12 +1072,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 +1090,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);
@@ -1135,7 +1106,7 @@ dump_full (const struct variable *v, const struct variable *wv)
   tab_box (t, TAL_1, TAL_1,
           cmd.spaces == FRQ_SINGLE ? -1 : TAL_GAP, TAL_1,
           0, 0, 4 + lab, r);
-  tab_hline (t, TAL_2, 0, 4 + lab, 2);
+  tab_hline (t, TAL_2, 0, 4 + lab, 1);
   tab_hline (t, TAL_2, 0, 4 + lab, r);
   tab_joint_text (t, 0, r, 0 + lab, r, TAB_RIGHT | TAT_TITLE, _("Total"));
   tab_vline (t, TAL_0, 1, r, r);
@@ -1150,7 +1121,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 +1145,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 +1161,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 +1171,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 +1179,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 +1205,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 +1241,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 +1251,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 +1262,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 +1305,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 +1318,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 +1360,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 +1413,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 +1473,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..fd36041636e5ad25d619ceffee2ea42824b7aa8f 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>
 #include "xalloc.h"
 #include "gettext.h"
 
-#define GLM_LARGE_DATA 1000
-
 /* (headers) */
 
 /* (specification)
    "GLM" (glm_):
    *dependent=custom;
+   design=custom;
    by=varlist;
    with=varlist.
 */
@@ -61,6 +61,7 @@
 /* (functions) */
 static struct cmd_glm cmd;
 
+
 /*
   Moments for each of the variables used.
  */
@@ -84,47 +85,143 @@ static const struct variable **v_dependent;
  */
 static size_t n_dependent;
 
-#if 0
-/*
-  Return value for the procedure.
- */
-static int pspp_glm_rc = CMD_SUCCESS;
-#else
+size_t n_inter; /* Number of interactions. */
+size_t n_members; /* Number of memebr variables in an interaction. */ 
+
+struct interaction_variable **interactions;
+
 int cmd_glm (struct lexer *lexer, struct dataset *ds);
-#endif
 
 static bool run_glm (struct casereader *,
                     struct cmd_glm *,
-                    const struct dataset *, pspp_linreg_cache *);
-
+                    const struct dataset *);
+/*
+  If the DESIGN subcommand was not specified, specify all possible
+  two-way interactions.
+ */
+static void
+check_interactions (struct dataset *ds, struct cmd_glm *cmd)
+{
+  size_t i;
+  size_t j;
+  size_t k = 0;
+  struct variable **interaction_vars;
+
+  /* 
+     User did not specify the design matrix, so we 
+     specify it here.
+  */
+  n_inter = (cmd->n_by + cmd->n_with) * (cmd->n_by + cmd->n_with - 1) / 2;
+  interactions = xnmalloc (n_inter, sizeof (*interactions));
+  interaction_vars = xnmalloc (2, sizeof (*interaction_vars));
+  for (i = 0; i < cmd->n_by; i++)
+    {
+      for (j = i + 1; j < cmd->n_by; j++)
+       {
+         interaction_vars[0] = cmd->v_by[i];
+         interaction_vars[1] = cmd->v_by[j];
+         interactions[k] = interaction_variable_create (interaction_vars, 2);
+         k++;
+       }
+      for (j = 0; j < cmd->n_with; j++)
+       {
+         interaction_vars[0] = cmd->v_by[i];
+         interaction_vars[1] = cmd->v_with[j];
+         interactions[k] = interaction_variable_create (interaction_vars, 2);
+         k++;
+       }
+    }
+  for (i = 0; i < cmd->n_with; i++)
+    {
+      for (j = i + 1; j < cmd->n_with; j++)
+       {
+         interaction_vars[0] = cmd->v_with[i];
+         interaction_vars[1] = cmd->v_with[j];
+         interactions[k] = interaction_variable_create (interaction_vars, 2);
+         k++;
+       }
+    }
+}
 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;
 
-  /* Data pass. */
+  if (!lex_match_id (lexer, "DESIGN"))
+    {
+      check_interactions (ds, &cmd);
+    }
+   /* Data pass. */
   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;
 }
 
+static int
+parse_interactions (struct lexer *lexer, const struct variable **interaction_vars, int n_members,
+                   int max_members, struct dataset *ds)
+{
+  if (lex_match (lexer, '*'))
+    {
+      if (n_members > max_members)
+       {
+         max_members *= 2;
+         xnrealloc (interaction_vars, max_members, sizeof (*interaction_vars));
+       }
+      interaction_vars[n_members] = parse_variable (lexer, dataset_dict (ds));
+      parse_interactions (lexer, interaction_vars, n_members++, max_members, ds);
+    }
+  return n_members;
+}
+/* Parser for the design subcommand. */
+static int
+glm_custom_design (struct lexer *lexer, struct dataset *ds,
+                  struct cmd_glm *cmd UNUSED, void *aux UNUSED)
+{
+  size_t n_allocated = 2;
+  size_t n_members;
+  struct variable **interaction_vars;
+  struct variable *this_var;
+
+  interactions = xnmalloc (n_allocated, sizeof (*interactions));
+  n_inter = 1;
+  while (lex_token (lexer) != T_STOP && lex_token (lexer) != '.')
+    {
+      this_var = parse_variable (lexer, dataset_dict (ds));
+      if (lex_match (lexer, '('))
+       {
+         lex_force_match (lexer, ')');
+       }
+      else if (lex_match (lexer, '*'))
+       {
+         interaction_vars = xnmalloc (2 * n_inter, sizeof (*interaction_vars));
+         n_members = parse_interactions (lexer, interaction_vars, 1, 2 * n_inter, ds);
+         if (n_allocated < n_inter)
+           {
+             n_allocated *= 2;
+             xnrealloc (interactions, n_allocated, sizeof (*interactions));
+           }
+         interactions [n_inter - 1] = 
+           interaction_variable_create (interaction_vars, n_members);
+         n_inter++;
+         free (interaction_vars);
+       }
+    }
+  return 1;
+}
 /* Parser for the dependent sub command */
 static int
 glm_custom_dependent (struct lexer *lexer, struct dataset *ds,
@@ -151,131 +248,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 +308,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 +325,30 @@ 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]);
+      
       reader = casereader_create_counter (reader, &row, -1);
-      for (; casereader_read (reader, &c); case_destroy (&c))
+
+      for (i = 0; i < n_inter; i++)
+      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);
-               }
-           }
+         */
+         n_data++;
        }
       casereader_destroy (reader);
-      for (i = 0; i < n_all_vars; i++)
-       {
-         moments1_destroy (mom[i]->m);
-         free (mom[i]->mean);
-         free (mom[i]->variance);
-         free (mom[i]->weight);
-         free (mom[i]);
-       }
-      free (mom);
-      covariance_matrix_destroy (X);
     }
   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 32ac1815ea95c42d471ba3a4c280469a14a36113..41a23f3e2580ebd9a9aac6ff289b89803c6f8da2 100644 (file)
@@ -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);
 
@@ -974,7 +947,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.
@@ -988,18 +962,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..86aee1f
--- /dev/null
@@ -0,0 +1,1230 @@
+/* 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;
+    }
+
+  value_init (&roc.state_value, var_get_width (roc.state_var));
+  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;
+
+  value_destroy (&roc.state_value, var_get_width (roc.state_var));
+  free (roc.vars);
+  return CMD_SUCCESS;
+
+ error:
+  value_destroy (&roc.state_value, var_get_width (roc.state_var));
+  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 ba7e9388a95274560589d30605ce2be3e0720718..8f23cefd4afa54c8be6cfebe7031128a913e3616 100644 (file)
@@ -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>
 #include <libpspp/taint.h>
 #include <math/group-proc.h>
 #include <math/levene.h>
+#include <math/correlation.h>
 #include <output/manager.h>
 #include <output/table.h>
 #include <data/format.h>
 
+#include "minmax.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, _("S.E. 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, _("S.E. 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, _("S.E. 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,893 +784,732 @@ 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;
-
-      /* corr2 will mathematically always be in the range [0, 1.0].  Inaccurate
-         calculations sometimes cause it to be slightly greater than 1.0, so
-         force it into the correct range to avoid NaN from sqrt(). */
-      double corr2 = MIN (1.0, pow2 (pairs[i].correlation));
-      double correlation_t =
-       pairs[i].correlation * sqrt (df) /
-       sqrt (1 - corr2);
-
+      struct pair *pair = &proc->pairs[i];
 
       /* 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 * significance_of_correlation (pair->correlation, pair->n), 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);
 }
@@ -1954,40 +1519,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..5c2b571ec57a7df2a7f9203abe2d23d244e083c9 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";
@@ -222,7 +224,7 @@ cmd_set (struct lexer *lexer, struct dataset *ds)
     msg (SW, _("%s is obsolete."), "TB1");
 
   if (cmd.sbc_case)
-    msg (SW, _("%s is not implemented."), "CASE");
+    msg (SW, _("%s is not yet implemented."), "CASE");
 
   if (cmd.sbc_compression)
     msg (SW, _("Active file compression is not implemented."));
@@ -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..6e6d83f
--- /dev/null
@@ -0,0 +1,59 @@
+/* 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>
+#include <stddef.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..746756d8900ba0207e45ae77718ed8dbab8dda91 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,18 +79,22 @@ 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_FOR_PERL)';" >> $@
+       echo "*/" >> $@
        echo "#include \"version.h\"" >> $@
-       echo "const char bare_version[] = \"@VERSION@\";" >> $@
-       echo "const char version[] = \"GNU @PACKAGE@ @VERSION@\";" >> $@
-       echo "const char stat_version[] = \"GNU @PACKAGE@ @VERSION@ \
+       echo "const char bare_version[] = \"$(VERSION)\";" >> $@
+       echo "const char version[] = \"GNU $(PACKAGE) $(VERSION)\";" >> $@
+       echo "const char stat_version[] = \"GNU $(PACKAGE) $(VERSION) \
 (`date`).\";" >> $@
        echo "const char host_system[] = \"$(host_triplet)\";" >> $@
        echo "const char build_system[] = \"$(build_triplet)\";" >> $@
index 563cc59b1e85b8a6d7d44bdba3e9414e4d17603d..d22819446f5e5ee7a5eececf9c4f0e3ef261e758 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
@@ -45,7 +45,7 @@
    non-`NULL' pointer it returns cannot alias any other pointer
    valid when the function returns. */
 #if __GNUC__ > 2
-#define MALLOC_LIKE ATTRIBUTE ((malloc))
+#define MALLOC_LIKE ATTRIBUTE ((__malloc__))
 #else
 #define MALLOC_LIKE
 #endif
index 1214b95ca1f3da178f6b34bac1af8893e0b5d3c3..302cd9137ddbc7b748ae07c9c4c34103ffecbf86 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
    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 <stdlib.h>
 #include <config.h>
 
+#include <stdlib.h>
+
 #include "getl.h"
 
 #include <libpspp/str.h>
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..80a0ed35dd57a388f622146482e56fddca9b7211 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))
+     as the converters have not yet been set up */
+  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 converter 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..a17919e405a92e81c56cd3828f4f5987c7af5082 100644 (file)
@@ -2,32 +2,39 @@
 
 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.c \
+       src/math/covariance.h \
        src/math/covariance-matrix.c \
        src/math/covariance-matrix.h \
+       src/math/correlation.c \
+       src/math/correlation.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++;
                }
diff --git a/src/math/correlation.c b/src/math/correlation.c
new file mode 100644 (file)
index 0000000..4776274
--- /dev/null
@@ -0,0 +1,68 @@
+/* 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 "correlation.h"
+
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_cdf.h>
+#include <math.h>
+#include <libpspp/misc.h>
+#include "minmax.h"
+
+
+double
+significance_of_correlation (double rho, double w)
+{
+  double t = w - 2;
+
+  /* |rho| will mathematically always be in the range [0, 1.0].  Inaccurate
+     calculations sometimes cause it to be slightly greater than 1.0, so
+     force it into the correct range to avoid NaN from sqrt(). */
+  t /= 1 - MIN (1, pow2 (rho));
+
+  t = sqrt (t);
+  t *= rho;
+  
+  if (t > 0)
+    return  gsl_cdf_tdist_Q (t, w - 2);
+  else
+    return  gsl_cdf_tdist_P (t, w - 2);
+}
+
+gsl_matrix *
+correlation_from_covariance (const gsl_matrix *cv, const gsl_matrix *v)
+{
+  size_t i, j;
+  gsl_matrix *corr = gsl_matrix_calloc (cv->size1, cv->size2);
+  
+  for (i = 0 ; i < cv->size1; ++i)
+    {
+      for (j = 0 ; j < cv->size2; ++j)
+       {
+         double rho = gsl_matrix_get (cv, i, j);
+         
+         rho /= sqrt (gsl_matrix_get (v, i, j))
+           * 
+           sqrt (gsl_matrix_get (v, j, i));
+         
+         gsl_matrix_set (corr, i, j, rho);
+       }
+    }
+  
+  return corr;
+}
diff --git a/src/math/correlation.h b/src/math/correlation.h
new file mode 100644 (file)
index 0000000..27621c4
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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 SRC_MATH_CORRELATION_H
+#define SRC_MATH_CORRELATION_H
+
+#include <gsl/gsl_matrix.h>
+
+gsl_matrix * correlation_from_covariance (const gsl_matrix *cv, const gsl_matrix *v);
+
+double significance_of_correlation (double rho, double w);
+
+#endif
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
diff --git a/src/math/covariance.c b/src/math/covariance.c
new file mode 100644 (file)
index 0000000..ba0de0b
--- /dev/null
@@ -0,0 +1,267 @@
+/* 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 "covariance.h"
+#include <gl/xalloc.h>
+#include "moments.h"
+#include <gsl/gsl_matrix.h>
+#include <data/case.h>
+#include <data/variable.h>
+#include <libpspp/misc.h>
+
+#define n_MOMENTS (MOMENT_VARIANCE + 1)
+
+
+struct covariance
+{
+  /* The variables for which the covariance matrix is to be calculated */
+  size_t n_vars;
+  const struct variable **vars;
+  
+  /* The weight variable (or NULL if none) */
+  const struct variable *wv;
+
+  /* A set of matrices containing the 0th, 1st and 2nd moments */
+  gsl_matrix **moments;
+
+  /* The class of missing values to exclude */
+  enum mv_class exclude;
+
+  /* An array of doubles representing the covariance matrix.
+     Only the top triangle is included, and no diagonals */
+  double *cm;
+  int n_cm;
+};
+
+
+
+/* Return a matrix containing the M th moments.
+   The matrix is of size  NxN where N is the number of variables.
+   Each row represents the moments of a variable.
+   In the absence of missing values, the columns of this matrix will
+   be identical.  If missing values are involved, then element (i,j)
+   is the moment of the i th variable, when paired with the j th variable.
+ */
+const gsl_matrix *
+covariance_moments (const struct covariance *cov, int m)
+{
+  return cov->moments[m];
+}
+
+
+
+/* Create a covariance struct */
+struct covariance *
+covariance_create (size_t n_vars, const struct variable **vars,
+                  const struct variable *weight, enum mv_class exclude)
+{
+  size_t i;
+  struct covariance *cov = xmalloc (sizeof *cov);
+  cov->vars = xmalloc (sizeof *cov->vars * n_vars);
+
+  cov->wv = weight;
+  cov->n_vars = n_vars;
+
+  for (i = 0; i < n_vars; ++i)
+    cov->vars[i] = vars[i];
+
+  cov->moments = xmalloc (sizeof *cov->moments * n_MOMENTS);
+  
+  for (i = 0; i < n_MOMENTS; ++i)
+    cov->moments[i] = gsl_matrix_calloc (n_vars, n_vars);
+
+  cov->exclude = exclude;
+
+  cov->n_cm = (n_vars * (n_vars - 1)  ) / 2;
+
+  cov->cm = xcalloc (sizeof *cov->cm, cov->n_cm);
+
+  return cov;
+}
+
+/* Return an integer, which can be used to index 
+   into COV->cm, to obtain the I, J th element
+   of the covariance matrix.  If COV->cm does not
+   contain that element, then a negative value
+   will be returned.
+*/
+static int
+cm_idx (const struct covariance *cov, int i, int j)
+{
+  int as;
+  const int n2j = cov->n_vars - 2 - j;
+  const int nj = cov->n_vars - 2 ;
+  
+  assert (i >= 0);
+  assert (j < cov->n_vars);
+
+  if ( i == 0)
+    return -1;
+
+  if (j >= cov->n_vars - 1)
+    return -1;
+
+  if ( i <= j) 
+    return -1 ;
+
+  as = nj * (nj + 1) ;
+  as -= n2j * (n2j + 1) ; 
+  as /= 2;
+
+  return i - 1 + as;
+}
+
+
+/* Call this function for every case in the data set */
+void
+covariance_accumulate (struct covariance *cov, const struct ccase *c)
+{
+  size_t i, j, m;
+  const double weight = cov->wv ? case_data (c, cov->wv)->f : 1.0;
+
+  for (i = 0 ; i < cov->n_vars; ++i)
+    {
+      const union value *val1 = case_data (c, cov->vars[i]);
+
+      if ( var_is_value_missing (cov->vars[i], val1, cov->exclude))
+       continue;
+
+      for (j = 0 ; j < cov->n_vars; ++j)
+       {
+         double pwr = 1.0;
+         int idx;
+         const union value *val2 = case_data (c, cov->vars[j]);
+
+         if ( var_is_value_missing (cov->vars[j], val2, cov->exclude))
+           continue;
+
+         idx = cm_idx (cov, i, j);
+         if (idx >= 0)
+           {
+             cov->cm [idx] += val1->f * val2->f * weight;
+           }
+
+         for (m = 0 ; m < n_MOMENTS; ++m)
+           {
+             double *x = gsl_matrix_ptr (cov->moments[m], i, j);
+
+             *x += pwr * weight;
+             pwr *= val1->f;
+           }
+       }
+    }
+}
+
+
+/* 
+   Allocate and return a gsl_matrix containing the covariances of the
+   data.
+*/
+static gsl_matrix *
+cm_to_gsl (struct covariance *cov)
+{
+  int i, j;
+  gsl_matrix *m = gsl_matrix_calloc (cov->n_vars, cov->n_vars);
+
+  /* Copy the non-diagonal elements from cov->cm */
+  for ( j = 0 ; j < cov->n_vars - 1; ++j)
+    {
+      for (i = j+1 ; i < cov->n_vars; ++i)
+       {
+         double x = cov->cm [cm_idx (cov, i, j)];
+         gsl_matrix_set (m, i, j, x);
+         gsl_matrix_set (m, j, i, x);
+       }
+    }
+
+  /* Copy the diagonal elements from cov->moments[2] */
+  for (j = 0 ; j < cov->n_vars ; ++j)
+    {
+      double sigma = gsl_matrix_get (cov->moments[2], j, j);
+      gsl_matrix_set (m, j, j, sigma);
+    }
+
+  return m;
+}
+
+
+
+/* 
+   Return a pointer to gsl_matrix containing the pairwise covariances.
+   The matrix remains owned by the COV object, and must not be freed.
+   Call this function only after all data have been accumulated.
+*/
+const gsl_matrix *
+covariance_calculate (struct covariance *cov)
+{
+  size_t i, j;
+  size_t m;
+
+  for (m = 0; m < n_MOMENTS; ++m)
+    {
+      /* Divide the moments by the number of samples */
+      if ( m > 0)
+       {
+         for (i = 0 ; i < cov->n_vars; ++i)
+           {
+             for (j = 0 ; j < cov->n_vars; ++j)
+               {
+                 double *x = gsl_matrix_ptr (cov->moments[m], i, j);
+                 *x /= gsl_matrix_get (cov->moments[0], i, j);
+
+                 if ( m == MOMENT_VARIANCE)
+                   *x -= pow2 (gsl_matrix_get (cov->moments[1], i, j));
+               }
+           }
+       }
+    }
+
+  /* Centre the moments */
+  for ( j = 0 ; j < cov->n_vars - 1; ++j)
+    {
+      for (i = j + 1 ; i < cov->n_vars; ++i)
+       {
+         double *x = &cov->cm [cm_idx (cov, i, j)];
+         
+         *x /= gsl_matrix_get (cov->moments[0], i, j);
+
+         *x -=
+           gsl_matrix_get (cov->moments[MOMENT_MEAN], i, j) 
+           *
+           gsl_matrix_get (cov->moments[MOMENT_MEAN], j, i); 
+       }
+    }
+
+  return cm_to_gsl (cov);
+}
+
+
+/* Destroy the COV object */
+void
+covariance_destroy (struct covariance *cov)
+{
+  size_t i;
+  free (cov->vars);
+
+  for (i = 0; i < n_MOMENTS; ++i)
+    gsl_matrix_free (cov->moments[i]);
+
+  free (cov->moments);
+  free (cov->cm);
+  free (cov);
+}
diff --git a/src/math/covariance.h b/src/math/covariance.h
new file mode 100644 (file)
index 0000000..8b8de88
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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 COVARIANCE_H
+#define COVARIANCE_H
+
+#include <stddef.h>
+
+#include <data/missing-values.h>
+#include <gsl/gsl_matrix.h>
+
+struct covariance;
+struct variable;
+struct ccase ;
+
+struct covariance * covariance_create (size_t n_vars, const struct variable **vars, 
+                                      const struct variable *wv, enum mv_class excl);
+
+void covariance_accumulate (struct covariance *, const struct ccase *);
+
+const gsl_matrix * covariance_calculate (struct covariance *cov);
+
+void covariance_destroy (struct covariance *cov);
+
+const gsl_matrix *covariance_moments (const struct covariance *cov, int m);
+
+#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..7fc9f0f63d2c5efee0098f06fe921d50758425e6 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
@@ -15,7 +15,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 /*
-  An interaction is a gsl_vector containing a "product" of other
+  An interaction is a structure containing a "product" of other
   variables. The variables can be either categorical or numeric.
   If the variables are all numeric, the interaction is just the
   scalar product. If any of the variables are categorical, their
   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 <gl/unistr.h>
+#include <math/interaction.h>
+#include <string.h>
+#include <xalloc.h>
 
-#include "xalloc.h"
+struct interaction_variable
+{
+  int n_vars;
+  const struct variable **members;
+  struct variable *intr;
+  size_t n_alpha;
+};
+
+struct interaction_value
+{
+  const struct interaction_variable *intr;
+  union value val; /* Concatenation of the string values in this
+                      interaction's value, or the product of a bunch
+                      of numeric values for a purely numeric
+                      interaction.
+                   */
+  double f; /* Product of the numerical values in this interaction's value. */
+};
 
 /*
-  Convert a list of values to a binary vector. The order of VALS must
-  correspond to the order of V.
+  An interaction_variable has type alpha if any of members have type
+  alpha. Otherwise, its type is numeric.
  */
-gsl_vector *
-get_interaction (union value **vals, const struct variable **v, size_t n_vars)
+struct interaction_variable *
+interaction_variable_create (const struct variable **vars, int n_vars)
 {
-  gsl_vector *result = NULL;
-  size_t *subs = NULL;
-  size_t length = 1;
+  struct interaction_variable *result = NULL;
   size_t i;
-  size_t j;
-  double tmp = 1.0;
 
-  assert (n_vars > 0);
-  for (i = 0; i < n_vars; i++)
+  if (n_vars > 0)
     {
-      if (var_is_alpha (v[i]))
-       {
-         length *= cat_get_n_categories (v[i]);
-       }
-      else
+      int width = 0;
+
+      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 = (length > 0) ? length : 1;
+         result->members[i] = vars[i];
+         if (var_is_alpha (vars[i]))
+           {
+             result->n_alpha++;
+             width += var_get_width (vars[i]);
+           }
        }
+      result->intr = var_create_internal (0, width);
     }
-  if (length > 0)
-    {
-      length--;
-    }
 
-  result = gsl_vector_calloc (length);
-  subs = xnmalloc (n_vars, sizeof (*subs));
-  for (j = 0; j < n_vars; j++)
+  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];
+}
+
+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;
+  
+  if (var != NULL)
     {
-      if (var_is_alpha (v[j]))
+      size_t i;
+      int val_width = var_get_width (interaction_get_variable (var));
+      int offset = 0;
+      size_t n_vars = interaction_get_n_vars (var);
+
+      result = xmalloc (sizeof (*result));
+      result->intr = var;
+
+      value_init (&result->val, val_width);
+
+      result->f = 1.0;
+      for (i = 0; i < n_vars; i++)
        {
-         subs[j] = cat_value_find (v[j], vals[j]);
+          const struct variable *member = interaction_get_member (var, i);
+
+         if (var_is_value_missing (member, vals[i], MV_ANY))
+           {
+             value_set_missing (&result->val, val_width);
+             result->f = SYSMIS;
+             break;
+           }
+         else
+           {
+             if (var_is_alpha (var->members[i]))
+               {
+                 uint8_t *val = value_str_rw (&result->val, val_width);
+                  int w = var_get_width (var->members[i]);
+                  u8_cpy (val + offset, value_str (vals[i], w), w);
+                  offset += w;
+               }
+             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 purely 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];
+      int val_width = var_get_width (interaction_get_variable (val->intr));
+
+      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 a01158b50b79ae49b654b6d878404700b1dc878c..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])
@@ -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 8bcad4947eee31d63e09264daf960e0d0f8a1008..33c445b09a61b0ef36714272bd221bfd2cd9f741 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 <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, "%s", 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..e00a41f7aa7a49629c8c418a95d4a64b892ea2f6 100644 (file)
 #endif
 
 void
-chart_write_title(struct chart *chart UNUSED, const char *title UNUSED, ...)
+chart_write_legend (struct chart *ch UNUSED)
 {
 }
 
+void
+chart_vector (struct chart *ch UNUSED, double x UNUSED, double y UNUSED)
+{
+}
 
 void
-chart_write_xscale(struct chart *ch UNUSED,
-                   double min UNUSED, double max UNUSED, int ticks UNUSED)
+chart_vector_end (struct chart *ch UNUSED)
 {
 }
 
+void
+chart_vector_start (struct chart *ch UNUSED, const char *name UNUSED)
+{
+}
 
 void
-chart_write_yscale(struct chart *ch UNUSED UNUSED,
-                   double smin UNUSED, double smax UNUSED, int ticks UNUSED)
+chart_write_title (struct chart *chart UNUSED, const char *title UNUSED, ...)
 {
 }
 
 
 void
-chart_write_xlabel(struct chart *ch UNUSED, const char *label UNUSED)
+chart_write_xscale (struct chart *ch UNUSED,
+                   double min UNUSED, double max UNUSED, int ticks UNUSED)
 {
 }
 
+
 void
-chart_write_ylabel(struct chart *ch UNUSED, const char *label UNUSED)
+chart_write_yscale (struct chart *ch UNUSED UNUSED,
+                   double smin UNUSED, double smax UNUSED, int ticks 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_write_xlabel (struct chart *ch UNUSED, const char *label UNUSED)
+{
+}
+
+void
+chart_write_ylabel (struct chart *ch UNUSED, const char *label UNUSED)
 {
 }
 
 
 void
-chart_datum(struct chart *ch UNUSED, int dataset UNUSED UNUSED,
-            double x UNUSED, double y 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)
 {
 }
 
-struct normal_curve;
 
 void
-histogram_plot(const gsl_histogram *hist UNUSED,
-              const char *factorname UNUSED,
-              const struct normal_curve *norm UNUSED,
-               short show_normal UNUSED)
+chart_datum (struct chart *ch UNUSED, int dataset UNUSED UNUSED,
+            double x UNUSED, double y UNUSED)
 {
 }
 
 void
-boxplot_draw_yscale(struct chart *ch UNUSED,
-                    double y_max UNUSED, double y_min UNUSED)
+histogram_plot (const struct histogram *hist UNUSED,
+               const char *label UNUSED,
+               const struct moments1 *m 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)
+histogram_plot_n (const struct histogram *hist UNUSED,
+                 const char *label UNUSED,
+                 double n UNUSED, double mean UNUSED, double stddev UNUSED,
+                 bool show_normal UNUSED)
 {
 }
 
 
+void
+boxplot_draw_yscale (struct chart *ch UNUSED,
+                    double y_max UNUSED, double y_min UNUSED)
+{
+}
+
+void
+boxplot_draw_boxplot (struct chart *ch UNUSED,
+                     double box_centre UNUSED,
+                     double box_width UNUSED,
+                     const struct box_whisker *w UNUSED,
+                     const char *name UNUSED)
+{
+}
+
+
+
 
 void
-piechart_plot(const char *title UNUSED,
-              const struct slice *slices UNUSED, int n_slices UNUSED)
+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 9a4df52052392cf1769dc1d2bade682a95af1e18..cfbaa4df8ddc396ee4d2338dee87acfd349bf0a5 100644 (file)
@@ -73,4 +73,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 88ad0899199d939dd5411cfb8fb2656368e395a4..fbf1925e635e165c3c096d119bace8123fa0d5bc 100644 (file)
@@ -4,7 +4,7 @@
    This 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);
 
     draw_tick (ch, TICK_ABSCISSA,
                x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
@@ -105,76 +106,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;
-
-  ch = chart_create();
-  if (ch == NULL)
-    return;
+  struct chart *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 e743fcf61dbfa7b8128c277b7c59e32dd4c49e9c..85185b3931f47633c7d943b3731d5db30df95f49 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,13 +60,24 @@ 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");
+
+  gtk_about_dialog_set_translator_credits 
+    (
+     GTK_ABOUT_DIALOG (about),
+     /* TRANSLATORS: Use this string to list the people who have helped with
+       translation to your language. */
+     _("translator-credits")
+     );
 
   gtk_window_set_transient_for (GTK_WINDOW (about), parent);
 
   gtk_window_set_modal (GTK_WINDOW (about), TRUE);
 
-  gtk_window_set_keep_above (GTK_WINDOW (about), TRUE);
-
   gtk_dialog_run (GTK_DIALOG (about));
 
   gtk_widget_hide (about);
index 8dabffbf695e643da35d7e63c5dfcf390ea1b2ab..b7d60a78edca7e250444598eb672aba525b63727 100644 (file)
@@ -1,9 +1,10 @@
 ## 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 -DGDK_MULTIHEAD_SAFE=1
 
 
 src_ui_gui_psppire_LDFLAGS = \
@@ -11,7 +12,6 @@ src_ui_gui_psppire_LDFLAGS = \
        $(PG_LDFLAGS)
 
 
-
 if RELOCATABLE_VIA_LD
 src_ui_gui_psppire_LDFLAGS += `$(RELOCATABLE_LDFLAGS) $(bindir)`
 else
@@ -19,74 +19,69 @@ 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 -f $(themedir)/$$size/$(context)/psppicon.png ; \
+       done 
+       gtk-update-icon-cache --ignore-theme-index $(themedir)
+
+UNINSTALL_DATA_HOOKS += uninstall-icons
+
+
+UI_FILES = \
+       src/ui/gui/correlation.ui \
+       src/ui/gui/crosstabs.ui \
+       src/ui/gui/descriptives.ui \
+       src/ui/gui/examine.ui \
+       src/ui/gui/find.ui \
+       src/ui/gui/frequencies.ui \
+       src/ui/gui/message-dialog.ui \
+       src/ui/gui/oneway.ui \
+       src/ui/gui/psppire.ui \
+       src/ui/gui/rank.ui \
+       src/ui/gui/sort.ui \
+       src/ui/gui/recode.ui \
+       src/ui/gui/regression.ui \
+       src/ui/gui/reliability.ui \
+       src/ui/gui/roc.ui \
+       src/ui/gui/t-test.ui \
+       src/ui/gui/text-data-import.ui \
+       src/ui/gui/var-sheet-dialogs.ui \
+       src/ui/gui/variable-info.ui
+
+nodist_src_ui_gui_psppire_DATA = \
+       $(top_builddir)/src/ui/gui/data-editor.ui \
+       $(top_builddir)/src/ui/gui/output-viewer.ui \
+       $(top_builddir)/src/ui/gui/syntax-editor.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 \
+       $(UI_FILES) \
        $(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 +100,107 @@ 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/correlation-dialog.c \
+       src/ui/gui/correlation-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-var-view.c \
+       src/ui/gui/psppire-var-view.h \
        src/ui/gui/psppire-selector.h \
+       src/ui/gui/psppire-select-dest.c \
+       src/ui/gui/psppire-select-dest.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/roc-dialog.c \
+       src/ui/gui/roc-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 +219,25 @@ 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/data-editor.glade \
+       src/ui/gui/output-viewer.glade \
+       src/ui/gui/syntax-editor.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 +248,32 @@ 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 $? > $@
+
+SUFFIXES += .glade .ui
+.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..02d6d96c763d3064840fe72ebcd6d590aaa4acf7 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 ;
@@ -145,7 +145,7 @@ comments_dialog (GObject *o, gpointer data)
   }
 
   cd.xml = xml;
-  cd.dict = vs->dict;
+  g_object_get (vs, "dictionary", &cd.dict, NULL);
 
   g_signal_connect (buffer, "mark-set",
                    G_CALLBACK (set_column_number), label);
@@ -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..8a98bb3dcac48d8158812d5f9b96bb7c4df6b687 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");
 
@@ -378,7 +379,6 @@ compute_dialog (GObject *o, gpointer data)
   GtkWidget *functions = get_widget_assert (xml, "compute-treeview2");
   GtkWidget *keypad    = get_widget_assert (xml, "psppire-keypad1");
   GtkWidget *target    = get_widget_assert (xml, "compute-entry1");
-  GtkWidget *syntax_area = get_widget_assert (xml, "compute-textview1");
   GtkWidget *var_selector = get_widget_assert (xml, "compute-selector1");
   GtkWidget *func_selector = get_widget_assert (xml, "compute-selector2");
   GtkWidget *type_and_label = get_widget_assert (xml, "compute-button1");
@@ -388,35 +388,26 @@ compute_dialog (GObject *o, gpointer data)
 
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
-  scd.dict = vs->dict;
+  g_object_get (vs, "dictionary", &scd.dict, NULL);
   scd.use_type = FALSE;
 
   g_signal_connect (expression, "toggled",
                    G_CALLBACK(on_expression_toggle), &scd);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
+  
+  g_object_set (dict_view, "model", scd.dict,
+               "selection-mode", GTK_SELECTION_SINGLE,
+               NULL);
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
-                                vs->dict,
-                                GTK_SELECTION_SINGLE, NULL);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (var_selector),
-                                dict_view, syntax_area,
-                                insert_source_row_into_text_view,
-                                NULL,
-                                NULL);
-
+  psppire_selector_set_select_func (PSPPIRE_SELECTOR (var_selector),
+                                   insert_source_row_into_text_view, NULL);
 
   function_list_populate (GTK_TREE_VIEW (functions));
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (func_selector),
-                                functions, syntax_area,
-                                insert_function_into_syntax_area,
-                                NULL,
-                                NULL);
-
+  psppire_selector_set_select_func (PSPPIRE_SELECTOR (func_selector),
+                                   insert_function_into_syntax_area, NULL);
 
   scd.xml = xml;
 
@@ -447,6 +438,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 +449,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 +595,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 +622,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);
 
diff --git a/src/ui/gui/correlation-dialog.c b/src/ui/gui/correlation-dialog.c
new file mode 100644 (file)
index 0000000..ff6a254
--- /dev/null
@@ -0,0 +1,180 @@
+/* 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 <ui/syntax-gen.h>
+#include <libpspp/str.h>
+
+#include "correlation-dialog.h"
+#include "psppire-selector.h"
+#include "psppire-dictview.h"
+#include "psppire-dialog.h"
+
+#include "psppire-data-window.h"
+#include "psppire-var-view.h"
+
+#include "executor.h"
+#include "helper.h"
+
+#include <gtk/gtk.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+struct correlation
+{
+  PsppireDict *dict;
+
+  GtkWidget *variables ;
+
+  GtkWidget *significant;
+  GtkWidget *two_tailed;
+};
+
+
+static char * generate_syntax (const struct correlation *rd);
+
+
+static void
+refresh (struct correlation *rd)
+{
+  GtkTreeModel *liststore =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variables));
+  gtk_list_store_clear (GTK_LIST_STORE (liststore));
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->significant), FALSE);
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->two_tailed), TRUE);
+}
+
+
+static gboolean
+dialog_state_valid (gpointer data)
+{
+  struct correlation *corr = data;
+
+  GtkTreeModel *liststore =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (corr->variables));
+
+  if  (gtk_tree_model_iter_n_children (liststore, NULL) >= 1)
+    return TRUE;
+
+  return FALSE;
+}
+
+
+/* Pops up the Correlation dialog box */
+void
+correlation_dialog (GObject *o, gpointer data)
+{
+  struct correlation rd;
+  gint response;
+
+  GtkBuilder *xml = builder_new ("correlation.ui");
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+  PsppireVarStore *vs;
+
+  GtkWidget *dialog = get_widget_assert   (xml, "correlation-dialog");
+  GtkWidget *source = get_widget_assert   (xml, "dict-view");
+
+  g_object_get (de->data_editor, "var-store", &vs, NULL);
+
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
+
+  g_object_get (vs, "dictionary", &rd.dict, NULL);
+  g_object_set (source,
+               "model", rd.dict, 
+               "predicate", var_is_numeric,
+               NULL);
+
+  rd.variables = get_widget_assert (xml, "psppire-var-view1");
+  rd.significant = get_widget_assert (xml, "button-flag-significants");
+  rd.two_tailed = get_widget_assert (xml, "button-two-tailed");
+
+  g_signal_connect_swapped (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 correlation *rd)
+{
+  gchar *text;
+  GString *string = g_string_new ("CORRELATION");
+  g_string_append (string, "\n\t/VARIABLES = ");
+
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variables), 0, string);
+
+
+  g_string_append (string, "\n\t/PRINT =");
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->two_tailed)))
+    g_string_append (string, " TWOTAIL");
+  else
+    g_string_append (string, " ONETAIL");
+
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->significant)))
+    g_string_append (string, " NOSIG");
+  else
+    g_string_append (string, " SIG");
+
+
+  g_string_append (string, ".\n");
+
+  text = string->str;
+
+  g_string_free (string, FALSE);
+
+  return text;
+}
diff --git a/src/ui/gui/correlation-dialog.h b/src/ui/gui/correlation-dialog.h
new file mode 100644 (file)
index 0000000..532970f
--- /dev/null
@@ -0,0 +1,24 @@
+/* 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 __CORRELATION_DIALOG_H
+#define __CORRELATION_DIALOG_H
+
+#include <gtk/gtk.h>
+
+void correlation_dialog (GObject *o, gpointer data);
+
+#endif
diff --git a/src/ui/gui/correlation.ui b/src/ui/gui/correlation.ui
new file mode 100644 (file)
index 0000000..1fad344
--- /dev/null
@@ -0,0 +1,265 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires psppire 0.0 -->
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="correlation-dialog">
+    <property name="title" translatable="yes">Bivariate Correlations</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="PsppireDictView" id="dict-view">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="border_width">5</property>
+                        <property name="headers_visible">False</property>
+                        <property name="headers_clickable">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox2">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="PsppireSelector" id="psppire-selector1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="border_width">5</property>
+                        <property name="source_widget">dict-view</property>
+                        <property name="dest_widget">psppire-var-view1</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">5</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="PsppireVarView" id="psppire-var-view1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="border_width">5</property>
+                        <property name="headers_visible">False</property>
+                        <property name="headers_clickable">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame2">
+                <property name="label_xalign">0</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkHButtonBox" id="hbuttonbox2">
+                        <property name="visible">True</property>
+                        <property name="layout_style">start</property>
+                        <child>
+                          <object class="GtkCheckButton" id="button-pearson">
+                            <property name="label" translatable="yes">Pearso_n</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="button-kendall">
+                            <property name="label" translatable="yes">_Kendall's tau-b</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="button-spearman">
+                            <property name="label" translatable="yes">_Spearman</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Correlation Coefficients</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkHButtonBox" id="hbuttonbox1">
+                        <property name="visible">True</property>
+                        <property name="layout_style">start</property>
+                        <child>
+                          <object class="GtkRadioButton" id="button-two-tailed">
+                            <property name="label" translatable="yes">_Two-tailed</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="button-one-tailed">
+                            <property name="label" translatable="yes">One-tai_led</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">button-two-tailed</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Test of Significance</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="button-flag-significants">
+                <property name="label" translatable="yes">_Flag significant correlations</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">2</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index edaa3e42a1f1a5c0185006feef0dfb8199f168f2..2b93a77b657afdfd2cdbb78657669c56ae11bb7b 100644 (file)
 
 #include "checkbox-treeview.h"
 #include "crosstabs-dialog.h"
+#include "psppire-var-view.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 "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)
@@ -259,9 +260,9 @@ generate_syntax (const struct crosstabs_dialog *cd)
   GString *string = g_string_new ("CROSSTABS");
 
   g_string_append (string, "\n\t/TABLES=");
-  append_variable_names (string, cd->dict, GTK_TREE_VIEW (cd->row_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (cd->row_vars), 0, string);
   g_string_append (string, "\tBY\t");
-  append_variable_names (string, cd->dict, GTK_TREE_VIEW (cd->col_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (cd->col_vars), 0, string);
 
   g_string_append (string, "\n\t/FORMAT=");
 
@@ -385,19 +386,19 @@ 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;
+  PsppireDict *dict = 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");
   GtkWidget *dest_cols =   get_widget_assert   (xml, "cols");
-  GtkWidget *row_selector = get_widget_assert (xml, "row-selector");
-  GtkWidget *col_selector = get_widget_assert (xml, "col-selector");
   GtkWidget *format_button = get_widget_assert (xml, "format-button");
   GtkWidget *stat_button = get_widget_assert (xml, "stats-button");
   GtkWidget *cell_button = get_widget_assert (xml, "cell-button");
@@ -419,32 +420,14 @@ 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);
-
-  set_dest_model (GTK_TREE_VIEW (dest_rows), vs->dict);
-  set_dest_model (GTK_TREE_VIEW (dest_cols), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (row_selector),
-                                source,
-                                dest_rows,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (col_selector),
-                                source,
-                                dest_cols,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  g_object_get (vs, "dictionary", &dict, NULL);
+  g_object_set (source, "model", dict, NULL);
 
   cd.row_vars = GTK_TREE_VIEW (dest_rows);
   cd.col_vars = GTK_TREE_VIEW (dest_cols);
-  cd.dict = vs->dict;
+  g_object_get (vs, "dictionary", &cd.dict, NULL);
   cd.format_dialog = get_widget_assert (xml, "format-dialog");
   cd.table_button = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "print-tables"));
   cd.pivot_button = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "pivot"));
@@ -461,9 +444,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 +468,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 +479,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);
diff --git a/src/ui/gui/crosstabs.glade b/src/ui/gui/crosstabs.glade
deleted file mode 100644 (file)
index d5907a5..0000000
+++ /dev/null
@@ -1,465 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.2 on Thu Feb  7 10:02:16 2008 by jhs@debs-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="crosstabs-dialog">
-    <property name="title">Crosstabs</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <widget class="GtkTable" id="table1">
-            <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="n_rows">3</property>
-            <property name="n_columns">3</property>
-            <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="alignment4">
-                    <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">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="rows">
-                            <property name="visible">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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="label" translatable="yes">Rows</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkFrame" id="frame2">
-                <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="GtkScrolledWindow" id="scrolledwindow3">
-                        <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="cols">
-                            <property name="visible">True</property>
-                            <property name="headers_visible">False</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label2">
-                    <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">Columns</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </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="GtkScrolledWindow" id="scrolledwindow1">
-                <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="dict-treeview">
-                    <property name="visible">True</property>
-                    <property name="headers_visible">False</property>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkHButtonBox" id="hbuttonbox3">
-                <property name="visible">True</property>
-                <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                <child>
-                  <widget class="GtkButton" id="format-button">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Format...</property>
-                    <property name="response_id">0</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkButton" id="stats-button">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Statistics...</property>
-                    <property name="response_id">0</property>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkButton" id="cell-button">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Cells...</property>
-                    <property name="response_id">0</property>
-                  </widget>
-                  <packing>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireSelector" id="row-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="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="col-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="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>
-          </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>
-  <widget class="PsppireDialog" id="format-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">Crosstabs: Format</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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>
-        <property name="spacing">2</property>
-        <child>
-          <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>
-            <child>
-              <widget class="GtkCheckButton" id="print-tables">
-                <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">Print tables</property>
-                <property name="response_id">0</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkCheckButton" id="pivot">
-                <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">Pivot</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="ascending">
-                <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="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkFrame" id="Labeling">
-                <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="vbox2">
-                        <property name="visible">True</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton1">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Label</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton2">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">No label</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton3">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Suppress value labels</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label4">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Labeling</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">3</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="cell-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">Crosstabs: Cells</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <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="shadow_type">GTK_SHADOW_NONE</property>
-            <child>
-              <widget class="GtkScrolledWindow" id="scrolledwindow4">
-                <property name="width_request">128</property>
-                <property name="height_request">180</property>
-                <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="cell-view">
-                    <property name="visible">True</property>
-                    <property name="headers_visible">False</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" 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="label" translatable="yes">Cell Display</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox3">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="stat-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">Crosstabs: Statistics</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox4">
-        <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="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="label_xalign">0</property>
-            <property name="shadow_type">GTK_SHADOW_NONE</property>
-            <child>
-              <widget class="GtkScrolledWindow" id="scrolledwindow5">
-                <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="stats-view">
-                    <property name="width_request">128</property>
-                    <property name="height_request">150</property>
-                    <property name="visible">True</property>
-                    <property name="headers_visible">False</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <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">Statistics</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox4">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
diff --git a/src/ui/gui/crosstabs.ui b/src/ui/gui/crosstabs.ui
new file mode 100644 (file)
index 0000000..e21ae80
--- /dev/null
@@ -0,0 +1,489 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <requires lib="psppire" version="2054.22072"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="PsppireDialog" id="crosstabs-dialog">
+    <property name="title">Crosstabs</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object class="GtkTable" id="table1">
+            <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="n_rows">3</property>
+            <property name="n_columns">3</property>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment4">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hscrollbar_policy">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="rows">
+                            <property name="visible">True</property>
+                            <property name="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">Rows</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame2">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment5">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hscrollbar_policy">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="cols">
+                            <property name="visible">True</property>
+                            <property name="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label2">
+                    <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">Columns</property>
+                  </object>
+                </child>
+              </object>
+              <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>
+              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object class="PsppireDictView" id="dict-treeview">
+                    <property name="visible">True</property>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHButtonBox" id="hbuttonbox3">
+                <property name="visible">True</property>
+                <property name="layout_style">spread</property>
+                <child>
+                  <object class="GtkButton" id="format-button">
+                    <property name="label" translatable="yes">Format...</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="stats-button">
+                    <property name="label" translatable="yes">Statistics...</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="cell-button">
+                    <property name="label" translatable="yes">Cells...</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="right_attach">3</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="PsppireSelector" id="row-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>
+                <property name="source_widget">dict-treeview</property>
+                <property name="dest_widget">rows</property>
+              </object>
+              <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>
+              <object class="PsppireSelector" id="col-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>
+                <property name="source_widget">dict-treeview</property>
+                <property name="dest_widget">cols</property>
+              </object>
+              <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>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="format-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">Crosstabs: Format</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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>
+        <property name="spacing">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <child>
+              <object class="GtkCheckButton" id="print-tables">
+                <property name="label" translatable="yes">Print tables</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="pivot">
+                <property name="label" translatable="yes">Pivot</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="ascending">
+                <property name="label" translatable="yes">Ascending</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="Labeling">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox2">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton1">
+                            <property name="label" translatable="yes">Label</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="receives_default">False</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton2">
+                            <property name="label" translatable="yes">No label</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="receives_default">False</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton3">
+                            <property name="label" translatable="yes">Suppress value labels</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="receives_default">False</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Labeling</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="cell-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">Crosstabs: Cells</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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="shadow_type">none</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow4">
+                <property name="width_request">128</property>
+                <property name="height_request">180</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <object class="GtkTreeView" id="cell-view">
+                    <property name="visible">True</property>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label999">
+                <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">Cell Display</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox3">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="stat-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">Crosstabs: Statistics</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox4">
+        <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>
+          <object 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="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <object class="GtkTreeView" id="stats-view">
+                    <property name="width_request">128</property>
+                    <property name="height_request">150</property>
+                    <property name="visible">True</property>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object 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">Statistics</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox4">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
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..8efdc12b31ab4dd3d021c00e0835785701e37337 100644 (file)
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--*- mode: xml -*-->
+<?xml version="1.0"?>
 <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>
+  <!-- interface-requires gtk+ 2.6 -->
+  <!-- interface-naming-policy toplevel-contextual -->
+  <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="label">gtk-new</property>
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_File</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="label">gtk-open</property>
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">_Edit</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="label">gtk-save</property>
                 <property name="visible">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">_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>
-                </child>
+                <property name="use_stock">True</property>
               </widget>
             </child>
             <child>
-              <widget class="GtkMenuItem" id="analyze">
+              <widget class="GtkImageMenuItem" id="file_save_as">
+                <property name="label">gtk-save-as</property>
                 <property name="visible">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">_Analyze</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>
-                <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="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>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="file_quit">
+                <property name="label">gtk-quit</property>
                 <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="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>
+                <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="label">Go To Case</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">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="label">gtk-cut</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_copy">
+                <property name="label">gtk-copy</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="edit_paste">
+                <property name="label">gtk-paste</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</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>
-                <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="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>
-                  </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="GtkImageMenuItem" id="edit_find">
+                <property name="label">gtk-find</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</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="label">_Sort Cases</property>
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">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>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">1</property>
-          </packing>
         </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>
-          <placeholder/>
+          <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" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+              </widget>
+            </child>
+          </widget>
         </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="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="GtkLabel" id="information-area">
+                      <widget class="GtkMenuItem" id="crosstabs">
                         <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">_Crosstabs</property>
+                        <property name="use_underline">True</property>
                       </widget>
                     </child>
                   </widget>
               </widget>
             </child>
             <child>
-              <widget class="GtkFrame" id="frame3">
+              <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="eventbox2">
+                  <widget class="GtkMenu" id="menu6">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Processor 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="GtkLabel" id="processor-area">
+                      <widget class="GtkMenuItem" id="one-sample-t-test">
                         <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">_One Sample T Test</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">
-                <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkEventBox" id="eventbox4">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Case Counter Area</property>
                     <child>
-                      <widget class="GtkLabel" id="case-counter-area">
+                      <widget class="GtkMenuItem" id="indep-t-test">
                         <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">_Independent Samples T Test</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">
-                <property name="visible">True</property>
-                <property name="label_xalign">0</property>
-                <property name="shadow_type">GTK_SHADOW_IN</property>
-                <child>
-                  <widget class="GtkEventBox" id="eventbox5">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Filter Use Status Area</property>
                     <child>
-                      <widget class="GtkLabel" id="filter-use-status-area">
+                      <widget class="GtkMenuItem" id="paired-t-test">
                         <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">_Paired Samples T Test</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>
-            <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="GtkEventBox" id="eventbox6">
-                    <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Weight Status Area</property>
                     <child>
-                      <widget class="GtkLabel" id="weight-status-area">
+                      <widget class="GtkMenuItem" id="oneway-anova">
                         <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>
+                        <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">5</property>
-              </packing>
             </child>
             <child>
-              <widget class="GtkFrame" id="frame8">
+              <widget class="GtkMenuItem" id="correlation">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Bivariate _Correlation...</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <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="eventbox7">
+                  <widget class="GtkMenu" id="menu4">
                     <property name="visible">True</property>
-                    <property name="tooltip" translatable="yes">Split File 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="split-file-status-area">
+                      <widget class="GtkMenuItem" id="binomial">
                         <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>
+                        <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">6</property>
-              </packing>
             </child>
+            <child>
+              <widget class="GtkMenuItem" id="roc-curve">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">ROC Cur_ve...</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </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="GtkMenuItem" id="utilities_variables">
+                <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="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="label">_Reference Manual</property>
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkImageMenuItem" id="help_about">
+                <property name="label">_About</property>
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkHandleBox" id="handlebox1">
+    <property name="visible">True</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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem1"/>
+          <packing>
+            <property name="expand">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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem2">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem5">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">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>
+            <property name="homogeneous">True</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem4">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkSeparatorToolItem" id="separatortoolitem6">
+            <property name="visible">True</property>
+          </widget>
+          <packing>
+            <property name="expand">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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</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>
           </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>
+            <property name="homogeneous">True</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>
+            <property name="homogeneous">True</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">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>
+      <packing>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <widget class="GtkFrame" id="frame3">
+        <property name="visible">True</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">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">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">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">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">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">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">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">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..9d8502de058b34b2cfa301338978ccc69267f4e2 100644 (file)
 
 #include "checkbox-treeview.h"
 #include "descriptives-dialog.h"
+#include "psppire-var-view.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 "executor.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -117,7 +118,7 @@ generate_syntax (const struct descriptives_dialog *scd)
 
   string = g_string_new ("DESCRIPTIVES");
   g_string_append (string, "\n    /VARIABLES=");
-  append_variable_names (string, scd->dict, GTK_TREE_VIEW (scd->stat_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (scd->stat_vars), 0, string);
 
   listwise = gtk_toggle_button_get_active (scd->exclude_missing_listwise);
   include = gtk_toggle_button_get_active (scd->include_user_missing);
@@ -202,39 +203,31 @@ 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.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "descriptives-dialog");
 
 
   GtkWidget *source = get_widget_assert   (xml, "all-variables");
-  GtkWidget *selector = get_widget_assert (xml, "stat-var-selector");
   GtkWidget *dest =   get_widget_assert   (xml, "stat-variables");
 
   GtkWidget *stats_treeview = get_widget_assert    (xml, "statistics");
 
   PsppireVarStore *vs = NULL;
+  PsppireDict *dict;
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
+  g_object_get (vs, "dictionary", &dict, NULL);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, var_is_numeric);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                dest,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  g_object_set (source, "model", dict,
+       "predicate", var_is_numeric, NULL);
 
   put_checkbox_items_in_treeview (GTK_TREE_VIEW (stats_treeview),
                                  B_DS_DEFAULT,
@@ -242,7 +235,9 @@ descriptives_dialog (GObject *o, gpointer data)
 
   scd.stat_vars = GTK_TREE_VIEW (dest);
   scd.stats = gtk_tree_view_get_model (GTK_TREE_VIEW (stats_treeview));
-  scd.dict = vs->dict;
+  
+  g_object_get (vs, "dictionary", &scd.dict, NULL);
+  
   scd.include_user_missing =
     GTK_TOGGLE_BUTTON (get_widget_assert (xml, "include_user_missing"));
   scd.exclude_missing_listwise =
@@ -263,6 +258,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 +268,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;
diff --git a/src/ui/gui/descriptives-dialog.glade b/src/ui/gui/descriptives-dialog.glade
deleted file mode 100644 (file)
index dff7078..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-<?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  5 21:18:15 2007 by blp@blp-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="descriptives-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">Descriptives</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox16">
-        <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="GtkVBox" id="vbox30">
-            <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="GtkHBox" id="hbox24">
-                <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="scrolledwindow16">
-                    <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="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>
-                        <property name="headers_visible">False</property>
-                        <property name="headers_clickable">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkAlignment" id="alignment19">
-                    <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.25</property>
-                    <property name="xscale">0</property>
-                    <property name="yscale">0</property>
-                    <child>
-                      <widget class="PsppireSelector" id="stat-var-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>
-                        <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="GtkVBox" id="vbox30">
-                    <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="vbox31">
-                        <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="label37">
-                            <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">Variables:</property>
-                            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkScrolledWindow" id="scrolledwindow17">
-                            <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="stat-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>
-                                <property name="headers_visible">False</property>
-                                <property name="headers_clickable">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="padding">5</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkVBox" id="vbox28">
-                        <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="label36">
-                            <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">Statistics:</property>
-                            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</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_AUTOMATIC</property>
-                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                            <child>
-                              <widget class="GtkTreeView" id="statistics">
-                                <property name="height_request">200</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="headers_visible">False</property>
-                                <property name="headers_clickable">True</property>
-                              </widget>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="padding">5</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkFrame" id="frame11">
-                <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="alignment20">
-                    <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="GtkVButtonBox" id="vbuttonbox5">
-                        <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="exclude_missing_listwise">
-                            <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">Exclude entire case if any selected variable is missing</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkCheckButton" id="include_user_missing">
-                            <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">Include user-missing data in analysis</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="save_z_scores">
-                            <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">Save Z-scores of selected variables as new variables</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label40">
-                    <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">Options:</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-buttonbox6">
-            <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>
-          </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 aa6e39d13b86b66d73f7aff1ecc2c7cf93979294..745663e73139eb35ce2d05026a55fb78a0569dc8 100644 (file)
@@ -19,7 +19,6 @@
 
 
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 
 void descriptives_dialog (GObject *o, gpointer data);
diff --git a/src/ui/gui/descriptives.ui b/src/ui/gui/descriptives.ui
new file mode 100644 (file)
index 0000000..62e601f
--- /dev/null
@@ -0,0 +1,273 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="descriptives-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">Descriptives</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox16">
+        <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>
+          <object 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>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkHBox" id="hbox24">
+                <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>
+                  <object class="GtkScrolledWindow" id="scrolledwindow16">
+                    <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">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object 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>
+                        <property name="headers_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkAlignment" id="alignment19">
+                    <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.25</property>
+                    <property name="xscale">0</property>
+                    <property name="yscale">0</property>
+                    <child>
+                      <object class="PsppireSelector" id="stat-var-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>
+                        <property name="source_widget">all-variables</property>
+                        <property name="dest_widget">stat-variables</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox30">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox31">
+                        <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="orientation">vertical</property>
+                        <child>
+                          <object class="GtkLabel" id="label37">
+                            <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">Variables:</property>
+                            <property name="wrap_mode">word-char</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkScrolledWindow" id="scrolledwindow17">
+                            <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">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="PsppireVarView" id="stat-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>
+                                <property name="headers_visible">False</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="padding">5</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="vbox28">
+                        <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="orientation">vertical</property>
+                        <child>
+                          <object class="GtkLabel" id="label36">
+                            <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">Statistics:</property>
+                            <property name="wrap_mode">word-char</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object 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">automatic</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="GtkTreeView" id="statistics">
+                                <property name="height_request">200</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="headers_visible">False</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="padding">5</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame11">
+                <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>
+                  <object class="GtkAlignment" id="alignment20">
+                    <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>
+                      <object class="GtkVButtonBox" id="vbuttonbox5">
+                        <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>
+                          <object class="GtkCheckButton" id="exclude_missing_listwise">
+                            <property name="label" translatable="yes">Exclude entire case if any selected variable is missing</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="include_user_missing">
+                            <property name="label" translatable="yes">Include user-missing data in analysis</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="save_z_scores">
+                            <property name="label" translatable="yes">Save Z-scores of selected variables as new variables</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label40">
+                    <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">Options:</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-buttonbox6">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index 0aab294e12ef0b2a8ca937851f60533525847619..c61cb14a64a88ef4b3dba174f194277c5c552216 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include <libpspp/i18n.h>
 #include "dialog-common.h"
 
 #include "psppire-var-ptr.h"
 #include "helper.h"
 
 
-/* Append the names of selected variables to STRING.
-   TREEVIEW is the treeview containing the variables.
-   COLUMN is the column in the treeview containing the variables.
-   DICT is the dictionary for those variables.
-*/
-gint
-append_variable_names (GString *string,
-                      PsppireDict *dict, GtkTreeView *treeview, gint column)
-{
-  gint n_vars = 0;
-  GtkTreeIter iter;
-
-  GtkTreeModel *list_store =
-    gtk_tree_view_get_model (treeview);
-
-  if ( gtk_tree_model_get_iter_first (list_store, &iter) )
-    {
-      do
-       {
-         GValue value = {0};
-         struct variable *var = NULL;
-         GtkTreePath *path = gtk_tree_model_get_path (list_store, &iter);
-
-         gtk_tree_model_get_value (list_store, &iter, column, &value);
-
-         /* FIXME:  G_TYPE_INT should be deprecated.
-            As well as being simpler, it'd be unecessary to pass dict */
-         if ( G_VALUE_TYPE (&value) == G_TYPE_INT )
-         var = psppire_dict_get_variable (dict, g_value_get_int (&value));
-
-         else if ( G_VALUE_TYPE (&value) == PSPPIRE_VAR_PTR_TYPE)
-           var = g_value_get_boxed (&value);
-
-         else
-           g_critical ("Unsupported type \"%s\", in variable name treeview.",
-                       G_VALUE_TYPE_NAME (&value));
-
-         g_value_unset (&value);
-
-         g_string_append (string, " ");
-         g_string_append (string, var_get_name (var));
-
-         gtk_tree_path_free (path);
-         n_vars++;
-       }
-      while (gtk_tree_model_iter_next (list_store, &iter));
-    }
-
-  return n_vars;
-}
-
-
-
-struct variable *
-get_selected_variable (GtkTreeModel *treemodel,
-                      GtkTreeIter *iter,
-                      PsppireDict *dict)
-{
-  struct variable *var;
-  GValue value = {0};
-
-  GtkTreePath *path = gtk_tree_model_get_path (treemodel, iter);
-
-  gtk_tree_model_get_value (treemodel, iter, 0, &value);
-
-  gtk_tree_path_free (path);
-
-  var =  psppire_dict_get_variable (dict, g_value_get_int (&value));
-
-  g_value_unset (&value);
-
-  return var;
-}
-
-
-
-
-/* A (*GtkTreeCellDataFunc) function.
-   This function expects TREEMODEL to hold G_TYPE_INT.  The ints it holds
-   are the indices of the variables in the dictionary, which DATA points to.
-   It renders the name of the variable into CELL.
-*/
-void
-cell_var_name (GtkTreeViewColumn *tree_column,
-              GtkCellRenderer *cell,
-              GtkTreeModel *tree_model,
-              GtkTreeIter *iter,
-              gpointer data)
-{
-  PsppireDict *dict = data;
-  struct variable *var;
-  gchar *name;
-
-  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);
-}
-
-
-
-/* Set a model for DEST, which is an GtkListStore of g_int's
-   whose values are the indices into DICT */
-void
-set_dest_model (GtkTreeView *dest, PsppireDict *dict)
-{
-  GtkTreeViewColumn *col;
-  GtkListStore *dest_list = gtk_list_store_new (1, G_TYPE_INT);
-  GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
-
-  gtk_tree_view_set_model (GTK_TREE_VIEW (dest), GTK_TREE_MODEL (dest_list));
-
-  col = gtk_tree_view_column_new_with_attributes ("Var",
-                                                 renderer,
-                                                 "text",
-                                                 0,
-                                                 NULL);
-
-  gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                          cell_var_name,
-                                          dict, 0);
-
-  /* FIXME: make this a value in terms of character widths */
-  g_object_set (col, "min-width",  100, NULL);
-
-  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
-
-  gtk_tree_view_append_column (GTK_TREE_VIEW (dest), col);
-}
-
-
-
 /* Returns FALSE if the variables represented by the union of the rows
    currently selected by SOURCE widget, and contents of the DEST
    widget, are of different types.
@@ -228,11 +96,8 @@ homogeneous_types (GtkWidget *source, GtkWidget *dest)
        ok;
        ok = gtk_tree_model_iter_next (model, &iter))
     {
-      gint idx;
       const struct variable *v;
-      gtk_tree_model_get (model, &iter, 0, &idx, -1);
-
-      v = psppire_dict_get_variable (dict, idx);
+      gtk_tree_model_get (model, &iter, 0, &v, -1);
 
       if ( type != -1 )
        {
@@ -246,7 +111,6 @@ homogeneous_types (GtkWidget *source, GtkWidget *dest)
       type = var_get_type (v);
     }
 
-
   return retval;
 }
 
index 9a003f35a20e84941b6ec83cc92870295ac38e0d..328904acfc9643fbf669cf6cd0f2f3451166d9c1 100644 (file)
 #include <gtk/gtk.h>
 #include "psppire-dict.h"
 
-/* Append the names of selected variables to STRING.
-   TREEVIEW is the treeview containing the variables.
-   COLUMN is column in treeview containing the variables.
-   DICT is the dictionary for those variables.
-*/
-gint append_variable_names (GString *string, PsppireDict *dict,
-                           GtkTreeView *treeview, gint column);
-
-
-/* Returns the variable currently selected by the iterator
-   pointing to TREEMODEL */
-struct variable * get_selected_variable (GtkTreeModel *treemodel,
-                                        GtkTreeIter *iter,
-                                        PsppireDict *dict);
-
-
-
 /* A (*GtkTreeCellDataFunc) function.
    This function expects TREEMODEL to hold G_TYPE_INT.  The ints it holds
    are the indices of the variables in the dictionary, which DATA points to.
    It renders the name of the variable into CELL.
 */
-void cell_var_name (GtkTreeViewColumn *tree_column,
+void XXX_cell_var_name (GtkTreeViewColumn *tree_column,
                    GtkCellRenderer *cell,
                    GtkTreeModel *tree_model,
                    GtkTreeIter *iter,
                    gpointer data);
 
 
-/* Set a model for DEST, which is an GtkListStore of g_int's
-   whose values are the indices into DICT */
-void set_dest_model (GtkTreeView *dest, PsppireDict *dict);
-
-
 /* Returns FALSE if the variables represented by the union of the rows
    currently selected by SOURCE widget, and contents of the DEST
    widget, are of different types.
index a8e9237a7edbb38c065e5d2c0f52dc6064fef862..160cdb2a1b8e635c4ad72a08892ca11edbddef9d 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,171 +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_markup_printf_escaped (
-                                    "<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
 insert_source_row_into_entry (GtkTreeIter iter,
@@ -290,7 +66,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,13 +79,10 @@ 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));
 }
 
 
-
 void
 insert_source_row_into_tree_view (GtkTreeIter iter,
                                  GtkWidget *dest,
@@ -322,37 +94,45 @@ insert_source_row_into_tree_view (GtkTreeIter iter,
   GtkTreeIter dest_iter;
   GtkTreeIter dict_iter;
   gint *row ;
-  GtkTreeModel *destmodel = gtk_tree_view_get_model ( GTK_TREE_VIEW (dest));
+  GtkTreeModel *destmodel = gtk_tree_view_get_model (GTK_TREE_VIEW (dest));
 
+  const struct variable *var;
   GtkTreeModel *dict;
 
-
   get_base_model (model, &iter, &dict, &dict_iter);
 
   path = gtk_tree_model_get_path (dict, &dict_iter);
 
   row = gtk_tree_path_get_indices (path);
 
+  var = psppire_dict_get_variable (PSPPIRE_DICT (dict), *row);
+
   gtk_list_store_append (GTK_LIST_STORE (destmodel),  &dest_iter);
-  gtk_list_store_set (GTK_LIST_STORE (destmodel), &dest_iter, 0, *row, -1);
+
+  gtk_list_store_set (GTK_LIST_STORE (destmodel), &dest_iter, 0, var, -1);
 
   gtk_tree_path_free (path);
 }
 
 
+
 gboolean
 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));
+  GtkWidget *entry = NULL;
+  const gchar *text = NULL;
+
+  g_object_get (selector, "dest-widget", &entry, NULL);
+
+  text = gtk_entry_get_text (GTK_ENTRY (entry));
 
   get_base_model (model, iter, &dict, &dict_iter);
 
@@ -366,11 +146,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..f1a8a0016f9aa2c9920cc9b6424f9b0c34b251ca 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 */
@@ -43,7 +31,6 @@ void insert_source_row_into_tree_view (GtkTreeIter source_iter,
                                       gpointer data
                                       );
 
-
 /* A SelectItemsFunc function for GtkEntry widgets */
 void insert_source_row_into_entry (GtkTreeIter source_iter,
                                   GtkWidget *dest,
@@ -58,6 +45,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..67523143fc48ea09eedcae8c5acfe67f8979ce5a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007, 2008  Free Software Foundation
+   Copyright (C) 2007, 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 <config.h>
 
 #include "examine-dialog.h"
+#include "psppire-var-view.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 "executor.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -96,13 +97,13 @@ generate_syntax (const struct examine_dialog *ed)
   GString *str = g_string_new ("EXAMINE ");
 
   g_string_append (str, "\n\t/VARIABLES=");
-  append_variable_names (str, ed->dict, GTK_TREE_VIEW (ed->dep_list), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (ed->dep_list), 0, str);
 
   if ( 0  < gtk_tree_model_iter_n_children
        (gtk_tree_view_get_model (GTK_TREE_VIEW (ed->fct_list)), NULL))
     {
       g_string_append (str, "\n\tBY ");
-      append_variable_names (str, ed->dict, GTK_TREE_VIEW (ed->fct_list), 0);
+      psppire_var_view_append_names (PSPPIRE_VAR_VIEW (ed->fct_list), 0, str);
     }
 
   label = gtk_entry_get_text (GTK_ENTRY (ed->id_entry));
@@ -236,13 +237,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");
@@ -252,8 +251,6 @@ examine_dialog (GObject *o, gpointer data)
 
 
   GtkWidget *dep_selector = get_widget_assert (xml, "psppire-selector1");
-  GtkWidget *fct_selector = get_widget_assert (xml, "psppire-selector2");
-  GtkWidget *id_selector = get_widget_assert (xml, "psppire-selector3");
 
   PsppireVarStore *vs = NULL;
 
@@ -277,44 +274,16 @@ 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));
 
-  set_dest_model (GTK_TREE_VIEW (ex_d.dep_list), vs->dict);
-  ex_d.dict = vs->dict;
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (dep_selector),
-                                source,
-                                ex_d.dep_list,
-                                insert_source_row_into_tree_view,
-                                NULL, NULL);
+  g_object_get (vs, "dictionary", &ex_d.dict, NULL);
+  g_object_set (source, "model", ex_d.dict, NULL);
 
   psppire_selector_set_allow (PSPPIRE_SELECTOR (dep_selector),
                              numeric_only);
 
-  set_dest_model (GTK_TREE_VIEW (ex_d.fct_list), vs->dict);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (fct_selector),
-                                source,
-                                ex_d.fct_list,
-                                insert_source_row_into_tree_view,
-                                NULL, NULL);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (id_selector),
-                                source,
-                                ex_d.id_entry,
-                                insert_source_row_into_entry,
-                                NULL, NULL);
-
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &ex_d);
 
   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog),
@@ -346,12 +315,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);
 
diff --git a/src/ui/gui/examine.glade b/src/ui/gui/examine.glade
deleted file mode 100644 (file)
index 0078539..0000000
+++ /dev/null
@@ -1,449 +0,0 @@
-<?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-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="examine-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">Explore</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <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">5</property>
-            <child>
-              <widget class="GtkTable" id="table1">
-                <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="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">
-                    <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">
-                        <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">
-                            <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">
-                        <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="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <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="shadow_type">GTK_SHADOW_NONE</property>
-                    <child>
-                      <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="left_padding">12</property>
-                        <child>
-                          <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>
-                            <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="treeview3">
-                                <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="label2">
-                        <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">Factor List:</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </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="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="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="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>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <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">Label Cases by:</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <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>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkHButtonBox" id="hbuttonbox1">
-                <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                <child>
-                  <widget class="GtkButton" id="stats-button">
-                    <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">Statistics...</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkButton" id="opts-button">
-                    <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">Options...</property>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="padding">5</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
-            <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>
-          </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="PsppireDialog" id="statistics-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">Explore: Statistics</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_VERTICAL</property>
-    <child internal-child="hbox">
-      <widget class="GtkVBox" 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>
-        <property name="spacing">2</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="homogeneous">True</property>
-            <child>
-              <widget class="GtkCheckButton" id="descriptives-button">
-                <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">Descriptives</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkCheckButton" id="extremes-button">
-                <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">Extremes</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkCheckButton" id="percentiles-button">
-                <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">Percentiles</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireHButtonBox" id="psppire-hbuttonbox1">
-            <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="options-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">Explore: Options</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <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>
-            <child>
-              <widget class="GtkAlignment" id="alignment5">
-                <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="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="GtkRadioButton" id="radiobutton1">
-                        <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">Exclude cases listwise</property>
-                        <property name="active">True</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton2">
-                        <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">Exclude cases pairwise</property>
-                        <property name="active">True</property>
-                        <property name="draw_indicator">True</property>
-                        <property name="group">radiobutton1</property>
-                      </widget>
-                      <packing>
-                        <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="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="active">True</property>
-                        <property name="draw_indicator">True</property>
-                        <property name="group">radiobutton1</property>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label5">
-                <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">Missing Values</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
diff --git a/src/ui/gui/examine.ui b/src/ui/gui/examine.ui
new file mode 100644 (file)
index 0000000..316f104
--- /dev/null
@@ -0,0 +1,481 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="examine-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">Explore</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object class="GtkTable" id="table1">
+                <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="n_rows">3</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <object 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="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object 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>
+                          <object 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>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object 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">Label Cases by:</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <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>
+                  <object 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>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object 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="left_padding">12</property>
+                        <child>
+                          <object 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>
+                            <property name="hscrollbar_policy">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="PsppireVarView" id="treeview3">
+                                <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>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label2">
+                        <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">Factor List:</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <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>
+                  <object 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>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object 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>
+                          <object 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">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="PsppireVarView" 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>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object 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">Dependent List:</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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>
+                    <property name="source_widget">treeview1</property>
+                    <property name="dest_widget">entry1</property>
+                  </object>
+                  <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>
+                  <object 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>
+                    <property name="source_widget">treeview1</property>
+                    <property name="dest_widget">treeview3</property>
+                  </object>
+                  <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>
+                  <object 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>
+                    <property name="primary">True</property>
+                    <property name="source_widget">treeview1</property>
+                    <property name="dest_widget">treeview2</property>
+                  </object>
+                  <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>
+                  <object 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">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object 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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHButtonBox" id="hbuttonbox1">
+                <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="layout_style">spread</property>
+                <child>
+                  <object class="GtkButton" id="stats-button">
+                    <property name="label" translatable="yes">Statistics...</property>
+                    <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>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="opts-button">
+                    <property name="label" translatable="yes">Options...</property>
+                    <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>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">5</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="statistics-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">Explore: Statistics</property>
+    <property name="modal">True</property>
+    <property name="orientation">Vertical</property>
+    <child internal-child="hbox">
+      <object class="GtkVBox" 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>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkCheckButton" id="descriptives-button">
+                <property name="label" translatable="yes">Descriptives</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="extremes-button">
+                <property name="label" translatable="yes">Extremes</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="percentiles-button">
+                <property name="label" translatable="yes">Percentiles</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireHButtonBox" id="psppire-hbuttonbox1">
+            <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="layout_style">spread</property>
+            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="options-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">Explore: Options</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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>
+            <child>
+              <object class="GtkAlignment" id="alignment5">
+                <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>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton1">
+                        <property name="label" translatable="yes">Exclude cases listwise</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton2">
+                        <property name="label" translatable="yes">Exclude cases pairwise</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton1</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton3">
+                        <property name="label" translatable="yes">Repeat values</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton1</property>
+                      </object>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label5">
+                <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">Missing Values</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
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..40e91bc0179f1f801a8d22086725735f5810af48 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);
@@ -220,8 +219,9 @@ find_dialog (GObject *o, gpointer data)
                "data-store", &ds,
                NULL);
 
-  fd.dict = vs->dict;
-  fd.data = ds->case_file->datasheet;
+  g_object_get (vs, "dictionary", &fd.dict, NULL);
+
+  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,21 +239,16 @@ 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));
+
 
+  g_object_set (source, "model", fd.dict,
+       "selection-mode", GTK_SELECTION_SINGLE,
+       NULL);
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                fd.dict,
-                                GTK_SELECTION_SINGLE,
-                                NULL);
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                fd.variable_entry,
-                                insert_source_row_into_entry,
-                                is_currently_in_entry,
-                                NULL
-                                );
+  psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector),
+                                   is_currently_in_entry);
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &fd);
 
@@ -287,7 +282,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 +296,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 +341,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 +424,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 +437,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 +460,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 +490,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 +528,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,46 +572,33 @@ 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 ;
   struct value_comparator *vc = xzalloc (sizeof (*vc));
   struct comparator *cmptr = (struct comparator *) vc;
 
   cmptr->flags = 0;
   cmptr->var = var;
   cmptr->compare  = value_compare ;
-  cmptr->destroy = value_destroy;
-
-  width = var_get_width (var);
-  fmt = var_get_write_format (var);
+  cmptr->destroy = cmptr_value_destroy;
+  cmptr->dict = dict;
 
-  vc->pattern = value_create (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, dict, var, &vc->pattern);
 
   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 +606,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 +620,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 +629,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 +679,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 +730,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 +744,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 +755,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.ui b/src/ui/gui/find.ui
new file mode 100644 (file)
index 0000000..d1cb203
--- /dev/null
@@ -0,0 +1,264 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2053.63976"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object 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">
+      <object 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>
+          <object 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>
+              <object 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">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object 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>
+                    <property name="source_widget">find-variable-treeview</property>
+                    <property name="dest_widget">find-variable-entry</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object 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>
+                      <object 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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="padding">5</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object 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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="padding">5</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="find-value-labels-checkbutton">
+                        <property name="label" translatable="yes">Search value labels</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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>
+                      <object class="GtkCheckButton" id="find-match-regexp-checkbutton">
+                        <property name="label" translatable="yes">Regular expression Match</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="find-match-substring-checkbutton">
+                        <property name="label" translatable="yes">Search substrings</property>
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="find-wrap">
+                        <property name="label" translatable="yes">Wrap around</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="find-backwards">
+                        <property name="label" translatable="yes">Search backward</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object 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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index 802a4664e710b8a2352d9938af4dba1930e2c6d6..2d5fcbb48d9f23485ab3b093c0bfe80fa273b80c 100644 (file)
 
 #include "checkbox-treeview.h"
 #include "frequencies-dialog.h"
+#include "psppire-var-view.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 "executor.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -147,7 +148,7 @@ generate_syntax (const struct frequencies_dialog *fd)
   GString *string = g_string_new ("FREQUENCIES");
 
   g_string_append (string, "\n\t/VARIABLES=");
-  append_variable_names (string, fd->dict, GTK_TREE_VIEW (fd->stat_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (fd->stat_vars), 0, string);
 
   g_string_append (string, "\n\t/FORMAT=");
 
@@ -307,16 +308,15 @@ 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");
   GtkWidget *dest =   get_widget_assert   (xml, "var-treeview");
-  GtkWidget *selector = get_widget_assert (xml, "selector1");
   GtkWidget *format_button = get_widget_assert (xml, "button1");
   GtkWidget *stats_treeview = get_widget_assert (xml, "stats-treeview");
 
@@ -331,25 +331,12 @@ frequencies_dialog (GObject *o, gpointer data)
                                  );
 
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
-
-  set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                dest,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
+  g_object_get (vs, "dictionary", &fd.dict, NULL);
+  g_object_set (source, "model", fd.dict, NULL);
 
   fd.stat_vars = GTK_TREE_VIEW (dest);
-  fd.dict = vs->dict;
   fd.table_button = get_widget_assert (xml, "checkbutton1");
   fd.format_dialog = get_widget_assert (xml, "format-dialog");
   fd.maximum_cats = get_widget_assert (xml, "hbox5");
@@ -369,7 +356,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 +380,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 +390,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);
 
diff --git a/src/ui/gui/frequencies.glade b/src/ui/gui/frequencies.glade
deleted file mode 100644 (file)
index 5617d40..0000000
+++ /dev/null
@@ -1,429 +0,0 @@
-<?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 Nov 30 15:42:55 2007 by john@marilyn-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="frequencies-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">Frequencies</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <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>
-            <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>
-                <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="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>
-                </child>
-                <child>
-                  <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="yalign">0.05000000074505806</property>
-                    <property name="yscale">0</property>
-                    <child>
-                      <widget class="PsppireSelector" id="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="no_show_all">True</property>
-                        <property name="border_width">5</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="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>
-                    <child>
-                      <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>
-                        <property name="shadow_type">GTK_SHADOW_NONE</property>
-                        <child>
-                          <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="var-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>
-                        </child>
-                        <child>
-                          <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">Variable(s):</property>
-                            <property name="use_markup">True</property>
-                          </widget>
-                          <packing>
-                            <property name="type">label_item</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <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>
-                        <property name="shadow_type">GTK_SHADOW_NONE</property>
-                        <child>
-                          <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>
-                            <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="stats-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>
-                        </child>
-                        <child>
-                          <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">Statistics:</property>
-                            <property name="use_markup">True</property>
-                          </widget>
-                          <packing>
-                            <property name="type">label_item</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <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="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>
-                <child>
-                  <widget class="GtkCheckButton" id="checkbutton1">
-                    <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">Display Frequency Table</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>
-                    <property name="spacing">5</property>
-                    <property name="homogeneous">True</property>
-                    <child>
-                      <widget class="GtkButton" id="button1">
-                        <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">Format...</property>
-                        <property name="response_id">0</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="padding">5</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="padding">5</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
-            <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>
-          </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="PsppireDialog" id="format-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">Frequencies: Format</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkHBox" id="hbox4">
-            <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="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="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="GtkVButtonBox" id="vbuttonbox1">
-                        <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="homogeneous">True</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton1">
-                            <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 Order</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton2">
-                            <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">Descending Order</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <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="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 Counts</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <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="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 Counts</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <property name="position">3</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label2">
-                    <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">Order by</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <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="yalign">0</property>
-                <property name="yscale">0</property>
-                <property name="top_padding">5</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>
-                    <child>
-                      <widget class="GtkCheckButton" id="checkbutton2">
-                        <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="draw_indicator">True</property>
-                        <child>
-                          <widget class="GtkLabel" id="label4">
-                            <property name="width_request">180</property>
-                            <property name="visible">True</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="label" translatable="yes">Supress tables with more than N categories</property>
-                            <property name="wrap">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkHBox" id="hbox5">
-                        <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">
-                            <property name="width_request">120</property>
-                            <property name="visible">True</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">Maximum no of categories</property>
-                            <property name="wrap">True</property>
-                            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkSpinButton" id="spinbutton1">
-                            <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">100 1 65536 1 10 10</property>
-                            <property name="numeric">True</property>
-                            <property name="update_policy">GTK_UPDATE_IF_VALID</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</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>
-              <packing>
-                <property name="expand">False</property>
-                <property name="padding">5</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
diff --git a/src/ui/gui/frequencies.ui b/src/ui/gui/frequencies.ui
new file mode 100644 (file)
index 0000000..0ab2f29
--- /dev/null
@@ -0,0 +1,466 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.22072"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="frequencies-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">Frequencies</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <child>
+              <object 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>
+                <child>
+                  <object 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">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object 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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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="yalign">0.05000000074505806</property>
+                    <property name="yscale">0</property>
+                    <child>
+                      <object class="PsppireSelector" id="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="no_show_all">True</property>
+                        <property name="border_width">5</property>
+                        <property name="source_widget">dict-treeview</property>
+                        <property name="dest_widget">var-treeview</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object 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="label_xalign">0</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <object 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">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="PsppireVarView" id="var-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>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object 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">Variable(s):</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <object 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>
+                            <property name="hscrollbar_policy">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="GtkTreeView" id="stats-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>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="label2">
+                            <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">Statistics:</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <child>
+                  <object class="GtkCheckButton" id="checkbutton1">
+                    <property name="label" translatable="yes">Display Frequency Table</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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">5</property>
+                    <property name="homogeneous">True</property>
+                    <child>
+                      <object class="GtkButton" id="button1">
+                        <property name="label" translatable="yes">Format...</property>
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">5</property>
+                    <property name="pack_type">end</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">5</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="format-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">Frequencies: Format</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkHBox" id="hbox4">
+            <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>
+              <object 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>
+                  <object 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>
+                      <object class="GtkVButtonBox" id="vbuttonbox1">
+                        <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="homogeneous">True</property>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton1">
+                            <property name="label" translatable="yes">Ascending Order</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton2">
+                            <property name="label" translatable="yes">Descending Order</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton3">
+                            <property name="label" translatable="yes">Ascending Counts</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton4">
+                            <property name="label" translatable="yes">Descending Counts</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">Order by</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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="yalign">0</property>
+                <property name="yscale">0</property>
+                <property name="top_padding">5</property>
+                <child>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkCheckButton" id="checkbutton2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                        <child>
+                          <object class="GtkLabel" id="label4">
+                            <property name="width_request">180</property>
+                            <property name="visible">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="label" translatable="yes">Supress tables with more than N categories</property>
+                            <property name="wrap">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox5">
+                        <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>
+                          <object class="GtkLabel" id="label5">
+                            <property name="width_request">120</property>
+                            <property name="visible">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Maximum no of categories</property>
+                            <property name="wrap">True</property>
+                            <property name="wrap_mode">word-char</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="spinbutton1">
+                            <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">adjustment1</property>
+                            <property name="numeric">True</property>
+                            <property name="update_policy">if-valid</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="padding">5</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="padding">5</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="value">100</property>
+    <property name="lower">1</property>
+    <property name="upper">65536</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+</interface>
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..b09a2f58c3f1cc7bff73e36aecbb4775a81c1049 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;
 }
 
 
+/* Converts TEXT to a value.
+
+   VAL will be initialised and filled by this function.
+   It is the caller's responsibility to destroy VAL when no longer needed.
+   VAR and DICT must be the variable and dictionary with which VAL
+   is associated.
 
-gboolean
-text_to_value (const gchar *text, union value *v,
-             struct fmt_spec format)
+   On success, VAL is returned, NULL otherwise.
+*/
+union value *
+text_to_value (const gchar *text,
+              const PsppireDict *dict,
+              const struct variable *var,
+              union value *val)
 {
-  bool ok;
+  const struct fmt_spec *format = var_get_print_format (var);
+  int width = var_get_width (var);
 
-  if ( format.type != FMT_A)
+  if ( format->type != FMT_A)
     {
-      if ( ! text ) return FALSE;
+      if ( ! text ) return NULL;
 
       {
        const gchar *s = text;
@@ -87,42 +91,96 @@ text_to_value (const gchar *text, union value *v,
            s++;
          }
 
-       if ( !*s) return FALSE;
+       if ( !*s) return NULL;
       }
     }
 
+  value_init (val, width);
   msg_disable ();
-  ok = data_in (ss_cstr (text), LEGACY_NATIVE, format.type, 0, 0, 0,
-                v, fmt_var_width (&format));
+  data_in (ss_cstr (text), UTF8, format->type, 0, 0, 0,
+               dict->dict,
+                val, width);
   msg_enable ();
 
-  return ok;
+  return val;
 }
 
 
-GtkWidget *
-get_widget_assert (GladeXML *xml, const gchar *name)
+GtkBuilder *
+builder_new_real (const gchar *name)
+{
+  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)
 {
-  GtkWidget *w;
-  g_assert (xml);
+  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));
 }
 
-/* Converts a string in the pspp locale to utf-8.
-   The return value must be freed when no longer required*/
+GtkWidget *
+get_widget_assert (GtkBuilder *builder, const gchar *name)
+{
+  return GTK_WIDGET (get_object_assert (builder, name, GTK_TYPE_WIDGET));
+}
+
+/* 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 +201,52 @@ give_help (void)
 }
 
 void
-connect_help (GladeXML *xml)
+connect_help (GtkBuilder *xml)
 {
-  GList *helps = glade_xml_get_widget_prefix (xml, "help_button_");
-
-  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);
-}
-
+  GSList *helps = gtk_builder_get_objects (xml);
 
-
-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);
 
+         if ( name)
+           strncpy (s, name, 11);
+         s[11] = '\0';
 
-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;
-  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 +291,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..dfdd89323a7bd5c7a9982f1f03b4aef133b601d8 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,
-                      struct fmt_spec format);
 
-GtkWidget * get_widget_assert (GladeXML *xml, const gchar *name);
+union value *
+text_to_value (const gchar *text,
+              const PsppireDict *dict,
+              const struct variable *var,
+              union value *);
 
-/* 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;
 }
 
diff --git a/src/ui/gui/message-dialog.glade b/src/ui/gui/message-dialog.glade
deleted file mode 100644 (file)
index 719c1aa..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?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 -->
-<glade-interface>
-  <widget class="GtkDialog" id="message-dialog">
-    <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="title" translatable="yes">Messages Reported</property>
-    <property name="modal">True</property>
-    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="has_separator">False</property>
-    <child internal-child="vbox">
-      <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>
-        <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>
-            <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="stock">gtk-dialog-info</property>
-                <property name="icon_size">6</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-              </packing>
-            </child>
-            <child>
-              <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>
-                <child>
-                  <widget class="GtkLabel" id="lead-in">
-                    <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">The PSPP processor reported # errors.  The first # and last # are shown below:</property>
-                    <property name="wrap">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</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_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                    <child>
-                      <widget class="GtkTextView" id="message">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="editable">False</property>
-                        <property name="wrap_mode">GTK_WRAP_WORD</property>
-                        <property name="cursor_visible">False</property>
-                      </widget>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area1">
-            <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="layout_style">GTK_BUTTONBOX_END</property>
-            <child>
-              <widget class="GtkButton" id="button1">
-                <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">gtk-close</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-</glade-interface>
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
diff --git a/src/ui/gui/message-dialog.ui b/src/ui/gui/message-dialog.ui
new file mode 100644 (file)
index 0000000..540a390
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object 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">12</property>
+    <property name="title" translatable="yes">Messages Reported</property>
+    <property name="modal">True</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object 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="orientation">vertical</property>
+        <property name="spacing">24</property>
+        <child>
+          <object 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>
+              <object 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>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="lead-in">
+                    <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">The PSPP processor reported # errors.  The first # and last # are shown below:</property>
+                    <property name="wrap">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object class="GtkTextView" id="message">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="editable">False</property>
+                        <property name="wrap_mode">word</property>
+                        <property name="left_margin">6</property>
+                        <property name="right_margin">6</property>
+                        <property name="cursor_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <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="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="close-button">
+                <property name="label">gtk-close</property>
+                <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="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">close-button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
index 4e9682f73544bcdcfae24cf26453b51d3482d8e6..7e04b8548e02fb6cd02602645e2b8fbd99287b59 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>
 
@@ -81,8 +80,6 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
 {
   struct missing_val_dialog *dialog = data;
 
-  const struct fmt_spec *write_spec = var_get_write_format (dialog->pv);
-
   if ( gtk_toggle_button_get_active (dialog->button_discrete))
     {
       gint nvals = 0;
@@ -101,7 +98,7 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
              continue;
            }
 
-         if ( text_to_value (text, &v, *write_spec))
+         if ( text_to_value (text, dialog->dict, dialog->pv, &v))
            {
              nvals++;
              mv_add_value (&dialog->mvl, &v);
@@ -109,6 +106,7 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
          else
              badvals++;
          g_free (text);
+         value_destroy (&v, var_get_width (dialog->pv));
        }
       if ( nvals == 0 || badvals > 0 )
        {
@@ -127,14 +125,16 @@ 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, dialog->dict, dialog->pv, &low_val)
           &&
-          text_to_value (high_text, &high_val, *write_spec) )
+          text_to_value (high_text, dialog->dict, dialog->pv, &high_val))
        {
          if ( low_val.f > high_val.f )
            {
              err_dialog (_("Incorrect range specification"),
                          GTK_WINDOW (dialog->window));
+             value_destroy (&low_val, var_get_width (dialog->pv));
+             value_destroy (&high_val, var_get_width (dialog->pv));
              return ;
            }
        }
@@ -142,6 +142,8 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
        {
          err_dialog (_("Incorrect range specification"),
                      GTK_WINDOW (dialog->window));
+         value_destroy (&low_val, var_get_width (dialog->pv));
+         value_destroy (&high_val, var_get_width (dialog->pv));
          return;
        }
 
@@ -151,18 +153,25 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data)
       mv_clear (&dialog->mvl);
       mv_add_range (&dialog->mvl, low_val.f, high_val.f);
 
+      value_destroy (&low_val, var_get_width (dialog->pv));
+      value_destroy (&high_val, var_get_width (dialog->pv));
+
       if ( discrete_text && strlen (g_strstrip (discrete_text)) > 0 )
        {
          union value discrete_val;
-         if ( !text_to_value (discrete_text, &discrete_val,
-                             *write_spec))
+         if ( !text_to_value (discrete_text, 
+                              dialog->dict,
+                              dialog->pv,
+                              &discrete_val))
            {
              err_dialog (_("Incorrect value for variable type"),
                         GTK_WINDOW (dialog->window) );
              g_free (discrete_text);
+             value_destroy (&discrete_val, var_get_width (dialog->pv));
              return;
            }
          mv_add_value (&dialog->mvl, &discrete_val);
+         value_destroy (&discrete_val, var_get_width (dialog->pv));
        }
       g_free (discrete_text);
     }
@@ -219,20 +228,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 +269,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 +319,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 +331,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 +351,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..57428667a2480a63b5a2956ab7e1a927169594d8 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-
 #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 "psppire-var-view.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,75 +120,58 @@ 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");
-
-  GtkWidget *selector1 =
-    get_widget_assert (xml, "oneway-anova-selector1");
+    get_widget_assert (builder, "oneway-anova-selector2");
 
   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;
+  g_object_get (vs, "dictionary", &ow.dict, NULL);
 
   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, "model", ow.dict, NULL);
 
-  set_dest_model (GTK_TREE_VIEW (ow.vars_treeview), vs->dict);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector1),
-                                dict_view, ow.vars_treeview,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector2),
-                                dict_view, ow.factor_entry,
-                                insert_source_row_into_entry,
-                                is_currently_in_entry,
-                                NULL);
 
+  psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector2),
+                                   is_currently_in_entry);
 
 
   g_signal_connect_swapped (ow.dialog, "refresh", G_CALLBACK (refresh),  &ow);
@@ -202,16 +184,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 +204,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 +214,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 +224,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 +235,7 @@ oneway_anova_dialog (GObject *o, gpointer data)
 
   g_array_free (ow.contrasts_array, FALSE);
 
-  g_object_unref (xml);
+  g_object_unref (builder);
 }
 
 
@@ -269,7 +248,7 @@ static gchar * generate_syntax (const struct oneway_anova_dialog *ow)
 
   GString *str = g_string_new ("ONEWAY /VARIABLES=");
 
-  append_variable_names (str, ow->dict, GTK_TREE_VIEW (ow->vars_treeview), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (ow->vars_treeview), 0, str);
 
   g_string_append (str, " BY ");
 
diff --git a/src/ui/gui/oneway.glade b/src/ui/gui/oneway.glade
deleted file mode 100644 (file)
index bdca13d..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-<?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="oneway-anova-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">One-Way ANOVA</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox15">
-        <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="GtkTable" id="table4">
-            <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="n_rows">3</property>
-            <property name="n_columns">3</property>
-            <child>
-              <widget class="GtkVBox" id="vbox30">
-                <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="label36">
-                    <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">_Factor:</property>
-                    <property name="use_underline">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="oneway-anova-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="position">1</property>
-                  </packing>
-                </child>
-              </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>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <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>
-                  <widget class="GtkLabel" id="label35">
-                    <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">Dependent _Variable(s):</property>
-                    <property name="use_underline">True</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkScrolledWindow" id="scrolledwindow15">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                    <child>
-                      <widget class="GtkTreeView" id="oneway-anova-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>
-                      </widget>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireSelector" id="oneway-anova-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="oneway-anova-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">GTK_EXPAND</property>
-              </packing>
-            </child>
-            <child>
-              <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="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>
-                    <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="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">5</property>
-                <child>
-                  <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="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="left_padding">12</property>
-                        <child>
-                          <widget class="GtkVButtonBox" id="vbuttonbox1">
-                            <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="checkbutton1">
-                                <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">_Descriptives</property>
-                                <property name="use_underline">True</property>
-                                <property name="response_id">0</property>
-                                <property name="draw_indicator">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkCheckButton" id="checkbutton2">
-                                <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">_Homogeneity</property>
-                                <property name="use_underline">True</property>
-                                <property name="response_id">0</property>
-                                <property name="draw_indicator">True</property>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <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">Statistics</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVButtonBox" id="vbuttonbox2">
-                    <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="GtkButton" id="contrasts-button">
-                        <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">_Contrasts...</property>
-                        <property name="use_underline">True</property>
-                        <property name="response_id">0</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                  </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">3</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-                <property name="y_options"></property>
-                <property name="x_padding">5</property>
-                <property name="y_padding">5</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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>
-          </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="PsppireDialog" id="contrasts-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">One-Way ANOVA: Contrasts</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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="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>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="top_padding">5</property>
-                <property name="bottom_padding">5</property>
-                <property name="left_padding">5</property>
-                <property name="right_padding">5</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">5</property>
-                    <child>
-                      <widget class="GtkHButtonBox" id="hbuttonbox1">
-                        <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="homogeneous">True</property>
-                        <property name="layout_style">GTK_BUTTONBOX_EDGE</property>
-                        <child>
-                          <widget class="GtkButton" id="prev-button">
-                            <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">gtk-go-back</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="next-button">
-                            <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">gtk-go-forward</property>
-                            <property name="use_stock">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <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>
-                        <child>
-                          <widget class="GtkLabel" id="label4">
-                            <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">1</property>
-                            <property name="label" translatable="yes">_Coefficients:</property>
-                            <property name="use_underline">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <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>
-                          </widget>
-                          <packing>
-                            <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>
-                    <child>
-                      <widget class="PsppireAcr" id="psppire-acr1">
-                        <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>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                    <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>
-                        <child>
-                          <widget class="GtkLabel" id="label2">
-                            <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">1</property>
-                            <property name="label" translatable="yes">Coefficient Total: </property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="entry2">
-                            <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="editable">False</property>
-                            <property name="max_length">5</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">3</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="contrast-stack-label">
-                <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">Contrast 1 of 1</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
diff --git a/src/ui/gui/oneway.ui b/src/ui/gui/oneway.ui
new file mode 100644 (file)
index 0000000..7381932
--- /dev/null
@@ -0,0 +1,480 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="oneway-anova-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">One-Way ANOVA</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox15">
+        <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>
+          <object class="GtkTable" id="table4">
+            <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="n_rows">3</property>
+            <property name="n_columns">3</property>
+            <child>
+              <object class="GtkVBox" id="vbox30">
+                <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="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="label36">
+                    <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">_Factor:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="oneway-anova-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>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <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>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="label35">
+                    <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">Dependent _Variable(s):</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow15">
+                    <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">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object class="PsppireVarView" id="oneway-anova-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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="PsppireSelector" id="oneway-anova-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>
+                <property name="source_widget">oneway-anova-treeview1</property>
+                <property name="dest_widget">oneway-anova-entry</property>
+              </object>
+              <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>
+              <object class="PsppireSelector" id="oneway-anova-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>
+                <property name="primary">True</property>
+                <property name="source_widget">oneway-anova-treeview1</property>
+                <property name="dest_widget">oneway-anova-treeview2</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="x_options"></property>
+                <property name="y_options">GTK_EXPAND</property>
+              </packing>
+            </child>
+            <child>
+              <object 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">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object 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">5</property>
+                <child>
+                  <object 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>
+                      <object 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="left_padding">12</property>
+                        <child>
+                          <object class="GtkVButtonBox" id="vbuttonbox1">
+                            <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>
+                              <object class="GtkCheckButton" id="checkbutton1">
+                                <property name="label" translatable="yes">_Descriptives</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="checkbutton2">
+                                <property name="label" translatable="yes">_Homogeneity</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object 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">Statistics</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVButtonBox" id="vbuttonbox2">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkButton" id="contrasts-button">
+                        <property name="label" translatable="yes">_Contrasts...</property>
+                        <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="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">3</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options"></property>
+                <property name="x_padding">5</property>
+                <property name="y_padding">5</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="contrasts-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">One-Way ANOVA: Contrasts</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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>
+          <object 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>
+              <object 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="top_padding">5</property>
+                <property name="bottom_padding">5</property>
+                <property name="left_padding">5</property>
+                <property name="right_padding">5</property>
+                <child>
+                  <object 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="orientation">vertical</property>
+                    <property name="spacing">5</property>
+                    <child>
+                      <object class="GtkHButtonBox" id="hbuttonbox1">
+                        <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="homogeneous">True</property>
+                        <property name="layout_style">edge</property>
+                        <child>
+                          <object class="GtkButton" id="prev-button">
+                            <property name="label">gtk-go-back</property>
+                            <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="use_stock">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="next-button">
+                            <property name="label">gtk-go-forward</property>
+                            <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="use_stock">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                        <child>
+                          <object class="GtkLabel" id="label4">
+                            <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">1</property>
+                            <property name="label" translatable="yes">_Coefficients:</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object 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>
+                          </object>
+                          <packing>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="PsppireAcr" id="psppire-acr1">
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <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">1</property>
+                            <property name="label" translatable="yes">Coefficient Total: </property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="entry2">
+                            <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="editable">False</property>
+                            <property name="max_length">5</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="contrast-stack-label">
+                <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">Contrast 1 of 1</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <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="orientation">vertical</property>
+            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/ui/gui/output-viewer.c b/src/ui/gui/output-viewer.c
deleted file mode 100644 (file)
index 09fd4f6..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/* PSPPIRE - a graphical user interface for PSPP.
-   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 <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..58aa0b8f91f740ba2630763ac7703cad8a243148 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_ROW_MENU,
-                                   row_menu_spec);
+                                   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_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), text);
 
-         gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), s);
-
-         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);
+
+
+  psppire_data_editor_remove_split (de);
 
-  gtk_widget_show_all (vbox);
+  gtk_widget_show_all (de->data_vbox);
 
-  gtk_notebook_append_page (GTK_NOTEBOOK (de), vbox,
+  gtk_notebook_append_page (GTK_NOTEBOOK (de), de->data_vbox,
                            gtk_label_new_with_mnemonic (_("Data View")));
 
   gtk_notebook_append_page (GTK_NOTEBOOK (de), sw_vs,
                            gtk_label_new_with_mnemonic (_("Variable View")));
 
-  g_signal_connect (de->data_sheet, "activate",
+  g_signal_connect (de->data_sheet[0], "activate",
                    G_CALLBACK (update_data_ref_entry),
                    de);
 
@@ -628,7 +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);
+}
+
+
+static void
+psppire_data_editor_remove_split (PsppireDataEditor *de)
+{
+  if ( !de->split ) return;
+  de->split = FALSE;
+
+  g_object_ref (de->sheet_bin[0]);
+  gtk_container_remove (GTK_CONTAINER (de->paned), de->sheet_bin[0]);
+
+  g_object_ref (de->paned);
+  gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->paned);
+
+  gtk_box_pack_start (GTK_BOX (de->data_vbox), de->sheet_bin[0],
+                     TRUE, TRUE, 0);
+
+  g_object_unref (de->sheet_bin[0]);
+
+  g_object_set (de->sheet_bin[0], "vscrollbar-policy",
+               GTK_POLICY_ALWAYS, NULL);
+
+  g_object_set (de->sheet_bin[0], "hscrollbar-policy",
+               GTK_POLICY_ALWAYS, NULL);
+}
+
+
+static void
+psppire_data_editor_set_split (PsppireDataEditor *de)
+{
+  if ( de->split ) return;
+  de->split = TRUE;
+
+  g_object_ref (de->sheet_bin[0]);
+  gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->sheet_bin[0]);
 
-  widget =  g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
-                         "var-store",  var_store,
-                         "data-store",  data_store,
-                         NULL);
+  gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin [0],
+                           TRUE, TRUE);
 
-  g_signal_connect (PSPPIRE_DATA_EDITOR(widget)->data_sheet, "traverse",
-                   G_CALLBACK (traverse_cell_callback), data_store);
+  gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned,
+                     TRUE, TRUE, 0);
 
-  return widget;
+  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);
 }
 
-static void data_sheet_set_clip (GtkSheet *sheet);
+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,47 @@ psppire_data_editor_clip_cut (PsppireDataEditor *de)
 /* Popup menu related stuff */
 
 static void
-popup_variable_menu (GtkSheet *sheet, gint column,
+popup_variable_column_menu (PsppireSheet *sheet, gint column,
                     GdkEventButton *event, gpointer data)
 {
   GtkMenu *menu = GTK_MENU (data);
 
   PsppireDataStore *data_store =
-    PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
+    PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
 
   const struct variable *v =
     psppire_dict_get_variable (data_store->dict, column);
 
   if ( v && event->button == 3)
     {
-      gtk_sheet_select_column (sheet, column);
+      psppire_sheet_select_column (sheet, column);
+
+      gtk_menu_popup (menu,
+                     NULL, NULL, NULL, NULL,
+                     event->button, event->time);
+    }
+}
+
+
+static void
+popup_variable_row_menu (PsppireSheet *sheet, gint row,
+                    GdkEventButton *event, gpointer data)
+{
+  GtkMenu *menu = GTK_MENU (data);
+
+  PsppireVarStore *var_store =
+    PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
+  
+  PsppireDict *dict;
+  const struct variable *v ;
+
+  g_object_get (var_store, "dictionary", &dict, NULL);
+
+  v = psppire_dict_get_variable (dict, row);
+
+  if ( v && event->button == 3)
+    {
+      psppire_sheet_select_row (sheet, row);
 
       gtk_menu_popup (menu,
                      NULL, NULL, NULL, NULL,
@@ -804,18 +1223,18 @@ popup_variable_menu (GtkSheet *sheet, gint column,
 
 
 static void
-popup_cases_menu (GtkSheet *sheet, gint row,
+popup_cases_menu (PsppireSheet *sheet, gint row,
                  GdkEventButton *event, gpointer data)
 {
   GtkMenu *menu = GTK_MENU (data);
 
   PsppireDataStore *data_store =
-    PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
+    PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
 
   if ( row <= psppire_data_store_get_case_count (data_store) &&
        event->button == 3)
     {
-      gtk_sheet_select_row (sheet, row);
+      psppire_sheet_select_row (sheet, row);
 
       gtk_menu_popup (menu,
                      NULL, NULL, NULL, NULL,
@@ -853,8 +1272,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 +1284,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 +1298,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 +1330,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 +1348,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
@@ -924,42 +1361,61 @@ psppire_data_editor_delete_cases    (PsppireDataEditor *de)
 void
 psppire_data_editor_delete_variables (PsppireDataEditor *de)
 {
+  PsppireDict *dict = NULL;
   gint first, n;
 
   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 ();
       break;
     }
 
-  psppire_dict_delete_variables (de->var_store->dict, first, n);
+  g_object_get (de->var_store, "dictionary", &dict, NULL);
+
+  psppire_dict_delete_variables (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 +1430,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 +1450,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 +1470,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 +1495,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 +1510,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 +1524,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,57 +1554,59 @@ 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;
+  gint row0, rowi;
+  gint col0, coli;
 
-  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);
+
+  col0 = MIN (range.col0, range.coli);
+  coli = MAX (range.col0, range.coli);
+  row0 = MIN (range.row0, range.rowi);
+  rowi = MAX (range.row0, range.rowi);
 
    /* If nothing selected, then use active cell */
-  if ( range.row0 < 0 || range.col0 < 0 )
+  if ( row0 < 0 || 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;
+      row0 = rowi = row;
+      col0 = coli = col;
     }
 
   /* The sheet range can include cells that do not include data.
      Exclude them from the range. */
   max_rows = psppire_data_store_get_case_count (ds);
-  if (range.rowi >= max_rows)
+  if (rowi >= max_rows)
     {
       if (max_rows == 0)
         return;
-      range.rowi = max_rows - 1;
+      rowi = max_rows - 1;
     }
   max_columns = dict_get_var_cnt (ds->dict->dict);
-  if (range.coli >= max_columns)
+  if (coli >= max_columns)
     {
       if (max_columns == 0)
         return;
-      range.coli = max_columns - 1;
+      coli = max_columns - 1;
     }
 
-  g_return_if_fail (range.rowi >= range.row0);
-  g_return_if_fail (range.row0 >= 0);
-  g_return_if_fail (range.coli >= range.col0);
-  g_return_if_fail (range.col0 >= 0);
-
   /* Destroy any existing clip */
   if ( clip_datasheet )
     {
@@ -1161,7 +1622,8 @@ data_sheet_set_clip (GtkSheet *sheet)
 
   /* Construct clip dictionary. */
   clip_dict = dict_create ();
-  for (i = range.col0; i <= range.coli; i++)
+  dict_set_encoding (clip_dict, dict_get_encoding (ds->dict->dict));
+  for (i = col0; i <= coli; i++)
     {
       const struct variable *old = dict_get_var (ds->dict->dict, i);
       dict_clone_var_assert (clip_dict, old, var_get_name (old));
@@ -1169,19 +1631,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));
-  for (i = range.row0; i <= range.rowi ; ++i )
+  writer = autopaging_writer_create (dict_get_proto (clip_dict));
+  for (i = row0; i <= 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 +1656,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 +1676,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 +1685,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 +1697,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 +1705,7 @@ clip_to_text (void)
       if ( r < case_cnt)
        g_string_append (string, "\n");
 
-      case_destroy (&cc);
+      case_unref (cc);
     }
 
   return string;
@@ -1263,20 +1718,22 @@ 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);
 
-
   /* Guestimate the size needed */
-  string = g_string_sized_new (20 * val_cnt * case_cnt);
+  string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
+
+  g_string_append (string,
+                  "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
 
   g_string_append (string, "<table>\n");
   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 +1744,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 +1811,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 +1848,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..e9a8946
--- /dev/null
@@ -0,0 +1,1935 @@
+/* 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 "roc-dialog.h"
+#include "correlation-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;
+      PsppireDict *dict = NULL;
+      struct variable *var ;
+      gchar *text ;
+
+      g_object_get (de->data_editor, "var-store", &vs, NULL);
+      g_object_get (vs, "dictionary", &dict, NULL);
+
+      var = psppire_dict_get_variable (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;
+      PsppireDict *dict = NULL;
+      gchar *text;
+
+      g_object_get (de->data_editor, "var-store", &vs, NULL);
+      g_object_get (vs, "dictionary", &dict, NULL);
+      
+      var = psppire_dict_get_variable (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;
+  PsppireDict *dict = NULL;
+
+  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_object_get (vs, "dictionary", &dict, NULL);
+
+  g_signal_connect (dict, "weight-changed",
+                   G_CALLBACK (on_weight_change),
+                   de);
+
+  g_signal_connect (dict, "filter-changed",
+                   G_CALLBACK (on_filter_change),
+                   de);
+
+  g_signal_connect (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,
+                 "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);
+  }
+
+  {
+    GtkAction *invoke_roc_dialog =
+      resolve_action (de->builder, "roc-curve", NULL);
+
+    g_object_set (invoke_roc_dialog,
+                 "tooltip", _("ROC Curve"),
+                 "stock-id", "pspp-roc",
+                 NULL
+                 );
+
+    g_signal_connect (invoke_roc_dialog, "activate",
+                     G_CALLBACK (roc_dialog), de);
+  }
+
+  {
+    GtkAction *invoke_correlation_dialog =
+      resolve_action (de->builder, "correlation", NULL);
+
+    g_object_set (invoke_correlation_dialog,
+                 "tooltip", _("Bivariate Correlation"),
+                 "stock-id", "pspp-correlation",
+                 NULL
+                 );
+
+    g_signal_connect (invoke_correlation_dialog, "activate",
+                     G_CALLBACK (correlation_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..9e2190b2909e181475309ed1c58e10b3ce304872 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-var-ptr.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 +40,7 @@ enum  {
   VARIABLE_RESIZED,
   VARIABLE_INSERTED,
   VARIABLE_DELETED,
+  VARIABLE_DISPLAY_WIDTH_CHANGED,
 
   WEIGHT_CHANGED,
   FILTER_CHANGED,
@@ -55,6 +48,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 +98,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 +155,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 +169,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 +237,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 +257,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 +280,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 +296,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 +316,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 +374,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 +435,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 +473,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 +542,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 );
 }
 
@@ -608,7 +649,7 @@ tree_model_column_type (GtkTreeModel *model, gint index)
       return G_TYPE_STRING;
       break;
     case DICT_TVM_COL_VAR:
-      return G_TYPE_POINTER;
+      return PSPPIRE_VAR_PTR_TYPE;
       break;
     default:
       g_return_val_if_reached ((GType)0);
@@ -720,15 +761,13 @@ 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:
-      g_value_init (value, G_TYPE_POINTER);
-      g_value_set_pointer (value, var);
+      g_value_init (value, PSPPIRE_VAR_PTR_TYPE);
+      g_value_set_boxed (value, var);
       break;
     default:
       g_return_if_reached ();
@@ -818,12 +857,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..4c07d1f
--- /dev/null
@@ -0,0 +1,558 @@
+/* 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_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_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 *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);
+
+  object_class->set_property = psppire_dict_view_set_property;
+  object_class->get_property = psppire_dict_view_get_property;
+
+  g_object_class_override_property (object_class,
+                                   PROP_DICTIONARY,
+                                   "model");
+
+  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_markup_printf_escaped (
+                                    "<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__ */
diff --git a/src/ui/gui/psppire-select-dest.c b/src/ui/gui/psppire-select-dest.c
new file mode 100644 (file)
index 0000000..c46abce
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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 "psppire-select-dest.h"
+#include <gtk/gtkwidget.h>
+
+GType
+psppire_select_dest_widget_get_type (void)
+{
+  static GType dest_widget_type = 0;
+
+  if (! dest_widget_type)
+    {
+      const GTypeInfo dest_widget_info =
+      {
+        sizeof (PsppireSelectDestWidgetIface), /* class_size */
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       NULL,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      dest_widget_type =
+       g_type_register_static (G_TYPE_INTERFACE, "PsppireSelectDestWidget",
+                               &dest_widget_info, 0);
+
+      g_type_interface_add_prerequisite (dest_widget_type, GTK_TYPE_WIDGET);
+    }
+
+  return dest_widget_type;
+}
+
+
+gboolean
+psppire_select_dest_widget_contains_var (PsppireSelectDestWidget *sdm, const GValue *value)
+{
+  return PSPPIRE_SELECT_DEST_GET_IFACE (sdm)->contains_var (sdm, value);
+}
diff --git a/src/ui/gui/psppire-select-dest.h b/src/ui/gui/psppire-select-dest.h
new file mode 100644 (file)
index 0000000..74f0165
--- /dev/null
@@ -0,0 +1,49 @@
+/* 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_SELECT_DEST_H__
+#define __PSPPIRE_SELECT_DEST_H__
+
+#include <glib-object.h>
+
+GType              psppire_select_dest_widget_get_type   (void) G_GNUC_CONST;
+
+#define PSPPIRE_TYPE_SELECT_DEST_WIDGET      (psppire_select_dest_widget_get_type ())
+#define PSPPIRE_SELECT_DEST_WIDGET(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_TYPE_SELECT_DEST_WIDGET, PsppireSelectDestWidget))
+#define PSPPIRE_IS_SELECT_DEST_WIDGET(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_SELECT_DEST_WIDGET))
+
+
+#define PSPPIRE_SELECT_DEST_GET_IFACE(obj) \
+   (G_TYPE_INSTANCE_GET_INTERFACE ((obj), PSPPIRE_TYPE_SELECT_DEST_WIDGET, PsppireSelectDestWidgetIface))
+
+typedef struct _PsppireSelectDestWidgetIface  PsppireSelectDestWidgetIface;
+
+
+typedef struct _PsppireSelectDestWidget  PsppireSelectDestWidget;  /* Dummy typedef */
+
+struct _PsppireSelectDestWidgetIface
+{
+  GTypeInterface g_iface;
+
+  /* Return TRUE iff DEST contains V */
+  gboolean (*contains_var) (PsppireSelectDestWidget *dest, const GValue *v);
+};
+
+
+gboolean psppire_select_dest_widget_contains_var (PsppireSelectDestWidget *m, const GValue *v);
+
+#endif
index 2f9dbe06a890cbe5903ed74891b2574c1b702cea..83fe2d82792a04fb34425b3f0f5a221dde2238fb 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
@@ -12,7 +12,8 @@
    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/>. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
 /*
   This module provides a widget, PsppireSelector derived from
 
 #include <config.h>
 
+#include "psppire-dictview.h"
+#include "psppire-var-view.h"
+#include "psppire-dict.h"
+#include "psppire-select-dest.h"
+
 #include <gtk/gtksignal.h>
 #include <gtk/gtkbutton.h>
 #include <gtk/gtkentry.h>
@@ -112,22 +118,50 @@ psppire_selector_get_type (void)
   return psppire_selector_type;
 }
 
+static GObjectClass * parent_class = NULL;
 
 static void
-psppire_selector_finalize (GObject *object)
+psppire_selector_finalize (GObject *obj)
 {
+   /* Chain up to the parent class */
+   G_OBJECT_CLASS (parent_class)->finalize (obj);
 }
 
+
+static void
+psppire_selector_dispose (GObject *obj)
+{
+  PsppireSelector *sel = PSPPIRE_SELECTOR (obj);
+
+  if (sel->dispose_has_run)
+    return;
+
+  /* Make sure dispose does not run twice. */
+  sel->dispose_has_run = TRUE;
+
+  g_object_unref (sel->dest);
+  g_object_unref (sel->source);
+
+  /* Chain up to the parent class */
+  G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+
 /* Properties */
 enum
 {
   PROP_0,
-  PROP_ORIENTATION
+  PROP_ORIENTATION,
+  PROP_PRIMARY,
+  PROP_SOURCE_WIDGET,
+  PROP_DEST_WIDGET
 };
 
 
 static void on_activate (PsppireSelector *selector, gpointer data);
 
+static void update_subjects (PsppireSelector *selector);
+
 
 static void
 psppire_selector_set_property (GObject         *object,
@@ -143,6 +177,18 @@ psppire_selector_set_property (GObject         *object,
       selector->orientation = g_value_get_enum (value);
       set_direction (selector, selector->direction);
       break;
+    case PROP_PRIMARY:
+      selector->primary_requested = TRUE;
+      update_subjects (selector);
+      break;
+    case PROP_SOURCE_WIDGET:
+      selector->source = g_value_dup_object (value);
+      update_subjects (selector);
+      break;
+    case PROP_DEST_WIDGET:
+      selector->dest = g_value_dup_object (value);
+      update_subjects (selector);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -163,14 +209,18 @@ psppire_selector_get_property (GObject         *object,
     case PROP_ORIENTATION:
       g_value_set_enum (value, selector->orientation);
       break;
+    case PROP_SOURCE_WIDGET:
+      g_value_take_object (value, selector->source);
+      break;
+    case PROP_DEST_WIDGET:
+      g_value_take_object (value, selector->dest);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     };
 }
 
-
-
 static void
 psppire_selector_class_init (PsppireSelectorClass *class)
 {
@@ -179,11 +229,34 @@ 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);
 
 
+ /* Meaningfull only if more than one selector shares this selectors source */
+  GParamSpec *primary_spec =
+    g_param_spec_boolean ("primary",
+                         "Primary",
+                         "Whether this selector should be the primary selector for the source",
+                         FALSE,
+                         G_PARAM_READWRITE);
+
+  GParamSpec *source_widget_spec = 
+    g_param_spec_object ("source-widget",
+                        "Source Widget",
+                        "The widget to be used as the source for this selector",
+                        GTK_TYPE_WIDGET,
+                        G_PARAM_READWRITE);
+
+  GParamSpec *dest_widget_spec = 
+    g_param_spec_object ("dest-widget",
+                        "Destination Widget",
+                        "The widget to be used as the destination for this selector",
+                        GTK_TYPE_WIDGET,
+                        G_PARAM_READWRITE);
+
+
   object_class->set_property = psppire_selector_set_property;
   object_class->get_property = psppire_selector_get_property;
 
@@ -191,6 +264,20 @@ psppire_selector_class_init (PsppireSelectorClass *class)
                                    PROP_ORIENTATION,
                                    orientation_spec);
 
+  g_object_class_install_property (object_class,
+                                   PROP_PRIMARY,
+                                   primary_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_SOURCE_WIDGET,
+                                   source_widget_spec);
+
+  g_object_class_install_property (object_class,
+                                   PROP_DEST_WIDGET,
+                                   dest_widget_spec);
+
+  parent_class = g_type_class_peek_parent (class);
+
   signals [SELECTED] =
     g_signal_new ("selected",
                  G_TYPE_FROM_CLASS (class),
@@ -210,6 +297,8 @@ psppire_selector_class_init (PsppireSelectorClass *class)
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE,
                  0);
+
+  class->default_selection_funcs = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 
@@ -219,6 +308,7 @@ psppire_selector_base_init (PsppireSelectorClass *class)
   GObjectClass *object_class = G_OBJECT_CLASS (class);
 
   object_class->finalize = psppire_selector_finalize;
+  object_class->dispose = psppire_selector_dispose;
 
   class->source_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
@@ -230,13 +320,82 @@ psppire_selector_base_finalize(PsppireSelectorClass *class,
                                gpointer class_data)
 {
   g_hash_table_destroy (class->source_hash);
+  g_hash_table_destroy (class->default_selection_funcs);
+}
+
+/* Callback for when the source treeview is activated (double clicked) */
+static void
+on_row_activate (GtkTreeView       *tree_view,
+                GtkTreePath       *path,
+                GtkTreeViewColumn *column,
+                gpointer           data)
+{
+  PsppireSelector *selector  = data;
+
+  gtk_action_activate (selector->action);
+}
+
+/* Callback for when the source selection changes */
+static void
+on_source_select (GtkTreeSelection *treeselection, gpointer data)
+{
+  PsppireSelector *selector = data;
+
+  set_direction (selector, PSPPIRE_SELECTOR_SOURCE_TO_DEST);
+
+  if ( selector->allow_selection )
+    {
+      gtk_action_set_sensitive (selector->action,
+                               selector->allow_selection (selector->source, selector->dest));
+    }
+  else if ( GTK_IS_ENTRY (selector->dest) )
+    {
+      gtk_action_set_sensitive (selector->action,
+                               gtk_tree_selection_count_selected_rows
+                               (treeselection) <= 1 );
+    }
 }
 
 
+static void
+on_realize (PsppireSelector *selector)
+{
+  PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
+  GtkTreeSelection* selection ;
+
+  GList *list = g_hash_table_lookup (class->source_hash, selector->source);
+
+  if ( NULL == list)
+    return;
+
+  if ( g_list_first (list)->data == selector)
+    {
+      if ( selector->row_activate_id )
+       g_signal_handler_disconnect (selector->source, selector->row_activate_id);
+
+      selector->row_activate_id =  
+       g_signal_connect (selector->source, "row-activated", G_CALLBACK (on_row_activate), selector);
+    }
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector->source));
+
+  if ( selector->source_select_id )
+    g_signal_handler_disconnect (selection, selector->source_select_id);
+
+  selector->source_select_id = 
+    g_signal_connect (selection, "changed", G_CALLBACK (on_source_select), selector);
+}
+
 
 static void
 psppire_selector_init (PsppireSelector *selector)
 {
+  selector->primary_requested = FALSE;
+  selector->select_user_data = NULL;
+  selector->select_items = NULL;
+  selector->allow_selection = NULL;
+  selector->filter = NULL;
+
   selector->arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
   selector->filtered_source = NULL;
 
@@ -251,6 +410,18 @@ psppire_selector_init (PsppireSelector *selector)
   g_signal_connect_swapped (selector->action, "activate", G_CALLBACK (on_activate), selector);
 
   selector->selecting = FALSE;
+
+  selector->source = NULL;
+  selector->dest = NULL;
+  selector->dispose_has_run = FALSE;
+
+
+  selector->row_activate_id = 0;
+  selector->source_select_id  = 0;
+
+  g_signal_connect (selector, "realize",
+                   G_CALLBACK (on_realize), NULL);
+
 }
 
 
@@ -313,27 +484,6 @@ set_direction (PsppireSelector *selector, enum psppire_selector_dir d)
     }
 }
 
-/* Callback for when the source selection changes */
-static void
-on_source_select (GtkTreeSelection *treeselection, gpointer data)
-{
-  PsppireSelector *selector = data;
-
-  set_direction (selector, PSPPIRE_SELECTOR_SOURCE_TO_DEST);
-
-  if ( selector->allow_selection )
-    {
-      gtk_action_set_sensitive (selector->action,
-                               selector->allow_selection (selector->source, selector->dest));
-    }
-  else if ( GTK_IS_ENTRY (selector->dest) )
-    {
-      gtk_action_set_sensitive (selector->action,
-                               gtk_tree_selection_count_selected_rows
-                               (treeselection) <= 1 );
-    }
-}
-
 /* Callback for when the destination treeview selection changes */
 static void
 on_dest_treeview_select (GtkTreeSelection *treeselection, gpointer data)
@@ -480,18 +630,6 @@ select_selection (PsppireSelector *selector)
   selector->selecting = FALSE;
 }
 
-/* Callback for when the source treeview is activated (double clicked) */
-static void
-on_row_activate (GtkTreeView       *tree_view,
-                GtkTreePath       *path,
-                GtkTreeViewColumn *column,
-                gpointer           data)
-{
-  PsppireSelector *selector  = data;
-
-  gtk_action_activate (selector->action);
-}
-
 /* Callback for when the selector button is clicked,
    or other event which causes the selector's action to occur.
  */
@@ -512,25 +650,21 @@ on_activate (PsppireSelector *selector, gpointer data)
     }
 }
 
-/* Default visibility filter for GtkTreeView DEST widget */
 static gboolean
-is_item_in_dest (GtkTreeModel *model, GtkTreeIter *iter,
-                PsppireSelector *selector)
+is_item_in_dest (GtkTreeModel *model, GtkTreeIter *iter, PsppireSelector *selector)
 {
-  GtkTreeModel *dest_model;
-  GtkTreeIter dest_iter;
+  gboolean result = FALSE;
   GtkTreeIter source_iter;
-  gint index;
-  GtkTreePath *path ;
   GtkTreeModel *source_model;
+  GValue value = {0};
 
-  if ( GTK_IS_TREE_MODEL_FILTER (model) )
+  if (GTK_IS_TREE_MODEL_FILTER (model))
     {
       source_model = gtk_tree_model_filter_get_model
        (GTK_TREE_MODEL_FILTER (model));
 
       gtk_tree_model_filter_convert_iter_to_child_iter
-       ( GTK_TREE_MODEL_FILTER (model),  &source_iter,  iter  );
+       (GTK_TREE_MODEL_FILTER (model),  &source_iter, iter);
     }
   else
     {
@@ -538,40 +672,17 @@ is_item_in_dest (GtkTreeModel *model, GtkTreeIter *iter,
       source_iter = *iter;
     }
 
-  dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
-
-  path = gtk_tree_model_get_path (source_model, &source_iter);
-
-  index = *gtk_tree_path_get_indices (path);
+  gtk_tree_model_get_value (source_model, &source_iter, DICT_TVM_COL_VAR, &value);
 
-  gtk_tree_path_free (path);
-
-  if ( ! gtk_tree_model_get_iter_first (dest_model, &dest_iter) )
-    return FALSE;
-
-  do
-    {
-      int x;
-      GValue value = {0};
-      GValue int_value = {0};
-      gtk_tree_model_get_value (dest_model, &dest_iter, 0, &value);
+  result = psppire_select_dest_widget_contains_var (PSPPIRE_SELECT_DEST_WIDGET (selector->dest),
+                                                   &value);
 
-      g_value_init (&int_value, G_TYPE_INT);
+  g_value_unset (&value);
 
-      g_value_transform (&value, &int_value);
-
-      x = g_value_get_int (&int_value);
-
-      g_value_unset (&int_value);
-      g_value_unset (&value);
+  return result;
+}
 
-      if ( x == index )
-       return TRUE;
-    }
-  while (gtk_tree_model_iter_next (dest_model, &dest_iter));
 
-  return FALSE;
-}
 
 /* Visibility function for items in the SOURCE widget.
    Returns TRUE iff *all* the selectors for which SOURCE is associated
@@ -606,18 +717,16 @@ static void
 set_tree_view_source (PsppireSelector *selector,
                      GtkTreeView *source)
 {
-  GtkTreeSelection* selection ;
   GList *list = NULL;
 
   PsppireSelectorClass *class = g_type_class_peek (PSPPIRE_SELECTOR_TYPE);
+  
+  GtkTreeModel *model = gtk_tree_view_get_model (source);
 
   if ( ! (list = g_hash_table_lookup (class->source_hash, source)))
     {
       selector->filtered_source =
-       GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new
-                              (gtk_tree_view_get_model (source),  NULL));
-
-      gtk_tree_view_set_model (source, NULL);
+       GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
 
       gtk_tree_view_set_model (source,
                               GTK_TREE_MODEL (selector->filtered_source));
@@ -635,20 +744,17 @@ set_tree_view_source (PsppireSelector *selector,
     {  /* Append this selector to the list and push the <source,list>
          pair onto the hash table */
 
-      selector->filtered_source = GTK_TREE_MODEL_FILTER (
-       gtk_tree_view_get_model (source));
+      selector->filtered_source = GTK_TREE_MODEL_FILTER (model);
 
-      list = g_list_append (list, selector);
-      g_hash_table_replace (class->source_hash, source, list);
+      if ( NULL == g_list_find (list, selector) )
+       {
+         if ( selector->primary_requested )
+           list = g_list_prepend (list, selector);
+         else
+           list = g_list_append (list, selector);
+         g_hash_table_replace (class->source_hash, source, list);
+       }
     }
-
-  selection = gtk_tree_view_get_selection (source);
-
-  g_signal_connect (source, "row-activated", G_CALLBACK (on_row_activate),
-                   selector);
-
-  g_signal_connect (selection, "changed", G_CALLBACK (on_source_select),
-                   selector);
 }
 
 
@@ -682,7 +788,17 @@ on_dest_data_delete (GtkTreeModel *tree_model,
 }
 
 
+static void
+on_dest_model_changed (PsppireSelector *selector)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->dest));
+
+  g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change),
+                   selector);
 
+  g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete),
+                   selector);
+}
 
 /* Set the destination widget to DEST */
 static void
@@ -690,21 +806,19 @@ set_tree_view_dest (PsppireSelector *selector,
                    GtkTreeView *dest)
 {
   GtkTreeSelection* selection = gtk_tree_view_get_selection (dest);
-  GtkTreeModel *model = gtk_tree_view_get_model (dest);
+
 
   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
 
   g_signal_connect (selection, "changed", G_CALLBACK (on_dest_treeview_select),
                    selector);
 
-  g_signal_connect (model, "row-changed", G_CALLBACK (on_dest_data_change),
-                     selector);
-
-  g_signal_connect (model, "row-deleted", G_CALLBACK (on_dest_data_delete),
-                     selector);
-
+  on_dest_model_changed (selector);
+  g_signal_connect_swapped (dest, "notify::model",
+                           G_CALLBACK (on_dest_model_changed), selector);
 }
 
+
 /* Callback which causes the filter to be refiltered.
    Called when the DEST GtkEntry is activated (Enter is pressed), or when it
    looses focus.
@@ -716,6 +830,7 @@ refilter (PsppireSelector *selector)
   return FALSE;
 }
 
+
 /* Callback for when the DEST GtkEntry is selected (clicked) */
 static gboolean
 on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data)
@@ -729,7 +844,6 @@ on_entry_dest_select (GtkWidget *widget, GdkEventFocus *event, gpointer data)
 }
 
 
-
 /* Callback for when an item disappears from the source list.
    By implication, this means that the item has been inserted into the
    destination.
@@ -777,67 +891,120 @@ set_entry_dest (PsppireSelector *selector,
                    G_CALLBACK (on_row_inserted), selector);
 }
 
+static void
+set_default_filter (PsppireSelector *selector)
+{
+  if ( selector->filter == NULL)
+    {
+      if  (GTK_IS_TREE_VIEW (selector->dest))
+       selector->filter = is_item_in_dest;
+    }
+}
 
-/* Set SOURCE and DEST for this selector, and
-   set SELECT_FUNC and FILTER_FUNC */
-void
-psppire_selector_set_subjects (PsppireSelector *selector,
-                              GtkWidget *source,
-                              GtkWidget *dest,
-                              SelectItemsFunc *select_func,
-                              FilterItemsFunc *filter_func,
-                              gpointer user_data)
+static void
+update_subjects (PsppireSelector *selector)
 {
-  g_assert(selector);
+  GtkTreeModel *model = NULL;
 
-  selector->filter = filter_func ;
+  if ( NULL == selector->dest )
+    return;
 
-  selector->source = source;
-  selector->dest = dest;
-  selector->select_user_data = user_data;
+  set_default_filter (selector);
 
-  if ( filter_func == NULL)
-    {
-      if  (GTK_IS_TREE_VIEW (dest))
-       selector->filter = is_item_in_dest;
-    }
+  if ( NULL == selector->source )
+    return;
 
-  if ( GTK_IS_TREE_VIEW (source))
-    set_tree_view_source (selector, GTK_TREE_VIEW (source) );
-  else
-    g_error ("Unsupported source widget: %s", G_OBJECT_TYPE_NAME (source));
+  g_signal_connect_swapped (selector->source, "notify::model",
+                           G_CALLBACK (update_subjects), selector);
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector->source));
+
+  if ( NULL == model)
+    return;
 
-  g_assert ( GTK_IS_TREE_MODEL_FILTER (selector->filtered_source));
 
-  if ( NULL == dest)
+  if ( GTK_IS_TREE_VIEW (selector->source))
+    set_tree_view_source (selector, GTK_TREE_VIEW (selector->source) );
+  else
+    g_error ("Unsupported source widget: %s", G_OBJECT_TYPE_NAME (selector->source));
+
+  if ( NULL == selector->dest)
     ;
-  else if  ( GTK_IS_TREE_VIEW (dest))
-    set_tree_view_dest (selector, GTK_TREE_VIEW (dest));
+  else if  ( GTK_IS_TREE_VIEW (selector->dest))
+    {
+      set_tree_view_dest (selector, GTK_TREE_VIEW (selector->dest));
+    }
 
-  else if ( GTK_IS_ENTRY (dest))
-    set_entry_dest (selector, GTK_ENTRY (dest));
+  else if ( GTK_IS_ENTRY (selector->dest))
+    set_entry_dest (selector, GTK_ENTRY (selector->dest));
 
-  else if (GTK_IS_TEXT_VIEW (dest))
+  else if (GTK_IS_TEXT_VIEW (selector->dest))
     {
       /* Nothing to be done */
     }
-
   else
-    g_error ("Unsupported destination widget: %s", G_OBJECT_TYPE_NAME (dest));
+    g_error ("Unsupported destination widget: %s", G_OBJECT_TYPE_NAME (selector->dest));
+
+
+  /* FIXME: Remove this dependency */
+  if ( PSPPIRE_IS_DICT_VIEW (selector->source) )
+    {
+      GObjectClass *class = G_OBJECT_GET_CLASS (selector);
+      GType type = G_OBJECT_TYPE (selector->dest);
 
+      SelectItemsFunc *func  = 
+       g_hash_table_lookup (PSPPIRE_SELECTOR_CLASS (class)->default_selection_funcs, (gpointer) type);
+
+      if ( func )
+       psppire_selector_set_select_func (PSPPIRE_SELECTOR (selector),
+                                         func, NULL);
+    }
+}
+
+
+void
+psppire_selector_set_default_selection_func (GType type, SelectItemsFunc *func)
+{
+  GObjectClass *class = g_type_class_ref (PSPPIRE_SELECTOR_TYPE);
+
+  g_hash_table_insert (PSPPIRE_SELECTOR_CLASS (class)->default_selection_funcs, (gpointer) type, func);
+
+  g_type_class_unref (class);
+}
+
+
+
+
+/* Set FILTER_FUNC for this selector */
+void
+psppire_selector_set_filter_func (PsppireSelector *selector,
+                                 FilterItemsFunc *filter_func)
+{
+  selector->filter = filter_func ;
+  
+  set_default_filter (selector);
+}
+
+
+/* Set SELECT_FUNC for this selector */
+void
+psppire_selector_set_select_func (PsppireSelector *selector,
+                              SelectItemsFunc *select_func,
+                              gpointer user_data)
+{
+  selector->select_user_data = user_data;
   selector->select_items = select_func;
 }
 
 
 
 void
-psppire_selector_set_allow        (PsppireSelector *selector , AllowSelectionFunc *allow)
+psppire_selector_set_allow (PsppireSelector *selector, AllowSelectionFunc *allow)
 {
   selector->allow_selection = allow;
 }
 
 
-
 GType
 psppire_selector_orientation_get_type (void)
 {
index bdd358ebab30ebdb1dba610a9f58b0767dca1c1f..bb267cd49cf08631a02e5cfd07e19cf54f31df4a 100644 (file)
@@ -77,7 +77,10 @@ struct _PsppireSelector
   GtkWidget *arrow;
   GtkAction *action;
 
+  gboolean dispose_has_run;
+
   enum psppire_selector_dir direction;
+
   GtkWidget *source;
   GtkWidget *dest;
 
@@ -97,6 +100,12 @@ struct _PsppireSelector
   FilterItemsFunc *filter;
 
   AllowSelectionFunc *allow_selection;
+
+  gulong row_activate_id ;
+
+  gulong source_select_id ;
+
+  gboolean primary_requested;
 };
 
 struct _PsppireSelectorClass
@@ -106,18 +115,26 @@ struct _PsppireSelectorClass
   /* This is a hash of Lists of FilterItemsFunc pointers, keyed by address of
      the source widget */
   GHashTable *source_hash;
+
+  /* A hash of SelectItemFuncs indexed by GType */
+  GHashTable *default_selection_funcs;
 };
 
 GType      psppire_selector_get_type        (void);
 GtkWidget* psppire_selector_new             (void);
-void       psppire_selector_set_subjects    (PsppireSelector *,
-                                            GtkWidget *,
-                                            GtkWidget *,
-                                            SelectItemsFunc *,
-                                            FilterItemsFunc *,
-                                            gpointer );
 
-void      psppire_selector_set_allow        (PsppireSelector *, AllowSelectionFunc *);
+
+/* Set FILTER_FUNC for this selector */
+void psppire_selector_set_filter_func (PsppireSelector *selector,
+                                      FilterItemsFunc *filter_func);
+
+/* Set SELECT_FUNC for this selector */
+void psppire_selector_set_select_func (PsppireSelector *selector,
+                                      SelectItemsFunc *select_func,
+                                      gpointer user_data);
+
+
+void psppire_selector_set_allow (PsppireSelector *, AllowSelectionFunc *);
 
 
 GType psppire_selector_orientation_get_type (void) G_GNUC_CONST;
@@ -130,10 +147,12 @@ typedef enum {
   PSPPIRE_SELECT_SOURCE_BELOW_DEST
 } PsppireSelectorOrientation;
 
-#define G_TYPE_PSPPIRE_SELECTOR_ORIENTATION \
+#define PSPPIRE_TYPE_SELECTOR_ORIENTATION \
   (psppire_selector_orientation_get_type())
 
 
+void psppire_selector_set_default_selection_func (GType type, SelectItemsFunc *);
+
 
 G_END_DECLS
 
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..70e1044fcb25ed9d46dbc2fcc8d2232c3687fda6 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,80 @@ 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->col >=  PSPPIRE_VAR_STORE_n_COLS)
+    return TRUE;
+
+  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;
+      if (! psppire_dict_check_name (var_store->dictionary, name, TRUE))
+       return TRUE;
 
-      psppire_dict_insert_variable (var_store->dictrow, name);
+      psppire_dict_insert_variable (var_store->dictionary, 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 )
-       psppire_dict_insert_variable (var_store->dict, i, NULL);
+      for ( i = n_vars ; i <= new_cell->row; ++i )
+       psppire_dict_insert_variable (var_store->dictionary, 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 +320,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 +334,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 +361,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 +379,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->dictionary;
+
        g_signal_connect_swapped (customEntry,
                                  "clicked",
                                  G_CALLBACK (missing_val_dialog_show),
@@ -432,10 +400,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 +420,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 +457,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 +470,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 +529,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..ece728127c142733b6986844146d22e06129b9fb 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>
 
 #include "var-display.h"
 
+static void
+var_change_callback (GtkWidget *w, gint n, gpointer data)
+{
+  PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
+
+  psppire_sheet_model_range_changed (model,
+                                n, 0, n, PSPPIRE_VAR_STORE_n_COLS);
+}
+
+
+static void
+var_delete_callback (GtkWidget *w, gint dict_idx, gint case_idx, gint val_cnt, gpointer data)
+{
+  PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
+
+  psppire_sheet_model_rows_deleted (model, dict_idx, 1);
+}
+
+
+
+static void
+var_insert_callback (GtkWidget *w, glong row, gpointer data)
+{
+  PsppireSheetModel *model = PSPPIRE_SHEET_MODEL (data);
+
+  psppire_sheet_model_rows_inserted (model, row, 1);
+}
+
+static void
+refresh (PsppireDict  *d, gpointer data)
+{
+  PsppireVarStore *vs = data;
+
+  psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (vs), -1, -1, -1, -1);
+}
+
 enum
   {
-    PSPPIRE_VAR_STORE_TRAILING_ROWS = 1,
-    PSPPIRE_VAR_STORE_FORMAT_TYPE
+    PROP_0,
+    PSPPIRE_VAR_STORE_FORMAT_TYPE,
+    PSPPIRE_VAR_STORE_DICT
   };
 
 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);
+static void         psppire_var_store_dispose        (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 +159,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,14 +179,31 @@ 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;
 
+    case PSPPIRE_VAR_STORE_DICT:
+      if ( self->dictionary)
+       g_object_unref (self->dictionary);
+      self->dictionary = g_value_dup_object (value);
+      g_signal_connect (self->dictionary, "variable-changed", G_CALLBACK (var_change_callback),
+                       self);
+
+      g_signal_connect (self->dictionary, "variable-deleted", G_CALLBACK (var_delete_callback),
+                       self);
+
+      g_signal_connect (self->dictionary, "variable-inserted",
+                       G_CALLBACK (var_insert_callback), self);
+
+      g_signal_connect (self->dictionary, "backend-changed", G_CALLBACK (refresh),
+                       self);
+
+      /* The entire model has changed */
+      psppire_sheet_model_range_changed (PSPPIRE_SHEET_MODEL (self), -1, -1, -1, -1);
+
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
       break;
@@ -185,16 +220,16 @@ 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;
 
+    case PSPPIRE_VAR_STORE_DICT:
+      g_value_take_object (value, self->dictionary);
+      break;
+
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
     }
 }
@@ -204,52 +239,49 @@ static void
 psppire_var_store_class_init (PsppireVarStoreClass *class)
 {
   GObjectClass *object_class;
-  GParamSpec *pspec;
+  GParamSpec *format_pspec;
+  GParamSpec *dict_pspec;
 
   parent_class = g_type_class_peek_parent (class);
   object_class = (GObjectClass*) class;
 
   object_class->finalize = psppire_var_store_finalize;
+  object_class->dispose = psppire_var_store_dispose;
   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",
+  format_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);
+                                   format_pspec);
+
+  dict_pspec = g_param_spec_object ("dictionary",
+                                   "Dictionary",
+                                   "The PsppireDict represented by this var store",
+                                   PSPPIRE_TYPE_DICT,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+                                   
+  g_object_class_install_property (object_class,
+                                   PSPPIRE_VAR_STORE_DICT,
+                                   dict_pspec);
 }
 
+#define DISABLED_COLOR "gray"
+
 static void
 psppire_var_store_init (PsppireVarStore *var_store)
 {
-  GdkColormap *colormap = gdk_colormap_get_system ();
+  if ( ! gdk_color_parse (DISABLED_COLOR, &var_store->disabled))
+       g_critical ("Could not parse color \"%s\"", DISABLED_COLOR);
 
-  g_assert (gdk_color_parse ("gray", &var_store->disabled));
-
-  gdk_colormap_alloc_color (colormap, &var_store->disabled, FALSE, TRUE);
-
-  var_store->dict = 0;
-  var_store->trailing_rows = 40;
+  var_store->dictionary = NULL;
   var_store->format_type = PSPPIRE_VAR_STORE_OUTPUT_FORMATS;
 }
 
@@ -297,19 +329,19 @@ psppire_var_store_item_editable (PsppireVarStore *var_store, glong row, glong co
 struct variable *
 psppire_var_store_get_var (PsppireVarStore *store, glong row)
 {
-  return psppire_dict_get_variable (store->dict, row);
+  return psppire_dict_get_variable (store->dictionary, 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 +352,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 +366,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:
@@ -363,49 +390,14 @@ psppire_var_store_new (PsppireDict *dict)
 {
   PsppireVarStore *retval;
 
-  retval = g_object_new (GTK_TYPE_VAR_STORE, NULL);
+  retval = g_object_new (GTK_TYPE_VAR_STORE, "dictionary", dict, NULL);
 
-  psppire_var_store_set_dictionary (retval, dict);
+  //  psppire_var_store_set_dictionary (retval, dict);
 
   return retval;
 }
 
-static void
-var_change_callback (GtkWidget *w, gint n, gpointer data)
-{
-  GSheetModel *model = G_SHEET_MODEL (data);
-
-  g_sheet_model_range_changed (model,
-                                n, 0, n, PSPPIRE_VAR_STORE_n_COLS);
-}
-
-
-static void
-var_delete_callback (GtkWidget *w, gint dict_idx, gint case_idx, gint val_cnt, gpointer data)
-{
-  GSheetModel *model = G_SHEET_MODEL (data);
-
-  g_sheet_model_rows_deleted (model, dict_idx, 1);
-}
-
-
-
-static void
-var_insert_callback (GtkWidget *w, glong row, gpointer data)
-{
-  GSheetModel *model = G_SHEET_MODEL (data);
-
-  g_sheet_model_rows_inserted (model, row, 1);
-}
-
-static void
-refresh (PsppireDict  *d, gpointer data)
-{
-  PsppireVarStore *vs = data;
-
-  g_sheet_model_range_changed (G_SHEET_MODEL (vs), -1, -1, -1, -1);
-}
-
+#if 0
 /**
  * psppire_var_store_replace_set_dictionary:
  * @var_store: The variable store
@@ -434,8 +426,9 @@ 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);
 }
+#endif
 
 static void
 psppire_var_store_finalize (GObject *object)
@@ -444,19 +437,33 @@ psppire_var_store_finalize (GObject *object)
   (* parent_class->finalize) (object);
 }
 
+static void
+psppire_var_store_dispose (GObject *object)
+{
+  PsppireVarStore *self = PSPPIRE_VAR_STORE (object);
+
+  if (self->dictionary)
+    g_object_unref (self->dictionary);
+
+  /* must chain up */
+  (* parent_class->finalize) (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);
 
   struct variable *pv;
 
-  if ( row >= psppire_dict_get_var_cnt (store->dict))
+  if ( row >= psppire_dict_get_var_cnt (store->dictionary))
     return 0;
 
-  pv = psppire_dict_get_variable (store->dict, row);
+  pv = psppire_dict_get_variable (store->dictionary, row);
 
-  return text_for_column (pv, column, 0);
+  return text_for_column (store, pv, column, 0);
 }
 
 
@@ -465,13 +472,13 @@ 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 ;
 
   PsppireVarStore *var_store = PSPPIRE_VAR_STORE (model);
 
-  if ( row >= psppire_dict_get_var_cnt (var_store->dict))
+  if ( row >= psppire_dict_get_var_cnt (var_store->dictionary))
       return FALSE;
 
   pv = psppire_var_store_get_var (var_store, row);
@@ -482,7 +489,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,14 +502,14 @@ 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 ;
 
   PsppireVarStore *var_store = PSPPIRE_VAR_STORE (model);
 
-  if ( row >= psppire_dict_get_var_cnt (var_store->dict))
+  if ( row >= psppire_dict_get_var_cnt (var_store->dictionary))
       return FALSE;
 
   pv = psppire_var_store_get_var (var_store, row);
@@ -513,8 +520,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->dictionary, pv, text);
+       return ok;
+      }
     case PSPPIRE_VAR_STORE_COL_COLUMNS:
       if ( ! text) return FALSE;
       var_set_display_width (pv, atoi (text));
@@ -522,10 +532,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 +587,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 +609,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->dictionary;
   static const gchar *const type_label[] =
     {
       N_("Numeric"),
@@ -604,6 +627,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 +636,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 +723,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 +742,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;
@@ -759,100 +780,72 @@ text_for_column (const struct variable *pv, gint c, GError **err)
 gint
 psppire_var_store_get_var_cnt (PsppireVarStore  *store)
 {
-  return psppire_dict_get_var_cnt (store->dict);
-}
-
-
-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);
+  return psppire_dict_get_var_cnt (store->dictionary);
 }
 
 
 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);
 
-  if (vs->dict)
-    rows =  psppire_dict_get_var_cnt (vs->dict);
+  if (vs->dictionary)
+    rows =  psppire_dict_get_var_cnt (vs->dictionary);
 
   return rows ;
 }
 
 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)
+  if ( ! vs->dictionary)
     return FALSE;
 
-  return  row < psppire_dict_get_var_cnt (vs->dict);
-}
-
-static
-gboolean always_true ()
-{
-  return TRUE;
+  return  row < psppire_dict_get_var_cnt (vs->dictionary);
 }
 
 
 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..e4c2ecd827c47fd9bc6747fddcb1d710357e157b 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. */
@@ -64,10 +63,8 @@ struct _PsppireVarStore
   GObject parent;
 
   /*< private >*/
-  PsppireDict *dict;
+  PsppireDict *dictionary;
   GdkColor disabled;
-  const PangoFontDescription *font_desc;
-  gint trailing_rows;
   PsppireVarStoreFormatType format_type;
 };
 
@@ -87,9 +84,6 @@ GType         psppire_var_store_get_type         (void) G_GNUC_CONST;
 PsppireVarStore *psppire_var_store_new              (PsppireDict *dict);
 struct variable * psppire_var_store_get_var (PsppireVarStore *store, glong row);
 
-void psppire_var_store_set_dictionary (PsppireVarStore *var_store, PsppireDict *dict);
-
-
 /* Return the number of variables */
 gint psppire_var_store_get_var_cnt (PsppireVarStore      *var_store);
 
diff --git a/src/ui/gui/psppire-var-view.c b/src/ui/gui/psppire-var-view.c
new file mode 100644 (file)
index 0000000..f7a017b
--- /dev/null
@@ -0,0 +1,332 @@
+/* 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 <gtk/gtkcellrenderertext.h>
+#include "psppire-var-view.h"
+#include "psppire-var-ptr.h"
+#include "psppire-select-dest.h"
+
+#include <data/variable.h>
+
+#include <gettext.h>
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+static void psppire_var_view_base_finalize (PsppireVarViewClass *, gpointer);
+static void psppire_var_view_base_init     (PsppireVarViewClass *class);
+static void psppire_var_view_class_init    (PsppireVarViewClass *class);
+static void psppire_var_view_init          (PsppireVarView      *var_view);
+
+/* Returns TRUE iff VV contains the item V.
+   V must be an initialised value containing a
+   PSPPIRE_VAR_PTR_TYPE.
+*/
+static gboolean
+var_view_contains_var (PsppireSelectDestWidget *sdm, const GValue *v)
+{
+  gboolean ok;
+  GtkTreeIter iter;
+  PsppireVarView *vv = PSPPIRE_VAR_VIEW (sdm);
+  g_return_val_if_fail (G_VALUE_HOLDS (v, PSPPIRE_VAR_PTR_TYPE), FALSE);
+
+  for (ok = psppire_var_view_get_iter_first (vv, &iter);
+       ok;
+       ok = psppire_var_view_get_iter_next (vv, &iter))
+    {
+      const struct variable *var = psppire_var_view_get_variable (vv, 0, &iter);
+      if (var == g_value_get_boxed (v))
+       return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+model_init (PsppireSelectDestWidgetIface *iface)
+{
+  iface->contains_var = var_view_contains_var;
+}
+
+GType
+psppire_var_view_get_type (void)
+{
+  static GType psppire_var_view_type = 0;
+
+  if (!psppire_var_view_type)
+    {
+      static const GTypeInfo psppire_var_view_info =
+      {
+       sizeof (PsppireVarViewClass),
+       (GBaseInitFunc) psppire_var_view_base_init,
+        (GBaseFinalizeFunc) psppire_var_view_base_finalize,
+       (GClassInitFunc)psppire_var_view_class_init,
+       (GClassFinalizeFunc) NULL,
+       NULL,
+        sizeof (PsppireVarView),
+       0,
+       (GInstanceInitFunc) psppire_var_view_init,
+      };
+
+      static const GInterfaceInfo var_view_model_info = {
+       (GInterfaceInitFunc) model_init, /* Fill this in */
+       NULL,
+       NULL
+      };
+
+      psppire_var_view_type =
+       g_type_register_static (GTK_TYPE_TREE_VIEW, "PsppireVarView",
+                               &psppire_var_view_info, 0);
+
+      g_type_add_interface_static (psppire_var_view_type,
+                                  PSPPIRE_TYPE_SELECT_DEST_WIDGET,
+                                  &var_view_model_info);
+    }
+
+  return psppire_var_view_type;
+}
+
+
+static void
+psppire_var_view_finalize (GObject *object)
+{
+  PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
+  g_free (var_view->nums);
+}
+
+/* Properties */
+enum
+{
+  PROP_0,
+  PROP_N_COLS
+};
+
+/* A (*GtkTreeCellDataFunc) function.
+   This function expects TREEMODEL to hold PSPPIRE_VAR_PTR_TYPE.
+   It renders the name of the variable into CELL.
+*/
+static void
+display_cell_var_name (GtkTreeViewColumn *tree_column,
+                      GtkCellRenderer *cell,
+                      GtkTreeModel *treemodel,
+                      GtkTreeIter *iter,
+                      gpointer data)
+{
+  struct variable *var;
+  GValue value = {0};
+  gint *col = data;
+
+  GtkTreePath *path = gtk_tree_model_get_path (treemodel, iter);
+
+  gtk_tree_model_get_value (treemodel, iter, *col, &value);
+
+  gtk_tree_path_free (path);
+
+  var = g_value_get_boxed (&value);
+
+  g_value_unset (&value);
+
+  g_object_set (cell, "text", var_get_name (var), NULL);
+}
+
+
+static void
+psppire_var_view_get_property (GObject         *object,
+                              guint            prop_id,
+                              GValue          *value,
+                              GParamSpec      *pspec)
+{
+  PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_N_COLS:
+      g_value_set_int (value,  gtk_tree_model_iter_n_children (GTK_TREE_MODEL (var_view->list), NULL));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+
+static void
+psppire_var_view_set_property (GObject         *object,
+                              guint            prop_id,
+                              const GValue    *value,
+                              GParamSpec      *pspec)
+{
+  PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_N_COLS:
+      {
+       gint n_cols = g_value_get_int (value);
+       gint c;
+
+
+       GType *array = g_alloca (sizeof (GType) *  n_cols);
+
+       var_view->nums = g_malloc (sizeof *var_view->nums * n_cols);
+
+       for (c = 0 ; c < n_cols; ++c)
+       {
+         GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
+         GtkTreeViewColumn *col = gtk_tree_view_column_new ();
+
+         gchar *label = g_strdup_printf (_("Var%d"), c + 1);
+
+         gtk_tree_view_column_set_min_width (col, 100);
+         gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
+         gtk_tree_view_column_set_resizable (col, TRUE);
+         gtk_tree_view_column_set_title (col, label);
+
+         g_free (label);
+
+         var_view->nums[c] = c;
+
+         gtk_tree_view_column_pack_start (col, renderer, TRUE);
+         gtk_tree_view_column_set_cell_data_func (col, renderer,
+                                                  display_cell_var_name,
+                                                  &var_view->nums[c], 0);
+
+         gtk_tree_view_append_column (GTK_TREE_VIEW (var_view), col);
+         array[c] = PSPPIRE_VAR_PTR_TYPE;
+       }
+
+       /* Set a model, which is an GtkListStore of gpointers which point to a variable */
+       var_view->list = gtk_list_store_newv  (n_cols, array);
+       gtk_tree_view_set_model (GTK_TREE_VIEW (var_view), GTK_TREE_MODEL (var_view->list));
+      }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    };
+}
+
+static void
+psppire_var_view_class_init (PsppireVarViewClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  GParamSpec *n_cols_spec =
+    g_param_spec_int ("n-cols",
+                     "Number of columns",
+                     "The Number of Columns in the Variable View",
+                     1, 20,
+                     1,
+                     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_WRITABLE);
+
+
+  object_class->set_property = psppire_var_view_set_property;
+  object_class->get_property = psppire_var_view_get_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_N_COLS,
+                                   n_cols_spec);
+}
+
+
+static void
+psppire_var_view_base_init (PsppireVarViewClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = psppire_var_view_finalize;
+}
+
+
+
+static void
+psppire_var_view_base_finalize (PsppireVarViewClass *class,
+                                gpointer class_data)
+{
+}
+
+
+
+static void
+psppire_var_view_init (PsppireVarView *var_view)
+{
+}
+
+
+GtkWidget*
+psppire_var_view_new (void)
+{
+  return GTK_WIDGET (g_object_new (psppire_var_view_get_type (), NULL));
+}
+
+
+gboolean
+psppire_var_view_get_iter_first (PsppireVarView *vv, GtkTreeIter *iter)
+{
+  return gtk_tree_model_get_iter_first (GTK_TREE_MODEL (vv->list), iter);
+}
+
+gboolean
+psppire_var_view_get_iter_next (PsppireVarView *vv, GtkTreeIter *iter)
+{
+  return gtk_tree_model_iter_next (GTK_TREE_MODEL (vv->list), iter);
+}
+
+const struct variable *
+psppire_var_view_get_variable (PsppireVarView *vv, gint column, GtkTreeIter *iter)
+{
+  const struct variable *var = NULL;
+  GValue value = {0};
+  gtk_tree_model_get_value (GTK_TREE_MODEL (vv->list), iter, column, &value);
+
+  if ( G_VALUE_TYPE (&value) == PSPPIRE_VAR_PTR_TYPE)
+    var = g_value_get_boxed (&value);
+  else
+    g_critical ("Unsupported type \"%s\", in variable name treeview.",
+               G_VALUE_TYPE_NAME (&value));
+
+  g_value_unset (&value);
+
+  return var;
+}
+
+/*
+  Append the names of selected variables to STRING.
+  Returns the number of variables appended.
+*/
+gint
+psppire_var_view_append_names (PsppireVarView *vv, gint column, GString *string)
+{
+  gint n_vars = 0;
+  GtkTreeIter iter;
+
+  if ( psppire_var_view_get_iter_first (vv, &iter) )
+    {
+      do
+       {
+         const struct variable *var = psppire_var_view_get_variable (vv, column, &iter);
+         g_string_append (string, " ");
+         g_string_append (string, var_get_name (var));
+
+         n_vars++;
+       }
+      while (psppire_var_view_get_iter_next (vv, &iter));
+    }
+
+  return n_vars;
+}
diff --git a/src/ui/gui/psppire-var-view.h b/src/ui/gui/psppire-var-view.h
new file mode 100644 (file)
index 0000000..35dc91d
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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_VAR_VIEW_H__
+#define __PSPPIRE_VAR_VIEW_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtktreeview.h>
+
+G_BEGIN_DECLS
+
+#define PSPPIRE_VAR_VIEW_TYPE            (psppire_var_view_get_type ())
+#define PSPPIRE_VAR_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PSPPIRE_VAR_VIEW_TYPE, PsppireVarView))
+#define PSPPIRE_VAR_VIEW_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), \
+    PSPPIRE_VAR_VIEW_TYPE, PsppireVarViewClass))
+#define PSPPIRE_IS_VAR_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+    PSPPIRE_VAR_VIEW_TYPE))
+#define PSPPIRE_IS_VAR_VIEW_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+    PSPPIRE_VAR_VIEW_TYPE))
+
+
+typedef struct _PsppireVarView       PsppireVarView;
+typedef struct _PsppireVarViewClass  PsppireVarViewClass;
+
+struct variable;
+
+struct _PsppireVarView
+{
+  GtkTreeView parent;
+
+  GtkListStore *list;
+  
+  gint *nums;
+};
+
+struct _PsppireVarViewClass
+{
+  GtkTreeViewClass parent_class;
+
+};
+
+GType      psppire_var_view_get_type        (void);
+
+gint psppire_var_view_append_names (PsppireVarView *vv, gint column, GString *string);
+
+gboolean psppire_var_view_get_iter_first (PsppireVarView *vv, GtkTreeIter *iter);
+
+gboolean psppire_var_view_get_iter_next (PsppireVarView *vv, GtkTreeIter *iter);
+
+const struct variable * psppire_var_view_get_variable (PsppireVarView *vv, gint column, GtkTreeIter *iter);
+
+
+
+G_END_DECLS
+
+#endif /* __PSPPIRE_VAR_VIEW_H__ */
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 a7b22037b868e7d523726c8f80faab41ebe1acc8..8491851383f4405805bbf71d0a35d5e172500b29 100644 (file)
 
 #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 "dict-display.h"
+#include "psppire-selector.h"
+#include "psppire-var-view.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 +73,28 @@ 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, relocate (locale_dir));
-
+  i18n_init ();
 
-  glade_init ();
+  preregister_widgets ();
 
   gsl_set_error_handler_off ();
   fn_init ();
@@ -134,7 +146,23 @@ initialize (void)
   journal_enable ();
   textdomain (PACKAGE);
 
-  new_data_window (NULL, NULL);
+
+  the_recent_mgr = gtk_recent_manager_get_default ();
+
+  psppire_selector_set_default_selection_func (GTK_TYPE_ENTRY, insert_source_row_into_entry);
+  psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE, insert_source_row_into_tree_view);
+  psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW, insert_source_row_into_tree_view);
+
+  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 +173,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 +256,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 +268,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;
+}
diff --git a/src/ui/gui/psppire.glade b/src/ui/gui/psppire.glade
deleted file mode 100644 (file)
index bffb34e..0000000
+++ /dev/null
@@ -1,2664 +0,0 @@
-<?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="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 internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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 | GDK_ENTER_NOTIFY_MASK</property>
-        <property name="spacing">2</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="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">
-                    <property name="visible">True</property>
-                    <property name="headers_visible">False</property>
-                    <property name="fixed_height_mode">True</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkVBox" id="vbox1">
-                <property name="visible">True</property>
-                <child>
-                  <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>
-                        <property name="border_width">5</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="weight-cases-radiobutton1">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Do not weight cases</property>
-                            <property name="focus_on_click">False</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton2">
-                            <property name="visible">True</property>
-                            <property name="sensitive">False</property>
-                            <property name="label" translatable="yes">Weight cases by</property>
-                            <property name="focus_on_click">False</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">weight-cases-radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkHBox" id="hbox3">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="PsppireSelector" id="weight-cases-selector">
-                                <property name="visible">True</property>
-                                <property name="border_width">5</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkVBox" id="vbox3">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkLabel" id="label1">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes">Frequency Variable</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkEntry" id="weight-cases-entry">
-                                    <property name="visible">True</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <placeholder/>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkHBox" id="hbox2">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkLabel" id="label3">
-                        <property name="visible">True</property>
-                        <property name="xalign">1</property>
-                        <property name="label" translatable="yes">Current Status: </property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="weight-status-label">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Do not weight cases</property>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <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>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-buttonbox1">
-            <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>
-  <widget class="PsppireDialog" id="transpose-dialog">
-    <property name="title">Transpose</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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 | GDK_ENTER_NOTIFY_MASK</property>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkHBox" id="hbox4">
-            <property name="visible">True</property>
-            <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="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <child>
-                      <widget class="GtkTreeView" id="source-treeview">
-                        <property name="visible">True</property>
-                        <property name="headers_visible">False</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <placeholder/>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkTable" id="table1">
-                <property name="visible">True</property>
-                <property name="n_rows">2</property>
-                <property name="n_columns">2</property>
-                <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">
-                    <property name="visible">True</property>
-                    <property name="border_width">5</property>
-                  </widget>
-                  <packing>
-                    <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="GtkVBox" id="vbox4">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkLabel" id="label2">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Variable(s):</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </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="hscrollbar_policy">GTK_POLICY_NEVER</property>
-                            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                            <child>
-                              <widget class="GtkTreeView" id="variables-treeview">
-                                <property name="visible">True</property>
-                                <property name="headers_visible">False</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <placeholder/>
-                          <packing>
-                            <property name="type">label_item</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox5">
-                    <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>
-                  </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>
-              </widget>
-              <packing>
-                <property name="padding">5</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-buttonbox2">
-            <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>
-  <widget class="PsppireDialog" id="split-file-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">Split File</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <widget class="GtkVBox" id="vbox6">
-            <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="GtkHBox" id="hbox5">
-                <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="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="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>
-                        <child>
-                          <widget class="GtkTreeView" 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="headers_visible">False</property>
-                            <property name="fixed_height_mode">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <placeholder/>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox7">
-                    <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="GtkVButtonBox" id="vbuttonbox2">
-                        <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="split-radiobutton0">
-                            <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">Analyze all cases.  Do not create groups.</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="split-radiobutton1">
-                            <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">Compare groups.</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">split-radiobutton0</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="split-radiobutton2">
-                            <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">Organize output by groups.</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">split-radiobutton0</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkHBox" id="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>
-                        <child>
-                          <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="yscale">0.5</property>
-                            <child>
-                              <widget class="PsppireSelector" id="split-file-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>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="padding">18</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkVBox" id="vbox8">
-                            <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="label5">
-                                <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">Groups based on:</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <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>
-                                  <widget class="GtkScrolledWindow" id="scrolledwindow4">
-                                    <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>
-                                    <child>
-                                      <widget class="GtkTreeView" id="split-file-grouping-vars">
-                                        <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="fixed_height_mode">True</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <placeholder/>
-                                  <packing>
-                                    <property name="type">label_item</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkVButtonBox" id="vbuttonbox1">
-                        <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="homogeneous">True</property>
-                        <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="split-radiobutton3">
-                            <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">Sort the file by grouping variables.</property>
-                            <property name="use_underline">True</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="split-radiobutton4">
-                            <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">File is already sorted.</property>
-                            <property name="use_underline">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">split-radiobutton3</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="padding">5</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkHSeparator" id="hseparator1">
-                <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="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkHBox" id="hbox7">
-                <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="label6">
-                    <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">1</property>
-                    <property name="label" translatable="yes">Current Status : </property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label7">
-                    <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">Analysis by groups is off</property>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-buttonbox3">
-            <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>
-          </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="PsppireDialog" id="sort-cases-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">Sort Cases</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox4">
-        <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="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>
-            <child>
-              <widget class="GtkScrolledWindow" id="scrolledwindow6">
-                <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="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>
-                    <property name="headers_visible">False</property>
-                    <property name="headers_clickable">True</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment6">
-                <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.25</property>
-                <property name="xscale">0</property>
-                <property name="yscale">0</property>
-                <child>
-                  <widget class="PsppireSelector" id="sort-cases-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>
-                </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="vbox12">
-                <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="vbox16">
-                    <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="label18">
-                        <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">Sort by:</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow7">
-                        <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="sort-cases-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>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="padding">5</property>
-                  </packing>
-                </child>
-                <child>
-                  <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="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="GtkVButtonBox" id="vbuttonbox4">
-                            <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                            <child>
-                              <widget class="GtkRadioButton" id="sort-cases-radiobutton0">
-                                <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="active">True</property>
-                                <property name="draw_indicator">True</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkRadioButton" id="sort-cases-radiobutton1">
-                                <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">Descending</property>
-                                <property name="draw_indicator">True</property>
-                                <property name="group">sort-cases-radiobutton0</property>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label17">
-                        <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">Sort Order</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="padding">5</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-buttonbox4">
-            <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>
-          </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="PsppireDialog" id="compute-variable-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">Compute Variable</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox5">
-        <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="GtkAlignment" id="alignment8">
-            <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="bottom_padding">5</property>
-            <property name="left_padding">5</property>
-            <child>
-              <widget class="GtkHBox" id="hbox15">
-                <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="vbox19">
-                    <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="GtkVBox" id="vbox20">
-                        <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="label20">
-                            <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">Target Variable:</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkEntry" id="compute-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>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkButton" id="compute-button1">
-                        <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">Type &amp; Label</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow9">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="shadow_type">GTK_SHADOW_IN</property>
-                        <child>
-                          <widget class="GtkTreeView" 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="headers_visible">False</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox21">
-                    <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="label21">
-                        <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">=</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="PsppireSelector" id="compute-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="expand">False</property>
-                        <property name="fill">False</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>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="GtkVBox" id="vbox17">
-            <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="GtkVBox" id="vbox18">
-                <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="label19">
-                    <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 Expressions:</property>
-                  </widget>
-                  <packing>
-                    <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>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_IN</property>
-                    <child>
-                      <widget class="GtkTextView" id="compute-textview1">
-                        <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>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkHBox" id="hbox16">
-                <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="PsppireKeypad" id="psppire-keypad1">
-                    <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 | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox22">
-                    <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="GtkHBox" id="hbox17">
-                        <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="label22">
-                            <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">Functions:</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="PsppireSelector" id="compute-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>
-                            <property name="orientation">PSPPIRE_SELECT_SOURCE_BELOW_DEST</property>
-                          </widget>
-                          <packing>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow10">
-                        <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="shadow_type">GTK_SHADOW_IN</property>
-                        <child>
-                          <widget class="GtkTreeView" id="compute-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>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkHBox" id="hbox18">
-                <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="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>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label23">
-                    <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="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireHButtonBox" id="psppire-hbuttonbox2">
-                <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="pack_type">GTK_PACK_END</property>
-                <property name="position">3</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-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</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_VERTICAL</property>
-    <child internal-child="hbox">
-      <widget class="GtkVBox" 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">2</property>
-        <child>
-          <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>
-            <child>
-              <widget class="GtkHBox" id="hbox8">
-                <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="scrolledwindow5">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_IN</property>
-                    <child>
-                      <widget class="GtkTreeView" 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>
-                        <property name="headers_visible">False</property>
-                        <property name="fixed_height_mode">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox13">
-                    <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="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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="xscale">0</property>
-                            <property name="left_padding">12</property>
-                            <property name="right_padding">5</property>
-                            <child>
-                              <widget class="GtkTable" id="table2">
-                                <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="n_rows">5</property>
-                                <property name="n_columns">2</property>
-                                <property name="row_spacing">5</property>
-                                <child>
-                                  <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="label" translatable="yes">All Cases</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="left_attach">1</property>
-                                    <property name="right_attach">2</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkVBox" id="vbox14">
-                                    <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>
-                                    <child>
-                                      <widget class="GtkLabel" id="label11">
-                                        <property name="visible">True</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="xalign">0</property>
-                                        <property name="label" translatable="yes">If condition is satisfied</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkHBox" id="hbox9">
-                                        <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="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>
-                                      </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>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <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="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">Random sample of cases</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <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="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="label" translatable="yes">Sample...</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="fill">False</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="random-sample-label">
-                                            <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="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="GtkVBox" id="vbox25">
-                                    <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="label15">
-                                        <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">Based on time or case range</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkHBox" id="hbox12">
-                                        <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="GtkButton" id="button-range">
-                                            <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">Range...</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="fill">False</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="range-sample-label">
-                                            <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="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="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="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">Use filter variable</property>
-                                      </widget>
-                                    </child>
-                                    <child>
-                                      <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>
-                                        <child>
-                                          <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="border_width">5</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="expand">False</property>
-                                            <property name="fill">False</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <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>
-                                            <property name="position">1</property>
-                                          </packing>
-                                        </child>
-                                      </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">4</property>
-                                    <property name="bottom_attach">5</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="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">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">radiobutton-all</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="top_attach">2</property>
-                                    <property name="bottom_attach">3</property>
-                                    <property name="x_options"></property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkAlignment" id="alignment17">
-                                    <property name="visible">True</property>
-                                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                    <property name="xalign">0</property>
-                                    <property name="yalign">0</property>
-                                    <property name="xscale">0</property>
-                                    <property name="yscale">0</property>
-                                    <child>
-                                      <widget class="GtkRadioButton" id="radiobutton-range">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">True</property>
-                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                                        <property name="active">True</property>
-                                        <property name="draw_indicator">True</property>
-                                        <property name="group">radiobutton-all</property>
-                                      </widget>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="top_attach">3</property>
-                                    <property name="bottom_attach">4</property>
-                                    <property name="x_options"></property>
-                                  </packing>
-                                </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="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>
-                                  </widget>
-                                  <packing>
-                                    <property name="top_attach">4</property>
-                                    <property name="bottom_attach">5</property>
-                                    <property name="x_options"></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>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="Select6">
-                            <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">Select</property>
-                            <property name="use_markup">True</property>
-                          </widget>
-                          <packing>
-                            <property name="type">label_item</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <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="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="GtkHButtonBox" id="filter-delete-button-box">
-                                <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                                <child>
-                                  <widget class="GtkRadioButton" id="radiobutton-filter">
-                                    <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="active">True</property>
-                                    <property name="draw_indicator">True</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkRadioButton" id="radiobutton-delete">
-                                    <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">Deleted</property>
-                                    <property name="active">True</property>
-                                    <property name="draw_indicator">True</property>
-                                    <property name="group">radiobutton-filter</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label9">
-                            <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">Unselected Cases Are</property>
-                            <property name="use_markup">True</property>
-                          </widget>
-                          <packing>
-                            <property name="type">label_item</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</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>
-        </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>
-          </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="PsppireDialog" id="comments-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">Data File Comments</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_VERTICAL</property>
-    <child internal-child="hbox">
-      <widget class="GtkVBox" id="dialog-hbox7">
-        <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="GtkAlignment" id="alignment7">
-            <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">5</property>
-            <property name="right_padding">5</property>
-            <child>
-              <widget class="GtkVBox" id="vbox9">
-                <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="label16">
-                    <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">Comments:</property>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                  </packing>
-                </child>
-                <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_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="shadow_type">GTK_SHADOW_IN</property>
-                    <child>
-                      <widget class="GtkTextView" id="comments-textview1">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkHBox" id="hbox13">
-            <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="comments-checkbutton1">
-                <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">Display comments in output</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="padding">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="column-number-label">
-                <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">1</property>
-                <property name="label" translatable="yes">Column Number: 0</property>
-              </widget>
-              <packing>
-                <property name="padding">5</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="pack_type">GTK_PACK_END</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireHButtonBox" id="psppire-hbuttonbox1">
-            <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>
-          </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="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="modal">True</property>
-    <property name="orientation">PSPPIRE_VERTICAL</property>
-    <child internal-child="hbox">
-      <widget class="GtkVBox" id="dialog-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="spacing">2</property>
-        <child>
-          <widget class="GtkTable" id="table3">
-            <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="n_rows">2</property>
-            <property name="n_columns">3</property>
-            <property name="column_spacing">5</property>
-            <child>
-              <widget class="GtkLabel" id="label14">
-                <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">Observation</property>
-              </widget>
-              <packing>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label12">
-                <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">Last case</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>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label8">
-                <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">First case</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="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="truncate_multiline">True</property>
-                <property name="adjustment">1 1 0 1 10 10</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="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="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>
-          </widget>
-          <packing>
-            <property name="fill">False</property>
-            <property name="padding">5</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="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="type-and-label-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">Compute Variable: Type and Label</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox9">
-        <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="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="GtkFrame" id="Label">
-                <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="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="GtkTable" id="table2">
-                        <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="n_rows">2</property>
-                        <property name="n_columns">2</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="radio-button-user-label">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                          <packing>
-                            <property name="x_options"></property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radio-button-expression-label">
-                            <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">
-                            <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="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>
-                                <property name="label" translatable="yes">Label:</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkEntry" id="type-and-label-label-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="pack_type">GTK_PACK_END</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </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="label27">
-                            <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 expression as label</property>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                            <property name="top_attach">1</property>
-                            <property name="bottom_attach">2</property>
-                            <property name="y_options"></property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <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="label" translatable="yes">Label</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkFrame" id="frame7">
-                <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="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="GtkTable" id="table3">
-                        <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="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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                            <child>
-                              <widget class="GtkLabel" id="label29">
-                                <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">String</property>
-                              </widget>
-                            </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox21">
-                                <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="label30">
-                                    <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">Width</property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkSpinButton" id="type-and-label-width">
-                                    <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">8 1 32767 1 10 10</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </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">GTK_FILL</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label15">
-                    <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">Type</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="goto-case-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">Goto Case</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_VERTICAL</property>
-    <child internal-child="hbox">
-      <widget class="GtkVBox" id="dialog-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="spacing">5</property>
-        <child>
-          <widget class="GtkHBox" id="hbox22">
-            <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="label31">
-                <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">1</property>
-                <property name="label" translatable="yes">Goto Case Number:</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkSpinButton" id="goto-case-case-num-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">1 1 100 1 10 10</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="fill">False</property>
-            <property name="padding">5</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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-            <property name="buttons">PSPPIRE_BUTTON_GOTO_MASK | PSPPIRE_BUTTON_CANCEL_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>
-  <widget class="PsppireDialog" id="select-cases-random-sample-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: Random Sample</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_VERTICAL</property>
-    <child internal-child="hbox">
-      <widget class="GtkVBox" id="dialog-hbox13">
-        <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="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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="top_padding">5</property>
-                <property name="bottom_padding">5</property>
-                <property name="left_padding">5</property>
-                <property name="right_padding">5</property>
-                <child>
-                  <widget class="GtkTable" id="select-cases-random-sample-table">
-                    <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="n_rows">2</property>
-                    <property name="n_columns">2</property>
-                    <property name="column_spacing">5</property>
-                    <property name="row_spacing">5</property>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment9">
-                        <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="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>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment4">
-                        <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="xscale">0</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</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="active">True</property>
-                        <property name="draw_indicator">True</property>
-                        <property name="group">radiobutton-sample-percent</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="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>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label32">
-                <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">Sample Size</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireHButtonBox" id="psppire-hbuttonbox5">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <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 815de4a07eda818a77d77499b1900355148b2c95..27a633f2ecf35c57cd30dc01ac85aa4a61db28a9 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 */
diff --git a/src/ui/gui/psppire.ui b/src/ui/gui/psppire.ui
new file mode 100644 (file)
index 0000000..2c7c341
--- /dev/null
@@ -0,0 +1,2274 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="weight-cases-dialog">
+    <property name="title">Weight Cases</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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 | GDK_ENTER_NOTIFY_MASK</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkHBox" id="hbox4">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object class="PsppireDictView" id="weight-cases-treeview">
+                    <property name="visible">True</property>
+                    <property name="headers_visible">False</property>
+                    <property name="fixed_height_mode">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox4">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkFrame" id="frame1">
+                    <property name="visible">True</property>
+                    <property name="border_width">5</property>
+                    <property name="label_xalign">0</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox2">
+                        <property name="visible">True</property>
+                        <property name="border_width">5</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkRadioButton" id="weight-cases-radiobutton1">
+                            <property name="label" translatable="yes">Do not weight cases</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="receives_default">False</property>
+                            <property name="focus_on_click">False</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton2">
+                            <property name="label" translatable="yes">Weight cases by</property>
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">False</property>
+                            <property name="receives_default">False</property>
+                            <property name="focus_on_click">False</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">weight-cases-radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox3">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="PsppireSelector" id="weight-cases-selector">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="receives_default">False</property>
+                                <property name="border_width">5</property>
+                                <property name="source_widget">weight-cases-treeview</property>
+                                <property name="dest_widget">weight-cases-entry</property>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkVBox" id="vbox3">
+                                <property name="visible">True</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkLabel" id="label3">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Frequency Variable</property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkEntry" id="weight-cases-entry">
+                                    <property name="visible">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label_item">
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label2">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Current Status: </property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="weight-status-label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Do not weight cases</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-buttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="transpose-dialog">
+    <property name="title">Transpose</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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 | GDK_ENTER_NOTIFY_MASK</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkFrame" id="frame3">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <child>
+                      <object class="PsppireDictView" id="source-treeview">
+                        <property name="visible">True</property>
+                        <property name="headers_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="table2">
+                <property name="visible">True</property>
+                <property name="n_rows">2</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">5</property>
+                <property name="row_spacing">5</property>
+                <child>
+                  <object class="GtkVBox" id="vbox5">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Name Variable:</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="new-name-entry">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <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>
+                  <object class="GtkVBox" id="vbox1">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Variable(s):</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkFrame" id="frame2">
+                        <property name="visible">True</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkScrolledWindow" id="scrolledwindow2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hscrollbar_policy">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <child>
+                              <object class="PsppireVarView" id="variables-treeview">
+                                <property name="visible">True</property>
+                                <property name="headers_visible">False</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label_item">
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="PsppireSelector" id="psppire-selector3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                    <property name="border_width">5</property>
+                    <property name="source_widget">source-treeview</property>
+                    <property name="dest_widget">new-name-entry</property>
+                  </object>
+                  <packing>
+                    <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>
+                  <object class="PsppireSelector" id="psppire-selector2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                    <property name="border_width">5</property>
+                    <property name="primary">True</property>
+                    <property name="source_widget">source-treeview</property>
+                    <property name="dest_widget">variables-treeview</property>
+                  </object>
+                  <packing>
+                    <property name="x_options"></property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="padding">5</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-buttonbox2">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="split-file-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">Split File</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object class="GtkVBox" id="vbox6">
+            <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="orientation">vertical</property>
+            <child>
+              <object class="GtkHBox" id="hbox5">
+                <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>
+                  <object 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">in</property>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow4">
+                        <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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <child>
+                          <object 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="headers_visible">False</property>
+                            <property name="fixed_height_mode">True</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label_item">
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox7">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkVButtonBox" id="vbuttonbox2">
+                        <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="layout_style">spread</property>
+                        <child>
+                          <object class="GtkRadioButton" id="split-radiobutton0">
+                            <property name="label" translatable="yes">Analyze all cases.  Do not create groups.</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="split-radiobutton1">
+                            <property name="label" translatable="yes">Compare groups.</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                            <property name="group">split-radiobutton0</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="split-radiobutton2">
+                            <property name="label" translatable="yes">Organize output by groups.</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                            <property name="group">split-radiobutton0</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="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>
+                        <child>
+                          <object 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.5</property>
+                            <child>
+                              <object class="PsppireSelector" id="split-file-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>
+                                <property name="source_widget">split-file-dict-treeview</property>
+                                <property name="dest_widget">split-file-grouping-vars</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="padding">18</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkVBox" id="vbox8">
+                            <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="orientation">vertical</property>
+                            <child>
+                              <object class="GtkLabel" id="label5">
+                                <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">Groups based on:</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object 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">in</property>
+                                <child>
+                                  <object class="GtkScrolledWindow" id="scrolledwindow5">
+                                    <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">never</property>
+                                    <property name="vscrollbar_policy">automatic</property>
+                                    <child>
+                                      <object class="PsppireVarView" id="split-file-grouping-vars">
+                                        <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="fixed_height_mode">True</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child type="label_item">
+                                  <placeholder/>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVButtonBox" id="vbuttonbox1">
+                        <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="homogeneous">True</property>
+                        <property name="layout_style">spread</property>
+                        <child>
+                          <object class="GtkRadioButton" id="split-radiobutton3">
+                            <property name="label" translatable="yes">Sort the file by grouping variables.</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="split-radiobutton4">
+                            <property name="label" translatable="yes">File is already sorted.</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">split-radiobutton3</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="padding">5</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHSeparator" id="hseparator1">
+                <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>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox7">
+                <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>
+                  <object class="GtkLabel" id="label6">
+                    <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">1</property>
+                    <property name="label" translatable="yes">Current Status : </property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label7">
+                    <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">Analysis by groups is off</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-buttonbox3">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="compute-variable-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">Compute Variable</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox5">
+        <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>
+          <object class="GtkAlignment" id="alignment8">
+            <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="bottom_padding">5</property>
+            <property name="left_padding">5</property>
+            <child>
+              <object class="GtkHBox" id="hbox15">
+                <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>
+                  <object class="GtkVBox" id="vbox19">
+                    <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="orientation">vertical</property>
+                    <property name="spacing">5</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox20">
+                        <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="orientation">vertical</property>
+                        <child>
+                          <object class="GtkLabel" id="label20">
+                            <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">Target Variable:</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="compute-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>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="compute-button1">
+                        <property name="label" translatable="yes">Type &amp; Label</property>
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow9">
+                        <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">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object 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="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox21">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label21">
+                        <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">=</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="PsppireSelector" id="compute-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>
+                        <property name="source_widget">compute-treeview1</property>
+                        <property name="dest_widget">compute-textview1</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox17">
+            <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="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object class="GtkVBox" id="vbox18">
+                <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="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="label19">
+                    <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 Expressions:</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow8">
+                    <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">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTextView" id="compute-textview1">
+                        <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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox16">
+                <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>
+                  <object class="PsppireKeypad" id="psppire-keypad1">
+                    <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 | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox22">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkHBox" id="hbox17">
+                        <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>
+                          <object class="GtkLabel" id="label22">
+                            <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">Functions:</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="PsppireSelector" id="compute-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>
+                            <property name="orientation">source below destination</property>
+                            <property name="source_widget">compute-treeview2</property>
+                            <property name="dest_widget">compute-textview1</property>
+                          </object>
+                          <packing>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow10">
+                        <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">never</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="compute-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>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox18">
+                <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>
+                  <object class="GtkButton" id="button4">
+                    <property name="label" translatable="yes">If...</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>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label23">
+                    <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>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="PsppireHButtonBox" id="psppire-hbuttonbox1">
+                <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="layout_style">spread</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">end</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="select-cases-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</property>
+    <property name="modal">True</property>
+    <property name="orientation">Vertical</property>
+    <child internal-child="hbox">
+      <object class="GtkVBox" 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">2</property>
+        <child>
+          <object 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="top_padding">5</property>
+            <property name="left_padding">5</property>
+            <property name="right_padding">5</property>
+            <child>
+              <object class="GtkHBox" id="hbox8">
+                <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>
+                  <object 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">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object 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>
+                        <property name="headers_visible">False</property>
+                        <property name="fixed_height_mode">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox13">
+                    <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>
+                      <object 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>
+                          <object class="GtkAlignment" id="alignment11">
+                            <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="xscale">0</property>
+                            <property name="left_padding">12</property>
+                            <property name="right_padding">5</property>
+                            <child>
+                              <object class="GtkTable" id="table1">
+                                <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="n_rows">5</property>
+                                <property name="n_columns">2</property>
+                                <property name="row_spacing">5</property>
+                                <child>
+                                  <object class="GtkRadioButton" id="radiobutton-all">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">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="active">True</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object 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="yalign">0</property>
+                                    <property name="xscale">0</property>
+                                    <property name="yscale">0</property>
+                                    <child>
+                                      <object class="GtkRadioButton" id="radiobutton-filter-variable">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">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="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="top_attach">4</property>
+                                    <property name="bottom_attach">5</property>
+                                    <property name="x_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object 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>
+                                      <object class="GtkRadioButton" id="radiobutton-range">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">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="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="top_attach">3</property>
+                                    <property name="bottom_attach">4</property>
+                                    <property name="x_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object 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>
+                                      <object class="GtkRadioButton" id="radiobutton-sample">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">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="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="top_attach">2</property>
+                                    <property name="bottom_attach">3</property>
+                                    <property name="x_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object 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>
+                                      <object class="GtkRadioButton" id="radiobutton-if">
+                                        <property name="sensitive">False</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">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="active">True</property>
+                                        <property name="draw_indicator">True</property>
+                                        <property name="group">radiobutton-all</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object 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>
+                                      <object 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">Use filter variable</property>
+                                      </object>
+                                      <packing>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object 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>
+                                        <child>
+                                          <object 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="border_width">5</property>
+                                            <property name="source_widget">select-cases-treeview</property>
+                                            <property name="dest_widget">filter-variable-entry</property>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object 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>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <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>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkVBox" id="vbox25">
+                                    <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>
+                                      <object class="GtkLabel" id="label15">
+                                        <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">Based on time or case range</property>
+                                      </object>
+                                      <packing>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHBox" id="hbox12">
+                                        <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>
+                                          <object class="GtkButton" id="button-range">
+                                            <property name="label" translatable="yes">Range...</property>
+                                            <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>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkLabel" id="range-sample-label">
+                                            <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>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <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>
+                                  <object 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>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object 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">Random sample of cases</property>
+                                      </object>
+                                      <packing>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object 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>
+                                          <object class="GtkButton" id="button-sample">
+                                            <property name="label" translatable="yes">Sample...</property>
+                                            <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>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkLabel" id="random-sample-label">
+                                            <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>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <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>
+                                <child>
+                                  <object 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>
+                                    <child>
+                                      <object class="GtkLabel" id="label11">
+                                        <property name="visible">True</property>
+                                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">If condition is satisfied</property>
+                                      </object>
+                                      <packing>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkHBox" id="hbox9">
+                                        <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>
+                                          <object class="GtkButton" id="button-if">
+                                            <property name="label" translatable="yes">If...</property>
+                                            <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>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object 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>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <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>
+                                  <object 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="label" translatable="yes">All Cases</property>
+                                  </object>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                  </packing>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="Select6">
+                            <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">Select</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                          <object class="GtkAlignment" id="alignment12">
+                            <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>
+                              <object class="GtkHButtonBox" id="filter-delete-button-box">
+                                <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="layout_style">spread</property>
+                                <child>
+                                  <object class="GtkRadioButton" id="radiobutton-filter">
+                                    <property name="label" translatable="yes">Filtered</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">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="active">True</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkRadioButton" id="radiobutton-delete">
+                                    <property name="label" translatable="yes">Deleted</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">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="active">True</property>
+                                    <property name="draw_indicator">True</property>
+                                    <property name="group">radiobutton-filter</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="label9">
+                            <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">Unselected Cases Are</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="pack_type">end</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object 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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="comments-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">Data File Comments</property>
+    <property name="modal">True</property>
+    <property name="orientation">Vertical</property>
+    <child internal-child="hbox">
+      <object class="GtkVBox" id="dialog-hbox7">
+        <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>
+          <object class="GtkAlignment" id="alignment7">
+            <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">5</property>
+            <property name="right_padding">5</property>
+            <child>
+              <object class="GtkVBox" id="vbox9">
+                <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="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel" id="label16">
+                    <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">Comments:</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTextView" id="comments-textview1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="hbox13">
+            <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>
+              <object class="GtkCheckButton" id="comments-checkbutton1">
+                <property name="label" translatable="yes">Display comments in output</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">5</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="column-number-label">
+                <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">1</property>
+                <property name="label" translatable="yes">Column Number: 0</property>
+              </object>
+              <packing>
+                <property name="padding">5</property>
+                <property name="pack_type">end</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireHButtonBox" id="psppire-hbuttonbox2">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object 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="modal">True</property>
+    <property name="orientation">Vertical</property>
+    <child internal-child="hbox">
+      <object class="GtkVBox" id="dialog-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="spacing">2</property>
+        <child>
+          <object class="GtkTable" id="table3">
+            <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="n_rows">2</property>
+            <property name="n_columns">3</property>
+            <property name="column_spacing">5</property>
+            <child>
+              <object 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="truncate_multiline">True</property>
+              </object>
+              <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>
+              <object 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="truncate_multiline">True</property>
+              </object>
+              <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>
+              <object class="GtkLabel" id="label8">
+                <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">First case</property>
+              </object>
+              <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>
+              <object class="GtkLabel" id="label12">
+                <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">Last case</property>
+              </object>
+              <packing>
+                <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>
+              <object class="GtkLabel" id="label14">
+                <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">Observation</property>
+              </object>
+              <packing>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="fill">False</property>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object 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="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="type-and-label-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">Compute Variable: Type and Label</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox9">
+        <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>
+          <object 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>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkFrame" id="Label">
+                <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>
+                  <object 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>
+                      <object class="GtkTable" id="table4">
+                        <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="n_rows">2</property>
+                        <property name="n_columns">2</property>
+                        <child>
+                          <object class="GtkLabel" id="label27">
+                            <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 expression as label</property>
+                          </object>
+                          <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>
+                          <object 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="spacing">5</property>
+                            <child>
+                              <object 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="label" translatable="yes">Label:</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkEntry" id="type-and-label-label-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>
+                              </object>
+                              <packing>
+                                <property name="pack_type">end</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radio-button-expression-label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radio-button-user-label</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radio-button-user-label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="x_options"></property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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="label" translatable="yes">Label</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame7">
+                <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>
+                  <object class="GtkAlignment" id="alignment10">
+                    <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>
+                      <object class="GtkTable" id="table5">
+                        <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="n_rows">2</property>
+                        <property name="n_columns">2</property>
+                        <child>
+                          <object class="GtkHBox" id="hbox20">
+                            <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>
+                              <object class="GtkLabel" id="label29">
+                                <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">String</property>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHBox" id="hbox21">
+                                <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>
+                                  <object class="GtkLabel" id="label30">
+                                    <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">Width</property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="type-and-label-width">
+                                    <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>
+                                  </object>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <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>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radio-button-string">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <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>
+                          <object class="GtkRadioButton" id="radio-button-numeric">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radio-button-string</property>
+                          </object>
+                          <packing>
+                            <property name="x_options"></property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label31">
+                            <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>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label32">
+                    <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">Type</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="goto-case-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">Goto Case</property>
+    <property name="modal">True</property>
+    <property name="orientation">Vertical</property>
+    <child internal-child="hbox">
+      <object class="GtkVBox" id="dialog-hbox8">
+        <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>
+          <object class="GtkHBox" id="hbox22">
+            <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>
+              <object 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">1</property>
+                <property name="label" translatable="yes">Goto Case Number:</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="goto-case-case-num-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>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="fill">False</property>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireHButtonBox" id="psppire-hbuttonbox5">
+            <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="layout_style">spread</property>
+            <property name="buttons">PSPPIRE_BUTTON_GOTO_MASK | PSPPIRE_BUTTON_CANCEL_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="select-cases-random-sample-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: Random Sample</property>
+    <property name="modal">True</property>
+    <property name="orientation">Vertical</property>
+    <child internal-child="hbox">
+      <object class="GtkVBox" id="dialog-hbox13">
+        <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>
+          <object 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>
+              <object class="GtkAlignment" id="alignment4">
+                <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="top_padding">5</property>
+                <property name="bottom_padding">5</property>
+                <property name="left_padding">5</property>
+                <property name="right_padding">5</property>
+                <child>
+                  <object class="GtkTable" id="select-cases-random-sample-table">
+                    <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="n_rows">2</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">5</property>
+                    <property name="row_spacing">5</property>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton-sample-percent">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton-sample-n-cases">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton-sample-percent</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment9">
+                        <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="xscale">0</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment14">
+                        <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="xscale">0</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <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>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object 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="label" translatable="yes">Sample Size</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireHButtonBox" id="psppire-hbuttonbox6">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index 174ac6d04138b9b1dfa9e4914b9ac68ebf7cc8ed..aa81e23bbe71fd77fb2e5997f53c3bd93045643a 100644 (file)
 #include <stdlib.h>
 
 #include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
+#include <ui/gui/psppire-var-view.h>
+#include "executor.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -110,7 +111,7 @@ generate_syntax (const struct rank_dialog *rd)
 
   GString *str = g_string_new ("RANK VARIABLES=");
 
-  append_variable_names (str, rd->dict, GTK_TREE_VIEW (rd->rank_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->rank_vars), 0, str);
 
   g_string_append_printf (str, " (%c)",
                   gtk_toggle_button_get_active (rd->ascending_togglebutton)
@@ -120,7 +121,7 @@ generate_syntax (const struct rank_dialog *rd)
     {
       g_string_append (str, "\n\tBY ");
 
-      append_variable_names (str, rd->dict, GTK_TREE_VIEW (rd->group_vars), 0);
+      psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->group_vars),  0, str);
     }
 
   g_string_append (str, "\n\t/PRINT = ");
@@ -220,77 +221,74 @@ 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 *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");
+  g_object_get (vs, "dictionary", &rd.dict, NULL);
+  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,31 +302,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);
-
-
-  set_dest_model (GTK_TREE_VIEW (rd.rank_vars), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector1),
-                                vars,
-                                rd.rank_vars,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
-  set_dest_model (GTK_TREE_VIEW (rd.group_vars), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector2),
-                                vars,
-                                rd.group_vars,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), GTK_WINDOW (de));
 
+  g_object_set (vars, "model", rd.dict, NULL);
 
   g_signal_connect (types_button, "clicked",
                    G_CALLBACK (run_types_dialog),  &rd);
@@ -349,6 +325,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 +335,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 +343,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);
 
diff --git a/src/ui/gui/rank.glade b/src/ui/gui/rank.glade
deleted file mode 100644 (file)
index 77c6cfc..0000000
+++ /dev/null
@@ -1,709 +0,0 @@
-<?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-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="rank-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">Rank Cases</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <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">5</property>
-            <child>
-              <widget class="GtkTable" id="table1">
-                <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="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">
-                    <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="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="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">Variable(s):</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</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="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>
-                            <property name="headers_visible">False</property>
-                            <property name="headers_clickable">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="right_attach">3</property>
-                  </packing>
-                </child>
-                <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>
-                    <child>
-                      <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">By:</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <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>
-                        <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="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>
-                            <property name="headers_visible">False</property>
-                            <property name="headers_clickable">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </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="GtkScrolledWindow" id="scrolledwindow5">
-                    <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="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="bottom_attach">2</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <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">5</property>
-                <child>
-                  <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>
-                        <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="GtkVButtonBox" id="vbuttonbox1">
-                            <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="homogeneous">True</property>
-                            <child>
-                              <widget class="GtkRadioButton" id="radiobutton1">
-                                <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">_Smallest Value</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>
-                            </child>
-                            <child>
-                              <widget class="GtkRadioButton" id="radiobutton2">
-                                <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">_Largest Value</property>
-                                <property name="use_underline">True</property>
-                                <property name="response_id">0</property>
-                                <property name="active">True</property>
-                                <property name="draw_indicator">True</property>
-                                <property name="group">radiobutton1</property>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label4">
-                        <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">Assign rank 1 to:</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <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>
-                    <child>
-                      <widget class="GtkCheckButton" id="summary-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">_Display summary tables</property>
-                        <property name="use_underline">True</property>
-                        <property name="response_id">0</property>
-                        <property name="draw_indicator">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkHButtonBox" id="hbuttonbox1">
-                        <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="GtkButton" id="button1">
-                            <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">Rank T_ypes</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkButton" id="button2">
-                            <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">_Ties...</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
-            <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>
-          </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="PsppireDialog" id="rank-types-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">Rank Cases: Types</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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>
-        <property name="spacing">2</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">5</property>
-            <child>
-              <widget class="GtkTable" id="table2">
-                <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="n_rows">3</property>
-                <property name="n_columns">2</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>
-                    <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">2</property>
-                    <property name="bottom_attach">3</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">
-                    <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="response_id">0</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="rfrac-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</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>
-                  </packing>
-                </child>
-                <child>
-                  <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">Fractional rank as %</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>
-                  </packing>
-                </child>
-                <child>
-                  <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>
-                    <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">1</property>
-                    <property name="bottom_attach">2</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkHBox" id="hbbox3">
-                <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="prop-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">Proportion Estimates</property>
-                    <property name="response_id">0</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="normal-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">Normal Scores</property>
-                    <property name="response_id">0</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <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>
-            <child>
-              <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>
-                    <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="GtkHBox" id="hbbox2">
-                        <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="GtkRadioButton" id="blom-button">
-                            <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">Blom</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="tukey-button">
-                            <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">Tukey</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">blom-button</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="rankit-button">
-                            <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">Rankit</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">blom-button</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="vw-button">
-                            <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">Van der Wärden</property>
-                            <property name="response_id">0</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">blom-button</property>
-                          </widget>
-                          <packing>
-                            <property name="position">3</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label2">
-                    <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">Proportion Estimation Formula</property>
-                    <property name="use_markup">True</property>
-                    <property name="justify">GTK_JUSTIFY_FILL</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="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="ties-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">Rank Cases: Ties</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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>
-        <child>
-          <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>
-                <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="GtkVBox" id="vbox6">
-                    <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="GtkHButtonBox" id="hbuttonbox2">
-                        <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="GtkRadioButton" id="mean-button">
-                            <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">_Mean</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>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="low-button">
-                            <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">_Low</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">mean-button</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="high-button">
-                            <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">_High</property>
-                            <property name="use_underline">True</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">mean-button</property>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="condense-button">
-                        <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">_Sequential ranks to unique 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>
-                        <property name="group">mean-button</property>
-                      </widget>
-                      <packing>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label5">
-                <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">Rank Assigned to Ties</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox3">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
diff --git a/src/ui/gui/rank.ui b/src/ui/gui/rank.ui
new file mode 100644 (file)
index 0000000..b05ab45
--- /dev/null
@@ -0,0 +1,765 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2053.63976"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="PsppireDialog" id="rank-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">Rank Cases</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object class="GtkTable" id="table1">
+                <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="n_rows">2</property>
+                <property name="n_columns">3</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow5">
+                    <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">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object 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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object 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">By:</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                        <property name="hscrollbar_policy">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" 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>
+                            <property name="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <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>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object 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">Variable(s):</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" 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>
+                            <property name="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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>
+                    <property name="source_widget">dict-treeview</property>
+                    <property name="dest_widget">group-vars-treeview</property>
+                  </object>
+                  <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>
+                  <object 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>
+                    <property name="primary">True</property>
+                    <property name="source_widget">dict-treeview</property>
+                    <property name="dest_widget">variables-treeview</property>
+                  </object>
+                  <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>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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">5</property>
+                <child>
+                  <object 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>
+                      <object 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>
+                          <object class="GtkVButtonBox" id="vbuttonbox1">
+                            <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="homogeneous">True</property>
+                            <child>
+                              <object class="GtkRadioButton" id="radiobutton1">
+                                <property name="label" translatable="yes">_Smallest Value</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="use_underline">True</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="radiobutton2">
+                                <property name="label" translatable="yes">_Largest Value</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="use_underline">True</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">radiobutton1</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label4">
+                        <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">Assign rank 1 to:</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkCheckButton" id="summary-checkbutton">
+                        <property name="label" translatable="yes">_Display summary tables</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHButtonBox" id="hbuttonbox1">
+                        <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>
+                          <object class="GtkButton" id="button1">
+                            <property name="label" translatable="yes">Rank T_ypes</property>
+                            <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="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="button2">
+                            <property name="label" translatable="yes">_Ties...</property>
+                            <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="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="rank-types-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">Rank Cases: Types</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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>
+        <property name="spacing">2</property>
+        <child>
+          <object 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">5</property>
+            <child>
+              <object class="GtkTable" id="table2">
+                <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="n_rows">3</property>
+                <property name="n_columns">2</property>
+                <child>
+                  <object class="GtkCheckButton" id="sum-checkbutton">
+                    <property name="label" translatable="yes">Sum of case weights</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <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>
+                  <object class="GtkCheckButton" id="percent-checkbutton">
+                    <property name="label" translatable="yes">Fractional rank as %</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="rfrac-checkbutton">
+                    <property name="label" translatable="yes">Fractional rank</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="savage-checkbutton">
+                    <property name="label" translatable="yes">Savage score</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="rank-checkbutton">
+                    <property name="label" translatable="yes">Rank</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object 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>
+                      <object class="GtkCheckButton" id="ntiles-checkbutton">
+                        <property name="label" translatable="yes">Ntiles</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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">adjustment1</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <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>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbbox3">
+                <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>
+                  <object class="GtkCheckButton" id="prop-checkbutton">
+                    <property name="label" translatable="yes">Proportion Estimates</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="normal-checkbutton">
+                    <property name="label" translatable="yes">Normal Scores</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                  <object 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="left_padding">12</property>
+                    <child>
+                      <object class="GtkHBox" id="hbbox2">
+                        <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>
+                          <object class="GtkRadioButton" id="blom-button">
+                            <property name="label" translatable="yes">Blom</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="tukey-button">
+                            <property name="label" translatable="yes">Tukey</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                            <property name="group">blom-button</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="rankit-button">
+                            <property name="label" translatable="yes">Rankit</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                            <property name="group">blom-button</property>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="vw-button">
+                            <property name="label" translatable="yes">Van der W&#xE4;rden</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                            <property name="group">blom-button</property>
+                          </object>
+                          <packing>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label2">
+                    <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">Proportion Estimation Formula</property>
+                    <property name="use_markup">True</property>
+                    <property name="justify">fill</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="ties-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">Rank Cases: Ties</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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>
+        <child>
+          <object 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>
+              <object 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>
+                  <object class="GtkVBox" id="vbox6">
+                    <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>
+                      <object class="GtkHButtonBox" id="hbuttonbox2">
+                        <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>
+                          <object class="GtkRadioButton" id="mean-button">
+                            <property name="label" translatable="yes">_Mean</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="low-button">
+                            <property name="label" translatable="yes">_Low</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">mean-button</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="high-button">
+                            <property name="label" translatable="yes">_High</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">mean-button</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="condense-button">
+                        <property name="label" translatable="yes">_Sequential ranks to unique values</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="use_underline">True</property>
+                        <property name="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">mean-button</property>
+                      </object>
+                      <packing>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label5">
+                <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">Rank Assigned to Ties</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox3">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+</interface>
index 314367462e6f403d691f2c9896972086b610075f..9a1af8d5d7a582a69119fb835b084c86b0ad5cad 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 "psppire-var-view.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 +76,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;
 }
@@ -334,13 +339,15 @@ struct recode_dialog
 
   gboolean input_var_is_string;
 
-  GtkListStore *var_map;
   GtkWidget *new_name_entry;
   GtkWidget *new_label_entry;
   GtkWidget *change_button;
 
   GtkWidget *string_button;
   GtkWidget *width_entry;
+
+  /* A hash table of struct nlp's indexed by variable */
+  GHashTable *varmap;
 };
 
 
@@ -349,33 +356,23 @@ static void run_old_and_new_dialog (struct recode_dialog *rd);
 static void
 refresh (PsppireDialog *dialog, struct recode_dialog *rd)
 {
+  GtkTreeModel *vars =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
+
+  gtk_list_store_clear (GTK_LIST_STORE (vars));
+
   gtk_widget_set_sensitive (rd->change_button, FALSE);
   gtk_widget_set_sensitive (rd->new_name_entry, FALSE);
   gtk_widget_set_sensitive (rd->new_label_entry, FALSE);
 
-
   if ( rd->different )
-    gtk_list_store_clear (GTK_LIST_STORE (rd->var_map));
-  else
-    {
-      GtkTreeModel *vars =
-       gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
-
-      gtk_list_store_clear (GTK_LIST_STORE (vars));
-    }
+    g_hash_table_remove_all (rd->varmap);
 
   gtk_list_store_clear (GTK_LIST_STORE (rd->value_map));
 }
 
 static char * generate_syntax (const struct recode_dialog *rd);
 
-enum {
-  COL_OLD,
-  COL_NEW_NAME,
-  COL_NEW_LABEL,
-  n_COL_VARS
-};
-
 enum {
   COL_VALUE_OLD,
   COL_VALUE_NEW,
@@ -400,26 +397,10 @@ dialog_state_valid (gpointer data)
 
   if ( rd->different )
     {
-      GtkTreeIter iter;
-
-      gboolean ok;
-
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
-          ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map),
-                                         &iter))
-       {
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name, -1);
+      GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd->variable_treeview)->list);
 
-         if ( name == NULL )
-           return FALSE;
-
-         g_free (name);
-       }
+      if (g_hash_table_size (rd->varmap) != gtk_tree_model_iter_n_children (model, NULL) )
+       return FALSE;
     }
   else
     {
@@ -428,7 +409,6 @@ dialog_state_valid (gpointer data)
 
       if ( !gtk_tree_model_get_iter_first (vars, &not_used))
        return FALSE;
-
     }
 
   return TRUE;
@@ -470,14 +450,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,27 +466,11 @@ 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);
 }
 
-static void
-render_new_var_name (GtkTreeViewColumn *tree_column,
-                    GtkCellRenderer *cell,
-                    GtkTreeModel *tree_model,
-                    GtkTreeIter *iter,
-                    gpointer data)
-{
-  gchar *new_var_name = NULL;
-
-  gtk_tree_model_get (tree_model, iter, COL_NEW_NAME, &new_var_name, -1);
-
-  g_object_set (cell, "text", new_var_name, NULL);
-
-  g_free (new_var_name);
-}
-
 
 /* This might need to be changed to something less naive.
    In particular, what happends with dates, etc?
@@ -683,6 +647,41 @@ on_acr_selection_change (GtkTreeSelection *selection, gpointer data)
     }
 }
 
+/* Name-Label pair */
+struct nlp
+{
+  char *name;
+  char *label;
+};
+
+static struct nlp *
+nlp_create (const char *name, const char *label)
+{
+  struct nlp *nlp = xmalloc (sizeof *nlp);
+
+  nlp->name = g_strdup (name);
+
+  nlp->label = NULL;
+
+  if ( 0 != strcmp ("", label))
+    nlp->label = g_strdup (label);
+
+  return nlp;
+}
+
+static void
+nlp_destroy (gpointer data)
+{
+  struct nlp *nlp = data ;
+  if ( ! nlp )
+    return;
+
+  g_free (nlp->name);
+  g_free (nlp->label);
+  g_free (nlp);
+}
+
+
 /* Callback which gets called when a new row is selected
    in the variable treeview.
    It sets the name and label entry widgets to reflect the
@@ -692,18 +691,18 @@ static void
 on_selection_change (GtkTreeSelection *selection, gpointer data)
 {
   struct recode_dialog *rd = data;
-  GtkTreeModel *model = GTK_TREE_MODEL (rd->var_map);
+
+  GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd->variable_treeview)->list);
 
   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
 
   if ( rows && !rows->next)
     {
       /* Exactly one row is selected */
-
+      struct nlp *nlp;
+      struct variable *var;
       gboolean ok;
       GtkTreeIter iter;
-      gchar *name = NULL;
-      gchar *label = NULL;
 
       gtk_widget_set_sensitive  (rd->change_button, TRUE);
       gtk_widget_set_sensitive  (rd->new_name_entry, TRUE);
@@ -711,16 +710,22 @@ on_selection_change (GtkTreeSelection *selection, gpointer data)
 
       ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data);
 
-      gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                         COL_NEW_NAME, &name,
-                         COL_NEW_LABEL, &label,
+      gtk_tree_model_get (model, &iter,
+                         0, &var, 
                          -1);
 
-      gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), name ? name : "");
-      gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), label ? label : "");
+      nlp = g_hash_table_lookup (rd->varmap, var);
 
-      g_free (name);
-      g_free (label);
+      if (nlp)
+       {
+         gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), nlp->name ? nlp->name : "");
+         gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), nlp->label ? nlp->label : "");
+       }
+      else
+       {
+         gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
+         gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
+       }
     }
   else
     {
@@ -732,6 +737,7 @@ on_selection_change (GtkTreeSelection *selection, gpointer data)
       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
     }
 
+
   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
   g_list_free (rows);
 }
@@ -759,12 +765,13 @@ on_convert_toggled (GtkToggleButton *b, struct recode_dialog *rd)
   gtk_widget_set_sensitive (rd->string_button, !active);
 }
 
-
 static void
 on_change_clicked (GObject *obj, gpointer data)
 {
   struct recode_dialog *rd = data;
-  GtkTreeModel *model = GTK_TREE_MODEL (rd->var_map);
+  struct variable *var = NULL;
+  struct nlp *nlp;
+  GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd->variable_treeview)->list);
   GtkTreeIter iter;
   GtkTreeSelection *selection =
     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
@@ -777,16 +784,22 @@ on_change_clicked (GObject *obj, gpointer data)
   const gchar *dest_var_label =
     gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry));
 
-  if ( NULL == rows )
-    return;
+  if ( NULL == rows || rows->next != NULL)
+    goto finish;
 
   gtk_tree_model_get_iter (model, &iter, rows->data);
 
-  gtk_list_store_set (rd->var_map, &iter,
-                     COL_NEW_NAME, dest_var_name,
-                     COL_NEW_LABEL, dest_var_label,
-                     -1);
+  gtk_tree_model_get (model, &iter, 0, &var, -1);
+
+  g_hash_table_remove (rd->varmap, var);
+
+  nlp = nlp_create (dest_var_name, dest_var_label);
+
+  g_hash_table_insert (rd->varmap, var, nlp);
+
+  gtk_tree_model_row_changed (model, rows->data, &iter);
 
+ finish:
   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
   g_list_free (rows);
 }
@@ -836,36 +849,59 @@ set_acr (struct recode_dialog *rd)
 }
 
 static void
-recode_dialog (struct data_editor *de, gboolean diff)
+render_new_var_name (GtkTreeViewColumn *tree_column,
+                    GtkCellRenderer *cell,
+                    GtkTreeModel *tree_model,
+                    GtkTreeIter *iter,
+                    gpointer data)
 {
-  gint response;
+  struct nlp *nlp = NULL;
+  struct recode_dialog *rd = data;
 
-  struct recode_dialog rd;
+  struct variable *var = NULL;
+
+  gtk_tree_model_get (tree_model, iter, 
+                     0, &var,
+                     -1);
 
-  GladeXML *xml = XML_NEW ("recode.glade");
+  nlp = g_hash_table_lookup (rd->varmap, var);
 
+  if ( nlp )
+    g_object_set (cell, "text", nlp->name, NULL);
+  else
+    g_object_set (cell, "text", "", NULL);
+}
 
-  GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
 
-  GtkWidget *oldandnew = get_widget_assert (xml, "button1");
 
+static void
+recode_dialog (PsppireDataWindow *de, gboolean diff)
+{
+  gint response;
 
-  GtkWidget *output_variable_box = get_widget_assert (xml,"frame4");
+  struct recode_dialog rd;
 
+  GtkBuilder *builder = builder_new ("recode.ui");
 
-  PsppireVarStore *vs = NULL;
+  GtkWidget *selector = get_widget_assert (builder, "psppire-selector1");
 
-  g_object_get (de->data_editor, "var-store", &vs, NULL);
+  GtkWidget *oldandnew = get_widget_assert (builder, "button1");
 
-  rd.change_button = get_widget_assert (xml, "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");
+  GtkWidget *output_variable_box = get_widget_assert (builder,"frame4");
 
-  rd.dict = vs->dict;
+  PsppireVarStore *vs = NULL;
+  g_object_get (de->data_editor, "var-store", &vs, NULL);
+
+  rd.change_button = get_widget_assert (builder, "change-button");
+  rd.varmap = NULL;
+  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");
+
+  g_object_get (vs, "dictionary", &rd.dict, NULL);
 
   rd.value_map = gtk_list_store_new (2,
                                     old_value_get_type (),
@@ -883,120 +919,90 @@ 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, "model", rd.dict, NULL);
 
-
-  if ( ! rd.different )
-    {
-      set_dest_model (GTK_TREE_VIEW (rd.variable_treeview), vs->dict);
-    }
-  else
+  if (rd.different)
     {
+      GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd.variable_treeview)->list);
       GtkTreeSelection *sel;
-      GtkTreeViewColumn *col;
-      GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
-
-      rd.var_map = gtk_list_store_new (n_COL_VARS, G_TYPE_INT,
-                                                   G_TYPE_STRING,
-                                                   G_TYPE_STRING);
-
 
+      GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
 
-      gtk_tree_view_set_model (GTK_TREE_VIEW (rd.variable_treeview),
-                              GTK_TREE_MODEL (rd.var_map));
-
-      col = gtk_tree_view_column_new_with_attributes (_("Old"),
-                                                 renderer,
-                                                 "text", NULL,
-                                                 NULL);
+      GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("New"),
+                                                                        renderer,
+                                                                        "text", NULL,
+                                                                        NULL);
 
       gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                              cell_var_name,
-                                              vs->dict, 0);
+                                              render_new_var_name,
+                                              &rd, NULL);
 
 
       gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
 
 
-      renderer = gtk_cell_renderer_text_new ();
+      col = gtk_tree_view_get_column (GTK_TREE_VIEW (rd.variable_treeview), 0);
 
-      col = gtk_tree_view_column_new_with_attributes (_("New"),
-                                                 renderer,
-                                                 "text", NULL,
-                                                 NULL);
-
-      gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                              render_new_var_name,
-                                              NULL, NULL);
-
-
-      gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
+      g_object_set (col, "title", _("Old"), NULL);
 
       g_object_set (rd.variable_treeview, "headers-visible", TRUE, NULL);
 
-      g_signal_connect (rd.change_button, "clicked",
-                       G_CALLBACK (on_change_clicked),  &rd);
+      rd.varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy);
 
       sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.variable_treeview));
+
       g_signal_connect (sel, "changed",
                        G_CALLBACK (on_selection_change), &rd);
 
-      g_signal_connect (rd.var_map, "row-inserted",
+      g_signal_connect (rd.change_button, "clicked",
+                       G_CALLBACK (on_change_clicked),  &rd);
+
+#if 0
+      g_signal_connect (model, "row-inserted",
                        G_CALLBACK (select_something), &rd);
+#endif
     }
 
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                rd.dict_treeview,
-                                rd.variable_treeview,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types);
 
   /* 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 +1053,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 +1091,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 +1101,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);
       }
@@ -1107,11 +1110,12 @@ recode_dialog (struct data_editor *de, gboolean diff)
       break;
     }
 
+  g_hash_table_destroy (rd.varmap);
 
   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 */
@@ -1280,7 +1284,6 @@ run_old_and_new_dialog (struct recode_dialog *rd)
     /* Find the type of the first variable (it's invariant that
        all variables are of the same type) */
     const struct variable *v;
-    gint idx;
     GtkTreeIter iter;
     GtkTreeModel *model =
       gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
@@ -1289,9 +1292,7 @@ run_old_and_new_dialog (struct recode_dialog *rd)
 
     g_return_if_fail (not_empty);
 
-    gtk_tree_model_get (model, &iter, 0, &idx, -1);
-
-    v = psppire_dict_get_variable (rd->dict, idx);
+    gtk_tree_model_get (model, &iter, 0, &v, -1);
 
     rd->input_var_is_string = var_is_alpha (v);
 
@@ -1420,33 +1421,26 @@ generate_syntax (const struct recode_dialog *rd)
   if ( rd->different &&
        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)))
     {
-      GtkTreeIter iter;
+      GHashTableIter iter;
 
+      struct variable *var = NULL;
+      struct nlp *nlp = NULL;
 
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
-          ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
+      g_hash_table_iter_init (&iter, rd->varmap);
+      while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
        {
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name, -1);
-
          g_string_append (str, "\nSTRING ");
-         g_string_append (str, name);
+         g_string_append (str, nlp->name);
          g_string_append_printf (str, " (A%d).",
                                  (int)
                                  gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry) )
                                  );
-
-         g_free (name);
        }
     }
 
   g_string_append (str, "\nRECODE ");
 
-  append_variable_names (str, rd->dict, GTK_TREE_VIEW (rd->variable_treeview), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, str);
 
   g_string_append (str, "\n\t");
 
@@ -1487,67 +1481,43 @@ generate_syntax (const struct recode_dialog *rd)
 
   if ( rd->different )
     {
+
       GtkTreeIter iter;
       g_string_append (str, "\n\tINTO ");
 
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
+      for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter);
           ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
-       {
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name, -1);
-
-         g_string_append (str, name);
-         g_string_append (str, " ");
+          ok = psppire_var_view_get_iter_next (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter))
+         {
+           struct nlp *nlp = NULL;
+           const struct variable *var = psppire_var_view_get_variable (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &iter);
 
-         g_free (name);
-       }
+           nlp = g_hash_table_lookup (rd->varmap, var);
+           
+           g_string_append (str, nlp->name);
+           g_string_append (str, " ");
+         }
     }
 
   g_string_append (str, ".");
 
-
   /* If applicable, set labels for the new variables. */
   if ( rd->different )
     {
-      GtkTreeIter iter;
+      GHashTableIter iter;
 
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
-          ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
+      struct variable *var = NULL;
+      struct nlp *nlp = NULL;
+
+      g_hash_table_iter_init (&iter, rd->varmap);
+      while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
        {
-         struct string ls;
-         gchar *label = NULL;
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name,
-                             COL_NEW_LABEL, &label, -1);
-
-         if ( 0 == strcmp (label, "") )
-           {
-             g_free (name);
-             g_free (label);
-             continue;
-           }
-
-         ds_init_empty (&ls);
-         syntax_gen_string (&ls, ss_cstr (label));
-         g_free (label);
-
-         g_string_append_printf (str, "\nVARIABLE LABELS %s %s.",
-                                 name, ds_cstr (&ls));
-
-         g_free (name);
-         ds_destroy (&ls);
+         if (nlp->label)
+           g_string_append_printf (str, "\nVARIABLE LABELS %s %s.",
+                                   nlp->name, nlp->label);
        }
     }
 
-
   g_string_append (str, "\nEXECUTE.\n");
 
 
diff --git a/src/ui/gui/recode.glade b/src/ui/gui/recode.glade
deleted file mode 100644 (file)
index a3b795e..0000000
+++ /dev/null
@@ -1,988 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.2 on Sat Nov 10 12:40:56 2007 by john@marilyn-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="old-new-values-dialog">
-    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">5</property>
-        <child>
-          <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>
-                <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>
-                <property name="right_padding">5</property>
-                <child>
-                  <widget class="GtkTable" id="table2">
-                    <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="n_rows">11</property>
-                    <property name="n_columns">2</property>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <placeholder/>
-                    </child>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment11">
-                        <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="xscale">0</property>
-                        <child>
-                          <widget class="GtkEntry" id="entry7">
-                            <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>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">9</property>
-                        <property name="bottom_attach">10</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment10">
-                        <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="xscale">0</property>
-                        <child>
-                          <widget class="GtkEntry" id="entry6">
-                            <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>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">7</property>
-                        <property name="bottom_attach">8</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton4">
-                        <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>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton6">
-                        <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">radiobutton4</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton7">
-                        <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">radiobutton4</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">3</property>
-                        <property name="bottom_attach">4</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton8">
-                        <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">radiobutton4</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">4</property>
-                        <property name="bottom_attach">5</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton10">
-                        <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">radiobutton4</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">6</property>
-                        <property name="bottom_attach">7</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton5">
-                        <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">radiobutton4</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">8</property>
-                        <property name="bottom_attach">9</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label6">
-                        <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="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label7">
-                        <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">System-Missing</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>
-                    <child>
-                      <widget class="GtkLabel" id="label8">
-                        <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">System-or user-missing</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="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="GtkAlignment" id="alignment9">
-                            <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="xscale">0</property>
-                            <child>
-                              <widget class="GtkEntry" id="entry5">
-                                <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>
-                            </child>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkLabel" id="label9">
-                            <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">through</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkAlignment" id="alignment7">
-                            <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="xscale">0</property>
-                            <child>
-                              <widget class="GtkEntry" id="entry3">
-                                <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>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">5</property>
-                        <property name="bottom_attach">6</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label11">
-                        <property name="visible">True</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Range, LOWEST thru value</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">6</property>
-                        <property name="bottom_attach">7</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label12">
-                        <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">Range, value thru HIGHEST</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">8</property>
-                        <property name="bottom_attach">9</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkRadioButton" id="radiobutton11">
-                        <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">radiobutton4</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">10</property>
-                        <property name="bottom_attach">11</property>
-                        <property name="x_options"></property>
-                      </packing>
-                    </child>
-                    <child>
-                      <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">All other values</property>
-                      </widget>
-                      <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">10</property>
-                        <property name="bottom_attach">11</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment6">
-                        <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="xscale">0</property>
-                        <child>
-                          <widget class="GtkEntry" id="entry2">
-                            <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>
-                        </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>
-                      </packing>
-                    </child>
-                    <child>
-                      <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="label" translatable="yes">Range:</property>
-                      </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>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label5">
-                <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">Old Value</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <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>
-            <child>
-              <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>
-                    <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="GtkTable" id="table1">
-                        <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="n_rows">3</property>
-                        <property name="n_columns">2</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton1">
-                            <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>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton2">
-                            <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">radiobutton1</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="GtkRadioButton" id="radiobutton3">
-                            <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">radiobutton1</property>
-                            <child>
-                              <placeholder/>
-                            </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="GtkLabel" id="label2">
-                            <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">System Missing</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="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">Copy old values</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>
-                        <child>
-                          <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="xalign">0</property>
-                            <property name="xscale">0</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>
-                                <child>
-                                  <widget class="GtkLabel" id="label4">
-                                    <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">1</property>
-                                    <property name="label" translatable="yes">Value: </property>
-                                  </widget>
-                                </child>
-                                <child>
-                                  <widget class="GtkEntry" id="entry1">
-                                    <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>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <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">New Value</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireAcr" id="psppire-acr1">
-                <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>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="table3">
-                <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="n_rows">2</property>
-                <property name="n_columns">2</property>
-                <child>
-                  <widget class="GtkCheckButton" id="checkbutton1">
-                    <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="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="checkbutton2">
-                    <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="draw_indicator">True</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="GtkLabel" id="label14">
-                    <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">Convert numeric strings to numbers ('5' -&gt; 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>
-                  </packing>
-                </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="GtkLabel" id="label15">
-                        <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">Output variables are strings</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <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>
-                        <child>
-                          <widget class="GtkLabel" id="label16">
-                            <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">1</property>
-                            <property name="label" translatable="yes">Width: </property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkSpinButton" id="spinbutton1">
-                            <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="adjustment">8 1 255 1 8 0</property>
-                            <property name="numeric">True</property>
-                            <property name="update_policy">GTK_UPDATE_IF_VALID</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <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>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireHButtonBox" id="psppire-hbuttonbox1">
-                <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">10</property>
-                <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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">3</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="PsppireDialog" id="recode-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">Recode into Same Variables</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_TABULAR</property>
-    <child internal-child="hbox">
-      <widget class="GtkTable" id="dialog-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="n_rows">3</property>
-        <property name="n_columns">4</property>
-        <property name="column_spacing">5</property>
-        <property name="row_spacing">5</property>
-        <child>
-          <widget class="GtkLabel" id="label22">
-            <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="left_attach">1</property>
-            <property name="right_attach">2</property>
-            <property name="top_attach">2</property>
-            <property name="bottom_attach">3</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="GtkHBox" id="hbox4">
-            <property name="visible">False</property>
-            <property name="sensitive">False</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="spacing">5</property>
-            <child>
-              <widget class="GtkButton" id="button2">
-                <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">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="label18">
-                <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">(optional case selection condition)</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="left_attach">2</property>
-            <property name="right_attach">4</property>
-            <property name="top_attach">2</property>
-            <property name="bottom_attach">3</property>
-            <property name="y_options"></property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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>
-          </widget>
-          <packing>
-            <property name="left_attach">3</property>
-            <property name="right_attach">4</property>
-          </packing>
-        </child>
-        <child>
-          <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="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>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="bottom_attach">3</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkLabel" id="label23">
-            <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="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="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>
-            <child>
-              <widget class="GtkAlignment" id="alignment8">
-                <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="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>
-                    <child>
-                      <widget class="GtkLabel" id="label20">
-                        <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">Name:</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="dest-name-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="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="label21">
-                        <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">Label:</property>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="dest-label-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="position">3</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkHButtonBox" id="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>
-                        <child>
-                          <widget class="GtkButton" id="change-button">
-                            <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">Change</property>
-                            <property name="response_id">0</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="padding">5</property>
-                        <property name="position">4</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label19">
-                <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">Output Variable</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="left_attach">3</property>
-            <property name="right_attach">4</property>
-            <property name="top_attach">1</property>
-            <property name="bottom_attach">2</property>
-          </packing>
-        </child>
-        <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">5</property>
-            <child>
-              <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="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="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="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="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>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label17">
-                    <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="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment5">
-                <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="xscale">0</property>
-                <child>
-                  <widget class="GtkHButtonBox" id="hbuttonbox1">
-                    <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="GtkButton" id="button1">
-                        <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">Old and New Values</property>
-                        <property name="response_id">0</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </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">2</property>
-            <property name="right_attach">3</property>
-            <property name="bottom_attach">2</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-</glade-interface>
diff --git a/src/ui/gui/recode.ui b/src/ui/gui/recode.ui
new file mode 100644 (file)
index 0000000..c573dba
--- /dev/null
@@ -0,0 +1,1019 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="PsppireDialog" id="old-new-values-dialog">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">5</property>
+        <child>
+          <object 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>
+              <object 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>
+                <property name="right_padding">5</property>
+                <child>
+                  <object class="GtkTable" id="table2">
+                    <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="n_rows">11</property>
+                    <property name="n_columns">2</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment11">
+                        <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="xscale">0</property>
+                        <child>
+                          <object class="GtkEntry" id="entry7">
+                            <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>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">9</property>
+                        <property name="bottom_attach">10</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment10">
+                        <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="xscale">0</property>
+                        <child>
+                          <object class="GtkEntry" id="entry6">
+                            <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>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">7</property>
+                        <property name="bottom_attach">8</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton6">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton4</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton7">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton4</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton8">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton4</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton10">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton4</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton5">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton4</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">8</property>
+                        <property name="bottom_attach">9</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label6">
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label7">
+                        <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">System Missing</property>
+                      </object>
+                      <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>
+                    <child>
+                      <object class="GtkLabel" id="label8">
+                        <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">System or User Missing</property>
+                      </object>
+                      <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>
+                      <object 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="orientation">vertical</property>
+                        <child>
+                          <object class="GtkAlignment" id="alignment9">
+                            <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="xscale">0</property>
+                            <child>
+                              <object class="GtkEntry" id="entry5">
+                                <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>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label9">
+                            <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">through</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkAlignment" id="alignment7">
+                            <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="xscale">0</property>
+                            <child>
+                              <object class="GtkEntry" id="entry3">
+                                <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>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label11">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Range, LOWEST thru value</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label12">
+                        <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">Range, value thru HIGHEST</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">8</property>
+                        <property name="bottom_attach">9</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkRadioButton" id="radiobutton11">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">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="active">True</property>
+                        <property name="draw_indicator">True</property>
+                        <property name="group">radiobutton4</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">10</property>
+                        <property name="bottom_attach">11</property>
+                        <property name="x_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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">All other values</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">10</property>
+                        <property name="bottom_attach">11</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment6">
+                        <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="xscale">0</property>
+                        <child>
+                          <object class="GtkEntry" id="entry2">
+                            <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>
+                          </object>
+                        </child>
+                      </object>
+                      <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>
+                      <object 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="label" translatable="yes">Range:</property>
+                      </object>
+                      <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>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label5">
+                <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">Old Value</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object 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="orientation">vertical</property>
+            <child>
+              <object 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>
+                  <object 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>
+                      <object class="GtkTable" id="table1">
+                        <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="n_rows">3</property>
+                        <property name="n_columns">2</property>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton1">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="x_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton3">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                            <property name="x_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <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">System Missing</property>
+                          </object>
+                          <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>
+                          <object 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">Copy old values</property>
+                          </object>
+                          <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>
+                        <child>
+                          <object 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="xalign">0</property>
+                            <property name="xscale">0</property>
+                            <child>
+                              <object 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>
+                                <child>
+                                  <object class="GtkLabel" id="label4">
+                                    <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">1</property>
+                                    <property name="label" translatable="yes">Value: </property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkEntry" id="entry1">
+                                    <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>
+                                  </object>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">New Value</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="PsppireAcr" id="psppire-acr1">
+                <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>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="table3">
+                <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="n_rows">2</property>
+                <property name="n_columns">2</property>
+                <child>
+                  <object class="GtkCheckButton" id="checkbutton1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="x_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="checkbutton2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">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="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label14">
+                    <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">Convert numeric strings to numbers ('5' -&gt; 5)</property>
+                  </object>
+                  <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>
+                  <object 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>
+                      <object class="GtkLabel" id="label15">
+                        <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">Output variables are strings</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                        <child>
+                          <object class="GtkLabel" id="label16">
+                            <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">1</property>
+                            <property name="label" translatable="yes">Width: </property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="spinbutton1">
+                            <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="adjustment">adjustment1</property>
+                            <property name="numeric">True</property>
+                            <property name="update_policy">if-valid</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="PsppireHButtonBox" id="psppire-hbuttonbox1">
+                <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">10</property>
+                <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">end</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="recode-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">Recode into Same Variables</property>
+    <property name="modal">True</property>
+    <property name="orientation">Tabular</property>
+    <child internal-child="hbox">
+      <object class="GtkTable" id="dialog-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="n_rows">3</property>
+        <property name="n_columns">4</property>
+        <property name="column_spacing">5</property>
+        <property name="row_spacing">5</property>
+        <child>
+          <object class="GtkLabel" id="label22">
+            <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>
+          </object>
+          <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="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <object 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>
+            <property name="source_widget">treeview1</property>
+            <property name="dest_widget">treeview2</property>
+          </object>
+          <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>
+          <object class="GtkHBox" id="hbox4">
+            <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="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label" translatable="yes">If...</property>
+                <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>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label18">
+                <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">(optional case selection condition)</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">4</property>
+            <property name="top_attach">2</property>
+            <property name="bottom_attach">3</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="right_attach">4</property>
+          </packing>
+        </child>
+        <child>
+          <object 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">never</property>
+            <property name="vscrollbar_policy">automatic</property>
+            <property name="shadow_type">etched-in</property>
+            <child>
+              <object 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>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="bottom_attach">3</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label23">
+            <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>
+          </object>
+          <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>
+          <object 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>
+            <child>
+              <object class="GtkAlignment" id="alignment8">
+                <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>
+                  <object 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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label20">
+                        <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">Name:</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="dest-name-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>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label21">
+                        <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">Label:</property>
+                      </object>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="dest-label-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>
+                      </object>
+                      <packing>
+                        <property name="position">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHButtonBox" id="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>
+                        <child>
+                          <object class="GtkButton" id="change-button">
+                            <property name="label" translatable="yes">Change</property>
+                            <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>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="padding">5</property>
+                        <property name="position">4</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label19">
+                <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">Output Variable</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="right_attach">4</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+          </packing>
+        </child>
+        <child>
+          <object 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="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object 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="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment4">
+                    <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>
+                      <object 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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" 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>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label17">
+                    <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="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment5">
+                <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="xscale">0</property>
+                <child>
+                  <object class="GtkHButtonBox" id="hbuttonbox1">
+                    <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>
+                      <object class="GtkButton" id="button1">
+                        <property name="label" translatable="yes">Old and New Values</property>
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">3</property>
+            <property name="bottom_attach">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="value">8</property>
+    <property name="lower">1</property>
+    <property name="upper">255</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">8</property>
+  </object>
+</interface>
index 4ee11fe9982ffd9a55ea4e1091aa7d9db081f795..070d30a6e74bbfe59749eba9c86903e79f502827 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 <ui/gui/psppire-var-view.h>
+
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -156,9 +158,9 @@ generate_syntax (const struct regression_dialog *rd)
   GString *string = g_string_new ("REGRESSION");
 
   g_string_append (string, "\n\t/VARIABLES=");
-  append_variable_names (string, rd->dict, GTK_TREE_VIEW (rd->indep_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->indep_vars), 0, string);
   g_string_append (string, "\n\t/DEPENDENT=\t");
-  append_variable_names (string, rd->dict, GTK_TREE_VIEW (rd->dep_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->dep_vars), 0, string);
 
   selected = 0;
   for (i = 0, ok = gtk_tree_model_get_iter_first (rd->stat, &iter); ok; 
@@ -214,7 +216,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,19 +225,16 @@ 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");
   GtkWidget *source = get_widget_assert   (xml, "dict-view");
   GtkWidget *dest_dep =   get_widget_assert   (xml, "dep-view");
   GtkWidget *dest_indep =   get_widget_assert   (xml, "indep-view");
-  GtkWidget *dep_selector = get_widget_assert (xml, "dep-selector");
-  GtkWidget *indep_selector = get_widget_assert (xml, "indep-selector");
   GtkWidget *stat_button = get_widget_assert (xml, "stat-button");
   GtkWidget *save_button = get_widget_assert (xml, "save-button");
 
@@ -251,32 +250,15 @@ regression_dialog (GObject *o, gpointer data)
                                  stats
                                  );
 
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
-
-  set_dest_model (GTK_TREE_VIEW (dest_dep), vs->dict);
-  set_dest_model (GTK_TREE_VIEW (dest_indep), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (dep_selector),
-                                source,
-                                dest_dep,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (indep_selector),
-                                source,
-                                dest_indep,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  g_object_get (vs, "dictionary", &rd.dict, NULL);
+  g_object_set (source, "model", rd.dict, NULL);
 
   rd.dep_vars = GTK_TREE_VIEW (dest_dep);
   rd.indep_vars = GTK_TREE_VIEW (dest_indep);
-  rd.dict = vs->dict;
+
+
   rd.save_dialog = get_widget_assert (xml, "save-dialog");
   rd.pred_button = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "pred-button"));
   rd.resid_button = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "resid-button"));
@@ -286,8 +268,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 +289,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 +299,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);
 
diff --git a/src/ui/gui/regression.glade b/src/ui/gui/regression.glade
deleted file mode 100644 (file)
index 3ee4973..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Sat Mar  1 12:42:34 2008 by john@marilyn-->
-<glade-interface>
-  <requires lib="psppire"/>
-  <widget class="PsppireDialog" id="regression-dialog">
-    <property name="title">Regression</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <widget class="GtkTable" id="table1">
-            <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="n_rows">3</property>
-            <property name="n_columns">3</property>
-            <child>
-              <widget class="GtkHButtonBox" id="hbuttonbox1">
-                <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                <child>
-                  <widget class="GtkButton" id="stat-button">
-                    <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">Statistics...</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkButton" id="save-button">
-                    <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">Save...</property>
-                  </widget>
-                  <packing>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-                <property name="y_options"></property>
-                <property name="y_padding">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="PsppireSelector" id="dep-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="no_show_all">True</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="indep-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="no_show_all">True</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="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>
-                <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-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>
-                    <property name="headers_visible">False</property>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <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="shadow_type">GTK_SHADOW_NONE</property>
-                <child>
-                  <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="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="dep-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>
-                            <property name="headers_visible">False</property>
-                            <property name="headers_clickable">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <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">Dependent</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <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="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="left_padding">12</property>
-                    <child>
-                      <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="indep-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>
-                            <property name="headers_visible">False</property>
-                            <property name="headers_clickable">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <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">Independent</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </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>
-          </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>
-  <widget class="PsppireDialog" id="save-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">Regression: Save</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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>
-        <property name="spacing">2</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>
-            <child>
-              <widget class="GtkCheckButton" id="pred-button">
-                <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">Predicted values</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkCheckButton" id="resid-button">
-                <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">Residuals</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="statistics-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">Regression: Statistics</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <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">
-                <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="scrolledwindow4">
-                    <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="stat-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>
-                        <property name="headers_visible">False</property>
-                        <property name="headers_clickable">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label2">
-                <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">Statistics</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox3">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
diff --git a/src/ui/gui/regression.ui b/src/ui/gui/regression.ui
new file mode 100644 (file)
index 0000000..61561f3
--- /dev/null
@@ -0,0 +1,367 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="regression-dialog">
+    <property name="title">Regression</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object class="GtkTable" id="table1">
+            <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="n_rows">3</property>
+            <property name="n_columns">3</property>
+            <child>
+              <object class="GtkHButtonBox" id="hbuttonbox1">
+                <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="layout_style">spread</property>
+                <child>
+                  <object class="GtkButton" id="stat-button">
+                    <property name="label" translatable="yes">Statistics...</property>
+                    <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>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="save-button">
+                    <property name="label" translatable="yes">Save...</property>
+                    <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>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="right_attach">3</property>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+                <property name="y_options"></property>
+                <property name="y_padding">5</property>
+              </packing>
+            </child>
+            <child>
+              <object class="PsppireSelector" id="dep-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="no_show_all">True</property>
+                <property name="border_width">5</property>
+                <property name="source_widget">dict-view</property>
+                <property name="dest_widget">dep-view</property>
+              </object>
+              <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>
+              <object class="PsppireSelector" id="indep-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="no_show_all">True</property>
+                <property name="border_width">5</property>
+                <property name="source_widget">dict-view</property>
+                <property name="dest_widget">indep-view</property>
+              </object>
+              <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>
+              <object class="GtkScrolledWindow" id="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>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object 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>
+                      <object 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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="dep-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>
+                            <property name="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">Dependent</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object 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="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object 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="left_padding">12</property>
+                    <child>
+                      <object 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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="indep-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>
+                            <property name="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">Independent</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <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>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="save-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">Regression: Save</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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>
+        <property name="spacing">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <child>
+              <object class="GtkCheckButton" id="pred-button">
+                <property name="label" translatable="yes">Predicted values</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="resid-button">
+                <property name="label" translatable="yes">Residuals</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="statistics-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">Regression: Statistics</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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>
+            <property name="shadow_type">none</property>
+            <child>
+              <object 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>
+                  <object class="GtkScrolledWindow" id="scrolledwindow4">
+                    <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">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object class="GtkTreeView" id="stat-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>
+                        <property name="headers_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label2">
+                <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">Statistics</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox3">
+            <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="orientation">vertical</property>
+            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/ui/gui/reliability-dialog.c b/src/ui/gui/reliability-dialog.c
new file mode 100644 (file)
index 0000000..9831ab7
--- /dev/null
@@ -0,0 +1,218 @@
+/* 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 "psppire-var-view.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");
+
+  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_get (vs, "dictionary", &rd.dict, NULL);
+  g_object_set (source, "model", rd.dict, 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=");
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variables), 0, string);
+
+
+  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.ui b/src/ui/gui/reliability.ui
new file mode 100644 (file)
index 0000000..25af6ca
--- /dev/null
@@ -0,0 +1,219 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-requires psppire 0.0 -->
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkListStore" id="model1">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">Alpha</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Split</col>
+      </row>
+    </data>
+  </object>
+  <object class="PsppireDialog" id="reliability-dialog">
+    <property name="title" translatable="yes">Reliability Analysis</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox5">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object 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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="xscale">0</property>
+                    <property name="yscale">0</property>
+                    <child>
+                      <object 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="source_widget">dict-view</property>
+                        <property name="dest_widget">treeview2</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="frame1">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment1">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkScrolledWindow" id="scrolledwindow2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="hscrollbar_policy">never</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="PsppireVarView" id="treeview2">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="headers_visible">False</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object 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>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox2">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">Model:   </property>
+                    <property name="justify">right</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="combobox1">
+                    <property name="visible">True</property>
+                    <property name="model">model1</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="renderer1"/>
+                      <attributes>
+                        <attribute name="text">0</attribute>
+                      </attributes>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="split-point-hbox">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="spacing">5</property>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Variables in first split:</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSpinButton" id="spinbutton1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="adjustment">adjustment1</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/ui/gui/roc-dialog.c b/src/ui/gui/roc-dialog.c
new file mode 100644 (file)
index 0000000..cf0a831
--- /dev/null
@@ -0,0 +1,260 @@
+/* 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 <ui/syntax-gen.h>
+#include <libpspp/str.h>
+
+#include "roc-dialog.h"
+#include "psppire-selector.h"
+#include "psppire-dictview.h"
+#include "psppire-dialog.h"
+
+#include "psppire-data-window.h"
+#include "psppire-var-view.h"
+
+#include "executor.h"
+#include "helper.h"
+
+#include <gtk/gtk.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+
+struct roc
+{
+  PsppireDict *dict;
+
+  GtkWidget *test_variables;
+  GtkWidget *state_variable;
+  GtkWidget *state_value;
+
+  GtkWidget *curve;
+  GtkWidget *reference;
+  GtkWidget *standard_error;
+  GtkWidget *coordinates;
+};
+
+
+static char * generate_syntax (const struct roc *rd);
+
+
+static void
+refresh (struct roc *rd)
+{
+  GtkTreeModel *liststore =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->test_variables));
+  gtk_list_store_clear (GTK_LIST_STORE (liststore));
+
+  gtk_entry_set_text (GTK_ENTRY (rd->state_variable), "");
+  gtk_entry_set_text (GTK_ENTRY (rd->state_value), "");
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->curve),          TRUE);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->reference),      FALSE);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->standard_error), FALSE);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->coordinates),    FALSE);
+}
+
+
+static gboolean
+dialog_state_valid (gpointer data)
+{
+  struct roc *rd = data;
+  const gchar *text;
+
+  GtkTreeModel *liststore =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->test_variables));
+
+  if  (gtk_tree_model_iter_n_children (liststore, NULL) < 1)
+    return FALSE;
+
+  
+  text = gtk_entry_get_text (GTK_ENTRY (rd->state_variable));
+  if ( 0 == strcmp ("", text))
+    return FALSE;
+
+
+  text = gtk_entry_get_text (GTK_ENTRY (rd->state_value));
+  if ( 0 == strcmp ("", text))
+    return FALSE;
+
+
+  return TRUE;
+}
+
+static void
+on_curve_button_toggle  (GtkCheckButton *curve, struct roc *rd)
+{
+  if ( !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (curve)))
+    {
+      if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->reference)))
+       g_object_set (rd->reference, "inconsistent", TRUE, NULL);
+      g_object_set (rd->reference, "sensitive", FALSE, NULL);
+    }
+  else 
+    {
+      g_object_set (rd->reference, "inconsistent", FALSE, NULL);
+      g_object_set (rd->reference, "sensitive", TRUE, NULL);
+    }
+}
+
+
+/* Pops up the Roc dialog box */
+void
+roc_dialog (GObject *o, gpointer data)
+{
+  struct roc rd;
+  gint response;
+
+  GtkBuilder *xml = builder_new ("roc.ui");
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+  PsppireVarStore *vs;
+
+  GtkWidget *dialog = get_widget_assert   (xml, "roc-dialog");
+  GtkWidget *source = get_widget_assert   (xml, "dict-view");
+
+  rd.test_variables    = get_widget_assert   (xml, "psppire-var-view1");
+  rd.state_variable    = get_widget_assert   (xml, "entry1");
+  rd.state_value       = get_widget_assert   (xml, "entry2");
+
+  rd.curve          = get_widget_assert   (xml, "curve");
+  rd.reference      = get_widget_assert   (xml, "reference-line");
+  rd.standard_error = get_widget_assert   (xml, "standard-error");
+  rd.coordinates    = get_widget_assert   (xml, "co-ordinates");
+
+
+  g_object_get (de->data_editor, "var-store", &vs, NULL);
+
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
+
+  g_object_get (vs, "dictionary", &rd.dict, NULL);
+  g_object_set (source, "model", rd.dict, NULL);
+
+  g_signal_connect (rd.curve, "toggled", G_CALLBACK (on_curve_button_toggle), &rd);
+
+  g_signal_connect_swapped (dialog, "refresh", G_CALLBACK (refresh),  &rd);
+
+  psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog),
+                                     dialog_state_valid, &rd);
+
+  psppire_selector_set_allow (PSPPIRE_SELECTOR (get_widget_assert (xml, "dep-selector")),
+                             numeric_only);
+
+  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 roc *rd)
+{
+  gchar *text;
+  const gchar *var_name = gtk_entry_get_text (GTK_ENTRY (rd->state_variable));
+  GString *string = g_string_new ("ROC");
+
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->test_variables), 0, string);
+
+  g_string_append (string, " BY ");
+
+  g_string_append (string, var_name);
+
+  g_string_append (string, " (");
+  {
+    const gchar *value = gtk_entry_get_text (GTK_ENTRY (rd->state_value));
+
+    const struct variable *var = psppire_dict_lookup_var (rd->dict, var_name);
+
+    g_return_val_if_fail (var, NULL);
+
+    if ( var_is_alpha (var))
+      {
+       struct string xx;
+       ds_init_empty (&xx);
+       syntax_gen_string (&xx, ss_cstr (value));
+       g_string_append (string, ds_cstr (&xx));
+       ds_destroy (&xx);
+      }
+    else
+      g_string_append (string, value);
+  }
+  g_string_append (string, ")");
+
+
+  /* The /PLOT subcommand */
+  g_string_append (string, "\n\t/PLOT ");
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->curve)))
+    {
+      g_string_append (string, "CURVE");
+      if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->reference)))
+       g_string_append (string, " (REFERENCE)");
+    }
+  else
+    g_string_append (string, "NONE");
+
+
+  /* The /PRINT subcommand */
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->standard_error)) ||
+       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->coordinates)) )
+    {
+      g_string_append (string, "\n\t/PRINT");
+
+      if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->standard_error)))
+       g_string_append (string, " SE");
+
+      if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->coordinates)))
+       g_string_append (string, " COORDINATES");
+    }
+
+  g_string_append (string, ".\n");
+
+  text = string->str;
+
+  g_string_free (string, FALSE);
+
+  return text;
+}
diff --git a/src/ui/gui/roc-dialog.h b/src/ui/gui/roc-dialog.h
new file mode 100644 (file)
index 0000000..0244980
--- /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 __ROC_DIALOG_H
+#define __ROC_DIALOG_H
+
+
+#include <gtk/gtk.h>
+
+void roc_dialog (GObject *o, gpointer data);
+
+#endif
diff --git a/src/ui/gui/roc.ui b/src/ui/gui/roc.ui
new file mode 100644 (file)
index 0000000..a3aee25
--- /dev/null
@@ -0,0 +1,327 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="roc-dialog">
+    <property name="title">ROC Curve</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object class="GtkTable" id="table1">
+            <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="n_rows">3</property>
+            <property name="n_columns">3</property>
+            <child>
+              <object class="PsppireSelector" id="dep-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="no_show_all">True</property>
+                <property name="border_width">5</property>
+                <property name="source_widget">dict-view</property>
+                <property name="dest_widget">psppire-var-view1</property>
+              </object>
+              <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>
+              <object class="PsppireSelector" id="indep-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="no_show_all">True</property>
+                <property name="border_width">5</property>
+                <property name="source_widget">dict-view</property>
+                <property name="dest_widget">entry1</property>
+              </object>
+              <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>
+              <object class="GtkScrolledWindow" id="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>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object 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>
+                      <object 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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="psppire-var-view1">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="border_width">5</property>
+                            <property name="headers_visible">False</property>
+                            <property name="headers_clickable">False</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">_Test Variable:</property>
+                    <property name="use_markup">True</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkEntry" id="entry1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x2022;</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">_State Variable:</property>
+                    <property name="use_markup">True</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                </child>
+              </object>
+              <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>
+                <property name="y_options">GTK_FILL</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">_Value of state variable:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">entry2</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="entry2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x2022;</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="frame3">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment3">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkVButtonBox" id="vbuttonbox1">
+                            <property name="visible">True</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkCheckButton" id="curve">
+                                <property name="label" translatable="yes">ROC C_urve</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHBox" id="hbox2">
+                                <property name="visible">True</property>
+                                <child>
+                                  <object class="GtkCheckButton" id="reference-line">
+                                    <property name="label" translatable="yes">_With diagonal reference line</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="padding">12</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="standard-error">
+                                <property name="label" translatable="yes">Standard _Error and Confidence Interval</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="co-ordinates">
+                                <property name="label" translatable="yes">_Coordinate points of the ROC Curve</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">3</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label3">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Display</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</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>
+                <property name="x_padding">5</property>
+                <property name="y_padding">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index f241c79d3558bcda1ec38f294f76f7a81373131d..5b027825bc6e62ccec66116837e7f085015a9dc7 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,21 +321,18 @@ 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, "model",
+                 scd.data_store->dict,
+                 "selection-mode",
+                 GTK_SELECTION_SINGLE, NULL);
 
-    psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                  source,
-                                  entry,
-                                  insert_source_row_into_entry,
-                                  is_currently_in_entry,
-                                  NULL);
+    psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector),
+                                  is_currently_in_entry);
   }
 
 
@@ -354,6 +352,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 +362,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 +385,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..22c537812311eb232837062c384b5a91dc22db70 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 "psppire-var-view.h"
 
 #include <language/syntax-string-source.h>
-#include "syntax-editor.h"
+#include "helper.h"
 
 static void
 refresh (PsppireDialog *dialog, GtkTreeView *dest)
@@ -39,7 +41,7 @@ refresh (PsppireDialog *dialog, GtkTreeView *dest)
 
 struct sort_cases_dialog
 {
-  GtkTreeView *tv;
+  PsppireVarView *tv;
   PsppireDict *dict;
   GtkToggleButton *ascending;
 };
@@ -49,7 +51,7 @@ static gboolean
 dialog_state_valid (gpointer data)
 {
   struct sort_cases_dialog *scd = data;
-  GtkTreeModel *model = gtk_tree_view_get_model (scd->tv);
+  GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (scd->tv));
 
   gint n_rows = gtk_tree_model_iter_n_children  (model, NULL);
 
@@ -64,8 +66,8 @@ generate_syntax (const struct sort_cases_dialog *scd)
 {
   gchar *text;
   GString *string = g_string_new ("SORT CASES BY ");
-  gint n_vars = append_variable_names (string,
-                                      scd->dict, GTK_TREE_VIEW (scd->tv), 0);
+
+  gint n_vars = psppire_var_view_append_names (scd->tv, 0, string);
 
   if ( n_vars == 0 )
     g_string_assign (string, "");
@@ -91,42 +93,29 @@ 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 ("sort.ui");
 
   GtkWidget *dialog = get_widget_assert   (xml, "sort-cases-dialog");
 
 
   GtkWidget *source = get_widget_assert   (xml, "sort-cases-treeview1");
-  GtkWidget *selector = get_widget_assert (xml, "sort-cases-selector");
   GtkWidget *dest =   get_widget_assert   (xml, "sort-cases-treeview2");
-
   PsppireVarStore *vs = NULL;
 
   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 (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                dest,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  g_object_get (vs, "dictionary", &scd.dict, NULL);
+  g_object_set (source, "model", scd.dict, NULL);
 
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  dest);
 
-  scd.tv = GTK_TREE_VIEW (dest);
-  scd.dict = vs->dict;
+  scd.tv = PSPPIRE_VAR_VIEW (dest);
   scd.ascending =
     GTK_TOGGLE_BUTTON (get_widget_assert (xml, "sort-cases-radiobutton0"));
 
@@ -143,6 +132,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 +142,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);
 
diff --git a/src/ui/gui/sort.ui b/src/ui/gui/sort.ui
new file mode 100644 (file)
index 0000000..19f8b71
--- /dev/null
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="sort-cases-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">Sort Cases</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox4">
+        <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>
+          <object class="GtkHBox" id="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>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow6">
+                <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">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment6">
+                <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.25</property>
+                <property name="xscale">0</property>
+                <property name="yscale">0</property>
+                <child>
+                  <object class="PsppireSelector" id="sort-cases-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>
+                    <property name="source_widget">sort-cases-treeview1</property>
+                    <property name="dest_widget">sort-cases-treeview2</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox12">
+                <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>
+                  <object class="GtkVBox" id="vbox16">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label18">
+                        <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">Sort by:</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow7">
+                        <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">never</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="sort-cases-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>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="padding">5</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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>
+                      <object class="GtkAlignment" id="alignment5">
+                        <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>
+                          <object class="GtkVButtonBox" id="vbuttonbox4">
+                            <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="layout_style">spread</property>
+                            <child>
+                              <object class="GtkRadioButton" id="sort-cases-radiobutton0">
+                                <property name="label" translatable="yes">Ascending</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="sort-cases-radiobutton1">
+                                <property name="label" translatable="yes">Descending</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="draw_indicator">True</property>
+                                <property name="group">sort-cases-radiobutton0</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label17">
+                        <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">Sort Order</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="padding">5</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-buttonbox4">
+            <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>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index 22cdb9140483b5393010997bf40eb05f29c80961..cb226d9fffc095a1f23e7bf668efdca6015ac1bb 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 "psppire-var-view.h"
+
 #include <gtk/gtk.h>
-#include <glade/glade.h>
 
 
 #include "dialog-common.h"
@@ -39,7 +40,7 @@
 struct split_file_dialog
 {
   /* The XML that created the dialog */
-  GladeXML *xml;
+  GtkBuilder *xml;
 
   /* The dictionary to which this dialog pertains */
   PsppireDict *dict;
@@ -69,8 +70,7 @@ generate_syntax (const struct split_file_dialog *sfd)
       GString * varlist = g_string_sized_new (80);
       GtkWidget *sort = get_widget_assert (sfd->xml, "split-radiobutton3");
       GtkWidget *layered = get_widget_assert (sfd->xml, "split-radiobutton1");
-      gint n_vars = append_variable_names (varlist,
-                                          sfd->dict, GTK_TREE_VIEW (vars), 0);
+      gint n_vars = psppire_var_view_append_names (PSPPIRE_VAR_VIEW (vars), 0, varlist);
 
       if ( n_vars > 0 )
        {
@@ -109,7 +109,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 +167,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 +177,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");
@@ -187,31 +187,18 @@ split_file_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  sfd.dict = vs->dict;
+  g_object_get (vs, "dictionary", &sfd.dict, NULL);
   sfd.tv = GTK_TREE_VIEW (dest);
   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, "model", sfd.dict, NULL);
 
   g_signal_connect (on_off, "toggled", G_CALLBACK(on_off_toggled),  sfd.xml);
 
-
-  set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                dest,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
   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 +208,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 +218,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..90c5ab54870b9439548d61ff0fc5efed2d4cedc9 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 "psppire-var-view.h"
+#include "executor.h"
+#include "psppire-data-window.h"
 #include "psppire-dialog.h"
 #include "dialog-common.h"
 #include "dict-display.h"
@@ -32,7 +32,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 +91,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 +118,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 +127,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;
@@ -161,7 +161,7 @@ generate_syntax (const struct tt_indep_samples_dialog *d)
 
   GString *str = g_string_new ("T-TEST /VARIABLES=");
 
-  append_variable_names (str, d->dict, GTK_TREE_VIEW (tv), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (tv), 0, str);
 
   g_string_append (str, "\n\t/GROUPS=");
 
@@ -341,10 +341,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 +353,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,18 +392,15 @@ 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");
 
-  GtkWidget *test_variables_treeview =
-    get_widget_assert (xml, "indep-samples-t-test-treeview2");
-
   GtkWidget *selector2 =
     get_widget_assert (xml, "indep-samples-t-test-selector2");
 
@@ -417,38 +414,24 @@ t_test_independent_samples_dialog (GObject *o, gpointer data)
 
   tt_d.dialog = get_widget_assert (xml, "t-test-independent-samples-dialog");
   tt_d.xml = xml;
-  tt_d.dict = vs->dict;
+  g_object_get (vs, "dictionary", &tt_d.dict, NULL);
 
   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);
-
-
-  gtk_window_set_transient_for (GTK_WINDOW (tt_d.dialog), 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));
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
 
-  set_dest_model (GTK_TREE_VIEW (test_variables_treeview), vs->dict);
+  gtk_window_set_transient_for (GTK_WINDOW (tt_d.dialog), GTK_WINDOW (de));
 
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector1),
-                                dict_view, test_variables_treeview,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  g_object_set (dict_view, "model", tt_d.dict, NULL);
 
   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector1),
                              numeric_only);
 
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector2),
-                                dict_view, tt_d.groups_entry,
-                                insert_source_row_into_entry,
-                                is_currently_in_entry,
-                                NULL);
+  psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector2),
+                                is_currently_in_entry);
 
   g_signal_connect_swapped (tt_d.define_groups_button, "clicked",
                            G_CALLBACK (run_define_groups), &tt_d);
@@ -475,6 +458,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 +468,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..9301b2c316893566b5f891bf1f081d5e3de613e1 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 "psppire-var-view.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)
@@ -59,7 +59,7 @@ generate_syntax (const struct tt_one_sample_dialog *d)
 
   g_string_append (str, "\n\t/VARIABLES=");
 
-  append_variable_names (str, d->dict, GTK_TREE_VIEW (d->vars_treeview), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (d->vars_treeview), 0, str);
 
   tt_options_dialog_append_syntax (d->opt, str);
 
@@ -124,11 +124,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");
@@ -136,33 +136,21 @@ t_test_one_sample_dialog (GObject *o, gpointer data)
   GtkWidget *options_button =
     get_widget_assert (xml, "button1");
 
-  GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
-
   GtkWidget *dialog = get_widget_assert (xml, "t-test-one-sample-dialog");
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  tt_d.dict = vs->dict;
+  g_object_get (vs, "dictionary", &tt_d.dict, NULL);
   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);
-
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE,
-                                var_is_numeric);
-
-  set_dest_model (GTK_TREE_VIEW (tt_d.vars_treeview), vs->dict);
+  tt_d.opt = tt_options_dialog_create (xml, GTK_WINDOW (de));
 
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                dict_view, tt_d.vars_treeview,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
+  g_object_set (dict_view, "model",
+               tt_d.dict,
+               "predicate",
+               var_is_numeric, NULL);
 
   g_signal_connect_swapped (dialog, "refresh",
                            G_CALLBACK (refresh),  &tt_d);
@@ -181,6 +169,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 +180,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..95e212aff7e21e12e95912a9ab46f9fd3824b9a0 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-var-view.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"
 
@@ -57,11 +57,11 @@ generate_syntax (const struct tt_paired_samples_dialog *d)
   gchar *text = NULL;
   GString *str =   g_string_new ("T-TEST \n\tPAIRS = ");
 
-  append_variable_names (str, d->dict, GTK_TREE_VIEW (d->pairs_treeview), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (d->pairs_treeview), 0, str);
 
   g_string_append (str, " WITH ");
 
-  append_variable_names (str, d->dict, GTK_TREE_VIEW (d->pairs_treeview), 1);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (d->pairs_treeview), 1, str);
 
   g_string_append (str, " (PAIRED)");
   g_string_append (str, "\n");
@@ -152,42 +152,17 @@ select_as_pair_member (GtkTreeIter source_iter,
     }
 }
 
-
-/* Append a new column to TV at position C, and heading TITLE */
-static void
-add_new_column (GtkTreeView *tv, const gchar *title, gint c)
-{
-  GtkTreeViewColumn *col = gtk_tree_view_column_new ();
-  GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
-
-  gtk_tree_view_column_set_min_width (col, 100);
-  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
-  gtk_tree_view_column_set_resizable (col, TRUE);
-
-
-  gtk_tree_view_column_set_title (col, title);
-
-  gtk_tree_view_column_pack_start (col, renderer, TRUE);
-
-  gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
-
-  gtk_tree_view_append_column (tv, col);
-
-  gtk_tree_view_column_add_attribute  (col, renderer, "text", c);
-}
-
-
 /* Pops up the dialog box */
 void
 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");
@@ -200,43 +175,24 @@ t_test_paired_samples_dialog (GObject *o, gpointer data)
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  tt_d.dict = vs->dict;
+  g_object_get (vs, "dictionary", &tt_d.dict, NULL);
   tt_d.pairs_treeview =
    get_widget_assert (xml, "paired-samples-t-test-treeview2");
-  tt_d.opt = tt_options_dialog_create (xml, de->parent.window);
-
-  gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
-
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE,
-                                var_is_numeric);
-
-  {
-    tt_d.list_store =
-      GTK_TREE_MODEL (
-                     gtk_list_store_new (2,
-                                         PSPPIRE_VAR_PTR_TYPE,
-                                         PSPPIRE_VAR_PTR_TYPE));
+  tt_d.opt = tt_options_dialog_create (xml, GTK_WINDOW (de));
 
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-    gtk_tree_view_set_model (GTK_TREE_VIEW (tt_d.pairs_treeview),
-                            GTK_TREE_MODEL (tt_d.list_store));
 
+  g_object_set (dict_view, "model", tt_d.dict,
+               "predicate",
+               var_is_numeric, NULL);
 
-    add_new_column (GTK_TREE_VIEW (tt_d.pairs_treeview), _("Var 1"), 0);
-    add_new_column (GTK_TREE_VIEW (tt_d.pairs_treeview), _("Var 2"), 1);
-  }
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                dict_view,
-                                tt_d.pairs_treeview,
-                                select_as_pair_member,
-                                NULL,
-                                &tt_d);
+  
+  tt_d.list_store = gtk_tree_view_get_model (GTK_TREE_VIEW (tt_d.pairs_treeview));
 
+  psppire_selector_set_select_func (PSPPIRE_SELECTOR (selector),
+                                   select_as_pair_member,
+                                   &tt_d);
 
   g_signal_connect_swapped (dialog, "refresh",
                            G_CALLBACK (refresh),  &tt_d);
@@ -255,6 +211,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 +221,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);
       }
diff --git a/src/ui/gui/t-test.glade b/src/ui/gui/t-test.glade
deleted file mode 100644 (file)
index e355daf..0000000
+++ /dev/null
@@ -1,813 +0,0 @@
-<?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="t-test-independent-samples-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">Independent-Samples T Test</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-hbox15">
-        <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="hbox23">
-            <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="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_ETCHED_IN</property>
-                <child>
-                  <widget class="GtkTreeView" 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="headers_visible">False</property>
-                    <property name="headers_clickable">True</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkTable" id="table4">
-                <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="n_rows">3</property>
-                <property name="n_columns">2</property>
-                <child>
-                  <widget class="GtkHButtonBox" id="hbuttonbox1">
-                    <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                    <child>
-                      <widget class="GtkButton" id="define-groups-button">
-                        <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">Define Groups</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkButton" id="indep-samples-t-test-options-button">
-                        <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">Options...</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options">GTK_FILL</property>
-                    <property name="y_padding">5</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="indep-samples-t-test-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="x_options"></property>
-                    <property name="y_options">GTK_EXPAND</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="PsppireSelector" id="indep-samples-t-test-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="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="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>
-                      <widget class="GtkLabel" id="label35">
-                        <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">Test Variable(s):</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow15">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
-                        <child>
-                          <widget class="GtkTreeView" id="indep-samples-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="headers_visible">False</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkVBox" id="vbox30">
-                    <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="label36">
-                        <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">Define Groups</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkEntry" id="indep-samples-t-test-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="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="y_options"></property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox2">
-            <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>
-          </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="PsppireDialog" id="define-groups-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">Define Groups</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" 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>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkTable" id="table1">
-            <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="n_rows">3</property>
-            <property name="n_columns">2</property>
-            <property name="column_spacing">5</property>
-            <property name="row_spacing">5</property>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <widget class="GtkRadioButton" id="radiobutton3">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="active">True</property>
-                <property name="draw_indicator">True</property>
-              </widget>
-              <packing>
-                <property name="x_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTable" id="table2">
-                <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="n_rows">2</property>
-                <property name="n_columns">2</property>
-                <property name="column_spacing">5</property>
-                <property name="row_spacing">5</property>
-                <child>
-                  <widget class="GtkLabel" id="label2">
-                    <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">Group_2 value:</property>
-                    <property name="use_underline">True</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="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">Group_1 value:</property>
-                    <property name="use_underline">True</property>
-                  </widget>
-                  <packing>
-                    <property name="x_options"></property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="group2-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="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="GtkEntry" id="group1-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="left_attach">1</property>
-                    <property name="right_attach">2</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="y_options">GTK_EXPAND</property>
-              </packing>
-            </child>
-            <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>
-                <child>
-                  <widget class="GtkLabel" id="label5">
-                    <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">_Cut point:</property>
-                    <property name="use_underline">True</property>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="cut-point-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="position">1</property>
-                  </packing>
-                </child>
-              </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="y_options"></property>
-                <property name="y_padding">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label4">
-                <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 specified values:</property>
-                <property name="use_underline">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="GtkRadioButton" id="radiobutton4">
-                <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">radiobutton3</property>
-              </widget>
-              <packing>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-                <property name="x_options"></property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox1">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="options-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">Options</property>
-    <property name="modal">True</property>
-    <child internal-child="hbox">
-      <widget class="GtkHBox" id="dialog-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">2</property>
-        <child>
-          <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>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <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>
-                <child>
-                  <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="GtkVButtonBox" id="vbuttonbox1">
-                        <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="layout_style">GTK_BUTTONBOX_SPREAD</property>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton1">
-                            <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">Exclude cases _analysis by analysis</property>
-                            <property name="use_underline">True</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <child>
-                          <widget class="GtkRadioButton" id="radiobutton2">
-                            <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">Exclude cases _listwise</property>
-                            <property name="use_underline">True</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                            <property name="group">radiobutton1</property>
-                          </widget>
-                          <packing>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <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">Missing Values</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>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox3">
-            <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_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_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>
-  <widget class="PsppireDialog" id="t-test-one-sample-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">One - Sample T Test</property>
-    <property name="modal">True</property>
-    <property name="orientation">PSPPIRE_TABULAR</property>
-    <child internal-child="hbox">
-      <widget class="GtkTable" id="dialog-hbox5">
-        <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="n_rows">2</property>
-        <property name="n_columns">4</property>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox5">
-            <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>
-          </widget>
-          <packing>
-            <property name="left_attach">3</property>
-            <property name="right_attach">4</property>
-          </packing>
-        </child>
-        <child>
-          <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="shadow_type">GTK_SHADOW_NONE</property>
-            <child>
-              <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="left_padding">12</property>
-                <child>
-                  <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>
-                    <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="one-sample-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="headers_visible">False</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label6">
-                <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">Test Variable(s):</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="left_attach">2</property>
-            <property name="right_attach">3</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkScrolledWindow" id="scrolledwindow4">
-            <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="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="headers_visible">False</property>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="bottom_attach">2</property>
-            <property name="x_padding">5</property>
-            <property name="y_padding">5</property>
-          </packing>
-        </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="GtkLabel" id="label7">
-                <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">Test Value: </property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkEntry" id="test-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="padding">5</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </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>
-            <property name="y_options"></property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkButton" id="button1">
-            <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">Options...</property>
-          </widget>
-          <packing>
-            <property name="left_attach">3</property>
-            <property name="right_attach">4</property>
-            <property name="top_attach">1</property>
-            <property name="bottom_attach">2</property>
-            <property name="y_options"></property>
-            <property name="x_padding">5</property>
-            <property name="y_padding">5</property>
-          </packing>
-        </child>
-        <child>
-          <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="yalign">0.059999998658895493</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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="no_show_all">True</property>
-                <property name="border_width">5</property>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="right_attach">2</property>
-            <property name="bottom_attach">2</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="PsppireDialog" id="t-test-paired-samples-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">Paired Samples T Test</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">2</property>
-        <child>
-          <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>
-            <child>
-              <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="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>
-                    <property name="headers_visible">False</property>
-                    <property name="headers_clickable">True</property>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment5">
-                <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.059999998658895493</property>
-                <property name="yscale">0</property>
-                <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="no_show_all">True</property>
-                    <property name="border_width">5</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="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">5</property>
-                <child>
-                  <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="shadow_type">GTK_SHADOW_NONE</property>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment6">
-                        <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="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="paired-samples-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="enable_search">False</property>
-                              </widget>
-                            </child>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <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="label" translatable="yes">Test Variable(s):</property>
-                        <property name="use_markup">True</property>
-                      </widget>
-                      <packing>
-                        <property name="type">label_item</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkVButtonBox" id="vbuttonbox2">
-                    <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="GtkButton" id="paired-samples-t-test-options-button">
-                        <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">Options...</property>
-                      </widget>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </widget>
-        </child>
-        <child>
-          <widget class="PsppireVButtonBox" id="psppire-vbuttonbox4">
-            <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>
-          </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>
diff --git a/src/ui/gui/t-test.ui b/src/ui/gui/t-test.ui
new file mode 100644 (file)
index 0000000..d8c2511
--- /dev/null
@@ -0,0 +1,865 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.17080"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="PsppireDialog" id="t-test-independent-samples-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">Independent-Samples T Test</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox15">
+        <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>
+          <object class="GtkHBox" id="hbox23">
+            <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>
+              <object 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">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="table4">
+                <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="n_rows">3</property>
+                <property name="n_columns">2</property>
+                <child>
+                  <object class="GtkHButtonBox" id="hbuttonbox1">
+                    <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="layout_style">spread</property>
+                    <child>
+                      <object class="GtkButton" id="define-groups-button">
+                        <property name="label" translatable="yes">Define Groups</property>
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="indep-samples-t-test-options-button">
+                        <property name="label" translatable="yes">Options...</property>
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                    <property name="y_options">GTK_FILL</property>
+                    <property name="y_padding">5</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="PsppireSelector" id="indep-samples-t-test-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>
+                    <property name="source_widget">indep-samples-t-test-treeview1</property>
+                    <property name="dest_widget">indep-samples-t-test-treeview2</property>
+                  </object>
+                  <packing>
+                    <property name="x_options"></property>
+                    <property name="y_options">GTK_EXPAND</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="PsppireSelector" id="indep-samples-t-test-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>
+                    <property name="source_widget">indep-samples-t-test-treeview1</property>
+                    <property name="dest_widget">indep-samples-t-test-entry</property>
+                  </object>
+                  <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>
+                  <object 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>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label35">
+                        <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">Test Variable(s):</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow15">
+                        <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">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object class="PsppireVarView" id="indep-samples-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="headers_visible">False</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox30">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="label36">
+                        <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">Define Groups</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="indep-samples-t-test-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>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <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>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox2">
+            <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="orientation">vertical</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="define-groups-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">Define Groups</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" 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>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkTable" id="table1">
+            <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="n_rows">3</property>
+            <property name="n_columns">2</property>
+            <property name="column_spacing">5</property>
+            <property name="row_spacing">5</property>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton3">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="x_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="table2">
+                <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="n_rows">2</property>
+                <property name="n_columns">2</property>
+                <property name="column_spacing">5</property>
+                <property name="row_spacing">5</property>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <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">Group_2 value:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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">Group_1 value:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="x_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="group2-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>
+                  </object>
+                  <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>
+                  <object class="GtkEntry" id="group1-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>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+              </object>
+              <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">GTK_EXPAND</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <child>
+                  <object class="GtkLabel" id="label5">
+                    <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">_Cut point:</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="cut-point-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>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <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="y_options"></property>
+                <property name="y_padding">5</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <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 specified values:</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton4">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">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="active">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton3</property>
+              </object>
+              <packing>
+                <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>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">5</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox1">
+            <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="orientation">vertical</property>
+            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="options-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">Options</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-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">2</property>
+        <child>
+          <object 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="orientation">vertical</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <object 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>
+                  <object 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>
+                      <object class="GtkVButtonBox" id="vbuttonbox1">
+                        <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="layout_style">spread</property>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton1">
+                            <property name="label" translatable="yes">Exclude cases _analysis by analysis</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="radiobutton2">
+                            <property name="label" translatable="yes">Exclude cases _listwise</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">radiobutton1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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">Missing Values</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox3">
+            <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="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="t-test-one-sample-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">One - Sample T Test</property>
+    <property name="modal">True</property>
+    <property name="orientation">Tabular</property>
+    <child internal-child="hbox">
+      <object class="GtkTable" id="dialog-hbox5">
+        <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="n_rows">2</property>
+        <property name="n_columns">4</property>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox5">
+            <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>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="right_attach">4</property>
+          </packing>
+        </child>
+        <child>
+          <object 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>
+            <property name="shadow_type">none</property>
+            <child>
+              <object 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="left_padding">12</property>
+                <child>
+                  <object 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>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">etched-in</property>
+                    <child>
+                      <object class="PsppireVarView" id="one-sample-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="headers_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label6">
+                <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">Test Variable(s):</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">3</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow4">
+            <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">never</property>
+            <property name="vscrollbar_policy">automatic</property>
+            <property name="shadow_type">etched-in</property>
+            <child>
+              <object 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="headers_visible">False</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="bottom_attach">2</property>
+            <property name="x_padding">5</property>
+            <property name="y_padding">5</property>
+          </packing>
+        </child>
+        <child>
+          <object 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>
+              <object class="GtkLabel" id="label7">
+                <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">Test Value: </property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="test-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>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">5</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <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>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="button1">
+            <property name="label" translatable="yes">Options...</property>
+            <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>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="right_attach">4</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="y_options"></property>
+            <property name="x_padding">5</property>
+            <property name="y_padding">5</property>
+          </packing>
+        </child>
+        <child>
+          <object 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="yalign">0.059999998658895493</property>
+            <property name="yscale">0</property>
+            <child>
+              <object 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="no_show_all">True</property>
+                <property name="border_width">5</property>
+                <property name="source_widget">one-sample-t-test-treeview2</property>
+                <property name="dest_widget">one-sample-t-test-treeview1</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="bottom_attach">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="PsppireDialog" id="t-test-paired-samples-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">Paired Samples T Test</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object 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">2</property>
+        <child>
+          <object 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>
+            <child>
+              <object 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">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">etched-in</property>
+                <child>
+                  <object 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>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="alignment5">
+                <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.059999998658895493</property>
+                <property name="yscale">0</property>
+                <child>
+                  <object 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="no_show_all">True</property>
+                    <property name="border_width">5</property>
+                    <property name="source_widget">paired-samples-t-test-treeview1</property>
+                    <property name="dest_widget">paired-samples-t-test-treeview2</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object 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="orientation">vertical</property>
+                <property name="spacing">5</property>
+                <child>
+                  <object 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="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment6">
+                        <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>
+                          <object 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">always</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="PsppireVarView" id="paired-samples-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="enable_search">False</property>
+                                <property name="n-cols">2</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object 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="label" translatable="yes">Test Variable(s):</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVButtonBox" id="vbuttonbox2">
+                    <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="orientation">vertical</property>
+                    <child>
+                      <object class="GtkButton" id="paired-samples-t-test-options-button">
+                        <property name="label" translatable="yes">Options...</property>
+                        <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>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox4">
+            <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="orientation">vertical</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
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
diff --git a/src/ui/gui/text-data-import.glade b/src/ui/gui/text-data-import.glade
deleted file mode 100644 (file)
index 6ee9dd5..0000000
+++ /dev/null
@@ -1,721 +0,0 @@
-<?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 -->
-<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="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>
-        <child>
-          <widget class="GtkLabel" id="intro-label">
-            <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">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.
-
-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.</property>
-            <property name="wrap">True</property>
-          </widget>
-        </child>
-        <child>
-          <widget class="GtkAlignment" id="alignment10">
-            <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="xscale">0</property>
-            <child>
-              <widget class="GtkFrame" id="frame7">
-                <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_NONE</property>
-                <child>
-                  <widget class="GtkAlignment" id="alignment11">
-                    <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="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>
-                        <child>
-                          <widget class="GtkRadioButton" id="import-all-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="label" translatable="yes">All cases</property>
-                            <property name="response_id">0</property>
-                            <property name="active">True</property>
-                            <property name="draw_indicator">True</property>
-                          </widget>
-                        </child>
-                        <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>
-                            <child>
-                              <widget class="GtkRadioButton" id="import-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="label" translatable="yes">Only first </property>
-                                <property name="response_id">0</property>
-                                <property name="active">True</property>
-                                <property name="draw_indicator">True</property>
-                                <property name="group">import-all-cases</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkSpinButton" id="n-cases-spin">
-                                <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="adjustment">1000 0 100000000 1 10 10</property>
-                                <property name="climb_rate">10</property>
-                                <property name="snap_to_ticks">True</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <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"> cases</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">2</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <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>
-                            <child>
-                              <widget class="GtkRadioButton" id="import-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="label" translatable="yes">Only first </property>
-                                <property name="response_id">0</property>
-                                <property name="active">True</property>
-                                <property name="draw_indicator">True</property>
-                                <property name="group">import-all-cases</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkSpinButton" id="percent-spin">
-                                <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="adjustment">100 1 100 1 10 10</property>
-                                <property name="snap_to_ticks">True</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkLabel" id="label2">
-                                <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">% of file (approximately)</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">False</property>
-                                <property name="position">2</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label8">
-                    <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">&lt;b&gt;Amount to Import&lt;/b&gt;</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </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="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>
-        <child>
-          <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">Select the first line of the data file that contains data.</property>
-          </widget>
-          <packing>
-            <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>
-            <property name="can_focus">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <child>
-              <widget class="GtkTreeView" id="first-line">
-                <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_clickable">True</property>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">2</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </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="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>
-        <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="GtkFrame" id="foo">
-                <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_NONE</property>
-                <child>
-                  <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="GtkTable" id="table1">
-                        <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="n_rows">4</property>
-                        <property name="n_columns">3</property>
-                        <child>
-                          <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>
-                        </child>
-                        <child>
-                          <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">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="left_attach">1</property>
-                            <property name="right_attach">2</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <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">Ban_g (!)</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">2</property>
-                            <property name="right_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <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">_Colon (:)</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>
-                          </packing>
-                        </child>
-                        <child>
-                          <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">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="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="hyphen">
-                            <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">H_yphen (-)</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">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="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">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="top_attach">2</property>
-                            <property name="bottom_attach">3</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <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">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="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="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">Slas_h (/)</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">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">
-                            <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="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>
-                          </packing>
-                        </child>
-                        <child>
-                          <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>
-                          </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>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label5">
-                    <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">&lt;b&gt;Separators&lt;/b&gt;</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <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>
-                <property name="shadow_type">GTK_SHADOW_NONE</property>
-                <child>
-                  <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="left_padding">12</property>
-                    <child>
-                      <widget class="GtkTable" id="table2">
-                        <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="n_rows">2</property>
-                        <property name="n_columns">2</property>
-                        <child>
-                          <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">Quote separator characters with</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>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkComboBoxEntry" id="quote-combo">
-                            <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>
-                                <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="width_chars">2</property>
-                              </widget>
-                            </child>
-                          </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="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">Doubled quote mark treated as escape</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>
-                          </packing>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label7">
-                    <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">&lt;b&gt;Quoting&lt;/b&gt;</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-          </packing>
-        </child>
-        <child>
-          <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="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="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="fields-scroller">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <child>
-                      <widget class="GtkTreeView" id="fields">
-                        <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_clickable">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label9">
-                <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">&lt;b&gt;Fields Preview&lt;/b&gt;</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="type">label_item</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </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="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>
-        <child>
-          <widget class="GtkLabel" id="label12">
-            <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">Check the data formats displayed below and fix any that are incorrect.  You may set other variable properties now or later.</property>
-            <property name="wrap">True</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkVPaned" id="vpaned1">
-            <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="position">94</property>
-            <child>
-              <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="shadow_type">GTK_SHADOW_NONE</property>
-                <child>
-                  <widget class="GtkAlignment" id="alignment5">
-                    <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="vars-scroller">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <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="label" translatable="yes">&lt;b&gt;Variables&lt;/b&gt;</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="resize">False</property>
-                <property name="shrink">True</property>
-              </packing>
-            </child>
-            <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_NONE</property>
-                <child>
-                  <widget class="GtkAlignment" id="alignment6">
-                    <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="data-scroller">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                        <child>
-                          <widget class="GtkTreeView" id="data">
-                            <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_clickable">True</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label11">
-                    <property name="visible">True</property>
-                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="label" translatable="yes">&lt;b&gt;Data Preview&lt;/b&gt;</property>
-                    <property name="use_markup">True</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
-                </child>
-              </widget>
-              <packing>
-                <property name="resize">True</property>
-                <property name="shrink">True</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-</glade-interface>
diff --git a/src/ui/gui/text-data-import.ui b/src/ui/gui/text-data-import.ui
new file mode 100644 (file)
index 0000000..4b4a24b
--- /dev/null
@@ -0,0 +1,738 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object 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>
+      <object 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="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkLabel" id="intro-label">
+            <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">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.
+
+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.</property>
+            <property name="wrap">True</property>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkAlignment" id="alignment10">
+            <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="xscale">0</property>
+            <child>
+              <object class="GtkFrame" id="frame7">
+                <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">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment11">
+                    <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>
+                      <object 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="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkRadioButton" id="import-all-cases">
+                            <property name="label" translatable="yes">All cases</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="active">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object 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>
+                              <object class="GtkRadioButton" id="import-n-cases">
+                                <property name="label" translatable="yes">Only first </property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">import-all-cases</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkSpinButton" id="n-cases-spin">
+                                <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="adjustment">adjustment2</property>
+                                <property name="climb_rate">10</property>
+                                <property name="snap_to_ticks">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object 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"> cases</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object 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>
+                              <object class="GtkRadioButton" id="import-percent">
+                                <property name="label" translatable="yes">Only first </property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">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="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">import-all-cases</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkSpinButton" id="percent-spin">
+                                <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="adjustment">adjustment1</property>
+                                <property name="snap_to_ticks">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label2">
+                                <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">% of file (approximately)</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label8">
+                    <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">&lt;b&gt;Amount to Import&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object 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>
+      <object 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="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object 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">Select the first line of the data file that contains data.</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="first-line-scroller">
+            <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">automatic</property>
+            <property name="vscrollbar_policy">automatic</property>
+            <child>
+              <object class="GtkTreeView" id="first-line">
+                <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>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkCheckButton" id="variable-names">
+            <property name="label" translatable="yes">Line above selected line contains variable names</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">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="draw_indicator">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object 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>
+      <object 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="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object 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>
+              <object class="GtkFrame" id="foo">
+                <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">none</property>
+                <child>
+                  <object 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>
+                      <object class="GtkTable" id="table1">
+                        <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="n_rows">4</property>
+                        <property name="n_columns">3</property>
+                        <property name="column_spacing">6</property>
+                        <property name="row_spacing">6</property>
+                        <child>
+                          <object 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>
+                          </object>
+                          <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>
+                          <object class="GtkCheckButton" id="custom-cb">
+                            <property name="label" translatable="yes">C_ustom</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">3</property>
+                            <property name="bottom_attach">4</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="slash">
+                            <property name="label" translatable="yes">Slas_h (/)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <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>
+                          <object class="GtkCheckButton" id="semicolon">
+                            <property name="label" translatable="yes">Semicolo_n (;)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <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>
+                        <child>
+                          <object class="GtkCheckButton" id="pipe">
+                            <property name="label" translatable="yes">P_ipe (|)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">2</property>
+                            <property name="bottom_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="hyphen">
+                            <property name="label" translatable="yes">H_yphen (-)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <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>
+                          <object class="GtkCheckButton" id="comma">
+                            <property name="label" translatable="yes">Co_mma (,)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <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>
+                          <object class="GtkCheckButton" id="colon">
+                            <property name="label" translatable="yes">_Colon (:)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="bang">
+                            <property name="label" translatable="yes">Ban_g (!)</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="right_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="tab">
+                            <property name="label" translatable="yes">Ta_b</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="space">
+                            <property name="label" translatable="yes">_Space</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="use_underline">True</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label5">
+                    <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">&lt;b&gt;Separators&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object 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="left_padding">12</property>
+                    <child>
+                      <object class="GtkTable" id="table2">
+                        <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="n_rows">2</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">6</property>
+                        <property name="row_spacing">6</property>
+                        <child>
+                          <object class="GtkCheckButton" id="escape">
+                            <property name="label" translatable="yes">Doubled quote mark treated as escape</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <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>
+                          <object class="GtkComboBoxEntry" id="quote-combo">
+                            <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>
+                          </object>
+                          <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>
+                          <object class="GtkCheckButton" id="quote-cb">
+                            <property name="label" translatable="yes">Quote separator characters with</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">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="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="x_options"></property>
+                            <property name="y_options">GTK_FILL</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label7">
+                    <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">&lt;b&gt;Quoting&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object 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="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object 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>
+                  <object class="GtkScrolledWindow" id="fields-scroller">
+                    <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">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <child>
+                      <object class="GtkTreeView" id="fields">
+                        <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>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label9">
+                <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">&lt;b&gt;Fields Preview&lt;/b&gt;</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object 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>
+      <object 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="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkLabel" id="label12">
+            <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">Check the data formats displayed below and fix any that are incorrect.  You may set other variable properties now or later.</property>
+            <property name="wrap">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVPaned" id="vpaned1">
+            <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="position">94</property>
+            <child>
+              <object 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="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment5">
+                    <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>
+                      <object class="GtkScrolledWindow" id="vars-scroller">
+                        <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">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object 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="label" translatable="yes">&lt;b&gt;Variables&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">False</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+            <child>
+              <object 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">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment6">
+                    <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>
+                      <object class="GtkScrolledWindow" id="data-scroller">
+                        <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">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <child>
+                          <object class="GtkTreeView" id="data">
+                            <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>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="label11">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Data Preview&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="value">100</property>
+    <property name="lower">1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment2">
+    <property name="value">1000</property>
+    <property name="upper">100000000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+</interface>
index 621e196c2914ea43e9b2f5995be2acf8f1c0ea91..0061193e16b1b86f1d9dfa6c7c26d336e04cfb6b 100644 (file)
 
 #include "transpose-dialog.h"
 #include "psppire-selector.h"
+#include "psppire-var-view.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 +57,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,44 +80,29 @@ void
 transpose_dialog (GObject *o, gpointer data)
 {
   gint response ;
-  struct data_editor *de = data;
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
+  PsppireDict *dict = NULL;
 
-  GladeXML *xml = XML_NEW ("psppire.glade");
+  GtkBuilder *xml = builder_new ("psppire.ui");
 
   PsppireVarStore *vs = NULL;
 
   GtkWidget *dialog = get_widget_assert (xml, "transpose-dialog");
   GtkWidget *source = get_widget_assert (xml, "source-treeview");
-  GtkWidget *dest = get_widget_assert (xml, "variables-treeview");
-  GtkWidget *selector1 = get_widget_assert (xml, "psppire-selector2");
   GtkWidget *selector2 = get_widget_assert (xml, "psppire-selector3");
-  GtkWidget *new_name_entry = get_widget_assert (xml, "new-name-entry");
 
   g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (source),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
-
-  set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector1),
-                                source, dest,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+  g_object_get (vs, "dictionary", &dict, NULL);
+  g_object_set (source, "model", dict, NULL);
 
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector2),
-                                source, new_name_entry,
-                                insert_source_row_into_entry,
-                                is_currently_in_entry,
-                                NULL);
+  psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector2),
+                                is_currently_in_entry);
 
 
   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);
@@ -128,7 +113,8 @@ transpose_dialog (GObject *o, gpointer data)
     {
     case GTK_RESPONSE_OK:
       {
-       gchar *syntax = generate_syntax (vs->dict, xml);
+       gchar *syntax = generate_syntax (dict, xml);
+
        struct getl_interface *sss = create_syntax_string_source (syntax);
        execute_syntax (sss);
 
@@ -137,12 +123,8 @@ transpose_dialog (GObject *o, gpointer data)
       break;
     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);
+       gchar *syntax = generate_syntax (dict, xml);
+        paste_syntax_in_new_window (syntax);
 
        g_free (syntax);
       }
@@ -159,7 +141,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");
@@ -170,7 +152,7 @@ generate_syntax (PsppireDict *dict, GladeXML *xml)
 
   g_string_append (string, " /VARIABLES = ");
 
-  append_variable_names (string, dict, GTK_TREE_VIEW (dest), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (dest), 0, string);
 
   text = gtk_entry_get_text (GTK_ENTRY (entry));
 
index 710d176653502ec876669733632802c03bedebb7..14eacabfadddb5842e81f444855233b0e903ec03 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;
+  PsppireDict *dict;
+
   /* The variable to be updated */
   struct variable *pv;
 
@@ -67,11 +72,12 @@ on_label_entry_change (GtkEntry *entry, gpointer data)
 
   text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
 
-  text_to_value (text, &v,
-               *var_get_write_format (dialog->pv));
-
+  text_to_value (text,
+                dialog->dict,
+                dialog->pv,
+                &v);
 
-  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);
@@ -81,6 +87,8 @@ on_label_entry_change (GtkEntry *entry, gpointer data)
       gtk_widget_set_sensitive (dialog->change_button, FALSE);
       gtk_widget_set_sensitive (dialog->add_button, TRUE);
     }
+
+  value_destroy (&v, var_get_width (dialog->pv));
 }
 
 
@@ -130,15 +138,17 @@ 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;
 
   const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
 
   union value v;
-  text_to_value (text, &v,
-               *var_get_write_format (dialog->pv));
+  text_to_value (text,
+                dialog->dict,
+                dialog->pv,
+                &v);
 
 
   g_signal_handler_block (GTK_ENTRY (dialog->label_entry),
@@ -147,7 +157,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);
@@ -162,6 +172,8 @@ on_value_entry_change (GtkEntry *entry, gpointer data)
 
   g_signal_handler_unblock (GTK_ENTRY (dialog->label_entry),
                         dialog->change_handler_id);
+
+  value_destroy (&v, var_get_width (dialog->pv));
 }
 
 
@@ -198,14 +210,12 @@ val_labs_cancel (struct val_labs_dialog *dialog)
 
 /* Callback for when the Value Labels dialog is closed using
    the Cancel button.*/
-static gint
+static void
 on_cancel (GtkWidget *w, gpointer data)
 {
   struct val_labs_dialog *dialog = data;
 
   val_labs_cancel (dialog);
-
-  return FALSE;
 }
 
 
@@ -223,14 +233,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,19 +251,20 @@ 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);
 }
 
 
 static void repopulate_dialog (struct val_labs_dialog *dialog);
 
 /* Callback which occurs when the "Change" button is clicked */
-static gint
+static void
 on_change (GtkWidget *w, gpointer data)
 {
   struct val_labs_dialog *dialog = data;
@@ -261,21 +273,24 @@ on_change (GtkWidget *w, gpointer data)
 
   union value v;
 
-  text_to_value (val_text, &v,
-               *var_get_write_format (dialog->pv));
+  text_to_value (val_text,
+                dialog->dict,
+                dialog->pv,
+                &v);
 
-  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;
+  value_destroy (&v, var_get_width (dialog->pv));
 }
 
 /* Callback which occurs when the "Add" button is clicked */
-static gint
+static void
 on_add (GtkWidget *w, gpointer data)
 {
   struct val_labs_dialog *dialog = data;
@@ -284,37 +299,42 @@ on_add (GtkWidget *w, gpointer data)
 
   const gchar *text = gtk_entry_get_text (GTK_ENTRY (dialog->value_entry));
 
-  text_to_value (text, &v,
-               *var_get_write_format (dialog->pv));
-
+  text_to_value (text,
+                dialog->dict,
+                dialog->pv,
+                &v);
 
-  if ( ! val_labs_add (dialog->labs, v,
-                      gtk_entry_get_text
-                      ( GTK_ENTRY (dialog->label_entry)) ) )
-    return FALSE;
-
-  gtk_widget_set_sensitive (dialog->add_button, FALSE);
+  if (val_labs_add (dialog->labs, &v,
+                   gtk_entry_get_text
+                   ( GTK_ENTRY (dialog->label_entry)) ) )
+    {
+      gtk_widget_set_sensitive (dialog->add_button, FALSE);
 
-  repopulate_dialog (dialog);
+      repopulate_dialog (dialog);
+      gtk_widget_grab_focus (dialog->value_entry);
+    }
 
-  return FALSE;
+  value_destroy (&v, var_get_width (dialog->pv));
 }
 
 /* Callback which occurs when the "Remove" button is clicked */
-static gint
+static void
 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);
-
-  return FALSE;
 }
 
 
@@ -322,16 +342,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->dict, *var_get_write_format (dialog->pv));
 
   g_signal_handler_block (GTK_ENTRY (dialog->value_entry),
                         dialog->value_handler_id);
@@ -345,10 +366,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 +381,24 @@ 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;
+  g_object_get (var_store, "dictionary", &dialog->dict, NULL);
   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");
@@ -399,41 +420,43 @@ val_labs_dialog_create (GladeXML *xml)
 
   g_signal_connect (get_widget_assert (xml, "val_labs_cancel"),
                   "clicked",
-                  GTK_SIGNAL_FUNC (on_cancel), dialog);
+                  G_CALLBACK (on_cancel), dialog);
 
   g_signal_connect (dialog->window, "delete-event",
-                   GTK_SIGNAL_FUNC (on_delete), dialog);
+                   G_CALLBACK (on_delete), dialog);
 
   g_signal_connect (get_widget_assert (xml, "val_labs_ok"),
                   "clicked",
-                  GTK_SIGNAL_FUNC (val_labs_ok), dialog);
+                  G_CALLBACK (val_labs_ok), dialog);
 
   dialog->change_handler_id =
     g_signal_connect (dialog->label_entry,
                     "changed",
-                    GTK_SIGNAL_FUNC (on_label_entry_change), dialog);
+                    G_CALLBACK (on_label_entry_change), dialog);
 
   dialog->value_handler_id  =
     g_signal_connect (dialog->value_entry,
                     "changed",
-                    GTK_SIGNAL_FUNC (on_value_entry_change), dialog);
+                    G_CALLBACK (on_value_entry_change), dialog);
 
   g_signal_connect (dialog->change_button,
                   "clicked",
-                  GTK_SIGNAL_FUNC (on_change), dialog);
+                  G_CALLBACK (on_change), dialog);
 
 
   g_signal_connect (dialog->treeview, "cursor-changed",
-                  GTK_SIGNAL_FUNC (on_select_row), dialog);
+                  G_CALLBACK (on_select_row), dialog);
 
   g_signal_connect (dialog->remove_button, "clicked",
-                  GTK_SIGNAL_FUNC (on_remove), dialog);
+                  G_CALLBACK (on_remove), dialog);
 
   g_signal_connect (dialog->add_button, "clicked",
-                  GTK_SIGNAL_FUNC (on_add), dialog);
+                  G_CALLBACK (on_add), dialog);
 
   dialog->labs = 0;
 
+  g_object_unref (xml);
+
   return dialog;
 }
 
@@ -452,8 +475,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 +498,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->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 +517,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 +548,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.ui b/src/ui/gui/var-sheet-dialogs.ui
new file mode 100644 (file)
index 0000000..fdc8171
--- /dev/null
@@ -0,0 +1,961 @@
+<?xml version="1.0"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object 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">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <object class="GtkHBox" id="hbox1">
+        <property name="visible">True</property>
+        <property name="border_width">5</property>
+        <property name="spacing">5</property>
+        <child>
+          <object class="GtkVBox" id="vbox2">
+            <property name="visible">True</property>
+            <property name="border_width">13</property>
+            <property name="orientation">vertical</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton1">
+                <property name="label" translatable="yes">Numeric</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton2">
+                <property name="label" translatable="yes">Comma</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton3">
+                <property name="label" translatable="yes">Dot</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton4">
+                <property name="label" translatable="yes">Scientific notation</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton5">
+                <property name="label" translatable="yes">Date</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton6">
+                <property name="label" translatable="yes">Dollar</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">5</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton7">
+                <property name="label" translatable="yes">Custom currency</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">6</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkRadioButton" id="radiobutton8">
+                <property name="label" translatable="yes">String</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">radiobutton1</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">7</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="middle_box">
+            <property name="visible">True</property>
+            <property name="spacing">10</property>
+            <child>
+              <object 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">never</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkTreeView" id="date_format_list_view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="custom_currency_hbox">
+                <property name="spacing">15</property>
+                <child>
+                  <object 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">never</property>
+                    <property name="vscrollbar_policy">never</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTreeView" id="custom_treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="headers_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="Sample">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment2">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkVBox" id="vbox10">
+                            <property name="visible">True</property>
+                            <property name="orientation">vertical</property>
+                            <property name="homogeneous">True</property>
+                            <child>
+                              <object class="GtkLabel" id="psample_label">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">positive</property>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="nsample_label">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">negative</property>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label13">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Sample</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="pack_type">end</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="dollar_window">
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkTreeView" id="dollar_treeview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+                  <object class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="width_label">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Width:</property>
+                        <property name="justify">right</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="pack_type">end</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options">GTK_FILL</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="decimals_entry">
+                    <property name="width_request">25</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                  <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>
+                  <object class="GtkEntry" id="width_entry">
+                    <property name="width_request">25</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object 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">right</property>
+                  </object>
+                  <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>
+              </object>
+              <packing>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVButtonBox" id="vbuttonbox6">
+            <property name="visible">True</property>
+            <property name="spacing">5</property>
+            <property name="layout_style">start</property>
+            <child>
+              <object class="GtkButton" id="var_type_ok">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="var_type_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="help_button_variable_type">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object 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">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <object class="GtkHBox" id="hbox3">
+        <property name="visible">True</property>
+        <property name="border_width">5</property>
+        <child>
+          <object class="GtkFrame" id="frame1">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <child>
+              <object class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="border_width">8</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <object 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>
+                      <object class="GtkScrolledWindow" id="scrolledwindow3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">etched-in</property>
+                        <child>
+                          <object 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>
+                          </object>
+                        </child>
+                      </object>
+                      <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>
+                      <object 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>
+                          <object class="GtkHBox" id="hbox4">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkEntry" id="value_entry">
+                                <property name="width_request">85</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="padding">1</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <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>
+                          <object class="GtkEntry" id="label_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                          </object>
+                          <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>
+                          <object class="GtkLabel" id="label6">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Value Label:</property>
+                          </object>
+                          <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>
+                          <object class="GtkLabel" id="label5">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Value:</property>
+                          </object>
+                          <packing>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="right_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVButtonBox" id="vbuttonbox2">
+                        <property name="visible">True</property>
+                        <property name="border_width">5</property>
+                        <child>
+                          <object class="GtkButton" id="val_labs_add">
+                            <property name="label">gtk-add</property>
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_stock">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="val_labs_change">
+                            <property name="label">gtk-apply</property>
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_stock">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="val_labs_remove">
+                            <property name="label">gtk-remove</property>
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="can_default">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_stock">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Value Labels</property>
+                <property name="use_markup">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">10</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVButtonBox" id="vbuttonbox3">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="spacing">5</property>
+            <property name="layout_style">start</property>
+            <child>
+              <object class="GtkButton" id="val_labs_ok">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="val_labs_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="help_button_value_labels">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object 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">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <child>
+      <object class="GtkVBox" id="vbox3">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkFrame" id="frame9">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkAlignment" id="alignment4">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkRadioButton" id="no_missing">
+                <property name="label" translatable="yes">_No missing values</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkFrame" id="frame4">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkAlignment" id="alignment3">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <object 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>
+                      <object class="GtkEntry" id="mv0">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="mv1">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="mv2">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkRadioButton" id="discrete_missing">
+                <property name="label" translatable="yes">_Discrete missing values</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="focus_on_click">False</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">no_missing</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkFrame" id="frame10">
+            <property name="visible">True</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkAlignment" id="alignment5">
+                <property name="visible">True</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <object 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>
+                      <object 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>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="mv-low">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object 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>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="mv-high">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <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>
+                      <object 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>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="mv-discrete">
+                        <property name="width_request">75</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <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>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkRadioButton" id="range_missing">
+                <property name="label" translatable="yes">_Range plus one optional discrete missing value</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="focus_on_click">False</property>
+                <property name="draw_indicator">True</property>
+                <property name="group">no_missing</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHButtonBox" id="hbuttonbox1">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="missing_val_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="help_button_missing_values">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="missing_val_ok">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</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..cbc4d5cadb97eedc10b929e4532da9e820d7df3d 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, "model", &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 +83,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 +98,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,33 +141,33 @@ 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.ui");
 
   GtkWidget *dialog = get_widget_assert (xml, "variable-info-dialog");
   GtkWidget *treeview = get_widget_assert (xml, "treeview2");
   GtkWidget *textview = get_widget_assert (xml, "textview1");
 
   PsppireVarStore *vs = NULL;
+  PsppireDict *dict = NULL;
 
   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_get (vs, "dictionary", &dict, NULL);
+  g_object_set (treeview, "model", 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 ("");
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);
 
diff --git a/src/ui/gui/variable-info.ui b/src/ui/gui/variable-info.ui
new file mode 100644 (file)
index 0000000..c0f038c
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="psppire" version="2054.22072"/>
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy project-wide -->
+  <object 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">
+      <object 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>
+          <object 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">never</property>
+            <property name="vscrollbar_policy">automatic</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object 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>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">False</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+        <child>
+          <object 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="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <child>
+              <object 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>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">5</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object 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">never</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object 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">word-char</property>
+                    <property name="left_margin">3</property>
+                    <property name="cursor_visible">False</property>
+                    <property name="accepts_tab">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="padding">5</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object 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>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="pack_type">end</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
index 223568bf0e1a7e6af9afd21028301e206221ba87..40951360d70186658642ec2a99c701ed8e9e042c 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");
@@ -121,8 +120,9 @@ weight_cases_dialog (GObject *o, gpointer data)
   PsppireVarStore *vs = NULL;
 
   g_object_get (de->data_editor, "var-store", &vs,  NULL);
+  g_object_get (vs, "dictionary", &wcd.dict, 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,22 +131,16 @@ 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, "model", wcd.dict,
+                                "selection-mode", GTK_SELECTION_SINGLE,
+                                "predicate", var_is_numeric,
+                                NULL);
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                entry,
-                                insert_source_row_into_entry,
-                                is_currently_in_entry,
-                                NULL
-                                );
+  psppire_selector_set_filter_func (PSPPIRE_SELECTOR (selector),
+                                   is_currently_in_entry);
 
 
-  wcd.dict = vs->dict;
   wcd.entry = GTK_ENTRY (entry);
   wcd.status = GTK_LABEL (status);
   wcd.off = GTK_TOGGLE_BUTTON (radiobutton1);
@@ -172,12 +166,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..a1a55d1
--- /dev/null
@@ -0,0 +1,30 @@
+#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"
+#include "psppire-var-view.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 ();
+  psppire_var_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..9cde11c9110641e1c6f39fa1aa4de959865cb74b 100644 (file)
@@ -1,43 +1,33 @@
 ## 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 = $(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@
-
-
+       $(LIBINTL) $(LIBREADLINE)
 
 
 src_ui_terminal_pspp_LDFLAGS = $(PSPP_LDFLAGS) $(PG_LDFLAGS)
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 8acbdc2dde79d7bd1a8a97be24da76523bf0f928..7ad162fca6d40b746a0155d891d8e08784e2e4df 100644 (file)
@@ -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"
@@ -60,7 +63,6 @@
 #define _(msgid) gettext (msgid)
 
 
-static void i18n_init (void);
 static void fpu_init (void);
 static void clean_up (void);
 
@@ -74,12 +76,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);
@@ -106,55 +111,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 ())
     {
-      msg_ui_init (the_source_stream);
-      if (!settings_get_testing_mode ())
-        outp_read_devices ();
+      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 (;;)
+    {
+      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, relocate (locale_dir));
-  textdomain (PACKAGE);
-#endif /* ENABLE_NLS */
-}
 
 static void
 fpu_init (void)
@@ -209,5 +221,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..8a974a32f7033545d91cd075b8c879180a7c7eed 100644 (file)
@@ -1,18 +1,22 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 TESTS_ENVIRONMENT = top_srcdir='$(top_srcdir)' top_builddir='$(top_builddir)'
-TESTS_ENVIRONMENT += PERL='@PERL@' PG_CONFIG='@PG_CONFIG@'
+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/correlation.sh \
        tests/command/data-list.sh \
        tests/command/do-if.sh \
        tests/command/do-repeat.sh \
@@ -28,9 +32,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 +44,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 +56,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 +77,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 +110,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 +118,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 +137,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 +156,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 +186,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 +200,21 @@ 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 = gl/libgl.la src/libpspp-core.la $(LIBINTL) 
+tests_data_datasheet_test_CFLAGS = $(AM_CFLAGS)
 
 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_LDADD = gl/libgl.la $(LIBINTL)
+tests_libpspp_ll_test_CFLAGS = $(AM_CFLAGS)
 
 tests_libpspp_llx_test_SOURCES = \
        src/libpspp/ll.c \
@@ -195,7 +222,8 @@ tests_libpspp_llx_test_SOURCES = \
        src/libpspp/llx.c \
        src/libpspp/llx.h \
        tests/libpspp/llx-test.c
-tests_libpspp_llx_test_LDADD = gl/libgl.la @LIBINTL@
+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,14 +231,30 @@ 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 \
        tests/libpspp/abt-test.c
-tests_libpspp_abt_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_abt_test_LDADD = gl/libgl.la $(LIBINTL)
 tests_libpspp_abt_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
 tests_libpspp_bt_test_SOURCES = \
@@ -226,7 +270,7 @@ tests_libpspp_range_map_test_SOURCES = \
        src/libpspp/range-map.c \
        src/libpspp/range-map.h \
        tests/libpspp/range-map-test.c
-tests_libpspp_range_map_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_range_map_test_LDADD = gl/libgl.la $(LIBINTL)
 tests_libpspp_range_map_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
 tests_libpspp_range_set_test_SOURCES = \
@@ -237,12 +281,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 +296,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 +305,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 +330,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 +373,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..5565d31ad4b5faf6ca12db873d1d933e7fb72c12 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|S.E. 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                        #
index 9a437efb8a466246a09859dc110d102c7dfc739a..c54bba3cf541a1ce7c738487e88ec6db09bb7a1e 100755 (executable)
@@ -94,12 +94,12 @@ diff  -b  $TEMPDIR/pspp.list - << EOF
 |gv      |A8    |
 +--------+------+
 2.1 T-TEST.  Group Statistics
-#==========#=#====#==============#========#
-#     gv   |N|Mean|Std. Deviation|SE. Mean#
-#==========#=#====#==============#========#
-#x One     |5|2.60|           .55|     .24#
-#  Two     |3|3.50|           .50|     .29#
-#==========#=#====#==============#========#
+#==========#=#====#==============#=========#
+#     gv   |N|Mean|Std. Deviation|S.E. Mean#
+#==========#=#====#==============#=========#
+#x One     |5|2.60|           .55|      .24#
+#  Two     |3|3.50|           .50|      .29#
+#==========#=#====#==============#=========#
 2.2 T-TEST.  Independent Samples Test
 #============================#=========#============================================================================#
 #                            # Levene's|                        t-test for Equality of Means                        #
index f722f614fcbd9042e1b5fae340fc69ceacba17cf..a9a311b7cc63290d4377f2c49e7208cbf741586e 100755 (executable)
@@ -88,12 +88,12 @@ diff  -b  $TEMPDIR/pspp.list - << EOF
 |B       |F8.0  |
 +--------+------+
 2.1 T-TEST.  Paired Sample Statistics
-#========#=====#=#==============#========#
-#        # Mean|N|Std. Deviation|SE. Mean#
-#========#=====#=#==============#========#
-#Pair 0 A#4.333|3|         5.774|   3.333#
-#       B#1.333|3|          .577|    .333#
-#========#=====#=#==============#========#
+#========#=====#=#==============#=========#
+#        # Mean|N|Std. Deviation|S.E. Mean#
+#========#=====#=#==============#=========#
+#Pair 0 A#4.333|3|         5.774|    3.333#
+#       B#1.333|3|          .577|     .333#
+#========#=====#=#==============#=========#
 2.2 T-TEST.  Paired Samples Correlations
 #======#=====#=#===========#====#
 #      |     #N|Correlation|Sig.#
index 4f96916f76d0a6bd304d66e742af7c932872cbe9..6168f4bb86fa6149a72062c363e848ff8485798b 100755 (executable)
@@ -96,17 +96,16 @@ diff -b  -w $TEMPDIR/pspp.list - << EOF
 |X       |F8.0  |
 +--------+------+
 2.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+--------+--------+
-|           |        |         |        |  Valid |   Cum  |
-|Value Label|  Value |Frequency| Percent| Percent| Percent|
-#===========#========#=========#========#========#========#
-|           |   12.00|        1|   25.00|   25.00|   25.00|
-|           |   13.00|        1|   25.00|   25.00|   50.00|
-|           |   21.00|        1|   25.00|   25.00|   75.00|
-|           |   31.00|        1|   25.00|   25.00|  100.00|
-#===========#========#=========#========#========#========#
-|               Total|        4|   100.0|   100.0|        |
-+--------------------+---------+--------+--------+--------+
++-----------+--------+---------+--------+-------------+-----------+
+|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
+#===========#========#=========#========#=============#===========#
+|           |   12.00|        1|   25.00|        25.00|      25.00|
+|           |   13.00|        1|   25.00|        25.00|      50.00|
+|           |   21.00|        1|   25.00|        25.00|      75.00|
+|           |   31.00|        1|   25.00|        25.00|     100.00|
+#===========#========#=========#========#=============#===========#
+|               Total|        4|   100.0|        100.0|           |
++--------------------+---------+--------+-------------+-----------+
 +---------------+-----+
 |N       Valid  |    4|
 |        Missing|    0|
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/correlation.sh b/tests/command/correlation.sh
new file mode 100755 (executable)
index 0000000..d8fcb1c
--- /dev/null
@@ -0,0 +1,221 @@
+#!/bin/sh
+
+# This program tests the CORRELATIONS 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 1"
+cat << EOF > $TESTFILE
+set format = F11.3.
+data list notable list /foo * bar * wiz * bang *.
+begin data.
+1   0   3   1
+3   9 -50   5
+3   4   3 203
+4  -9   0  -4
+98 78 104   2
+3  50 -49 200
+.   4   4   4
+5   3   0   .
+end data.
+
+correlations 
+       variables = foo bar wiz bang
+       /print nosig
+       /missing = listwise
+       .
+
+correlations 
+       variables = bar wiz
+       /print nosig
+       /missing = listwise
+       .
+
+correlations 
+       variables = foo bar wiz bang
+       /print nosig
+       /missing = pairwise
+       .
+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 results 1"
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
+diff -b  $TEMPDIR/pspp.list - << EOF
+1.1 CORRELATIONS.  Correlations
+#========================#=====#=====#=====#=====#
+#                        #foo  |bar  |wiz  |bang #
+#----+-------------------#-----+-----+-----+-----#
+#foo |Pearson Correlation#1.000| .802| .890|-.308#
+#    |Sig. (2-tailed)    #     | .055| .017| .553#
+#----+-------------------#-----+-----+-----+-----#
+#bar |Pearson Correlation# .802|1.000| .519| .118#
+#    |Sig. (2-tailed)    # .055|     | .291| .824#
+#----+-------------------#-----+-----+-----+-----#
+#wiz |Pearson Correlation# .890| .519|1.000|-.344#
+#    |Sig. (2-tailed)    # .017| .291|     | .505#
+#----+-------------------#-----+-----+-----+-----#
+#bang|Pearson Correlation#-.308| .118|-.344|1.000#
+#    |Sig. (2-tailed)    # .553| .824| .505|     #
+#====#===================#=====#=====#=====#=====#
+2.1 CORRELATIONS.  Correlations
+#=======================#=====#=====#
+#                       #bar  |wiz  #
+#---+-------------------#-----+-----#
+#bar|Pearson Correlation#1.000| .497#
+#   |Sig. (2-tailed)    #     | .210#
+#---+-------------------#-----+-----#
+#wiz|Pearson Correlation# .497|1.000#
+#   |Sig. (2-tailed)    # .210|     #
+#===#===================#=====#=====#
+3.1 CORRELATIONS.  Correlations
+#========================#=====#=====#=====#=====#
+#                        #foo  |bar  |wiz  |bang #
+#----+-------------------#-----+-----+-----+-----#
+#foo |Pearson Correlation#1.000| .805| .883|-.308#
+#    |Sig. (2-tailed)    #     | .029| .008| .553#
+#    |N                  #    7|    7|    7|    6#
+#----+-------------------#-----+-----+-----+-----#
+#bar |Pearson Correlation# .805|1.000| .497| .164#
+#    |Sig. (2-tailed)    # .029|     | .210| .725#
+#    |N                  #    7|    8|    8|    7#
+#----+-------------------#-----+-----+-----+-----#
+#wiz |Pearson Correlation# .883| .497|1.000|-.337#
+#    |Sig. (2-tailed)    # .008| .210|     | .460#
+#    |N                  #    7|    8|    8|    7#
+#----+-------------------#-----+-----+-----+-----#
+#bang|Pearson Correlation#-.308| .164|-.337|1.000#
+#    |Sig. (2-tailed)    # .553| .725| .460|     #
+#    |N                  #    6|    7|    7|    7#
+#====#===================#=====#=====#=====#=====#
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+# Now test that weights are properly handled.
+
+activity="create program 2"
+cat << EOF > $TESTFILE
+set format = F11.3.
+data list notable list /foo * bar * wiz * bang * w *.
+begin data.
+1   0   3   1  1
+3   9 -50   5  2
+3   4   3 203  1
+4  -9   0  -4  1
+98 78 104   2  3
+3  50 -49 200  1
+end data.
+
+weight by w.
+
+correlations 
+       variables = foo bar wiz bang
+       /statistics=descriptives xprod
+       .
+
+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="copy results"
+cp $TEMPDIR/pspp.list $TEMPDIR/weighted
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create program 3"
+cat << EOF > $TESTFILE
+set format = F11.3.
+data list notable list /foo * bar * wiz * bang * w *.
+begin data.
+1   0   3   1  1
+3   9 -50   5  1
+3   9 -50   5  1
+3   4   3 203  1
+4  -9   0  -4  1
+98 78 104   2  1
+98 78 104   2  1
+98 78 104   2  1
+3  50 -49 200  1
+end data.
+
+weight by w.
+
+correlations 
+       variables = foo bar wiz bang
+       /statistics=descriptives xprod
+       .
+
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program 3"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="Compare weighted and unweighted results"
+diff $TEMPDIR/pspp.list $TEMPDIR/weighted
+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..d8f8ae3647ffe306e531c945184190eb9d0478a8 100755 (executable)
@@ -96,9 +96,10 @@ File:           pro.sav
 Label:          No label.
 Variables:      2
 Cases:          3
-Type:           System File.
+Type:           System File
 Weight:         Not weighted.
 Mode:           Compression on.
+Charset:        Unknown
 +--------+-------------+---+
 |Variable|Description  |Pos|
 |        |             |iti|
index d1272a3db32e7dd3feccdfe3a344b3ee51da03bd..ba3e5c5f4e15aabccc4e8c96da676f4327e29da7 100755 (executable)
@@ -105,12 +105,12 @@ diff  -b $TEMPDIR/pspp.list - <<EOF
 |DEP     |F8.0  |
 +--------+------+
 2.1 T-TEST.  Group Statistics
-#===========#==#====#==============#========#
-#     INDEP | N|Mean|Std. Deviation|SE. Mean#
-#===========#==#====#==============#========#
-#DEP >=1.514|11|9.00|          3.82|    1.15#
-#    <1.514 |11|8.00|          2.86|     .86#
-#===========#==#====#==============#========#
+#===========#==#====#==============#=========#
+#     INDEP | N|Mean|Std. Deviation|S.E. Mean#
+#===========#==#====#==============#=========#
+#DEP >=1.514|11|9.00|          3.82|     1.15#
+#    <1.514 |11|8.00|          2.86|      .86#
+#===========#==#====#==============#=========#
 2.2 T-TEST.  Independent Samples Test
 #==============================#========#============================================================================#
 #                              #Levene's|                        t-test for Equality of Means                        #
index 2b45701c97ff3385433901b1b59b1703e80dae67..843698182f461315511502fd77861d7c725237b2 100755 (executable)
@@ -88,11 +88,11 @@ diff  -b $TEMPDIR/pspp.list - <<EOF
 |ABC     |F8.0  |
 +--------+------+
 2.1 T-TEST.  One-Sample Statistics
-#===#=#====#==============#========#
-#   #N|Mean|Std. Deviation|SE. Mean#
-#===#=#====#==============#========#
-#ABC#6|3.00|           .84|     .34#
-#===#=#====#==============#========#
+#===#=#====#==============#=========#
+#   #N|Mean|Std. Deviation|S.E. Mean#
+#===#=#====#==============#=========#
+#ABC#6|3.00|           .84|      .34#
+#===#=#====#==============#=========#
 2.2 T-TEST.  One-Sample Test
 #===#===================================================#
 #   #               Test Value = 2.000000               #
index 3ba90f14b2d80c7cbfa673b237814de7e5ce738e..37f833049b0842adb2d2fc83216c571070809d59 100755 (executable)
@@ -88,12 +88,12 @@ diff  -b $TEMPDIR/pspp.list - <<EOF
 |B       |F8.0  |
 +--------+------+
 2.1 T-TEST.  Paired Sample Statistics
-#========#====#=#==============#========#
-#        #Mean|N|Std. Deviation|SE. Mean#
-#========#====#=#==============#========#
-#Pair 0 A#2.00|5|           .71|     .32#
-#       B#4.00|5|          1.54|     .69#
-#========#====#=#==============#========#
+#========#====#=#==============#=========#
+#        #Mean|N|Std. Deviation|S.E. Mean#
+#========#====#=#==============#=========#
+#Pair 0 A#2.00|5|           .71|      .32#
+#       B#4.00|5|          1.54|      .69#
+#========#====#=#==============#=========#
 2.2 T-TEST.  Paired Samples Correlations
 #======#=====#=#===========#====#
 #      |     #N|Correlation|Sig.#
diff --git a/tests/command/update.sh b/tests/command/update.sh
new file mode 100755 (executable)
index 0000000..8bcb567
--- /dev/null
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+# This program tests the UPDATE procedure
+
+TEMPDIR=/tmp/pspp-tst-$$
+TESTFILE=$TEMPDIR/update.pspp
+
+
+# ensure that top_builddir  are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+cleanup()
+{
+     if [ x"$PSPP_TEST_NO_CLEANUP" != x ] ; then 
+       echo "NOT cleaning $TEMPDIR" 
+       return ; 
+     fi
+    cd /
+    rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="data create"
+cat > a.data <<EOF
+1aB
+8aM
+3aE
+5aG
+0aA
+5aH
+6aI
+7aJ
+2aD
+7aK
+1aC
+7aL
+4aF
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+cat > b.data <<EOF
+1bN
+3bO
+4bP
+6bQ
+7bR
+9bS
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+cat > update.out <<EOF
+A B C D INA INB
+- - - - --- ---
+0 a A     1   0
+1 b B N   1   1
+1 a C     1   0
+2 a D     1   0
+3 b E O   1   1
+4 b F P   1   1
+5 a G     1   0
+5 a H     1   0
+6 b I Q   1   1
+7 b J R   1   1
+7 a K     1   0
+7 a L     1   0
+8 a M     1   0
+9 b   S   0   1
+EOF
+perl -pi -e 's/^\s*$//g' update.out
+
+# Test UPDATE.
+dla="data list notable file='a.data' /A B C 1-3 (a)."
+sa="save outfile='a.sys'."
+dlb="data list notable file='b.data' /A B C 1-3 (a)."
+sb="save outfile='b.sys'."
+for sources in ss sa as; do
+    name="$sources"
+    activity="create $name.pspp"
+    {
+       if [ $sources = ss ]; then
+           cat <<EOF
+set errors=terminal.
+$dla
+$sa
+$dlb
+$sb
+update file='a.sys' /in=INA /sort
+         /file='b.sys' /in=INB /rename c=D
+         /by a.
+EOF
+       elif [ $sources = sa ]; then
+           cat <<EOF
+set errors=terminal.
+$dla
+$sa
+$dlb
+
+update file='a.sys' /in=INA /sort
+      /file=* /in=INB /rename c=D
+      /by a.
+EOF
+       elif [ $sources = as ]; then
+           cat <<EOF
+set errors=terminal.
+$dlb
+$sb
+$dla
+
+update file=* /in=INA /sort
+      /file='b.sys' /in=INB /rename c=D
+      /by a.
+EOF
+       else
+           activity="internal error"
+           no_result
+       fi
+       echo 'list.'
+    } > $name.pspp
+    if [ $? -ne 0 ] ; then no_result ; fi
+
+    activity="run $name.pspp"
+    rm -f errors
+    $SUPERVISOR $PSPP --testing-mode --error-file=errors $name.pspp
+    if [ $? -ne 0 ] ; then no_result ; fi
+
+    activity="check $name output"
+    perl -pi -e 's/^\s*$//g' pspp.list
+    diff -c -b -w pspp.list update.out
+    if [ $? -ne 0 ] ; then fail ; fi
+    diff -c -b -w - errors <<EOF
+$name.pspp:8: warning: UPDATE: Encountered 3 sets of duplicate cases in the master file.
+EOF
+    if [ $? -ne 0 ] ; then fail ; fi
+done
+
+pass;
index 5da366d9968d9941f34eb5561a7538eaf031dc0a..0d4fb3970b21fc685f0c059d72651ff177a75b46 100755 (executable)
@@ -87,11 +87,11 @@ diff  -b $TEMPDIR/pspp.list - <<EOF
 |BVAR    |     1|  6- 10|F5.0  |
 +--------+------+-------+------+
 2.1 DESCRIPTIVES.  Valid cases = 730; cases with missing value(s) = 0.
-+--------#-------+---------+------+--------+-------+--------+--------+--------+--------+--------+------+-------+-------+--------+
-|Variable#Valid N|Missing N| Mean |S E Mean|Std Dev|Variance|Kurtosis|S E Kurt|Skewness|S E Skew| Range|Minimum|Maximum|   Sum  |
-#========#=======#=========#======#========#=======#========#========#========#========#========#======#=======#=======#========#
-|AVAR    #    730|        0|31.515|    .405| 10.937| 119.608|   2.411|    .181|   1.345|    .090|76.000| 18.000| 94.000|23006.00|
-+--------#-------+---------+------+--------+-------+--------+--------+--------+--------+--------+------+-------+-------+--------+
++--------#-------+---------+------+---------+-------+--------+--------+---------+--------+---------+------+-------+-------+--------+
+|Variable#Valid N|Missing N| Mean |S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew| Range|Minimum|Maximum|   Sum  |
+#========#=======#=========#======#=========#=======#========#========#=========#========#=========#======#=======#=======#========#
+|AVAR    #    730|        0|31.515|     .405| 10.937| 119.608|   2.411|     .181|   1.345|     .090|76.000| 18.000| 94.000|23006.00|
++--------#-------+---------+------+---------+-------+--------+--------+---------+--------+---------+------+-------+-------+--------+
 3.1 FREQUENCIES.  AVAR
 +--------+--------+---+---+
 |        |        |   |Cum|
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..8bab745f3ab23a52ca566b4e75b2c8001d194e30 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;
@@ -43,8 +45,14 @@ struct sfm_reader
 
     int n_variable_records, n_variables;
 
+    int *var_widths;
+    size_t n_var_widths, allocated_var_widths;
+
     enum integer_format integer_format;
     enum float_format float_format;
+
+    bool compressed;
+    double bias;
   };
 
 static void read_header (struct sfm_reader *);
@@ -62,12 +70,23 @@ 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 void read_compressed_data (struct sfm_reader *);
+
+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 +106,68 @@ 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;
+      r.n_var_widths = 0;
+      r.allocated_var_widths = 0;
+      r.var_widths = 0;
+      r.compressed = false;
+
+      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);
+
+      if (r.compressed)
+        read_compressed_data (&r);
 
+      fclose (r.file);
+    }
+  
   return 0;
 }
 
@@ -146,7 +183,6 @@ read_header (struct sfm_reader *r)
   int32_t weight_index;
   int32_t ncases;
   uint8_t raw_bias[8];
-  double bias;
   char creation_date[10];
   char creation_time[9];
   char file_label[65];
@@ -170,10 +206,12 @@ read_header (struct sfm_reader *r)
                              raw_layout_code, sizeof raw_layout_code);
 
   nominal_case_size = read_int (r);
-  compressed = read_int (r) != 0;
+  compressed = read_int (r);
   weight_index = read_int (r);
   ncases = read_int (r);
 
+  r->compressed = compressed != 0;
+
   /* Identify floating-point format and obtain compression bias. */
   read_bytes (r, raw_bias, sizeof raw_bias);
   if (float_identify (100.0, raw_bias, sizeof raw_bias, &r->float_format) == 0)
@@ -186,7 +224,7 @@ read_header (struct sfm_reader *r)
       else
         r->float_format = FLOAT_IEEE_DOUBLE_LE;
     }
-  bias = float_get_double (r->float_format, raw_bias);
+  r->bias = float_get_double (r->float_format, raw_bias);
 
   read_string (r, creation_date, sizeof creation_date);
   read_string (r, creation_time, sizeof creation_time);
@@ -200,7 +238,7 @@ read_header (struct sfm_reader *r)
   printf ("\t%17s: %"PRId32"\n", "Compressed", compressed);
   printf ("\t%17s: %"PRId32"\n", "Weight index", weight_index);
   printf ("\t%17s: %"PRId32"\n", "Number of cases", ncases);
-  printf ("\t%17s: %g\n", "Compression bias", bias);
+  printf ("\t%17s: %g\n", "Compression bias", r->bias);
   printf ("\t%17s: %s\n", "Creation date", creation_date);
   printf ("\t%17s: %s\n", "Creation time", creation_time);
   printf ("\t%17s: \"%s\"\n", "File label", file_label);
@@ -278,6 +316,11 @@ read_variable_record (struct sfm_reader *r)
   if (width >= 0)
     r->n_variables++;
 
+  if (r->n_var_widths >= r->allocated_var_widths)
+    r->var_widths = x2nrealloc (r->var_widths, &r->allocated_var_widths,
+                                sizeof *r->var_widths);
+  r->var_widths[r->n_var_widths++] = width;
+
   printf ("\tWidth: %d (%s)\n",
           width,
           width > 0 ? "string"
@@ -357,6 +400,20 @@ read_variable_record (struct sfm_reader *r)
     }
 }
 
+static void
+print_untyped_value (struct sfm_reader *r, char raw_value[8])
+{
+  int n_printable;
+  double value;
+
+  value = float_get_double (r->float_format, raw_value);
+  for (n_printable = 0; n_printable < sizeof raw_value; n_printable++)
+    if (!isprint (raw_value[n_printable]))
+      break;
+
+  printf ("%g/\"%.*s\"", value, n_printable, raw_value);
+}
+
 /* Reads value labels from sysfile R and inserts them into the
    associated dictionary. */
 static void
@@ -372,17 +429,11 @@ read_value_label_record (struct sfm_reader *r)
   for (i = 0; i < label_cnt; i++)
     {
       char raw_value[8];
-      double value;
-      int n_printable;
       unsigned char label_len;
       size_t padded_len;
       char label[256];
 
       read_bytes (r, raw_value, sizeof raw_value);
-      value = float_get_double (r->float_format, raw_value);
-      for (n_printable = 0; n_printable < sizeof raw_value; n_printable++)
-        if (!isprint (raw_value[n_printable]))
-          break;
 
       /* Read label length. */
       read_bytes (r, &label_len, sizeof label_len);
@@ -392,7 +443,9 @@ read_value_label_record (struct sfm_reader *r)
       read_bytes (r, label, padded_len - 1);
       label[label_len] = 0;
 
-      printf ("\t%g/\"%.*s\": \"%s\"\n", value, n_printable, raw_value, label);
+      printf ("\t");
+      print_untyped_value (r, raw_value);
+      printf (": \"%s\"\n", label);
     }
 
   /* Now, read the type 4 record that has the list of variables
@@ -486,9 +539,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 +613,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 +682,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 +698,316 @@ 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);
+}
+
+static void
+read_compressed_data (struct sfm_reader *r)
+{
+  enum { N_OPCODES = 8 };
+  uint8_t opcodes[N_OPCODES];
+  long int opcode_ofs;
+  int opcode_idx;
+  int case_num;
+  int i;
+
+  read_int (r);
+  printf ("\n%08lx: compressed data:\n", ftell (r->file));
+
+  opcode_idx = N_OPCODES;
+  case_num = 0;
+  for (case_num = 0; ; case_num++)
+    {
+      printf ("%08lx: case %d's uncompressible data begins\n",
+              ftell (r->file), case_num);
+      for (i = 0; i < r->n_var_widths; i++)
+        {
+          int width = r->var_widths[i];
+          char raw_value[8];
+          int opcode;
+
+          if (opcode_idx >= N_OPCODES)
+            {
+              opcode_ofs = ftell (r->file);
+              read_bytes (r, opcodes, 8);
+              opcode_idx = 0;
+            }
+          opcode = opcodes[opcode_idx];
+          printf ("%08lx: variable %d: opcode %d: ",
+                  opcode_ofs + opcode_idx, i, opcode);
+
+          switch (opcode)
+            {
+            default:
+              printf ("%g", opcode - r->bias);
+              if (width != 0)
+                printf (", but this is a string variable (width=%d)", width);
+              printf ("\n");
+              break;
+
+            case 252:
+              printf ("end of data\n");
+              return;
+
+            case 253:
+              read_bytes (r, raw_value, 8);
+              printf ("uncompressible data: ");
+              print_untyped_value (r, raw_value);
+              printf ("\n");
+              break;
+
+            case 254:
+              printf ("spaces");
+              if (width == 0)
+                printf (", but this is a numeric variable");
+              printf ("\n");
+              break;
+
+            case 255:
+              printf ("SYSMIS");
+              if (width != 0)
+                printf (", but this is a string variable (width=%d)", width);
+
+              printf ("\n");
+              break;
+            }
+
+          opcode_idx++;
+        }
+    }
 }
 \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..4ca7f92
--- /dev/null
@@ -0,0 +1,1011 @@
+/* PSPP - a program for statistical analysis.
+   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 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. */
+
+/* GCC 4.3 miscompiles some of the tests below, so we do not run
+   these tests on GCC 4.3.  This is a bug in GCC 4.3 triggered by
+   the test program, not a bug in the library under test.  GCC
+   4.2 or earlier and GCC 4.4 or later do not have this bug.
+
+   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;
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ == 3
+  return;
+#endif  /* GCC 4.3 */
+
+  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;
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ == 3
+  return;
+#endif  /* GCC 4.3 */
+
+  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');
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ == 3
+  /* We skipped some of the tests, so return a value that
+     Automake will interpret as "skipped", instead of one that
+     means success. */
+  return 77;
+#else
+  return 0;
+#endif
+}
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..279863e
--- /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 %d,%d (of %d,%d) "
+                          "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 %d:%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 %d:%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=%d, "
+                     "max_memory_rows=%d",
+                     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 ab9618fc0b87a7590e251b8471f9ad75020f0d8f..a1f4a64e6aaf18d52fae489164f38f56e1c250c1 100755 (executable)
@@ -111,27 +111,27 @@ diff -b $TEMPDIR/pspp.list - <<EOF
 |V16     |     1| 17- 17|F1.0  |
 +--------+------+-------+------+
 2.1 DESCRIPTIVES.  Valid cases = 10; cases with missing value(s) = 0.
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+-----+
-|Variable#Valid N|Missing N|Mean|S E Mean|Std Dev|Variance|Kurtosis|S E Kurt|Skewness|S E Skew|Range|Minimum|Maximum| Sum |
-#========#=======#=========#====#========#=======#========#========#========#========#========#=====#=======#=======#=====#
-|V0      #     10|        0|3.80|     .84|   2.66|    7.07|    -.03|    1.33|     .89|     .69| 8.00|   1.00|   9.00|38.00|
-|V1      #     10|        0|4.60|     .96|   3.03|    9.16|   -1.39|    1.33|    -.03|     .69| 9.00|    .00|   9.00|46.00|
-|V2      #     10|        0|4.10|    1.16|   3.67|   13.43|   -2.02|    1.33|     .48|     .69| 8.00|   1.00|   9.00|41.00|
-|V3      #     10|        0|4.10|     .87|   2.77|    7.66|   -2.05|    1.33|     .42|     .69| 7.00|   1.00|   8.00|41.00|
-|V4      #     10|        0|7.00|     .47|   1.49|    2.22|    7.15|    1.33|   -2.52|     .69| 5.00|   3.00|   8.00|70.00|
-|V5      #     10|        0|4.90|    1.03|   3.25|   10.54|   -1.40|    1.33|    -.20|     .69| 9.00|    .00|   9.00|49.00|
-|V6      #     10|        0|5.90|     .80|   2.51|    6.32|    -.29|    1.33|    -.96|     .69| 7.00|   1.00|   8.00|59.00|
-|V7      #     10|        0|4.70|    1.10|   3.47|   12.01|   -1.99|    1.33|    -.16|     .69| 9.00|    .00|   9.00|47.00|
-|V8      #     10|        0|4.10|    1.10|   3.48|   12.10|   -1.93|    1.33|     .37|     .69| 9.00|    .00|   9.00|41.00|
-|V9      #     10|        0|4.30|     .87|   2.75|    7.57|    -.87|    1.33|     .73|     .69| 8.00|   1.00|   9.00|43.00|
-|V10     #     10|        0|5.50|     .85|   2.68|    7.17|   -1.84|    1.33|    -.33|     .69| 7.00|   2.00|   9.00|55.00|
-|V11     #     10|        0|6.50|     .78|   2.46|    6.06|   -1.28|    1.33|    -.89|     .69| 6.00|   3.00|   9.00|65.00|
-|V12     #     10|        0|7.90|     .60|   1.91|    3.66|    5.24|    1.33|   -2.21|     .69| 6.00|   3.00|   9.00|79.00|
-|V13     #     10|        0|4.30|     .99|   3.13|    9.79|   -1.25|    1.33|     .33|     .69| 9.00|    .00|   9.00|43.00|
-|V14     #     10|        0|3.60|    1.01|   3.20|   10.27|    -.96|    1.33|     .81|     .69| 9.00|    .00|   9.00|36.00|
-|V15     #     10|        0|3.70|     .92|   2.91|    8.46|   -1.35|    1.33|     .71|     .69| 7.00|   1.00|   8.00|37.00|
-|V16     #     10|        0|6.40|     .91|   2.88|    8.27|   -1.14|    1.33|    -.92|     .69| 7.00|   2.00|   9.00|64.00|
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+-----+
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
+|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum |
+#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#=====#
+|V0      #     10|        0|3.80|      .84|   2.66|    7.07|    -.03|     1.33|     .89|      .69| 8.00|   1.00|   9.00|38.00|
+|V1      #     10|        0|4.60|      .96|   3.03|    9.16|   -1.39|     1.33|    -.03|      .69| 9.00|    .00|   9.00|46.00|
+|V2      #     10|        0|4.10|     1.16|   3.67|   13.43|   -2.02|     1.33|     .48|      .69| 8.00|   1.00|   9.00|41.00|
+|V3      #     10|        0|4.10|      .87|   2.77|    7.66|   -2.05|     1.33|     .42|      .69| 7.00|   1.00|   8.00|41.00|
+|V4      #     10|        0|7.00|      .47|   1.49|    2.22|    7.15|     1.33|   -2.52|      .69| 5.00|   3.00|   8.00|70.00|
+|V5      #     10|        0|4.90|     1.03|   3.25|   10.54|   -1.40|     1.33|    -.20|      .69| 9.00|    .00|   9.00|49.00|
+|V6      #     10|        0|5.90|      .80|   2.51|    6.32|    -.29|     1.33|    -.96|      .69| 7.00|   1.00|   8.00|59.00|
+|V7      #     10|        0|4.70|     1.10|   3.47|   12.01|   -1.99|     1.33|    -.16|      .69| 9.00|    .00|   9.00|47.00|
+|V8      #     10|        0|4.10|     1.10|   3.48|   12.10|   -1.93|     1.33|     .37|      .69| 9.00|    .00|   9.00|41.00|
+|V9      #     10|        0|4.30|      .87|   2.75|    7.57|    -.87|     1.33|     .73|      .69| 8.00|   1.00|   9.00|43.00|
+|V10     #     10|        0|5.50|      .85|   2.68|    7.17|   -1.84|     1.33|    -.33|      .69| 7.00|   2.00|   9.00|55.00|
+|V11     #     10|        0|6.50|      .78|   2.46|    6.06|   -1.28|     1.33|    -.89|      .69| 6.00|   3.00|   9.00|65.00|
+|V12     #     10|        0|7.90|      .60|   1.91|    3.66|    5.24|     1.33|   -2.21|      .69| 6.00|   3.00|   9.00|79.00|
+|V13     #     10|        0|4.30|      .99|   3.13|    9.79|   -1.25|     1.33|     .33|      .69| 9.00|    .00|   9.00|43.00|
+|V14     #     10|        0|3.60|     1.01|   3.20|   10.27|    -.96|     1.33|     .81|      .69| 9.00|    .00|   9.00|36.00|
+|V15     #     10|        0|3.70|      .92|   2.91|    8.46|   -1.35|     1.33|     .71|      .69| 7.00|   1.00|   8.00|37.00|
+|V16     #     10|        0|6.40|      .91|   2.88|    8.27|   -1.14|     1.33|    -.92|      .69| 7.00|   2.00|   9.00|64.00|
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index dacd35824c4ba6072da097f6872a530d3b687e92..9de2770e1e5ae3bf1007636d84db803278926f19 100755 (executable)
@@ -98,37 +98,37 @@ diff -b $TEMPDIR/pspp.list - <<EOF
 |V3      |     1|  3-  3|F1.0  |
 +--------+------+-------+------+
 2.1 DESCRIPTIVES.  Valid cases = 7; cases with missing value(s) = 6.
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+----+
-|Variable#Valid N|Missing N|Mean|S E Mean|Std Dev|Variance|Kurtosis|S E Kurt|Skewness|S E Skew|Range|Minimum|Maximum| Sum|
-#========#=======#=========#====#========#=======#========#========#========#========#========#=====#=======#=======#====#
-|V1      #      1|        6|2.00|     .  |    .  |     .  |     .  |     .  |     .  |     .  |  .00|   2.00|   2.00|2.00|
-|V2      #      2|        5|2.50|     .50|    .71|     .50|     .  |     .  |     .  |     .  | 1.00|   2.00|   3.00|5.00|
-|V3      #      3|        4|3.00|     .58|   1.00|    1.00|     .  |     .  |     .00|    1.22| 2.00|   2.00|   4.00|9.00|
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+----+
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
+|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum|
+#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#====#
+|V1      #      1|        6|2.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   2.00|   2.00|2.00|
+|V2      #      2|        5|2.50|      .50|    .71|     .50|     .  |      .  |     .  |      .  | 1.00|   2.00|   3.00|5.00|
+|V3      #      3|        4|3.00|      .58|   1.00|    1.00|     .  |      .  |     .00|     1.22| 2.00|   2.00|   4.00|9.00|
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
 3.1 DESCRIPTIVES.  Valid cases = 7; cases with missing value(s) = 3.
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+-----+
-|Variable#Valid N|Missing N|Mean|S E Mean|Std Dev|Variance|Kurtosis|S E Kurt|Skewness|S E Skew|Range|Minimum|Maximum| Sum |
-#========#=======#=========#====#========#=======#========#========#========#========#========#=====#=======#=======#=====#
-|V1      #      5|        2|1.20|     .20|    .45|     .20|    5.00|    2.00|    2.24|     .91| 1.00|   1.00|   2.00| 6.00|
-|V2      #      5|        2|1.60|     .40|    .89|     .80|     .31|    2.00|    1.26|     .91| 2.00|   1.00|   3.00| 8.00|
-|V3      #      5|        2|2.20|     .58|   1.30|    1.70|   -1.49|    2.00|     .54|     .91| 3.00|   1.00|   4.00|11.00|
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+-----+
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
+|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum |
+#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#=====#
+|V1      #      5|        2|1.20|      .20|    .45|     .20|    5.00|     2.00|    2.24|      .91| 1.00|   1.00|   2.00| 6.00|
+|V2      #      5|        2|1.60|      .40|    .89|     .80|     .31|     2.00|    1.26|      .91| 2.00|   1.00|   3.00| 8.00|
+|V3      #      5|        2|2.20|      .58|   1.30|    1.70|   -1.49|     2.00|     .54|      .91| 3.00|   1.00|   4.00|11.00|
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
 4.1 DESCRIPTIVES.  Valid cases = 1; cases with missing value(s) = 6.
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+----+
-|Variable#Valid N|Missing N|Mean|S E Mean|Std Dev|Variance|Kurtosis|S E Kurt|Skewness|S E Skew|Range|Minimum|Maximum| Sum|
-#========#=======#=========#====#========#=======#========#========#========#========#========#=====#=======#=======#====#
-|V1      #      1|        0|2.00|     .  |    .  |     .  |     .  |     .  |     .  |     .  |  .00|   2.00|   2.00|2.00|
-|V2      #      1|        0|3.00|     .  |    .  |     .  |     .  |     .  |     .  |     .  |  .00|   3.00|   3.00|3.00|
-|V3      #      1|        0|4.00|     .  |    .  |     .  |     .  |     .  |     .  |     .  |  .00|   4.00|   4.00|4.00|
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+----+
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
+|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum|
+#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#====#
+|V1      #      1|        0|2.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   2.00|   2.00|2.00|
+|V2      #      1|        0|3.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   3.00|   3.00|3.00|
+|V3      #      1|        0|4.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   4.00|   4.00|4.00|
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
 5.1 DESCRIPTIVES.  Valid cases = 4; cases with missing value(s) = 3.
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+-----+
-|Variable#Valid N|Missing N|Mean|S E Mean|Std Dev|Variance|Kurtosis|S E Kurt|Skewness|S E Skew|Range|Minimum|Maximum| Sum |
-#========#=======#=========#====#========#=======#========#========#========#========#========#=====#=======#=======#=====#
-|V1      #      4|        0|1.25|     .25|    .50|     .25|    4.00|    2.62|    2.00|    1.01| 1.00|   1.00|   2.00| 5.00|
-|V2      #      4|        0|1.75|     .48|    .96|     .92|   -1.29|    2.62|     .85|    1.01| 2.00|   1.00|   3.00| 7.00|
-|V3      #      4|        0|2.50|     .65|   1.29|    1.67|   -1.20|    2.62|     .00|    1.01| 3.00|   1.00|   4.00|10.00|
-+--------#-------+---------+----+--------+-------+--------+--------+--------+--------+--------+-----+-------+-------+-----+
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
+|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum |
+#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#=====#
+|V1      #      4|        0|1.25|      .25|    .50|     .25|    4.00|     2.62|    2.00|     1.01| 1.00|   1.00|   2.00| 5.00|
+|V2      #      4|        0|1.75|      .48|    .96|     .92|   -1.29|     2.62|     .85|     1.01| 2.00|   1.00|   3.00| 7.00|
+|V3      #      4|        0|2.50|      .65|   1.29|    1.67|   -1.20|     2.62|     .00|     1.01| 3.00|   1.00|   4.00|10.00|
++--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 8ac4c772a410bf8b6de9308de1ac4e30af7651f1..9b170240538561a34803fbe91e38f093356081cf 100755 (executable)
@@ -89,18 +89,17 @@ activity="compare output $i"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
 1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+--------+--------+
-|           |        |         |        |  Valid |   Cum  |
-|Value Label|  Value |Frequency| Percent| Percent| Percent|
-#===========#========#=========#========#========#========#
-|           |    1.00|        1|   20.00|   20.00|   20.00|
-|           |    2.00|        1|   20.00|   20.00|   40.00|
-|           |    3.00|        1|   20.00|   20.00|   60.00|
-|           |    4.00|        1|   20.00|   20.00|   80.00|
-|           |    5.00|        1|   20.00|   20.00|  100.00|
-#===========#========#=========#========#========#========#
-|               Total|        5|   100.0|   100.0|        |
-+--------------------+---------+--------+--------+--------+
++-----------+--------+---------+--------+-------------+-----------+
+|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
+#===========#========#=========#========#=============#===========#
+|           |    1.00|        1|   20.00|        20.00|      20.00|
+|           |    2.00|        1|   20.00|        20.00|      40.00|
+|           |    3.00|        1|   20.00|        20.00|      60.00|
+|           |    4.00|        1|   20.00|        20.00|      80.00|
+|           |    5.00|        1|   20.00|        20.00|     100.00|
+#===========#========#=========#========#=============#===========#
+|               Total|        5|   100.0|        100.0|           |
++--------------------+---------+--------+-------------+-----------+
 +-----------------------+----+
 |N           Valid      |   5|
 |            Missing    |   0|
index 4e30d20b8dddb2931df44031fc3438ccfeeb01b5..9ee5a8c9b7d084a74055e099afc61a97e457db5f 100755 (executable)
@@ -88,18 +88,17 @@ activity="compare output $i"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
 1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+--------+--------+
-|           |        |         |        |  Valid |   Cum  |
-|Value Label|  Value |Frequency| Percent| Percent| Percent|
-#===========#========#=========#========#========#========#
-|           |    1.00|        1|   20.00|   20.00|   20.00|
-|           |    2.00|        1|   20.00|   20.00|   40.00|
-|           |    3.00|        1|   20.00|   20.00|   60.00|
-|           |    4.00|        1|   20.00|   20.00|   80.00|
-|           |    5.00|        1|   20.00|   20.00|  100.00|
-#===========#========#=========#========#========#========#
-|               Total|        5|   100.0|   100.0|        |
-+--------------------+---------+--------+--------+--------+
++-----------+--------+---------+--------+-------------+-----------+
+|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
+#===========#========#=========#========#=============#===========#
+|           |    1.00|        1|   20.00|        20.00|      20.00|
+|           |    2.00|        1|   20.00|        20.00|      40.00|
+|           |    3.00|        1|   20.00|        20.00|      60.00|
+|           |    4.00|        1|   20.00|        20.00|      80.00|
+|           |    5.00|        1|   20.00|        20.00|     100.00|
+#===========#========#=========#========#=============#===========#
+|               Total|        5|   100.0|        100.0|           |
++--------------------+---------+--------+-------------+-----------+
 +-----------------------+----+
 |N           Valid      |   5|
 |            Missing    |   0|
@@ -151,18 +150,17 @@ activity="compare output $i"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
 1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+--------+--------+
-|           |        |         |        |  Valid |   Cum  |
-|Value Label|  Value |Frequency| Percent| Percent| Percent|
-#===========#========#=========#========#========#========#
-|           |    1.00|     2.00|   20.00|   20.00|   20.00|
-|           |    2.00|     2.00|   20.00|   20.00|   40.00|
-|           |    3.00|     2.00|   20.00|   20.00|   60.00|
-|           |    4.00|     2.00|   20.00|   20.00|   80.00|
-|           |    5.00|     2.00|   20.00|   20.00|  100.00|
-#===========#========#=========#========#========#========#
-|               Total|    10.00|   100.0|   100.0|        |
-+--------------------+---------+--------+--------+--------+
++-----------+--------+---------+--------+-------------+-----------+
+|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
+#===========#========#=========#========#=============#===========#
+|           |    1.00|     2.00|   20.00|        20.00|      20.00|
+|           |    2.00|     2.00|   20.00|        20.00|      40.00|
+|           |    3.00|     2.00|   20.00|        20.00|      60.00|
+|           |    4.00|     2.00|   20.00|        20.00|      80.00|
+|           |    5.00|     2.00|   20.00|        20.00|     100.00|
+#===========#========#=========#========#=============#===========#
+|               Total|    10.00|   100.0|        100.0|           |
++--------------------+---------+--------+-------------+-----------+
 +-----------------------+-----+
 |N           Valid      |10.00|
 |            Missing    |  .00|
@@ -212,17 +210,16 @@ activity="compare output $i"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
 1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+--------+--------+
-|           |        |         |        |  Valid |   Cum  |
-|Value Label|  Value |Frequency| Percent| Percent| Percent|
-#===========#========#=========#========#========#========#
-|           |    1.00|     1.00|   16.67|   16.67|   16.67|
-|           |    3.00|     2.00|   33.33|   33.33|   50.00|
-|           |    4.00|     1.00|   16.67|   16.67|   66.67|
-|           |    5.00|     2.00|   33.33|   33.33|  100.00|
-#===========#========#=========#========#========#========#
-|               Total|     6.00|   100.0|   100.0|        |
-+--------------------+---------+--------+--------+--------+
++-----------+--------+---------+--------+-------------+-----------+
+|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
+#===========#========#=========#========#=============#===========#
+|           |    1.00|     1.00|   16.67|        16.67|      16.67|
+|           |    3.00|     2.00|   33.33|        33.33|      50.00|
+|           |    4.00|     1.00|   16.67|        16.67|      66.67|
+|           |    5.00|     2.00|   33.33|        33.33|     100.00|
+#===========#========#=========#========#=============#===========#
+|               Total|     6.00|   100.0|        100.0|           |
++--------------------+---------+--------+-------------+-----------+
 +-----------------------+----+
 |N           Valid      |6.00|
 |            Missing    | .00|
@@ -272,18 +269,17 @@ activity="compare output $i"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b $TEMPDIR/pspp.list - <<EOF
 1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+--------+--------+
-|           |        |         |        |  Valid |   Cum  |
-|Value Label|  Value |Frequency| Percent| Percent| Percent|
-#===========#========#=========#========#========#========#
-|           |    1.00|     1.00|   10.00|   16.67|   16.67|
-|           |    3.00|     2.00|   20.00|   33.33|   50.00|
-|           |    4.00|     1.00|   10.00|   16.67|   66.67|
-|           |    5.00|     2.00|   20.00|   33.33|  100.00|
-|           |   99.00|     4.00|   40.00| Missing|        |
-#===========#========#=========#========#========#========#
-|               Total|    10.00|   100.0|   100.0|        |
-+--------------------+---------+--------+--------+--------+
++-----------+--------+---------+--------+-------------+-----------+
+|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
+#===========#========#=========#========#=============#===========#
+|           |    1.00|     1.00|   10.00|        16.67|      16.67|
+|           |    3.00|     2.00|   20.00|        33.33|      50.00|
+|           |    4.00|     1.00|   10.00|        16.67|      66.67|
+|           |    5.00|     2.00|   20.00|        33.33|     100.00|
+|           |   99.00|     4.00|   40.00|      Missing|           |
+#===========#========#=========#========#=============#===========#
+|               Total|    10.00|   100.0|        100.0|           |
++--------------------+---------+--------+-------------+-----------+
 +-----------------------+----+
 |N           Valid      |6.00|
 |            Missing    |4.00|
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
diff --git a/texinfo.tex b/texinfo.tex
new file mode 100644 (file)
index 0000000..3569bd5
--- /dev/null
@@ -0,0 +1,9153 @@
+% texinfo.tex -- TeX macros to handle Texinfo files.
+%
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2008-11-17.21}
+%
+% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
+% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+% 2007, 2008 Free Software Foundation, Inc.
+%
+% This texinfo.tex file 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 texinfo.tex file is distributed in the hope that 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/>.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction.  (This has been our intent since Texinfo was invented.)
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+%   http://www.gnu.org/software/texinfo/ (the Texinfo home page), or
+%   ftp://tug.org/tex/texinfo.tex
+%     (and all CTAN mirrors, see http://www.ctan.org).
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org.  Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem.  Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution.  For a simple
+% manual foo.texi, however, you can get away with this:
+%   tex foo.texi
+%   texindex foo.??
+%   tex foo.texi
+%   tex foo.texi
+%   dvips foo.dvi -o  # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent.  You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is http://www.gnu.org/software/texinfo.
+
+
+\message{Loading texinfo [version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}%
+  \catcode`+=\active \catcode`\_=\active}
+
+
+\chardef\other=12
+
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
+
+% Save some plain tex macros whose names we will redefine.
+\let\ptexb=\b
+\let\ptexbullet=\bullet
+\let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv=\equiv
+\let\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
+\let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexstar=\*
+\let\ptext=\t
+\let\ptextop=\top
+{\catcode`\'=\active
+\global\let\ptexquoteright'}% Math-mode def from plain.tex.
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+  \let\linenumber = \empty % Pre-3.0.
+\else
+  \def\linenumber{l.\the\inputlineno:\space}
+\fi
+
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined  \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined   \gdef\putwordChapter{Chapter}\fi
+\ifx\putwordfile\undefined      \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined        \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined     \gdef\putwordIndexIsEmpty{(Index is empty)}\fi
+\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi
+\ifx\putwordInfo\undefined      \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined  \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined   \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined        \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined        \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined      \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined   \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined   \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined       \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined       \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined  \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined       \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined    \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined   \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined    \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined    \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined   \gdef\putwordDeffunc{Function}\fi
+
+% Since the category of space is not known, we have to be careful.
+\chardef\spacecat = 10
+\def\spaceisspace{\catcode`\ =\spacecat}
+
+% sometimes characters are active, so we need control sequences.
+\chardef\colonChar = `\:
+\chardef\commaChar = `\,
+\chardef\dashChar  = `\-
+\chardef\dotChar   = `\.
+\chardef\exclamChar= `\!
+\chardef\lquoteChar= `\`
+\chardef\questChar = `\?
+\chardef\rquoteChar= `\'
+\chardef\semiChar  = `\;
+\chardef\underChar = `\_
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+  Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+  ap-pen-dix bit-map bit-maps
+  data-base data-bases eshell fall-ing half-way long-est man-u-script
+  man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+  par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+  spell-ing spell-ings
+  stand-alone strong-est time-stamp time-stamps which-ever white-space
+  wide-spread wrap-around
+}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen\bindingoffset
+\newdimen\normaloffset
+\newdimen\pagewidth \newdimen\pageheight
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+% @| inserts a changebar to the left of the current line.  It should
+% surround any changed text.  This approach does *not* work if the
+% change spans more than two lines of output.  To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+  % \vadjust can only be used in horizontal mode.
+  \leavevmode
+  %
+  % Append this vertical mode material after the current line in the output.
+  \vadjust{%
+    % We want to insert a rule with the height and depth of the current
+    % leading; that is exactly what \strutbox is supposed to record.
+    \vskip-\baselineskip
+    %
+    % \vadjust-items are inserted at the left edge of the type.  So
+    % the \llap here moves out into the left-hand margin.
+    \llap{%
+      %
+      % For a thicker or thinner bar, change the `1pt'.
+      \vrule height\baselineskip width1pt
+      %
+      % This is the space between the bar and the text.
+      \hskip 12pt
+    }%
+  }%
+}
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal.  We don't just call \tracingall here,
+% since that produces some useless output on the terminal.  We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{%
+  \tracingstats2
+  \tracingpages1
+  \tracinglostchars2  % 2 gives us more in etex
+  \tracingparagraphs1
+  \tracingoutput1
+  \tracingmacros2
+  \tracingrestores1
+  \showboxbreadth\maxdimen \showboxdepth\maxdimen
+  \ifx\eTeXversion\undefined\else % etex gives us more logging
+    \tracingscantokens1
+    \tracingifs1
+    \tracinggroups1
+    \tracingnesting2
+    \tracingassigns1
+  \fi
+  \tracingcommands3  % 3 gives us more in etex
+  \errorcontextlines16
+}%
+
+% add check for \lastpenalty to plain's definitions.  If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+  \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+  \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+  \removelastskip\penalty-200\bigskip\fi\fi}
+
+% For @cropmarks command.
+% Do @cropmarks to get crop marks.
+%
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
+%
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\cornerlong  \cornerlong=1pc
+\newdimen\cornerthick \cornerthick=.3pt
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% Output a mark which sets \thischapter, \thissection and \thiscolor.
+% We dump everything together because we only have one kind of mark.
+% This works because we only use \botmark / \topmark, not \firstmark.
+%
+% A mark contains a subexpression of the \ifcase ... \fi construct.
+% \get*marks macros below extract the needed part using \ifcase.
+%
+% Another complication is to let the user choose whether \thischapter
+% (\thissection) refers to the chapter (section) in effect at the top
+% of a page, or that at the bottom of a page.  The solution is
+% described on page 260 of The TeXbook.  It involves outputting two
+% marks for the sectioning macros, one before the section break, and
+% one after.  I won't pretend I can describe this better than DEK...
+\def\domark{%
+  \toks0=\expandafter{\lastchapterdefs}%
+  \toks2=\expandafter{\lastsectiondefs}%
+  \toks4=\expandafter{\prevchapterdefs}%
+  \toks6=\expandafter{\prevsectiondefs}%
+  \toks8=\expandafter{\lastcolordefs}%
+  \mark{%
+                   \the\toks0 \the\toks2
+      \noexpand\or \the\toks4 \the\toks6
+    \noexpand\else \the\toks8
+  }%
+}
+% \topmark doesn't work for the very first chapter (after the title
+% page or the contents), so we use \firstmark there -- this gets us
+% the mark with the chapter defs, unless the user sneaks in, e.g.,
+% @setcolor (or @url, or @link, etc.) between @contents and the very
+% first @chapter.
+\def\gettopheadingmarks{%
+  \ifcase0\topmark\fi
+  \ifx\thischapter\empty \ifcase0\firstmark\fi \fi
+}
+\def\getbottomheadingmarks{\ifcase1\botmark\fi}
+\def\getcolormarks{\ifcase2\topmark\fi}
+
+% Avoid "undefined control sequence" errors.
+\def\lastchapterdefs{}
+\def\lastsectiondefs{}
+\def\prevchapterdefs{}
+\def\prevsectiondefs{}
+\def\lastcolordefs{}
+
+% Main output routine.
+\chardef\PAGE = 255
+\output = {\onepageout{\pagecontents\PAGE}}
+
+\newbox\headlinebox
+\newbox\footlinebox
+
+% \onepageout takes a vbox as an argument.  Note that \pagecontents
+% does insertions, but you have to call it yourself.
+\def\onepageout#1{%
+  \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+  %
+  \ifodd\pageno  \advance\hoffset by \bindingoffset
+  \else \advance\hoffset by -\bindingoffset\fi
+  %
+  % Do this outside of the \shipout so @code etc. will be expanded in
+  % the headline as they should be, not taken literally (outputting ''code).
+  \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi
+  \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}%
+  \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi
+  \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}%
+  %
+  {%
+    % Have to do this stuff outside the \shipout because we want it to
+    % take effect in \write's, yet the group defined by the \vbox ends
+    % before the \shipout runs.
+    %
+    \indexdummies         % don't expand commands in the output.
+    \normalturnoffactive  % \ in index entries must not stay \, e.g., if
+               % the page break happens to be in the middle of an example.
+               % We don't want .vr (or whatever) entries like this:
+               % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}}
+               % "\acronym" won't work when it's read back in;
+               % it needs to be 
+               % {\code {{\tt \backslashcurfont }acronym}
+    \shipout\vbox{%
+      % Do this early so pdf references go to the beginning of the page.
+      \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+      %
+      \ifcropmarks \vbox to \outervsize\bgroup
+        \hsize = \outerhsize
+        \vskip-\topandbottommargin
+        \vtop to0pt{%
+          \line{\ewtop\hfil\ewtop}%
+          \nointerlineskip
+          \line{%
+            \vbox{\moveleft\cornerthick\nstop}%
+            \hfill
+            \vbox{\moveright\cornerthick\nstop}%
+          }%
+          \vss}%
+        \vskip\topandbottommargin
+        \line\bgroup
+          \hfil % center the page within the outer (page) hsize.
+          \ifodd\pageno\hskip\bindingoffset\fi
+          \vbox\bgroup
+      \fi
+      %
+      \unvbox\headlinebox
+      \pagebody{#1}%
+      \ifdim\ht\footlinebox > 0pt
+        % Only leave this space if the footline is nonempty.
+        % (We lessened \vsize for it in \oddfootingyyy.)
+        % The \baselineskip=24pt in plain's \makefootline has no effect.
+        \vskip 24pt
+        \unvbox\footlinebox
+      \fi
+      %
+      \ifcropmarks
+          \egroup % end of \vbox\bgroup
+        \hfil\egroup % end of (centering) \line\bgroup
+        \vskip\topandbottommargin plus1fill minus1fill
+        \boxmaxdepth = \cornerthick
+        \vbox to0pt{\vss
+          \line{%
+            \vbox{\moveleft\cornerthick\nsbot}%
+            \hfill
+            \vbox{\moveright\cornerthick\nsbot}%
+          }%
+          \nointerlineskip
+          \line{\ewbot\hfil\ewbot}%
+        }%
+      \egroup % \vbox from first cropmarks clause
+      \fi
+    }% end of \shipout\vbox
+  }% end of group with \indexdummies
+  \advancepageno
+  \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+  \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1\relax \unvbox#1\relax
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+% Here are the rules for the cropmarks.  Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+  {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+  {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1.  The argument is the rest of
+% the input line (except we remove a trailing comment).  #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+%
+\def\parsearg{\parseargusing{}}
+\def\parseargusing#1#2{%
+  \def\argtorun{#2}%
+  \begingroup
+    \obeylines
+    \spaceisspace
+    #1%
+    \parseargline\empty% Insert the \empty token, see \finishparsearg below.
+}
+
+{\obeylines %
+  \gdef\parseargline#1^^M{%
+    \endgroup % End of the group started in \parsearg.
+    \argremovecomment #1\comment\ArgTerm%
+  }%
+}
+
+% First remove any @comment, then any @c comment.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
+
+% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
+%    @end itemize  @c foo
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+  \def\temp{#3}%
+  \ifx\temp\empty
+    % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp:
+    \let\temp\finishparsearg
+  \else
+    \let\temp\argcheckspaces
+  \fi
+  % Put the space token in:
+  \temp#1 #3\ArgTerm
+}
+
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \argtorun.
+% (Similarly, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
+%
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}}
+
+% \parseargdef\foo{...}
+%      is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+%
+% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my
+% favourite TeX trick.  --kasal, 16nov03
+
+\def\parseargdef#1{%
+  \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+  \def#2{\parsearg#1}%
+  \def#1##1%
+}
+
+% Several utility definitions with active space:
+{
+  \obeyspaces
+  \gdef\obeyedspace{ }
+
+  % Make each space character in the input produce a normal interword
+  % space in the output.  Don't allow a line break at this space, as this
+  % is used only in environments like @example, where each line of input
+  % should produce a line of output anyway.
+  %
+  \gdef\sepspaces{\obeyspaces\let =\tie}
+
+  % If an index command is used in an @example environment, any spaces
+  % therein should become regular spaces in the raw index file, not the
+  % expansion of \tie (\leavevmode \penalty \@M \ ).
+  \gdef\unsepspaces{\let =\space}
+}
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+% Define the framework for environments in texinfo.tex.  It's used like this:
+%
+%   \envdef\foo{...}
+%   \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo.  \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches.  The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as environments; they don't open a group.  (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
+
+
+% At run-time, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
+
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
+
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+  \def\temp{#1}%
+  \ifx\thisenv\temp
+  \else
+    \badenverr
+  \fi
+}
+
+% Environment mismatch, #1 expected:
+\def\badenverr{%
+  \errhelp = \EMsimple
+  \errmessage{This command can appear only \inenvironment\temp,
+    not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+  \ifx#1\empty
+    out of any environment%
+  \else
+    in environment \expandafter\string#1%
+  \fi
+}
+
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
+%
+\parseargdef\end{%
+  \if 1\csname iscond.#1\endcsname
+  \else
+    % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03
+    \expandafter\checkenv\csname#1\endcsname
+    \csname E#1\endcsname
+    \endgroup
+  \fi
+}
+
+\newhelp\EMsimple{Press RETURN to continue.}
+
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\tt\char64}}
+
+% This is turned off because it was never documented
+% and you can use @w{...} around a quote to suppress ligatures.
+%% Define @` and @' to be the same as ` and '
+%% but suppressing ligatures.
+%\def\`{{`}}
+%\def\'{{'}}
+
+% Used to generate quoted braces.
+\def\mylbrace {{\tt\char123}}
+\def\myrbrace {{\tt\char125}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+\begingroup
+  % Definitions to produce \{ and \} commands for indices,
+  % and @{ and @} for the aux/toc files.
+  \catcode`\{ = \other \catcode`\} = \other
+  \catcode`\[ = 1 \catcode`\] = 2
+  \catcode`\! = 0 \catcode`\\ = \other
+  !gdef!lbracecmd[\{]%
+  !gdef!rbracecmd[\}]%
+  !gdef!lbraceatcmd[@{]%
+  !gdef!rbraceatcmd[@}]%
+!endgroup
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
+\let\, = \c
+\let\dotaccent = \.
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \t
+\let\ubaraccent = \b
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+  \def\temp{#1}%
+  \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi
+  \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi
+  \else \errmessage{@dotless can be used only with i or j}%
+  \fi\fi
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence.  (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo.  Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+  L\kern-.36em
+  {\setbox0=\hbox{T}%
+   \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}%
+  \kern-.15em
+  \TeX
+}
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break\hbox{}\ignorespaces}
+
+% @/ allows a line break.
+\let\/=\allowbreak
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
+
+% @! is an end-of-sentence bang.
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
+
+% @? is an end-of-sentence query.
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
+
+% @frenchspacing on|off  says whether to put extra space after punctuation.
+% 
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+  \def\temp{#1}%
+  \ifx\temp\onword \plainfrenchspacing
+  \else\ifx\temp\offword \plainnonfrenchspacing
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @frenchspacing option `\temp', must be on/off}%
+  \fi\fi
+}
+
+% @w prevents a word break.  Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox.  We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line.  According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0).  If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+% Another complication is that the group might be very large.  This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material.  In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom.  The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+  \ifnum\catcode`\^^M=\active \else
+    \errhelp = \groupinvalidhelp
+    \errmessage{@group invalid in context where filling is enabled}%
+  \fi
+  \startsavinginserts
+  %
+  \setbox\groupbox = \vtop\bgroup
+    % Do @comment since we are called inside an environment such as
+    % @example, where each end-of-line in the input causes an
+    % end-of-line in the output.  We don't want the end-of-line after
+    % the `@group' to put extra space in the output.  Since @group
+    % should appear on a line by itself (according to the Texinfo
+    % manual), we don't worry about eating any user text.
+    \comment
+}
+%
+% The \vtop produces a box with normal height and large depth; thus, TeX puts
+% \baselineskip glue before it, and (when the next line of text is done)
+% \lineskip glue after it.  Thus, space below is not quite equal to space
+% above.  But it's pretty close.
+\def\Egroup{%
+    % To get correct interline space between the last line of the group
+    % and the first line afterwards, we have to propagate \prevdepth.
+    \endgraf % Not \par, as it may have been set to \lisppar.
+    \global\dimen1 = \prevdepth
+  \egroup           % End the \vtop.
+  % \dimen0 is the vertical size of the group's box.
+  \dimen0 = \ht\groupbox  \advance\dimen0 by \dp\groupbox
+  % \dimen2 is how much space is left on the page (more or less).
+  \dimen2 = \pageheight   \advance\dimen2 by -\pagetotal
+  % if the group doesn't fit on the current page, and it's a big big
+  % group, force a page break.
+  \ifdim \dimen0 > \dimen2
+    \ifdim \pagetotal < \vfilllimit\pageheight
+      \page
+    \fi
+  \fi
+  \box\groupbox
+  \prevdepth = \dimen1
+  \checkinserts
+}
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil  \mil=0.001in
+
+% Old definition--didn't work.
+%\parseargdef\need{\par %
+%% This method tries to make TeX break the page naturally
+%% if the depth of the box does not fit.
+%{\baselineskip=0pt%
+%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak
+%\prevdepth=-1000pt
+%}}
+
+\parseargdef\need{%
+  % Ensure vertical mode, so we don't make a big box in the middle of a
+  % paragraph.
+  \par
+  %
+  % If the @need value is less than one line space, it's useless.
+  \dimen0 = #1\mil
+  \dimen2 = \ht\strutbox
+  \advance\dimen2 by \dp\strutbox
+  \ifdim\dimen0 > \dimen2
+    %
+    % Do a \strut just to make the height of this box be normal, so the
+    % normal leading is inserted relative to the preceding line.
+    % And a page break here is fine.
+    \vtop to #1\mil{\strut\vfil}%
+    %
+    % TeX does not even consider page breaks if a penalty added to the
+    % main vertical list is 10000 or more.  But in order to see if the
+    % empty box we just added fits on the page, we must make it consider
+    % page breaks.  On the other hand, we don't want to actually break the
+    % page after the empty box.  So we use a penalty of 9999.
+    %
+    % There is an extremely small chance that TeX will actually break the
+    % page at this \penalty, if there are no other feasible breakpoints in
+    % sight.  (If the user is using lots of big @group commands, which
+    % almost-but-not-quite fill up a page, TeX will have a hard time doing
+    % good page breaking, for example.)  However, I could not construct an
+    % example where a page broke at this \penalty; if it happens in a real
+    % document, then we can reconsider our strategy.
+    \penalty9999
+    %
+    % Back up by the size of the box, whether we did a page break or not.
+    \kern -#1\mil
+    %
+    % Do not allow a page break right after this kern.
+    \nobreak
+  \fi
+}
+
+% @br   forces paragraph break (and is undocumented).
+
+\let\br = \par
+
+% @page forces the start of a new page.
+%
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
+
+% This defn is used inside nofill environments such as @example.
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+  \leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph.  For more general purposes, use the \margin insertion
+% class.  WHICH is `l' or `r'.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+  \nobreak
+  \kern-\strutdepth
+  \vtop to \strutdepth{%
+    \baselineskip=\strutdepth
+    \vss
+    % if you have multiple lines of stuff to put here, you'll need to
+    % make the vbox yourself of the appropriate size.
+    \ifx#1l%
+      \llap{\ignorespaces #2\hskip\inmarginspacing}%
+    \else
+      \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+    \fi
+    \null
+  }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+  \setbox0 = \hbox{\ignorespaces #2}%
+  \ifdim\wd0 > 0pt
+    \def\lefttext{#1}%  have both texts
+    \def\righttext{#2}%
+  \else
+    \def\lefttext{#1}%  have only one text
+    \def\righttext{#1}%
+  \fi
+  %
+  \ifodd\pageno
+    \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+  \else
+    \def\temp{\inleftmargin\lefttext}%
+  \fi
+  \temp
+}
+
+% @include FILE -- \input text of FILE.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+  \pushthisfilestack
+  \def\thisfile{#1}%
+  {%
+    \makevalueexpandable  % we want to expand any @value in FILE.
+    \turnoffactive        % and allow special characters in the expansion
+    \indexnofonts         % Allow `@@' and other weird things in file names.
+    \edef\temp{\noexpand\input #1 }%
+    %
+    % This trickery is to read FILE outside of a group, in case it makes
+    % definitions, etc.
+    \expandafter
+  }\temp
+  \popthisfilestack
+}
+\def\filenamecatcodes{%
+  \catcode`\\=\other
+  \catcode`~=\other
+  \catcode`^=\other
+  \catcode`_=\other
+  \catcode`|=\other
+  \catcode`<=\other
+  \catcode`>=\other
+  \catcode`+=\other
+  \catcode`-=\other
+  \catcode`\`=\other
+  \catcode`\'=\other
+}
+
+\def\pushthisfilestack{%
+  \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+  \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+  \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
+
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+  the stack of filenames is empty.}}
+
+\def\thisfile{}
+
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+  \ifhmode
+    \let\next\centerH
+  \else
+    \let\next\centerV
+  \fi
+  \next{\hfil \ignorespaces#1\unskip \hfil}%
+}
+\def\centerH#1{%
+  {%
+    \hfil\break
+    \advance\hsize by -\leftskip
+    \advance\hsize by -\rightskip
+    \line{#1}%
+    \break
+  }%
+}
+\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}}
+
+% @sp n   outputs n lines of vertical space
+
+\parseargdef\sp{\vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore  is another way to write a comment
+
+\def\comment{\begingroup \catcode`\^^M=\other%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\commentxxx}
+{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}}
+
+\let\c=\comment
+
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
+%
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
+%
+\parseargdef\paragraphindent{%
+  \def\temp{#1}%
+  \ifx\temp\asisword
+  \else
+    \ifx\temp\noneword
+      \defaultparindent = 0pt
+    \else
+      \defaultparindent = #1em
+    \fi
+  \fi
+  \parindent = \defaultparindent
+}
+
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+  \def\temp{#1}%
+  \ifx\temp\asisword
+  \else
+    \ifx\temp\noneword
+      \lispnarrowing = 0pt
+    \else
+      \lispnarrowing = #1em
+    \fi
+  \fi
+}
+
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading.  If WORD is `insert', then do indent at such
+% paragraphs.
+%
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
+%
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
+%
+\parseargdef\firstparagraphindent{%
+  \def\temp{#1}%
+  \ifx\temp\noneword
+    \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+  \else\ifx\temp\insertword
+    \let\suppressfirstparagraphindent = \relax
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @firstparagraphindent option `\temp'}%
+  \fi\fi
+}
+
+% Here is how we actually suppress indentation.  Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
+%
+% We also make \indent itself not actually do anything until the next
+% paragraph.
+%
+\gdef\dosuppressfirstparagraphindent{%
+  \gdef\indent{%
+    \restorefirstparagraphindent
+    \indent
+  }%
+  \gdef\noindent{%
+    \restorefirstparagraphindent
+    \noindent
+  }%
+  \global\everypar = {%
+    \kern -\parindent
+    \restorefirstparagraphindent
+  }%
+}
+
+\gdef\restorefirstparagraphindent{%
+  \global \let \indent = \ptexindent
+  \global \let \noindent = \ptexnoindent
+  \global \everypar = {}%
+}
+
+
+% @asis just yields its argument.  Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}.  So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+  \catcode`\_ = \active
+  \gdef\mathunderscore{%
+    \catcode`\_=\active
+    \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+  }
+}
+% Another complication: we want \\ (and @\) to output a \ character.
+% FYI, plain.tex uses \\ as a temporary control sequence (why?), but
+% this is not advertised and we don't care.  Texinfo does not
+% otherwise define @\.
+%
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
+%
+\def\math{%
+  \tex
+  \mathunderscore
+  \let\\ = \mathbackslash
+  \mathactive
+  % make the texinfo accent commands work in math mode
+  \let\"=\ddot
+  \let\'=\acute
+  \let\==\bar
+  \let\^=\hat
+  \let\`=\grave
+  \let\u=\breve
+  \let\v=\check
+  \let\~=\tilde
+  \let\dotaccent=\dot
+  $\finishmath
+}
+\def\finishmath#1{#1$\endgroup}  % Close the group opened by \tex.
+
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+  \catcode`^ = \active
+  \catcode`< = \active
+  \catcode`> = \active
+  \catcode`+ = \active
+  \catcode`' = \active
+  \gdef\mathactive{%
+    \let^ = \ptexhat
+    \let< = \ptexless
+    \let> = \ptexgtr
+    \let+ = \ptexplus
+    \let' = \ptexquoteright
+  }
+}
+
+% Some math mode symbols.
+\def\bullet{$\ptexbullet$}
+\def\geq{\ifmmode \ge\else $\ge$\fi}
+\def\leq{\ifmmode \le\else $\le$\fi}
+\def\minus{\ifmmode -\else $-$\fi}
+
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in the cm
+% typewriter fonts as three actual period characters; on the other hand,
+% in other typewriter fonts three periods are wider than 1.5em.  So do
+% whichever is larger.
+%
+\def\dots{%
+  \leavevmode
+  \setbox0=\hbox{...}% get width of three periods
+  \ifdim\wd0 > 1.5em
+    \dimen0 = \wd0
+  \else
+    \dimen0 = 1.5em
+  \fi
+  \hbox to \dimen0{%
+    \hskip 0pt plus.25fil
+    .\hskip 0pt plus1fil
+    .\hskip 0pt plus1fil
+    .\hskip 0pt plus.5fil
+  }%
+}
+
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+  \dots
+  \spacefactor=\endofsentencespacefactor
+}
+
+% @comma{} is so commas can be inserted into text without messing up
+% Texinfo's parsing.
+%
+\let\comma = ,
+
+% @refill is a no-op.
+\let\refill=\relax
+
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate (before @setfilename).
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+   \fixbackslash  % Turn off hack to swallow `\input texinfo'.
+   \iflinks
+     \tryauxfile
+     % Open the new aux file.  TeX will close it automatically at exit.
+     \immediate\openout\auxfile=\jobname.aux
+   \fi % \openindices needs to do some work in any case.
+   \openindices
+   \let\setfilename=\comment % Ignore extra @setfilename cmds.
+   %
+   % If texinfo.cnf is present on the system, read it.
+   % Useful for site-wide @afourpaper, etc.
+   \openin 1 texinfo.cnf
+   \ifeof 1 \else \input texinfo.cnf \fi
+   \closein 1
+   %
+   \comment % Ignore the actual filename.
+}
+
+% Called from \setfilename.
+%
+\def\openindices{%
+  \newindex{cp}%
+  \newcodeindex{fn}%
+  \newcodeindex{vr}%
+  \newcodeindex{tp}%
+  \newcodeindex{ky}%
+  \newcodeindex{pg}%
+}
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set).  So we test for \relax and 0 as well as \undefined,
+% borrowed from ifpdf.sty.
+\ifx\pdfoutput\undefined
+\else
+  \ifx\pdfoutput\relax
+  \else
+    \ifcase\pdfoutput
+    \else
+      \pdftrue
+    \fi
+  \fi
+\fi
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places.  Thus, we have to
+% double any backslashes.  Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e.  Not good.
+% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html
+% (and related messages, the final outcome is that it is up to the TeX
+% user to double the backslashes and otherwise make the string valid, so
+% that's what we do).
+
+% double active backslashes.
+% 
+{\catcode`\@=0 \catcode`\\=\active
+ @gdef@activebackslashdouble{%
+   @catcode`@\=@active
+   @let\=@doublebackslash}
+}
+
+% To handle parens, we must adopt a different approach, since parens are
+% not active characters.  hyperref.dtx (which has the same problem as
+% us) handles it with this amazing macro to replace tokens, with minor
+% changes for Texinfo.  It is included here under the GPL by permission
+% from the author, Heiko Oberdiek.
+% 
+% #1 is the tokens to replace.
+% #2 is the replacement.
+% #3 is the control sequence with the string.
+% 
+\def\HyPsdSubst#1#2#3{%
+  \def\HyPsdReplace##1#1##2\END{%
+    ##1%
+    \ifx\\##2\\%
+    \else
+      #2%
+      \HyReturnAfterFi{%
+        \HyPsdReplace##2\END
+      }%
+    \fi
+  }%
+  \xdef#3{\expandafter\HyPsdReplace#3#1\END}%
+}
+\long\def\HyReturnAfterFi#1\fi{\fi#1}
+
+% #1 is a control sequence in which to do the replacements.
+\def\backslashparens#1{%
+  \xdef#1{#1}% redefine it as its expansion; the definition is simply
+             % \lastnode when called from \setref -> \pdfmkdest.
+  \HyPsdSubst{(}{\realbackslash(}{#1}%
+  \HyPsdSubst{)}{\realbackslash)}{#1}%
+}
+
+\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images
+with PDF output, and none of those formats could be found.  (.eps cannot
+be supported due to the design of the PDF format; use regular TeX (DVI
+output) for that.)}
+
+\ifpdf
+  %
+  % Color manipulation macros based on pdfcolor.tex.
+  \def\cmykDarkRed{0.28 1 1 0.35}
+  \def\cmykBlack{0 0 0 1}
+  %
+  \def\pdfsetcolor#1{\pdfliteral{#1 k}}
+  % Set color, and create a mark which defines \thiscolor accordingly,
+  % so that \makeheadline knows which color to restore.
+  \def\setcolor#1{%
+    \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}%
+    \domark
+    \pdfsetcolor{#1}%
+  }
+  %
+  \def\maincolor{\cmykBlack}
+  \pdfsetcolor{\maincolor}
+  \edef\thiscolor{\maincolor}
+  \def\lastcolordefs{}
+  %
+  \def\makefootline{%
+    \baselineskip24pt
+    \line{\pdfsetcolor{\maincolor}\the\footline}%
+  }
+  %
+  \def\makeheadline{%
+    \vbox to 0pt{%
+      \vskip-22.5pt
+      \line{%
+        \vbox to8.5pt{}%
+        % Extract \thiscolor definition from the marks.
+        \getcolormarks
+        % Typeset the headline with \maincolor, then restore the color.
+        \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}%
+      }%
+      \vss
+    }%
+    \nointerlineskip
+  }
+  %
+  %
+  \pdfcatalog{/PageMode /UseOutlines}
+  %
+  % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
+  \def\dopdfimage#1#2#3{%
+    \def\imagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
+    \def\imageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
+    %
+    % pdftex (and the PDF format) support .png, .jpg, .pdf (among
+    % others).  Let's try in that order.
+    \let\pdfimgext=\empty
+    \begingroup
+      \openin 1 #1.png \ifeof 1
+        \openin 1 #1.jpg \ifeof 1
+          \openin 1 #1.jpeg \ifeof 1
+            \openin 1 #1.JPG \ifeof 1
+              \openin 1 #1.pdf \ifeof 1
+                \openin 1 #1.PDF \ifeof 1
+                  \errhelp = \nopdfimagehelp
+                  \errmessage{Could not find image file #1 for pdf}%
+                \else \gdef\pdfimgext{PDF}%
+                \fi
+              \else \gdef\pdfimgext{pdf}%
+              \fi
+            \else \gdef\pdfimgext{JPG}%
+            \fi
+          \else \gdef\pdfimgext{jpeg}%
+          \fi
+        \else \gdef\pdfimgext{jpg}%
+        \fi
+      \else \gdef\pdfimgext{png}%
+      \fi
+      \closein 1
+    \endgroup
+    %
+    % without \immediate, ancient pdftex seg faults when the same image is
+    % included twice.  (Version 3.14159-pre-1.0-unofficial-20010704.)
+    \ifnum\pdftexversion < 14
+      \immediate\pdfimage
+    \else
+      \immediate\pdfximage
+    \fi
+      \ifdim \wd0 >0pt width \imagewidth \fi
+      \ifdim \wd2 >0pt height \imageheight \fi
+      \ifnum\pdftexversion<13
+         #1.\pdfimgext
+       \else
+         {#1.\pdfimgext}%
+       \fi
+    \ifnum\pdftexversion < 14 \else
+      \pdfrefximage \pdflastximage
+    \fi}
+  %
+  \def\pdfmkdest#1{{%
+    % We have to set dummies so commands such as @code, and characters
+    % such as \, aren't expanded when present in a section title.
+    \indexnofonts
+    \turnoffactive
+    \activebackslashdouble
+    \makevalueexpandable
+    \def\pdfdestname{#1}%
+    \backslashparens\pdfdestname
+    \safewhatsit{\pdfdest name{\pdfdestname} xyz}%
+  }}
+  %
+  % used to mark target names; must be expandable.
+  \def\pdfmkpgn#1{#1}
+  %
+  % by default, use a color that is dark enough to print on paper as
+  % nearly black, but still distinguishable for online viewing.
+  \def\urlcolor{\cmykDarkRed}
+  \def\linkcolor{\cmykDarkRed}
+  \def\endlink{\setcolor{\maincolor}\pdfendlink}
+  %
+  % Adding outlines to PDF; macros for calculating structure of outlines
+  % come from Petr Olsak
+  \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+    \else \csname#1\endcsname \fi}
+  \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+    \advance\tempnum by 1
+    \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+  %
+  % #1 is the section text, which is what will be displayed in the
+  % outline by the pdf viewer.  #2 is the pdf expression for the number
+  % of subentries (or empty, for subsubsections).  #3 is the node text,
+  % which might be empty if this toc entry had no corresponding node.
+  % #4 is the page number
+  %
+  \def\dopdfoutline#1#2#3#4{%
+    % Generate a link to the node text if that exists; else, use the
+    % page number.  We could generate a destination for the section
+    % text in the case where a section has no node, but it doesn't
+    % seem worth the trouble, since most documents are normally structured.
+    \def\pdfoutlinedest{#3}%
+    \ifx\pdfoutlinedest\empty
+      \def\pdfoutlinedest{#4}%
+    \else
+      % Doubled backslashes in the name.
+      {\activebackslashdouble \xdef\pdfoutlinedest{#3}%
+       \backslashparens\pdfoutlinedest}%
+    \fi
+    %
+    % Also double the backslashes in the display string.
+    {\activebackslashdouble \xdef\pdfoutlinetext{#1}%
+     \backslashparens\pdfoutlinetext}%
+    %
+    \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
+  }
+  %
+  \def\pdfmakeoutlines{%
+    \begingroup
+      % Thanh's hack / proper braces in bookmarks
+      \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace
+      \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace
+      %
+      % Read toc silently, to get counts of subentries for \pdfoutline.
+      \def\numchapentry##1##2##3##4{%
+       \def\thischapnum{##2}%
+       \def\thissecnum{0}%
+       \def\thissubsecnum{0}%
+      }%
+      \def\numsecentry##1##2##3##4{%
+       \advancenumber{chap\thischapnum}%
+       \def\thissecnum{##2}%
+       \def\thissubsecnum{0}%
+      }%
+      \def\numsubsecentry##1##2##3##4{%
+       \advancenumber{sec\thissecnum}%
+       \def\thissubsecnum{##2}%
+      }%
+      \def\numsubsubsecentry##1##2##3##4{%
+       \advancenumber{subsec\thissubsecnum}%
+      }%
+      \def\thischapnum{0}%
+      \def\thissecnum{0}%
+      \def\thissubsecnum{0}%
+      %
+      % use \def rather than \let here because we redefine \chapentry et
+      % al. a second time, below.
+      \def\appentry{\numchapentry}%
+      \def\appsecentry{\numsecentry}%
+      \def\appsubsecentry{\numsubsecentry}%
+      \def\appsubsubsecentry{\numsubsubsecentry}%
+      \def\unnchapentry{\numchapentry}%
+      \def\unnsecentry{\numsecentry}%
+      \def\unnsubsecentry{\numsubsecentry}%
+      \def\unnsubsubsecentry{\numsubsubsecentry}%
+      \readdatafile{toc}%
+      %
+      % Read toc second time, this time actually producing the outlines.
+      % The `-' means take the \expnumber as the absolute number of
+      % subentries, which we calculated on our first read of the .toc above.
+      %
+      % We use the node names as the destinations.
+      \def\numchapentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+      \def\numsecentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+      \def\numsubsecentry##1##2##3##4{%
+        \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+      \def\numsubsubsecentry##1##2##3##4{% count is always zero
+        \dopdfoutline{##1}{}{##3}{##4}}%
+      %
+      % PDF outlines are displayed using system fonts, instead of
+      % document fonts.  Therefore we cannot use special characters,
+      % since the encoding is unknown.  For example, the eogonek from
+      % Latin 2 (0xea) gets translated to a | character.  Info from
+      % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+      %
+      % xx to do this right, we have to translate 8-bit characters to
+      % their "best" equivalent, based on the @documentencoding.  Right
+      % now, I guess we'll just let the pdf reader have its way.
+      \indexnofonts
+      \setupdatafile
+      \catcode`\\=\active \otherbackslash
+      \input \tocreadfilename
+    \endgroup
+  }
+  %
+  \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+    \ifx\PP\D\let\nextsp\relax
+    \else\let\nextsp\skipspaces
+      \ifx\p\space\else\addtokens{\filename}{\PP}%
+        \advance\filenamelength by 1
+      \fi
+    \fi
+    \nextsp}
+  \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax}
+  \ifnum\pdftexversion < 14
+    \let \startlink \pdfannotlink
+  \else
+    \let \startlink \pdfstartlink
+  \fi
+  % make a live url in pdf output.
+  \def\pdfurl#1{%
+    \begingroup
+      % it seems we really need yet another set of dummies; have not
+      % tried to figure out what each command should do in the context
+      % of @url.  for now, just make @/ a no-op, that's the only one
+      % people have actually reported a problem with.
+      % 
+      \normalturnoffactive
+      \def\@{@}%
+      \let\/=\empty
+      \makevalueexpandable
+      \leavevmode\setcolor{\urlcolor}%
+      \startlink attr{/Border [0 0 0]}%
+        user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+    \endgroup}
+  \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+  \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+  \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+  \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+  \def\maketoks{%
+    \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+    \ifx\first0\adn0
+    \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+    \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+    \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+    \else
+      \ifnum0=\countA\else\makelink\fi
+      \ifx\first.\let\next=\done\else
+        \let\next=\maketoks
+        \addtokens{\toksB}{\the\toksD}
+        \ifx\first,\addtokens{\toksB}{\space}\fi
+      \fi
+    \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+    \next}
+  \def\makelink{\addtokens{\toksB}%
+    {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+  \def\pdflink#1{%
+    \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+    \setcolor{\linkcolor}#1\endlink}
+  \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+  \let\pdfmkdest = \gobble
+  \let\pdfurl = \gobble
+  \let\endlink = \relax
+  \let\setcolor = \gobble
+  \let\pdfsetcolor = \gobble
+  \let\pdfmakeoutlines = \relax
+\fi  % \ifx\pdfoutput
+
+
+\message{fonts,}
+
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+  \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+  \csname ten#1\endcsname  % change the current font
+}
+
+% Select #1 fonts with the current style.
+%
+\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
+
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Unfortunately, we have to override this for titles and the like, since
+% in those cases "rm" is bold.  Sigh.
+\def\rmisbold{\rm\def\curfontstyle{bf}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
+\newfam\sffam
+\def\sf{\fam=\sffam \setfontstyle{sf}}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
+
+
+% Default leading.
+\newdimen\textleading  \textleading = 13.2pt
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly.  There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+% can get a sort of poor man's double spacing by redefining this.
+\def\baselinefactor{1}
+%
+\def\setleading#1{%
+  \dimen0 = #1\relax
+  \normalbaselineskip = \baselinefactor\dimen0
+  \normallineskip = \lineskipfactor\normalbaselineskip
+  \normalbaselines
+  \setbox\strutbox =\hbox{%
+    \vrule width0pt height\strutheightpercent\baselineskip
+                    depth \strutdepthpercent \baselineskip
+  }%
+}
+
+% PDF CMaps.  See also LaTeX's t1.cmap.
+%
+% do nothing with this by default.
+\expandafter\let\csname cmapOT1\endcsname\gobble
+\expandafter\let\csname cmapOT1IT\endcsname\gobble
+\expandafter\let\csname cmapOT1TT\endcsname\gobble
+
+% if we are producing pdf, and we have \pdffontattr, then define cmaps.
+% (\pdffontattr was introduced many years ago, but people still run
+% older pdftex's; it's easy to conditionalize, so we do.)
+\ifpdf \ifx\pdffontattr\undefined \else
+  \begingroup
+    \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+    \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1-0)
+%%Title: (TeX-OT1-0 TeX OT1 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<23> <26> <0023>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+40 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+    }\endgroup
+  \expandafter\edef\csname cmapOT1\endcsname#1{%
+    \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+  }%
+%
+% \cmapOT1IT
+  \begingroup
+    \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+    \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1IT-0)
+%%Title: (TeX-OT1IT-0 TeX OT1IT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1IT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1IT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<25> <26> <0025>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+42 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<23> <0023>
+<24> <00A3>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+    }\endgroup
+  \expandafter\edef\csname cmapOT1IT\endcsname#1{%
+    \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+  }%
+%
+% \cmapOT1TT
+  \begingroup
+    \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+    \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1TT-0)
+%%Title: (TeX-OT1TT-0 TeX OT1TT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1TT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1TT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+5 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<21> <26> <0021>
+<28> <5F> <0028>
+<61> <7E> <0061>
+endbfrange
+32 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <2191>
+<0C> <2193>
+<0D> <0027>
+<0E> <00A1>
+<0F> <00BF>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<20> <2423>
+<27> <2019>
+<60> <2018>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+    }\endgroup
+  \expandafter\edef\csname cmapOT1TT\endcsname#1{%
+    \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+  }%
+\fi\fi
+
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+% #3 is the font's design size, #4 is a scale factor, #5 is the CMap
+% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass
+% empty to omit).
+\def\setfont#1#2#3#4#5{%
+  \font#1=\fontprefix#2#3 scaled #4
+  \csname cmap#5\endcsname#1%
+}
+% This is what gets called when #5 of \setfont is empty.
+\let\cmap\gobble
+% emacs-page end of cmaps
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx}               %where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+% Definitions for a main text size of 11pt.  This is the default in
+% Texinfo.
+% 
+\def\definetextfontsizexi{%
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1095}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstep1}{OT1}
+\setfont\deftt\ttshape{10}{\magstep1}{OT1TT}
+\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\setfont\chaprm\rmbshape{12}{\magstep2}{OT1}
+\setfont\chapit\itbshape{10}{\magstep3}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep3}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT}
+\setfont\chapsf\sfbshape{17}{1000}{OT1}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}{OT1}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+\def\chapecsize{1728}
+
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\setfont\secrm\rmbshape{12}{\magstep1}{OT1}
+\setfont\secit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep2}{OT1}
+\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\secsf\sfbshape{12}{\magstep1}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}{OT1}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+\def\sececsize{1440}
+
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1}
+\setfont\ssecit\itbshape{10}{1315}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1315}{OT1}
+\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}{OT1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+\def\ssececsize{1200}
+
+% Reduced fonts for @acro in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}{OT1}
+\setfont\reducedtt\ttshape{10}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{1000}{OT1}
+\setfont\reducedit\itshape{10}{1000}{OT1IT}
+\setfont\reducedsl\slshape{10}{1000}{OT1}
+\setfont\reducedsf\sfshape{10}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{1000}{OT1}
+\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
+\def\reducedecsize{1000}
+
+% reset the current fonts
+\textfonts
+\rm
+} % end of 11pt text font size definitions
+
+
+% Definitions to make the main text be 10pt Computer Modern, with
+% section, chapter, etc., sizes following suit.  This is for the GNU
+% Press printing of the Emacs 22 manual.  Maybe other manuals in the
+% future.  Used with @smallbook, which sets the leading to 12pt.
+% 
+\def\definetextfontsizex{%
+% Text fonts (10pt).
+\def\textnominalsize{10pt}
+\edef\mainmagstep{1000}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1000}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstephalf}{OT1}
+\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT}
+\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter fonts (14.4pt).
+\def\chapnominalsize{14pt}
+\setfont\chaprm\rmbshape{12}{\magstep1}{OT1}
+\setfont\chapit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep2}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\chapsf\sfbshape{12}{\magstep1}{OT1}
+\let\chapbf\chaprm
+\setfont\chapsc\scbshape{10}{\magstep2}{OT1}
+\font\chapi=cmmi12 scaled \magstep1
+\font\chapsy=cmsy10 scaled \magstep2
+\def\chapecsize{1440}
+
+% Section fonts (12pt).
+\def\secnominalsize{12pt}
+\setfont\secrm\rmbshape{12}{1000}{OT1}
+\setfont\secit\itbshape{10}{\magstep1}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep1}{OT1}
+\setfont\sectt\ttbshape{12}{1000}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT}
+\setfont\secsf\sfbshape{12}{1000}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep1}{OT1}
+\font\seci=cmmi12 
+\font\secsy=cmsy10 scaled \magstep1
+\def\sececsize{1200}
+
+% Subsection fonts (10pt).
+\def\ssecnominalsize{10pt}
+\setfont\ssecrm\rmbshape{10}{1000}{OT1}
+\setfont\ssecit\itbshape{10}{1000}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1000}{OT1}
+\setfont\ssectt\ttbshape{10}{1000}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT}
+\setfont\ssecsf\sfbshape{10}{1000}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1000}{OT1}
+\font\sseci=cmmi10
+\font\ssecsy=cmsy10
+\def\ssececsize{1000}
+
+% Reduced fonts for @acro in text (9pt).
+\def\reducednominalsize{9pt}
+\setfont\reducedrm\rmshape{9}{1000}{OT1}
+\setfont\reducedtt\ttshape{9}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{900}{OT1}
+\setfont\reducedit\itshape{9}{1000}{OT1IT}
+\setfont\reducedsl\slshape{9}{1000}{OT1}
+\setfont\reducedsf\sfshape{9}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{900}{OT1}
+\setfont\reducedttsl\ttslshape{10}{900}{OT1TT}
+\font\reducedi=cmmi9
+\font\reducedsy=cmsy9
+\def\reducedecsize{0900}
+
+% reduce space between paragraphs
+\divide\parskip by 2
+
+% reset the current fonts
+\textfonts
+\rm
+} % end of 10pt text font size definitions
+
+
+% We provide the user-level command
+%   @fonttextsize 10
+% (or 11) to redefine the text font size.  pt is assumed.
+% 
+\def\xword{10}
+\def\xiword{11}
+%
+\parseargdef\fonttextsize{%
+  \def\textsizearg{#1}%
+  \wlog{doing @fonttextsize \textsizearg}%
+  %
+  % Set \globaldefs so that documents can use this inside @tex, since
+  % makeinfo 4.8 does not support it, but we need it nonetheless.
+  % 
+ \begingroup \globaldefs=1
+  \ifx\textsizearg\xword \definetextfontsizex
+  \else \ifx\textsizearg\xiword \definetextfontsizexi
+  \else
+    \errhelp=\EMsimple
+    \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'}
+  \fi\fi
+ \endgroup
+}
+
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families.  Since
+% texinfo doesn't allow for producing subscripts and superscripts except
+% in the main text, we don't bother to reset \scriptfont and
+% \scriptscriptfont (which would also require loading a lot more fonts).
+%
+\def\resetmathfonts{%
+  \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy
+  \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf
+  \textfont\ttfam=\tentt \textfont\sffam=\tensf
+}
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE.  We do this because \STYLE needs to also set the
+% current \fam for math mode.  Our \STYLE (e.g., \rm) commands hardwire
+% \tenSTYLE to set the current font.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower).  These relative commands are used in
+% the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
+\def\textfonts{%
+  \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+  \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+  \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
+  \let\tenttsl=\textttsl
+  \def\curfontsize{text}%
+  \def\lsize{reduced}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{\textleading}}
+\def\titlefonts{%
+  \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+  \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+  \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+  \let\tenttsl=\titlettsl
+  \def\curfontsize{title}%
+  \def\lsize{chap}\def\lllsize{subsec}%
+  \resetmathfonts \setleading{25pt}}
+\def\titlefont#1{{\titlefonts\rmisbold #1}}
+\def\chapfonts{%
+  \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+  \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+  \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
+  \let\tenttsl=\chapttsl
+  \def\curfontsize{chap}%
+  \def\lsize{sec}\def\lllsize{text}%
+  \resetmathfonts \setleading{19pt}}
+\def\secfonts{%
+  \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+  \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+  \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
+  \let\tenttsl=\secttsl
+  \def\curfontsize{sec}%
+  \def\lsize{subsec}\def\lllsize{reduced}%
+  \resetmathfonts \setleading{16pt}}
+\def\subsecfonts{%
+  \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+  \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+  \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
+  \let\tenttsl=\ssecttsl
+  \def\curfontsize{ssec}%
+  \def\lsize{text}\def\lllsize{small}%
+  \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts
+\def\reducedfonts{%
+  \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
+  \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
+  \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
+  \let\tenttsl=\reducedttsl
+  \def\curfontsize{reduced}%
+  \def\lsize{small}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{10.5pt}}
+\def\smallfonts{%
+  \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
+  \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
+  \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
+  \let\tenttsl=\smallttsl
+  \def\curfontsize{small}%
+  \def\lsize{smaller}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{10.5pt}}
+\def\smallerfonts{%
+  \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
+  \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
+  \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
+  \let\tenttsl=\smallerttsl
+  \def\curfontsize{smaller}%
+  \def\lsize{smaller}\def\lllsize{smaller}%
+  \resetmathfonts \setleading{9.5pt}}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts.  If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+%   8.5x11=86   smallbook=72  a4=90  a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+%   8.5x11=90+  smallbook=80  a4=90+  a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt.  So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+%   8.5x11=71  smallbook=60  a4=75  a5=58
+%
+% I wish the USA used A4 paper.
+% --karl, 24jan03.
+
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\definetextfontsizexi
+
+% Define these so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}{OT1}
+\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1}  % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}{OT1}
+\setfont\shortconttt\ttshape{12}{1000}{OT1TT}
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
+% unless the following character is such as not to need one.
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else
+                    \ptexslash\fi\fi\fi}
+\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx}
+\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally uses \ttsl.
+% @var is set to this for defun arguments.
+\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally use \sl.  We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\slanted=\smartslanted
+\def\var#1{{\setupmarkupstyle{var}\smartslanted{#1}}}
+\let\dfn=\smartslanted
+\let\emph=\smartitalic
+
+% @b, explicit bold.
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph.  Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1  \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\catcode`@=11
+  \def\plainfrenchspacing{%
+    \sfcode\dotChar  =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m
+    \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m
+    \def\endofsentencespacefactor{1000}% for @. and friends
+  }
+  \def\plainnonfrenchspacing{%
+    \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+    \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+    \def\endofsentencespacefactor{3000}% for @. and friends
+  }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
+\def\t#1{%
+  {\tt \rawbackslash \plainfrenchspacing #1}%
+  \null
+}
+\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}}
+\setfont\keyrm\rmshape{8}{1000}{OT1}
+\font\keysy=cmsy9
+\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+  \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+    \vbox{\hrule\kern-0.4pt
+     \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+    \kern-0.4pt\hrule}%
+  \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+\def\key #1{{\setupmarkupstyle{key}\nohyphenation \uppercase{#1}}\null}
+% The old definition, with no lozenge:
+%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+% @file, @option are the same as @samp.
+\let\file=\samp
+\let\option=\samp
+
+% @code is a modification of @t,
+% which makes spaces the same size as normal in the surrounding text.
+\def\tclose#1{%
+  {%
+    % Change normal interword space to be same as for the current font.
+    \spaceskip = \fontdimen2\font
+    %
+    % Switch to typewriter.
+    \tt
+    %
+    % But `\ ' produces the large typewriter interword space.
+    \def\ {{\spaceskip = 0pt{} }}%
+    %
+    % Turn off hyphenation.
+    \nohyphenation
+    %
+    \rawbackslash
+    \plainfrenchspacing
+    #1%
+  }%
+  \null
+}
+
+% We *must* turn on hyphenation at `-' and `_' in @code.
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate at a dash.
+%  -- rms.
+{
+  \catcode`\-=\active \catcode`\_=\active
+  \catcode`\'=\active \catcode`\`=\active
+  \global\let'=\rq \global\let`=\lq  % default definitions
+  %
+  \global\def\code{\begingroup
+    \setupmarkupstyle{code}%
+    % The following should really be moved into \setupmarkupstyle handlers.
+    \catcode\dashChar=\active  \catcode\underChar=\active
+    \ifallowcodebreaks
+     \let-\codedash
+     \let_\codeunder
+    \else
+     \let-\realdash
+     \let_\realunder
+    \fi
+    \codex
+  }
+}
+
+\def\realdash{-}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{%
+  % this is all so @math{@code{var_name}+1} can work.  In math mode, _
+  % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+  % will therefore expand the active definition of _, which is us
+  % (inside @code that is), therefore an endless loop.
+  \ifusingtt{\ifmmode
+               \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+             \else\normalunderscore \fi
+             \discretionary{}{}{}}%
+            {\_}%
+}
+\def\codex #1{\tclose{#1}\endgroup}
+
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__.  This is undesirable in
+% some manuals, especially if they don't have long identifiers in
+% general.  @allowcodebreaks provides a way to control this.
+% 
+\newif\ifallowcodebreaks  \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\keywordtrue
+    \allowcodebreakstrue
+  \else\ifx\txiarg\keywordfalse
+    \allowcodebreaksfalse
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @allowcodebreaks option `\txiarg'}%
+  \fi\fi
+}
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+%   `example' (@kbd uses ttsl only inside of @example and friends),
+%   or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+  \def\txiarg{#1}%
+  \ifx\txiarg\worddistinct
+    \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+  \else\ifx\txiarg\wordexample
+    \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+  \else\ifx\txiarg\wordcode
+    \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @kbdinputstyle option `\txiarg'}%
+  \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct.'
+\kbdinputstyle distinct
+
+\def\xkey{\key}
+\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
+\ifx\one\xkey\ifx\threex\three \key{#2}%
+\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi}
+
+% For @indicateurl, @env, @command quotes seem unnecessary, so use \code.
+\let\indicateurl=\code
+\let\env=\code
+\let\command=\code
+
+% @clicksequence{File @click{} Open ...}
+\def\clicksequence#1{\begingroup #1\endgroup}
+
+% @clickstyle @arrow   (by default)
+\parseargdef\clickstyle{\def\click{#1}}
+\def\click{\arrow}
+
+% @uref (abbreviation for `urlref') takes an optional (comma-separated)
+% second argument specifying the text to display and an optional third
+% arg as text to display instead of (rather than in addition to) the url
+% itself.  First (mandatory) arg is the url.  Perhaps eventually put in
+% a hypertex \special here.
+%
+\def\uref#1{\douref #1,,,\finish}
+\def\douref#1,#2,#3,#4\finish{\begingroup
+  \unsepspaces
+  \pdfurl{#1}%
+  \setbox0 = \hbox{\ignorespaces #3}%
+  \ifdim\wd0 > 0pt
+    \unhbox0 % third arg given, show only that
+  \else
+    \setbox0 = \hbox{\ignorespaces #2}%
+    \ifdim\wd0 > 0pt
+      \ifpdf
+        \unhbox0             % PDF: 2nd arg given, show only it
+      \else
+        \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url
+      \fi
+    \else
+      \code{#1}% only url given, so show it
+    \fi
+  \fi
+  \endlink
+\endgroup}
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
+
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdf
+  \def\email#1{\doemail#1,,\finish}
+  \def\doemail#1,#2,#3\finish{\begingroup
+    \unsepspaces
+    \pdfurl{mailto:#1}%
+    \setbox0 = \hbox{\ignorespaces #2}%
+    \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+    \endlink
+  \endgroup}
+\else
+  \let\email=\uref
+\fi
+
+% Check if we are currently using a typewriter font.  Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Typeset a dimension, e.g., `in' or `pt'.  The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+\def\kbd#1{{\setupmarkupstyle{kbd}\def\look{#1}\expandafter\kbdfoo\look??\par}}
+
+% @l was never documented to mean ``switch to the Lisp font'',
+% and it is not used as such in any manual I can find.  We need it for
+% Polish suppressed-l.  --karl, 22sep96.
+%\def\l#1{{\li #1}\null}
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}}              % roman font
+\def\sc#1{{\smallcaps#1}}       % smallcaps font
+\def\ii#1{{\it #1}}             % italic font
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+% 
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+  {\selectfonts\lsize #1}%
+  \def\temp{#2}%
+  \ifx\temp\empty \else
+    \space ({\unsepspaces \ignorespaces \temp \unskip})%
+  \fi
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+% 
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+  {\plainfrenchspacing #1}%
+  \def\temp{#2}%
+  \ifx\temp\empty \else
+    \space ({\unsepspaces \ignorespaces \temp \unskip})%
+  \fi
+}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+% 
+% Although only regular is the truly official Euro symbol, we ignore
+% that.  The Euro is designed to be slightly taller than the regular
+% font height.
+% 
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+% 
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+% 
+% Also doesn't work in math.  Do we need to do math with euro symbols?
+% Hope not.
+% 
+% 
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+  % We set the font at each command, rather than predefining it in
+  % \textfonts and the other font-switching commands, so that
+  % installations which never need the symbol don't have to have the
+  % font installed.
+  % 
+  % There is only one designed size (nominal 10pt), so we always scale
+  % that to the current nominal size.
+  % 
+  % By the way, simply using "at 1em" works for cmr10 and the like, but
+  % does not work for cmbx10 and other extended/shrunken fonts.
+  % 
+  \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+  %
+  \ifx\curfontstyle\bfstylename 
+    % bold:
+    \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+  \else 
+    % regular:
+    \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+  \fi
+  \thiseurofont
+}
+
+% Hacks for glyphs from the EC fonts similar to \euro.  We don't
+% use \let for the aliases, because sometimes we redefine the original
+% macro, and the alias should reflect the redefinition.
+\def\guillemetleft{{\ecfont \char"13}}
+\def\guillemotleft{\guillemetleft}
+\def\guillemetright{{\ecfont \char"14}}
+\def\guillemotright{\guillemetright}
+\def\guilsinglleft{{\ecfont \char"0E}}
+\def\guilsinglright{{\ecfont \char"0F}}
+\def\quotedblbase{{\ecfont \char"12}}
+\def\quotesinglbase{{\ecfont \char"0D}}
+%
+% This positioning is not perfect (see the ogonek LaTeX package), but
+% we have the precomposed glyphs for the most common cases.  We put the
+% tests to use those glyphs in the single \ogonek macro so we have fewer
+% dummy definitions to worry about for index entries, etc.
+% 
+% ogonek is also used with other letters in Lithuanian (IOU), but using
+% the precomposed glyphs for those is not so easy since they aren't in
+% the same EC font.
+\def\ogonek#1{{%
+  \def\temp{#1}%
+  \ifx\temp\macrocharA\Aogonek
+  \else\ifx\temp\macrochara\aogonek
+  \else\ifx\temp\macrocharE\Eogonek
+  \else\ifx\temp\macrochare\eogonek
+  \else
+    \ecfont \setbox0=\hbox{#1}%
+    \ifdim\ht0=1ex\accent"0C #1%
+    \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}%
+    \fi
+  \fi\fi\fi\fi
+  }%
+}
+\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A}
+\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a}
+\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E}
+\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e}
+%
+\def\ecfont{%
+  % We can't distinguish serif/sans and italic/slanted, but this
+  % is used for crude hacks anyway (like adding French and German
+  % quotes to documents typeset with CM, where we lose kerning), so
+  % hopefully nobody will notice/care.
+  \edef\ecsize{\csname\curfontsize ecsize\endcsname}%
+  \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}%
+  \ifx\curfontstyle\bfstylename
+    % bold:
+    \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize
+  \else
+    % regular:
+    \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize
+  \fi
+  \thisecfont
+}
+
+% @registeredsymbol - R in a circle.  The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+  $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}%
+               \hfil\crcr\Orb}}%
+    }$%
+}
+
+% @textdegree - the normal degrees sign.
+%
+\def\textdegree{$^\circ$}
+
+% Laurent Siebenmann reports \Orb undefined with:
+%  Textures 1.7.7 (preloaded format=plain 93.10.14)  (68K)  16 APR 2004 02:38
+% so we'll define it if necessary.
+% 
+\ifx\Orb\undefined
+\def\Orb{\mathhexbox20D}
+\fi
+
+% Quotes.
+\chardef\quotedblleft="5C
+\chardef\quotedblright=`\"
+\chardef\quoteleft=`\`
+\chardef\quoteright=`\'
+
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page.  Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+% Do an implicit @contents or @shortcontents after @end titlepage if the
+% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
+%
+\newif\ifsetcontentsaftertitlepage
+ \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
+\newif\ifsetshortcontentsaftertitlepage
+ \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
+
+\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+        \endgroup\page\hbox{}\page}
+
+\envdef\titlepage{%
+  % Open one extra group, as we want to close it in the middle of \Etitlepage.
+  \begingroup
+    \parindent=0pt \textfonts
+    % Leave some space at the very top of the page.
+    \vglue\titlepagetopglue
+    % No rule at page bottom unless we print one at the top with @title.
+    \finishedtitlepagetrue
+    %
+    % Most title ``pages'' are actually two pages long, with space
+    % at the top of the second.  We don't want the ragged left on the second.
+    \let\oldpage = \page
+    \def\page{%
+      \iffinishedtitlepage\else
+        \finishtitlepage
+      \fi
+      \let\page = \oldpage
+      \page
+      \null
+    }%
+}
+
+\def\Etitlepage{%
+    \iffinishedtitlepage\else
+       \finishtitlepage
+    \fi
+    % It is important to do the page break before ending the group,
+    % because the headline and footline are only empty inside the group.
+    % If we use the new definition of \page, we always get a blank page
+    % after the title page, which we certainly don't want.
+    \oldpage
+  \endgroup
+  %
+  % Need this before the \...aftertitlepage checks so that if they are
+  % in effect the toc pages will come out with page numbers.
+  \HEADINGSon
+  %
+  % If they want short, they certainly want long too.
+  \ifsetshortcontentsaftertitlepage
+    \shortcontents
+    \contents
+    \global\let\shortcontents = \relax
+    \global\let\contents = \relax
+  \fi
+  %
+  \ifsetcontentsaftertitlepage
+    \contents
+    \global\let\contents = \relax
+    \global\let\shortcontents = \relax
+  \fi
+}
+
+\def\finishtitlepage{%
+  \vskip4pt \hrule height 2pt width \hsize
+  \vskip\titlepagebottomglue
+  \finishedtitlepagetrue
+}
+
+%%% Macros to be used within @titlepage:
+
+\let\subtitlerm=\tenrm
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\parseargdef\title{%
+  \checkenv\titlepage
+  \leftline{\titlefonts\rmisbold #1}
+  % print a rule at the page bottom also.
+  \finishedtitlepagefalse
+  \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+  \checkenv\titlepage
+  {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+  \def\temp{\quotation}%
+  \ifx\thisenv\temp
+    \def\quotationauthor{#1}% printed in \Equotation.
+  \else
+    \checkenv\titlepage
+    \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+    {\secfonts\rmisbold \leftline{#1}}%
+  \fi
+}
+
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks\evenheadline    % headline on even pages
+\newtoks\oddheadline     % headline on odd pages
+\newtoks\evenfootline    % footline on even pages
+\newtoks\oddfootline     % footline on odd pages
+
+% Now make TeX use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+                            \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+                            \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what  @headings on  does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\evenheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\oddheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\evenfootingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+  \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+  %
+  % Leave some space for the footline.  Hopefully ok to assume
+  % @evenfooting will not be used by itself.
+  \global\advance\pageheight by -12pt
+  \global\advance\vsize by -12pt
+}
+
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
+
+% @evenheadingmarks top     \thischapter <- chapter at the top of a page
+% @evenheadingmarks bottom  \thischapter <- chapter at the bottom of a page
+%
+% The same set of arguments for:
+%
+% @oddheadingmarks
+% @evenfootingmarks
+% @oddfootingmarks
+% @everyheadingmarks
+% @everyfootingmarks
+
+\def\evenheadingmarks{\headingmarks{even}{heading}}
+\def\oddheadingmarks{\headingmarks{odd}{heading}}
+\def\evenfootingmarks{\headingmarks{even}{footing}}
+\def\oddfootingmarks{\headingmarks{odd}{footing}}
+\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1}
+                          \headingmarks{odd}{heading}{#1} }
+\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1}
+                          \headingmarks{odd}{footing}{#1} }
+% #1 = even/odd, #2 = heading/footing, #3 = top/bottom.
+\def\headingmarks#1#2#3 {%
+  \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname
+  \global\expandafter\let\csname get#1#2marks\endcsname \temp
+}
+
+\everyheadingmarks bottom
+\everyfootingmarks bottom
+
+% @headings double      turns headings on for double-sided printing.
+% @headings single      turns headings on for single-sided printing.
+% @headings off         turns them off.
+% @headings on          same as @headings double, retained for compatibility.
+% @headings after       turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{%
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+\let\contentsalignmacro = \chappager
+
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+
+% Subroutines used in generating headings
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\undefined
+\def\today{%
+  \number\day\space
+  \ifcase\month
+  \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+  \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+  \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+  \fi
+  \space\number\year}
+\fi
+
+% @settitle line...  specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
+
+
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent  \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin  \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\itemzzz #1{\begingroup %
+  \advance\hsize by -\rightskip
+  \advance\hsize by -\tableindent
+  \setbox0=\hbox{\itemindicate{#1}}%
+  \itemindex{#1}%
+  \nobreak % This prevents a break before @itemx.
+  %
+  % If the item text does not fit in the space we have, put it on a line
+  % by itself, and do not allow a page break either before or after that
+  % line.  We do not start a paragraph here because then if the next
+  % command is, e.g., @kindex, the whatsit would get put into the
+  % horizontal list on a line by itself, resulting in extra blank space.
+  \ifdim \wd0>\itemmax
+    %
+    % Make this a paragraph so we get the \parskip glue and wrapping,
+    % but leave it ragged-right.
+    \begingroup
+      \advance\leftskip by-\tableindent
+      \advance\hsize by\tableindent
+      \advance\rightskip by0pt plus1fil
+      \leavevmode\unhbox0\par
+    \endgroup
+    %
+    % We're going to be starting a paragraph, but we don't want the
+    % \parskip glue -- logically it's part of the @item we just started.
+    \nobreak \vskip-\parskip
+    %
+    % Stop a page break at the \parskip glue coming up.  However, if
+    % what follows is an environment such as @example, there will be no
+    % \parskip glue; then the negative vskip we just inserted would
+    % cause the example and the item to crash together.  So we use this
+    % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+    % \parskip glue after all.  Section titles are handled this way also.
+    % 
+    \penalty 10001
+    \endgroup
+    \itemxneedsnegativevskipfalse
+  \else
+    % The item text fits into the space.  Start a paragraph, so that the
+    % following text (if any) will end up on the same line.
+    \noindent
+    % Do this with kerns and \unhbox so that if there is a footnote in
+    % the item text, it can migrate to the main vertical list and
+    % eventually be printed.
+    \nobreak\kern-\tableindent
+    \dimen0 = \itemmax  \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+    \unhbox0
+    \nobreak\kern\dimen0
+    \endgroup
+    \itemxneedsnegativevskiptrue
+  \fi
+}
+
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
+
+% @table, @ftable, @vtable.
+\envdef\table{%
+  \let\itemindex\gobble
+  \tablecheck{table}%
+}
+\envdef\ftable{%
+  \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+  \tablecheck{ftable}%
+}
+\envdef\vtable{%
+  \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+  \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+  \ifnum \the\catcode`\^^M=\active
+    \endgroup
+    \errmessage{This command won't work in this context; perhaps the problem is
+      that we are \inenvironment\thisenv}%
+    \def\next{\doignore{#1}}%
+  \else
+    \let\next\tablex
+  \fi
+  \next
+}
+\def\tablex#1{%
+  \def\itemindicate{#1}%
+  \parsearg\tabley
+}
+\def\tabley#1{%
+  {%
+    \makevalueexpandable
+    \edef\temp{\noexpand\tablez #1\space\space\space}%
+    \expandafter
+  }\temp \endtablez
+}
+\def\tablez #1 #2 #3 #4\endtablez{%
+  \aboveenvbreak
+  \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+  \ifnum 0#2>0 \tableindent=#2\mil \fi
+  \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+  \itemmax=\tableindent
+  \advance \itemmax by -\itemmargin
+  \advance \leftskip by \tableindent
+  \exdentamount=\tableindent
+  \parindent = 0pt
+  \parskip = \smallskipamount
+  \ifdim \parskip=0pt \parskip=2pt \fi
+  \let\item = \internalBitem
+  \let\itemx = \internalBitemx
+}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\envdef\itemize{\parsearg\doitemize}
+
+\def\doitemize#1{%
+  \aboveenvbreak
+  \itemmax=\itemindent
+  \advance\itemmax by -\itemmargin
+  \advance\leftskip by \itemindent
+  \exdentamount=\itemindent
+  \parindent=0pt
+  \parskip=\smallskipamount
+  \ifdim\parskip=0pt \parskip=2pt \fi
+  \def\itemcontents{#1}%
+  % @itemize with no arg is equivalent to @itemize @bullet.
+  \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+  \let\item=\itemizeitem
+}
+
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+  \advance\itemno by 1  % for enumerations
+  {\let\par=\endgraf \smallbreak}% reasonable place to break
+  {%
+   % If the document has an @itemize directly after a section title, a
+   % \nobreak will be last on the list, and \sectionheading will have
+   % done a \vskip-\parskip.  In that case, we don't want to zero
+   % parskip, or the item text will crash with the heading.  On the
+   % other hand, when there is normal text preceding the item (as there
+   % usually is), we do want to zero parskip, or there would be too much
+   % space.  In that case, we won't have a \nobreak before.  At least
+   % that's the theory.
+   \ifnum\lastpenalty<10000 \parskip=0in \fi
+   \noindent
+   \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+   \vadjust{\penalty 1200}}% not good to break after first line of item.
+  \flushcr
+}
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list.  No
+% argument is the same as `1'.
+%
+\envparseargdef\enumerate{\enumeratey #1  \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+  % If we were given no argument, pretend we were given `1'.
+  \def\thearg{#1}%
+  \ifx\thearg\empty \def\thearg{1}\fi
+  %
+  % Detect if the argument is a single token.  If so, it might be a
+  % letter.  Otherwise, the only valid thing it can be is a number.
+  % (We will always have one token, because of the test we just made.
+  % This is a good thing, since \splitoff doesn't work given nothing at
+  % all -- the first parameter is undelimited.)
+  \expandafter\splitoff\thearg\endmark
+  \ifx\rest\empty
+    % Only one token in the argument.  It could still be anything.
+    % A ``lowercase letter'' is one whose \lccode is nonzero.
+    % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+    %   not equal to itself.
+    % Otherwise, we assume it's a number.
+    %
+    % We need the \relax at the end of the \ifnum lines to stop TeX from
+    % continuing to look for a <number>.
+    %
+    \ifnum\lccode\expandafter`\thearg=0\relax
+      \numericenumerate % a number (we hope)
+    \else
+      % It's a letter.
+      \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+        \lowercaseenumerate % lowercase letter
+      \else
+        \uppercaseenumerate % uppercase letter
+      \fi
+    \fi
+  \else
+    % Multiple tokens in the argument.  We hope it's a number.
+    \numericenumerate
+  \fi
+}
+
+% An @enumerate whose labels are integers.  The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+  \itemno = \thearg
+  \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+  \itemno = \expandafter`\thearg
+  \startenumeration{%
+    % Be sure we're not beyond the end of the alphabet.
+    \ifnum\itemno=0
+      \errmessage{No more lowercase letters in @enumerate; get a bigger
+                  alphabet}%
+    \fi
+    \char\lccode\itemno
+  }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+  \itemno = \expandafter`\thearg
+  \startenumeration{%
+    % Be sure we're not beyond the end of the alphabet.
+    \ifnum\itemno=0
+      \errmessage{No more uppercase letters in @enumerate; get a bigger
+                  alphabet}
+    \fi
+    \char\uccode\itemno
+  }%
+}
+
+% Call \doitemize, adding a period to the first argument and supplying the
+% common last two arguments.  Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+  \advance\itemno by -1
+  \doitemize{#1.}\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble.  Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+%   @multitable @columnfractions .25 .3 .45
+%   @item ...
+%
+%   Numbers following @columnfractions are the percent of the total
+%   current hsize to be used for each column. You may use as many
+%   columns as desired.
+
+
+% Or use a template:
+%   @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+%   @item ...
+%   using the widest term desired in each column.
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab do not need to be on their own lines, but it will not hurt
+% if they are.
+
+% Sample multitable:
+
+%   @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+%   @item first col stuff @tab second col stuff @tab third col
+%   @item
+%   first col stuff
+%   @tab
+%   second col stuff
+%   @tab
+%   third col
+%   @item first col stuff @tab second col stuff
+%   @tab Many paragraphs of text may be used in any column.
+%
+%         They will wrap at the width determined by the template.
+%   @item@tab@tab This will be in third column.
+%   @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+%                                                            to baseline.
+%   0pt means it depends on current normal line spacing.
+%
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+% Macros used to set up halign preamble:
+%
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1.  We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+  \global\advance\colcount by 1
+  \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}%
+  \setuptable
+}
+
+\newcount\colcount
+\def\setuptable#1{%
+  \def\firstarg{#1}%
+  \ifx\firstarg\xendsetuptable
+    \let\go = \relax
+  \else
+    \ifx\firstarg\xcolumnfractions
+      \global\setpercenttrue
+    \else
+      \ifsetpercent
+         \let\go\pickupwholefraction
+      \else
+         \global\advance\colcount by 1
+         \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a
+                   % separator; typically that is always in the input, anyway.
+         \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+      \fi
+    \fi
+    \ifx\go\pickupwholefraction
+      % Put the argument back for the \pickupwholefraction call, so
+      % we'll always have a period there to be parsed.
+      \def\go{\pickupwholefraction#1}%
+    \else
+      \let\go = \setuptable
+    \fi%
+  \fi
+  \go
+}
+
+% multitable-only commands.
+%
+% @headitem starts a heading row, which we typeset in bold.
+% Assignments have to be global since we are inside the implicit group
+% of an alignment entry.  Note that \everycr resets \everytab.
+\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}%
+%
+% A \tab used to include \hskip1sp.  But then the space in a template
+% line is not enough.  That is bad.  So let's go back to just `&' until
+% we encounter the problem it was intended to solve again.
+%                                      --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab  % insert after every tab.
+%
+\envdef\multitable{%
+  \vskip\parskip
+  \startsavinginserts
+  %
+  % @item within a multitable starts a normal row.
+  % We use \def instead of \let so that if one of the multitable entries
+  % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+  % \endtemplate) expanding \doitemize.
+  \def\item{\crcr}%
+  %
+  \tolerance=9500
+  \hbadness=9500
+  \setmultitablespacing
+  \parskip=\multitableparskip
+  \parindent=\multitableparindent
+  \overfullrule=0pt
+  \global\colcount=0
+  %
+  \everycr = {%
+    \noalign{%
+      \global\everytab={}%
+      \global\colcount=0 % Reset the column counter.
+      % Check for saved footnotes, etc.
+      \checkinserts
+      % Keeps underfull box messages off when table breaks over pages.
+      %\filbreak
+       % Maybe so, but it also creates really weird page breaks when the
+       % table breaks over pages. Wouldn't \vfil be better?  Wait until the
+       % problem manifests itself, so it can be fixed for real --karl.
+    }%
+  }%
+  %
+  \parsearg\domultitable
+}
+\def\domultitable#1{%
+  % To parse everything between @multitable and @item:
+  \setuptable#1 \endsetuptable
+  %
+  % This preamble sets up a generic column definition, which will
+  % be used as many times as user calls for columns.
+  % \vtop will set a single line and will also let text wrap and
+  % continue for many paragraphs if desired.
+  \halign\bgroup &%
+    \global\advance\colcount by 1
+    \multistrut
+    \vtop{%
+      % Use the current \colcount to find the correct column width:
+      \hsize=\expandafter\csname col\the\colcount\endcsname
+      %
+      % In order to keep entries from bumping into each other
+      % we will add a \leftskip of \multitablecolspace to all columns after
+      % the first one.
+      %
+      % If a template has been used, we will add \multitablecolspace
+      % to the width of each template entry.
+      %
+      % If the user has set preamble in terms of percent of \hsize we will
+      % use that dimension as the width of the column, and the \leftskip
+      % will keep entries from bumping into each other.  Table will start at
+      % left margin and final column will justify at right margin.
+      %
+      % Make sure we don't inherit \rightskip from the outer environment.
+      \rightskip=0pt
+      \ifnum\colcount=1
+       % The first column will be indented with the surrounding text.
+       \advance\hsize by\leftskip
+      \else
+       \ifsetpercent \else
+         % If user has not set preamble in terms of percent of \hsize
+         % we will advance \hsize by \multitablecolspace.
+         \advance\hsize by \multitablecolspace
+       \fi
+       % In either case we will make \leftskip=\multitablecolspace:
+      \leftskip=\multitablecolspace
+      \fi
+      % Ignoring space at the beginning and end avoids an occasional spurious
+      % blank line, when TeX decides to break the line at the space before the
+      % box from the multistrut, so the strut ends up on a line by itself.
+      % For example:
+      % @multitable @columnfractions .11 .89
+      % @item @code{#}
+      % @tab Legal holiday which is valid in major parts of the whole country.
+      % Is automatically provided with highlighting sequences respectively
+      % marking characters.
+      \noindent\ignorespaces##\unskip\multistrut
+    }\cr
+}
+\def\Emultitable{%
+  \crcr
+  \egroup % end the \halign
+  \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+  \def\multistrut{\strut}% just use the standard line spacing
+  %
+  % Compute \multitablelinespace (if not defined by user) for use in
+  % \multitableparskip calculation.  We used define \multistrut based on
+  % this, but (ironically) that caused the spacing to be off.
+  % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
+\fi
+%% Test to see if parskip is larger than space between lines of
+%% table. If not, do nothing.
+%%        If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+                                      %% than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+                                      %% than skip between lines in the table.
+\fi}
+
+
+\message{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed.  They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested.  But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+  \expandafter\let\csname #1\endcsname = \relax
+  \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+  % Scan in ``verbatim'' mode:
+  \obeylines
+  \catcode`\@ = \other
+  \catcode`\{ = \other
+  \catcode`\} = \other
+  %
+  % Make sure that spaces turn into tokens that match what \doignoretext wants.
+  \spaceisspace
+  %
+  % Count number of #1's that we've seen.
+  \doignorecount = 0
+  %
+  % Swallow text until we reach the matching `@end #1'.
+  \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+  \obeylines %
+  %
+  \gdef\dodoignore#1{%
+    % #1 contains the command name as a string, e.g., `ifinfo'.
+    %
+    % Define a command to find the next `@end #1'.
+    \long\def\doignoretext##1^^M@end #1{%
+      \doignoretextyyy##1^^M@#1\_STOP_}%
+    %
+    % And this command to find another #1 command, at the beginning of a
+    % line.  (Otherwise, we would consider a line `@c @ifset', for
+    % example, to count as an @ifset for nesting.)
+    \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+    %
+    % And now expand that command.
+    \doignoretext ^^M%
+  }%
+}
+
+\def\doignoreyyy#1{%
+  \def\temp{#1}%
+  \ifx\temp\empty                      % Nothing found.
+    \let\next\doignoretextzzz
+  \else                                        % Found a nested condition, ...
+    \advance\doignorecount by 1
+    \let\next\doignoretextyyy          % ..., look for another.
+    % If we're here, #1 ends with ^^M\ifinfo (for example).
+  \fi
+  \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+  \ifnum\doignorecount = 0     % We have just found the outermost @end.
+    \let\next\enddoignore
+  \else                                % Still inside a nested condition.
+    \advance\doignorecount by -1
+    \let\next\doignoretext      % Look for the next @end.
+  \fi
+  \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+  % Ignore anything after the last `@end #1'; this matters in verbatim
+  % environments, where otherwise the newline after an ignored conditional
+  % would result in a blank line in the output.
+  \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+  {%
+    \makevalueexpandable
+    \def\temp{#2}%
+    \edef\next{\gdef\makecsname{SET#1}}%
+    \ifx\temp\empty
+      \next{}%
+    \else
+      \setzzz#2\endsetzzz
+    \fi
+  }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+  {%
+    \makevalueexpandable
+    \global\expandafter\let\csname SET#1\endcsname=\relax
+  }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+  \catcode`\- = \active \catcode`\_ = \active
+  %
+  \gdef\makevalueexpandable{%
+    \let\value = \expandablevalue
+    % We don't want these characters active, ...
+    \catcode`\-=\other \catcode`\_=\other
+    % ..., but we might end up with active ones in the argument if
+    % we're called from @code, as @code{@value{foo-bar_}}, though.
+    % So \let them to their normal equivalents.
+    \let-\realdash \let_\normalunderscore
+  }
+}
+
+% We have this subroutine so that we can handle at least some @value's
+% properly in indexes (we call \makevalueexpandable in \indexdummies).
+% The command has to be fully expandable (if the variable is set), since
+% the result winds up in the index file.  This means that if the
+% variable's value contains other Texinfo commands, it's almost certain
+% it will fail (although perhaps we could fix that with sufficient work
+% to do a one-level expansion on the result, instead of complete).
+%
+\def\expandablevalue#1{%
+  \expandafter\ifx\csname SET#1\endcsname\relax
+    {[No value for ``#1'']}%
+    \message{Variable `#1', used in @value, is not set.}%
+  \else
+    \csname SET#1\endcsname
+  \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+% To get special treatment of `@end ifset,' call \makeond and the redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+  {%
+    \makevalueexpandable
+    \let\next=\empty
+    \expandafter\ifx\csname SET#2\endcsname\relax
+      #1% If not set, redefine \next.
+    \fi
+    \expandafter
+  }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @dircategory CATEGORY  -- specify a category of the dir file
+% which this file should belong to.  Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
+
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index.  The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+%
+\def\newindex#1{%
+  \iflinks
+    \expandafter\newwrite \csname#1indfile\endcsname
+    \openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+  \fi
+  \expandafter\xdef\csname#1index\endcsname{%     % Define @#1index
+    \noexpand\doindex{#1}}
+}
+
+% @defindex foo  ==  \newindex{foo}
+%
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+%
+\def\defcodeindex{\parsearg\newcodeindex}
+%
+\def\newcodeindex#1{%
+  \iflinks
+    \expandafter\newwrite \csname#1indfile\endcsname
+    \openout \csname#1indfile\endcsname \jobname.#1
+  \fi
+  \expandafter\xdef\csname#1index\endcsname{%
+    \noexpand\docodeindex{#1}}%
+}
+
+
+% @synindex foo bar    makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+%
+% @syncodeindex foo bar   similar, but put all entries made for index foo
+% inside @code.
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+  % Only do \closeout if we haven't already done it, else we'll end up
+  % closing the target index.
+  \expandafter \ifx\csname donesynindex#2\endcsname \relax
+    % The \closeout helps reduce unnecessary open files; the limit on the
+    % Acorn RISC OS is a mere 16 files.
+    \expandafter\closeout\csname#2indfile\endcsname
+    \expandafter\let\csname donesynindex#2\endcsname = 1
+  \fi
+  % redefine \fooindfile:
+  \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+  \expandafter\let\csname#2indfile\endcsname=\temp
+  % redefine \fooindex:
+  \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+%  and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+% Take care of Texinfo commands that can appear in an index entry.
+% Since there are some commands we want to expand, and others we don't,
+% we have to laboriously prevent expansion for those that we don't.
+%
+\def\indexdummies{%
+  \escapechar = `\\     % use backslash in output files.
+  \def\@{@}% change to @@ when we switch to @ as escape char in index files.
+  \def\ {\realbackslash\space }%
+  %
+  % Need these in case \tex is in effect and \{ is a \delimiter again.
+  % But can't use \lbracecmd and \rbracecmd because texindex assumes
+  % braces and backslashes are used only as delimiters.
+  \let\{ = \mylbrace
+  \let\} = \myrbrace
+  %
+  % I don't entirely understand this, but when an index entry is
+  % generated from a macro call, the \endinput which \scanmacro inserts
+  % causes processing to be prematurely terminated.  This is,
+  % apparently, because \indexsorttmp is fully expanded, and \endinput
+  % is an expandable command.  The redefinition below makes \endinput
+  % disappear altogether for that purpose -- although logging shows that
+  % processing continues to some further point.  On the other hand, it
+  % seems \endinput does not hurt in the printed index arg, since that
+  % is still getting written without apparent harm.
+  % 
+  % Sample source (mac-idx3.tex, reported by Graham Percival to
+  % help-texinfo, 22may06):
+  % @macro funindex {WORD}
+  % @findex xyz
+  % @end macro
+  % ...
+  % @funindex commtest
+  % 
+  % The above is not enough to reproduce the bug, but it gives the flavor.
+  % 
+  % Sample whatsit resulting:
+  % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}}
+  % 
+  % So:
+  \let\endinput = \empty
+  %
+  % Do the redefinitions.
+  \commondummies
+}
+
+% For the aux and toc files, @ is the escape character.  So we want to
+% redefine everything using @ as the escape character (instead of
+% \realbackslash, still used for index files).  When everything uses @,
+% this will be simpler.
+%
+\def\atdummies{%
+  \def\@{@@}%
+  \def\ {@ }%
+  \let\{ = \lbraceatcmd
+  \let\} = \rbraceatcmd
+  %
+  % Do the redefinitions.
+  \commondummies
+  \otherbackslash
+}
+
+% Called from \indexdummies and \atdummies.
+%
+\def\commondummies{%
+  %
+  % \definedummyword defines \#1 as \string\#1\space, thus effectively
+  % preventing its expansion.  This is used only for control% words,
+  % not control letters, because the \space would be incorrect for
+  % control characters, but is needed to separate the control word
+  % from whatever follows.
+  %
+  % For control letters, we have \definedummyletter, which omits the
+  % space.
+  %
+  % These can be used both for control words that take an argument and
+  % those that do not.  If it is followed by {arg} in the input, then
+  % that will dutifully get written to the index (or wherever).
+  %
+  \def\definedummyword  ##1{\def##1{\string##1\space}}%
+  \def\definedummyletter##1{\def##1{\string##1}}%
+  \let\definedummyaccent\definedummyletter
+  %
+  \commondummiesnofonts
+  %
+  \definedummyletter\_%
+  %
+  % Non-English letters.
+  \definedummyword\AA
+  \definedummyword\AE
+  \definedummyword\L
+  \definedummyword\OE
+  \definedummyword\O
+  \definedummyword\aa
+  \definedummyword\ae
+  \definedummyword\l
+  \definedummyword\oe
+  \definedummyword\o
+  \definedummyword\ss
+  \definedummyword\exclamdown
+  \definedummyword\questiondown
+  \definedummyword\ordf
+  \definedummyword\ordm
+  %
+  % Although these internal commands shouldn't show up, sometimes they do.
+  \definedummyword\bf
+  \definedummyword\gtr
+  \definedummyword\hat
+  \definedummyword\less
+  \definedummyword\sf
+  \definedummyword\sl
+  \definedummyword\tclose
+  \definedummyword\tt
+  %
+  \definedummyword\LaTeX
+  \definedummyword\TeX
+  %
+  % Assorted special characters.
+  \definedummyword\bullet
+  \definedummyword\comma
+  \definedummyword\copyright
+  \definedummyword\registeredsymbol
+  \definedummyword\dots
+  \definedummyword\enddots
+  \definedummyword\equiv
+  \definedummyword\error
+  \definedummyword\euro
+  \definedummyword\guillemetleft
+  \definedummyword\guillemetright
+  \definedummyword\guilsinglleft
+  \definedummyword\guilsinglright
+  \definedummyword\expansion
+  \definedummyword\minus
+  \definedummyword\ogonek
+  \definedummyword\pounds
+  \definedummyword\point
+  \definedummyword\print
+  \definedummyword\quotedblbase
+  \definedummyword\quotedblleft
+  \definedummyword\quotedblright
+  \definedummyword\quoteleft
+  \definedummyword\quoteright
+  \definedummyword\quotesinglbase
+  \definedummyword\result
+  \definedummyword\textdegree
+  %
+  % We want to disable all macros so that they are not expanded by \write.
+  \macrolist
+  %
+  \normalturnoffactive
+  %
+  % Handle some cases of @value -- where it does not contain any
+  % (non-fully-expandable) commands.
+  \makevalueexpandable
+}
+
+% \commondummiesnofonts: common to \commondummies and \indexnofonts.
+%
+\def\commondummiesnofonts{%
+  % Control letters and accents.
+  \definedummyletter\!%
+  \definedummyaccent\"%
+  \definedummyaccent\'%
+  \definedummyletter\*%
+  \definedummyaccent\,%
+  \definedummyletter\.%
+  \definedummyletter\/%
+  \definedummyletter\:%
+  \definedummyaccent\=%
+  \definedummyletter\?%
+  \definedummyaccent\^%
+  \definedummyaccent\`%
+  \definedummyaccent\~%
+  \definedummyword\u
+  \definedummyword\v
+  \definedummyword\H
+  \definedummyword\dotaccent
+  \definedummyword\ogonek
+  \definedummyword\ringaccent
+  \definedummyword\tieaccent
+  \definedummyword\ubaraccent
+  \definedummyword\udotaccent
+  \definedummyword\dotless
+  %
+  % Texinfo font commands.
+  \definedummyword\b
+  \definedummyword\i
+  \definedummyword\r
+  \definedummyword\sc
+  \definedummyword\t
+  %
+  % Commands that take arguments.
+  \definedummyword\acronym
+  \definedummyword\cite
+  \definedummyword\code
+  \definedummyword\command
+  \definedummyword\dfn
+  \definedummyword\emph
+  \definedummyword\env
+  \definedummyword\file
+  \definedummyword\kbd
+  \definedummyword\key
+  \definedummyword\math
+  \definedummyword\option
+  \definedummyword\pxref
+  \definedummyword\ref
+  \definedummyword\samp
+  \definedummyword\strong
+  \definedummyword\tie
+  \definedummyword\uref
+  \definedummyword\url
+  \definedummyword\var
+  \definedummyword\verb
+  \definedummyword\w
+  \definedummyword\xref
+}
+
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names.  It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+  % Accent commands should become @asis.
+  \def\definedummyaccent##1{\let##1\asis}%
+  % We can just ignore other control letters.
+  \def\definedummyletter##1{\let##1\empty}%
+  % Hopefully, all control words can become @asis.
+  \let\definedummyword\definedummyaccent
+  %
+  \commondummiesnofonts
+  %
+  % Don't no-op \tt, since it isn't a user-level command
+  % and is used in the definitions of the active chars like <, >, |, etc.
+  % Likewise with the other plain tex font commands.
+  %\let\tt=\asis
+  %
+  \def\ { }%
+  \def\@{@}%
+  % how to handle braces?
+  \def\_{\normalunderscore}%
+  %
+  % Non-English letters.
+  \def\AA{AA}%
+  \def\AE{AE}%
+  \def\L{L}%
+  \def\OE{OE}%
+  \def\O{O}%
+  \def\aa{aa}%
+  \def\ae{ae}%
+  \def\l{l}%
+  \def\oe{oe}%
+  \def\o{o}%
+  \def\ss{ss}%
+  \def\exclamdown{!}%
+  \def\questiondown{?}%
+  \def\ordf{a}%
+  \def\ordm{o}%
+  %
+  \def\LaTeX{LaTeX}%
+  \def\TeX{TeX}%
+  %
+  % Assorted special characters.
+  % (The following {} will end up in the sort string, but that's ok.)
+  \def\bullet{bullet}%
+  \def\comma{,}%
+  \def\copyright{copyright}%
+  \def\registeredsymbol{R}%
+  \def\dots{...}%
+  \def\enddots{...}%
+  \def\equiv{==}%
+  \def\error{error}%
+  \def\euro{euro}%
+  \def\guillemetleft{<<}%
+  \def\guillemetright{>>}%
+  \def\guilsinglleft{<}%
+  \def\guilsinglright{>}%
+  \def\expansion{==>}%
+  \def\minus{-}%
+  \def\pounds{pounds}%
+  \def\point{.}%
+  \def\print{-|}%
+  \def\quotedblbase{"}%
+  \def\quotedblleft{"}%
+  \def\quotedblright{"}%
+  \def\quoteleft{`}%
+  \def\quoteright{'}%
+  \def\quotesinglbase{,}%
+  \def\result{=>}%
+  \def\textdegree{degrees}%
+  %
+  % We need to get rid of all macros, leaving only the arguments (if present).
+  % Of course this is not nearly correct, but it is the best we can do for now.
+  % makeinfo does not expand macros in the argument to @deffn, which ends up
+  % writing an index entry, and texindex isn't prepared for an index sort entry
+  % that starts with \.
+  % 
+  % Since macro invocations are followed by braces, we can just redefine them
+  % to take a single TeX argument.  The case of a macro invocation that
+  % goes to end-of-line is not handled.
+  % 
+  \macrolist
+}
+
+\let\indexbackslash=0  %overridden during \printindex.
+\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
+
+% Most index entries go through here, but \dosubind is the general case.
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{\dosubind{#1}{#2}{}}
+
+% Workhorse for all \fooindexes.
+% #1 is name of index, #2 is stuff to put there, #3 is subentry --
+% empty if called from \doind, as we usually are (the main exception
+% is with most defuns, which call us directly).
+%
+\def\dosubind#1#2#3{%
+  \iflinks
+  {%
+    % Store the main index entry text (including the third arg).
+    \toks0 = {#2}%
+    % If third arg is present, precede it with a space.
+    \def\thirdarg{#3}%
+    \ifx\thirdarg\empty \else
+      \toks0 = \expandafter{\the\toks0 \space #3}%
+    \fi
+    %
+    \edef\writeto{\csname#1indfile\endcsname}%
+    %
+    \safewhatsit\dosubindwrite
+  }%
+  \fi
+}
+
+% Write the entry in \toks0 to the index file:
+%
+\def\dosubindwrite{%
+  % Put the index entry in the margin if desired.
+  \ifx\SETmarginindex\relax\else
+    \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
+  \fi
+  %
+  % Remember, we are within a group.
+  \indexdummies % Must do this here, since \bf, etc expand at this stage
+  \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now
+      % so it will be output as is; and it will print as backslash.
+  %
+  % Process the index entry with all font commands turned off, to
+  % get the string to sort by.
+  {\indexnofonts
+   \edef\temp{\the\toks0}% need full expansion
+   \xdef\indexsorttmp{\temp}%
+  }%
+  %
+  % Set up the complete index entry, with both the sort key and
+  % the original text, including any font commands.  We write
+  % three arguments to \entry to the .?? file (four in the
+  % subentry case), texindex reduces to two when writing the .??s
+  % sorted result.
+  \edef\temp{%
+    \write\writeto{%
+      \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}%
+  }%
+  \temp
+}
+
+% Take care of unwanted page breaks/skips around a whatsit:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again.  Otherwise, the whatsit generated by the
+% \write or \pdfdest will make \lastskip zero.  The result is that
+% sequences like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode.  We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip.  \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip.  The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+\newskip\whatsitskip
+\newcount\whatsitpenalty
+%
+% ..., ready, GO:
+%
+\def\safewhatsit#1{%
+\ifhmode
+  #1%
+\else
+  % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+  \whatsitskip = \lastskip
+  \edef\lastskipmacro{\the\lastskip}%
+  \whatsitpenalty = \lastpenalty
+  %
+  % If \lastskip is nonzero, that means the last item was a
+  % skip.  And since a skip is discardable, that means this
+  % -\whatsitskip glue we're inserting is preceded by a
+  % non-discardable item, therefore it is not a potential
+  % breakpoint, therefore no \nobreak needed.
+  \ifx\lastskipmacro\zeroskipmacro
+  \else
+    \vskip-\whatsitskip
+  \fi
+  %
+  #1%
+  %
+  \ifx\lastskipmacro\zeroskipmacro
+    % If \lastskip was zero, perhaps the last item was a penalty, and
+    % perhaps it was >=10000, e.g., a \nobreak.  In that case, we want
+    % to re-insert the same penalty (values >10000 are used for various
+    % signals); since we just inserted a non-discardable item, any
+    % following glue (such as a \parskip) would be a breakpoint.  For example:
+    % 
+    %   @deffn deffn-whatever
+    %   @vindex index-whatever
+    %   Description.
+    % would allow a break between the index-whatever whatsit
+    % and the "Description." paragraph.
+    \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi
+  \else
+    % On the other hand, if we had a nonzero \lastskip,
+    % this make-up glue would be preceded by a non-discardable item
+    % (the whatsit from the \write), so we must insert a \nobreak.
+    \nobreak\vskip\whatsitskip
+  \fi
+\fi
+}
+
+% The index entry written in the file actually looks like
+%  \entry {sortstring}{page}{topic}
+% or
+%  \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+%  \initial {c}
+%     before the first topic whose initial is c
+%  \entry {topic}{pagelist}
+%     for a topic that is used without subtopics
+%  \primary {topic}
+%     for the beginning of a topic that is used with subtopics
+%  \secondary {subtopic}{pagelist}
+%     for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+  \dobreak \chapheadingskip{10000}%
+  %
+  \smallfonts \rm
+  \tolerance = 9500
+  \plainfrenchspacing
+  \everypar = {}% don't want the \kern\-parindent from indentation suppression.
+  %
+  % See if the index file exists and is nonempty.
+  % Change catcode of @ here so that if the index file contains
+  % \initial {@}
+  % as its first line, TeX doesn't complain about mismatched braces
+  % (because it thinks @} is a control sequence).
+  \catcode`\@ = 11
+  \openin 1 \jobname.#1s
+  \ifeof 1
+    % \enddoublecolumns gets confused if there is no text in the index,
+    % and it loses the chapter title and the aux file entries for the
+    % index.  The easiest way to prevent this problem is to make sure
+    % there is some text.
+    \putwordIndexNonexistent
+  \else
+    %
+    % If the index file exists but is empty, then \openin leaves \ifeof
+    % false.  We have to make TeX try to read something from the file, so
+    % it can discover if there is anything in it.
+    \read 1 to \temp
+    \ifeof 1
+      \putwordIndexIsEmpty
+    \else
+      % Index files are almost Texinfo source, but we use \ as the escape
+      % character.  It would be better to use @, but that's too big a change
+      % to make right now.
+      \def\indexbackslash{\backslashcurfont}%
+      \catcode`\\ = 0
+      \escapechar = `\\
+      \begindoublecolumns
+      \input \jobname.#1s
+      \enddoublecolumns
+    \fi
+  \fi
+  \closein 1
+\endgroup}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+\def\initial#1{{%
+  % Some minor font changes for the special characters.
+  \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+  %
+  % Remove any glue we may have, we'll be inserting our own.
+  \removelastskip
+  %
+  % We like breaks before the index initials, so insert a bonus.
+  \nobreak
+  \vskip 0pt plus 3\baselineskip
+  \penalty 0
+  \vskip 0pt plus -3\baselineskip
+  %
+  % Typeset the initial.  Making this add up to a whole number of
+  % baselineskips increases the chance of the dots lining up from column
+  % to column.  It still won't often be perfect, because of the stretch
+  % we need before each entry, but it's better.
+  %
+  % No shrink because it confuses \balancecolumns.
+  \vskip 1.67\baselineskip plus .5\baselineskip
+  \leftline{\secbf #1}%
+  % Do our best not to break after the initial.
+  \nobreak
+  \vskip .33\baselineskip plus .1\baselineskip
+}}
+
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin.  It is used for index
+% and table of contents entries.  The paragraph is indented by \leftskip.
+%
+% A straightforward implementation would start like this:
+%      \def\entry#1#2{...
+% But this freezes the catcodes in the argument, and can cause problems to
+% @code, which sets - active.  This problem was fixed by a kludge---
+% ``-'' was active throughout whole index, but this isn't really right.
+%
+% The right solution is to prevent \entry from swallowing the whole text.
+%                                 --kasal, 21nov03
+\def\entry{%
+  \begingroup
+    %
+    % Start a new paragraph if necessary, so our assignments below can't
+    % affect previous text.
+    \par
+    %
+    % Do not fill out the last line with white space.
+    \parfillskip = 0in
+    %
+    % No extra space above this paragraph.
+    \parskip = 0in
+    %
+    % Do not prefer a separate line ending with a hyphen to fewer lines.
+    \finalhyphendemerits = 0
+    %
+    % \hangindent is only relevant when the entry text and page number
+    % don't both fit on one line.  In that case, bob suggests starting the
+    % dots pretty far over on the line.  Unfortunately, a large
+    % indentation looks wrong when the entry text itself is broken across
+    % lines.  So we use a small indentation and put up with long leaders.
+    %
+    % \hangafter is reset to 1 (which is the value we want) at the start
+    % of each paragraph, so we need not do anything with that.
+    \hangindent = 2em
+    %
+    % When the entry text needs to be broken, just fill out the first line
+    % with blank space.
+    \rightskip = 0pt plus1fil
+    %
+    % A bit of stretch before each entry for the benefit of balancing
+    % columns.
+    \vskip 0pt plus1pt
+    %
+    % Swallow the left brace of the text (first parameter):
+    \afterassignment\doentry
+    \let\temp =
+}
+\def\doentry{%
+    \bgroup % Instead of the swallowed brace.
+      \noindent
+      \aftergroup\finishentry
+      % And now comes the text of the entry.
+}
+\def\finishentry#1{%
+    % #1 is the page number.
+    %
+    % The following is kludged to not output a line of dots in the index if
+    % there are no page numbers.  The next person who breaks this will be
+    % cursed by a Unix daemon.
+    \setbox\boxA = \hbox{#1}%
+    \ifdim\wd\boxA = 0pt
+      \ %
+    \else
+      %
+      % If we must, put the page number on a line of its own, and fill out
+      % this line with blank space.  (The \hfil is overwhelmed with the
+      % fill leaders glue in \indexdotfill if the page number does fit.)
+      \hfil\penalty50
+      \null\nobreak\indexdotfill % Have leaders before the page number.
+      %
+      % The `\ ' here is removed by the implicit \unskip that TeX does as
+      % part of (the primitive) \par.  Without it, a spurious underfull
+      % \hbox ensues.
+      \ifpdf
+       \pdfgettoks#1.%
+       \ \the\toksA
+      \else
+       \ #1%
+      \fi
+    \fi
+    \par
+  \endgroup
+}
+
+% Like plain.tex's \dotfill, except uses up at least 1 em.
+\def\indexdotfill{\cleaders
+  \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+\def\secondary#1#2{{%
+  \parfillskip=0in
+  \parskip=0in
+  \hangindent=1in
+  \hangafter=1
+  \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
+  \ifpdf
+    \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
+  \else
+    #2
+  \fi
+  \par
+}}
+
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11
+
+\newbox\partialpage
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
+  % Grab any single-column material above us.
+  \output = {%
+    %
+    % Here is a possibility not foreseen in manmac: if we accumulate a
+    % whole lot of material, we might end up calling this \output
+    % routine twice in a row (see the doublecol-lose test, which is
+    % essentially a couple of indexes with @setchapternewpage off).  In
+    % that case we just ship out what is in \partialpage with the normal
+    % output routine.  Generally, \partialpage will be empty when this
+    % runs and this will be a no-op.  See the indexspread.tex test case.
+    \ifvoid\partialpage \else
+      \onepageout{\pagecontents\partialpage}%
+    \fi
+    %
+    \global\setbox\partialpage = \vbox{%
+      % Unvbox the main output page.
+      \unvbox\PAGE
+      \kern-\topskip \kern\baselineskip
+    }%
+  }%
+  \eject % run that output routine to set \partialpage
+  %
+  % Use the double-column output routine for subsequent pages.
+  \output = {\doublecolumnout}%
+  %
+  % Change the page size parameters.  We could do this once outside this
+  % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+  % format, but then we repeat the same computation.  Repeating a couple
+  % of assignments once per index is clearly meaningless for the
+  % execution time, so we may as well do it in one place.
+  %
+  % First we halve the line length, less a little for the gutter between
+  % the columns.  We compute the gutter based on the line length, so it
+  % changes automatically with the paper format.  The magic constant
+  % below is chosen so that the gutter has the same value (well, +-<1pt)
+  % as it did when we hard-coded it.
+  %
+  % We put the result in a separate register, \doublecolumhsize, so we
+  % can restore it in \pagesofar, after \hsize itself has (potentially)
+  % been clobbered.
+  %
+  \doublecolumnhsize = \hsize
+    \advance\doublecolumnhsize by -.04154\hsize
+    \divide\doublecolumnhsize by 2
+  \hsize = \doublecolumnhsize
+  %
+  % Double the \vsize as well.  (We don't need a separate register here,
+  % since nobody clobbers \vsize.)
+  \vsize = 2\vsize
+}
+
+% The double-column output routine for all double-column pages except
+% the last.
+%
+\def\doublecolumnout{%
+  \splittopskip=\topskip \splitmaxdepth=\maxdepth
+  % Get the available space for the double columns -- the normal
+  % (undoubled) page height minus any material left over from the
+  % previous page.
+  \dimen@ = \vsize
+  \divide\dimen@ by 2
+  \advance\dimen@ by -\ht\partialpage
+  %
+  % box0 will be the left-hand column, box2 the right.
+  \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+  \onepageout\pagesofar
+  \unvbox255
+  \penalty\outputpenalty
+}
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+  \unvbox\partialpage
+  %
+  \hsize = \doublecolumnhsize
+  \wd0=\hsize \wd2=\hsize
+  \hbox to\pagewidth{\box0\hfil\box2}%
+}
+%
+% All done with double columns.
+\def\enddoublecolumns{%
+  % The following penalty ensures that the page builder is exercised
+  % _before_ we change the output routine.  This is necessary in the
+  % following situation:
+  %
+  % The last section of the index consists only of a single entry.
+  % Before this section, \pagetotal is less than \pagegoal, so no
+  % break occurs before the last section starts.  However, the last
+  % section, consisting of \initial and the single \entry, does not
+  % fit on the page and has to be broken off.  Without the following
+  % penalty the page builder will not be exercised until \eject
+  % below, and by that time we'll already have changed the output
+  % routine to the \balancecolumns version, so the next-to-last
+  % double-column page will be processed with \balancecolumns, which
+  % is wrong:  The two columns will go to the main vertical list, with
+  % the broken-off section in the recent contributions.  As soon as
+  % the output routine finishes, TeX starts reconsidering the page
+  % break.  The two columns and the broken-off section both fit on the
+  % page, because the two columns now take up only half of the page
+  % goal.  When TeX sees \eject from below which follows the final
+  % section, it invokes the new output routine that we've set after
+  % \balancecolumns below; \onepageout will try to fit the two columns
+  % and the final section into the vbox of \pageheight (see
+  % \pagebody), causing an overfull box.
+  %
+  % Note that glue won't work here, because glue does not exercise the
+  % page builder, unlike penalties (see The TeXbook, pp. 280-281).
+  \penalty0
+  %
+  \output = {%
+    % Split the last of the double-column material.  Leave it on the
+    % current page, no automatic page break.
+    \balancecolumns
+    %
+    % If we end up splitting too much material for the current page,
+    % though, there will be another page break right after this \output
+    % invocation ends.  Having called \balancecolumns once, we do not
+    % want to call it again.  Therefore, reset \output to its normal
+    % definition right away.  (We hope \balancecolumns will never be
+    % called on to balance too much material, but if it is, this makes
+    % the output somewhat more palatable.)
+    \global\output = {\onepageout{\pagecontents\PAGE}}%
+  }%
+  \eject
+  \endgroup % started in \begindoublecolumns
+  %
+  % \pagegoal was set to the doubled \vsize above, since we restarted
+  % the current page.  We're now back to normal single-column
+  % typesetting, so reset \pagegoal to the normal \vsize (after the
+  % \endgroup where \vsize got restored).
+  \pagegoal = \vsize
+}
+%
+% Called at the end of the double column material.
+\def\balancecolumns{%
+  \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
+  \dimen@ = \ht0
+  \advance\dimen@ by \topskip
+  \advance\dimen@ by-\baselineskip
+  \divide\dimen@ by 2 % target to split to
+  %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}%
+  \splittopskip = \topskip
+  % Loop until we get a decent breakpoint.
+  {%
+    \vbadness = 10000
+    \loop
+      \global\setbox3 = \copy0
+      \global\setbox1 = \vsplit3 to \dimen@
+    \ifdim\ht3>\dimen@
+      \global\advance\dimen@ by 1pt
+    \repeat
+  }%
+  %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}%
+  \setbox0=\vbox to\dimen@{\unvbox1}%
+  \setbox2=\vbox to\dimen@{\unvbox3}%
+  %
+  \pagesofar
+}
+\catcode`\@ = \other
+
+
+\message{sectioning,}
+% Chapters, sections, etc.
+
+% \unnumberedno is an oxymoron, of course.  But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number".  We avoid collisions with chapter
+% numbers by starting them at 10000.  (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\newcount\chapno
+\newcount\secno        \secno=0
+\newcount\subsecno     \subsecno=0
+\newcount\subsubsecno  \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount\appendixno  \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+  \ifnum\appendixno=`A A%
+  \else\ifnum\appendixno=`B B%
+  \else\ifnum\appendixno=`C C%
+  \else\ifnum\appendixno=`D D%
+  \else\ifnum\appendixno=`E E%
+  \else\ifnum\appendixno=`F F%
+  \else\ifnum\appendixno=`G G%
+  \else\ifnum\appendixno=`H H%
+  \else\ifnum\appendixno=`I I%
+  \else\ifnum\appendixno=`J J%
+  \else\ifnum\appendixno=`K K%
+  \else\ifnum\appendixno=`L L%
+  \else\ifnum\appendixno=`M M%
+  \else\ifnum\appendixno=`N N%
+  \else\ifnum\appendixno=`O O%
+  \else\ifnum\appendixno=`P P%
+  \else\ifnum\appendixno=`Q Q%
+  \else\ifnum\appendixno=`R R%
+  \else\ifnum\appendixno=`S S%
+  \else\ifnum\appendixno=`T T%
+  \else\ifnum\appendixno=`U U%
+  \else\ifnum\appendixno=`V V%
+  \else\ifnum\appendixno=`W W%
+  \else\ifnum\appendixno=`X X%
+  \else\ifnum\appendixno=`Y Y%
+  \else\ifnum\appendixno=`Z Z%
+  % The \the is necessary, despite appearances, because \appendixletter is
+  % expanded while writing the .toc file.  \char\appendixno is not
+  % expandable, thus it is written literally, thus all appendixes come out
+  % with the same letter (or @) in the toc without it.
+  \else\char\the\appendixno
+  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+% Each @chapter defines these (using marks) as the number+name, number
+% and name of the chapter.  Page headings and footings can use
+% these.  @section does likewise.
+\def\thischapter{}
+\def\thischapternum{}
+\def\thischaptername{}
+\def\thissection{}
+\def\thissectionnum{}
+\def\thissectionname{}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achive this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unmlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+  % Compute the abs. sec. level:
+  \absseclevel=#2
+  \advance\absseclevel by \secbase
+  % Make sure \absseclevel doesn't fall outside the range:
+  \ifnum \absseclevel < 0
+    \absseclevel = 0
+  \else
+    \ifnum \absseclevel > 3
+      \absseclevel = 3
+    \fi
+  \fi
+  % The heading type:
+  \def\headtype{#1}%
+  \if \headtype U%
+    \ifnum \absseclevel < \unmlevel
+      \chardef\unmlevel = \absseclevel
+    \fi
+  \else
+    % Check for appendix sections:
+    \ifnum \absseclevel = 0
+      \edef\chapheadtype{\headtype}%
+    \else
+      \if \headtype A\if \chapheadtype N%
+       \errmessage{@appendix... within a non-appendix chapter}%
+      \fi\fi
+    \fi
+    % Check for numbered within unnumbered:
+    \ifnum \absseclevel > \unmlevel
+      \def\headtype{U}%
+    \else
+      \chardef\unmlevel = 3
+    \fi
+  \fi
+  % Now print the heading:
+  \if \headtype U%
+    \ifcase\absseclevel
+       \unnumberedzzz{#3}%
+    \or \unnumberedseczzz{#3}%
+    \or \unnumberedsubseczzz{#3}%
+    \or \unnumberedsubsubseczzz{#3}%
+    \fi
+  \else
+    \if \headtype A%
+      \ifcase\absseclevel
+         \appendixzzz{#3}%
+      \or \appendixsectionzzz{#3}%
+      \or \appendixsubseczzz{#3}%
+      \or \appendixsubsubseczzz{#3}%
+      \fi
+    \else
+      \ifcase\absseclevel
+         \chapterzzz{#3}%
+      \or \seczzz{#3}%
+      \or \numberedsubseczzz{#3}%
+      \or \numberedsubsubseczzz{#3}%
+      \fi
+    \fi
+  \fi
+  \suppressfirstparagraphindent
+}
+
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
+
+% @chapter, @appendix, @unnumbered.  Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v.  By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+  % section resetting is \global in case the chapter is in a group, such
+  % as an @include file.
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\chapno by 1
+  %
+  % Used for \float.
+  \gdef\chaplevelprefix{\the\chapno.}%
+  \resetallfloatnos
+  %
+  \message{\putwordChapter\space \the\chapno}%
+  %
+  % Write the actual heading.
+  \chapmacro{#1}{Ynumbered}{\the\chapno}%
+  %
+  % So @section and the like are numbered underneath this chapter.
+  \global\let\section = \numberedsec
+  \global\let\subsection = \numberedsubsec
+  \global\let\subsubsection = \numberedsubsubsec
+}
+
+\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz#1{%
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\appendixno by 1
+  \gdef\chaplevelprefix{\appendixletter.}%
+  \resetallfloatnos
+  %
+  \def\appendixnum{\putwordAppendix\space \appendixletter}%
+  \message{\appendixnum}%
+  %
+  \chapmacro{#1}{Yappendix}{\appendixletter}%
+  %
+  \global\let\section = \appendixsec
+  \global\let\subsection = \appendixsubsec
+  \global\let\subsubsection = \appendixsubsubsec
+}
+
+\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz#1{%
+  \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+    \global\advance\unnumberedno by 1
+  %
+  % Since an unnumbered has no number, no prefix for figures.
+  \global\let\chaplevelprefix = \empty
+  \resetallfloatnos
+  %
+  % This used to be simply \message{#1}, but TeX fully expands the
+  % argument to \message.  Therefore, if #1 contained @-commands, TeX
+  % expanded them.  For example, in `@unnumbered The @cite{Book}', TeX
+  % expanded @cite (which turns out to cause errors because \cite is meant
+  % to be executed, not expanded).
+  %
+  % Anyway, we don't want the fully-expanded definition of @cite to appear
+  % as a result of the \message, we just want `@cite' itself.  We use
+  % \the<toks register> to achieve this: TeX expands \the<toks> only once,
+  % simply yielding the contents of <toks register>.  (We also do this for
+  % the toc entries.)
+  \toks0 = {#1}%
+  \message{(\the\toks0)}%
+  %
+  \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+  %
+  \global\let\section = \unnumberedsec
+  \global\let\subsection = \unnumberedsubsec
+  \global\let\subsubsection = \unnumberedsubsubsec
+}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+  % Well, we could do the following in a group, but that would break
+  % an assumption that \chapmacro is called at the outermost level.
+  % Thus we are safer this way:                --kasal, 24feb04
+  \let\centerparametersmaybe = \centerparameters
+  \unnmhead0{#1}%
+  \let\centerparametersmaybe = \relax
+}
+
+% @top is like @unnumbered.
+\let\top\unnumbered
+
+% Sections.
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
+
+\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
+
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz#1{%
+  \global\subsecno=0 \global\subsubsecno=0  \global\advance\secno by 1
+  \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
+
+% Subsections.
+\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Yappendix}%
+                 {\appendixletter.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz#1{%
+  \global\subsubsecno=0  \global\advance\subsecno by 1
+  \sectionheading{#1}{subsec}{Ynothing}%
+                 {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
+
+% Subsubsections.
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Ynumbered}%
+                 {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Yappendix}%
+                 {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz#1{%
+  \global\advance\subsubsecno by 1
+  \sectionheading{#1}{subsubsec}{Ynothing}%
+                 {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+% NOTE on use of \vbox for chapter headings, section headings, and such:
+%       1) We use \vbox rather than the earlier \line to permit
+%          overlong headings to fold.
+%       2) \hyphenpenalty is set to 10000 because hyphenation in a
+%          heading is obnoxious; this forbids it.
+%       3) Likewise, headings look best if no \parindent is used, and
+%          if justification is not attempted.  Hence \raggedright.
+
+\def\majorheading{%
+  {\advance\chapheadingskip by 10pt \chapbreak }%
+  \parsearg\chapheadingzzz
+}
+
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+  {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+                    \parindent=0pt\raggedright
+                    \rmisbold #1\hfill}}%
+  \bigskip \par\penalty 200\relax
+  \suppressfirstparagraphindent
+}
+
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+  \suppressfirstparagraphindent}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip\chapheadingskip
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+% Because \domark is called before \chapoddpage, the filler page will
+% get the headings for the next chapter, which is wrong.  But we don't
+% care -- we just disable all headings on the filler page.
+\def\chapoddpage{%
+  \chappager
+  \ifodd\pageno \else
+    \begingroup
+      \evenheadline={\hfil}\evenfootline={\hfil}%
+      \oddheadline={\hfil}\oddfootline={\hfil}%
+      \hbox to 0pt{}%
+      \chappager
+    \endgroup
+  \fi
+}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+% Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+\def\Yappendixkeyword{Yappendix}
+%
+\def\chapmacro#1#2#3{%
+  % Insert the first mark before the heading break (see notes for \domark).
+  \let\prevchapterdefs=\lastchapterdefs
+  \let\prevsectiondefs=\lastsectiondefs
+  \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}%
+                        \gdef\thissection{}}%
+  %
+  \def\temptype{#2}%
+  \ifx\temptype\Ynothingkeyword
+    \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+                          \gdef\thischapter{\thischaptername}}%
+  \else\ifx\temptype\Yomitfromtockeyword
+    \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+                          \gdef\thischapter{}}%
+  \else\ifx\temptype\Yappendixkeyword
+    \toks0={#1}%
+    \xdef\lastchapterdefs{%
+      \gdef\noexpand\thischaptername{\the\toks0}%
+      \gdef\noexpand\thischapternum{\appendixletter}%
+      \gdef\noexpand\thischapter{\putwordAppendix{} \noexpand\thischapternum:
+                                 \noexpand\thischaptername}%
+    }%
+  \else
+    \toks0={#1}%
+    \xdef\lastchapterdefs{%
+      \gdef\noexpand\thischaptername{\the\toks0}%
+      \gdef\noexpand\thischapternum{\the\chapno}%
+      \gdef\noexpand\thischapter{\putwordChapter{} \noexpand\thischapternum:
+                                 \noexpand\thischaptername}%
+    }%
+  \fi\fi\fi
+  %
+  % Output the mark.  Pass it through \safewhatsit, to take care of
+  % the preceding space.
+  \safewhatsit\domark
+  %
+  % Insert the chapter heading break.
+  \pchapsepmacro
+  %
+  % Now the second mark, after the heading break.  No break points
+  % between here and the heading.
+  \let\prevchapterdefs=\lastchapterdefs
+  \let\prevsectiondefs=\lastsectiondefs
+  \domark
+  %
+  {%
+    \chapfonts \rmisbold
+    %
+    % Have to define \lastsection before calling \donoderef, because the
+    % xref code eventually uses it.  On the other hand, it has to be called
+    % after \pchapsepmacro, or the headline will change too soon.
+    \gdef\lastsection{#1}%
+    %
+    % Only insert the separating space if we have a chapter/appendix
+    % number, and don't print the unnumbered ``number''.
+    \ifx\temptype\Ynothingkeyword
+      \setbox0 = \hbox{}%
+      \def\toctype{unnchap}%
+    \else\ifx\temptype\Yomitfromtockeyword
+      \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+      \def\toctype{omit}%
+    \else\ifx\temptype\Yappendixkeyword
+      \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+      \def\toctype{app}%
+    \else
+      \setbox0 = \hbox{#3\enspace}%
+      \def\toctype{numchap}%
+    \fi\fi\fi
+    %
+    % Write the toc entry for this chapter.  Must come before the
+    % \donoderef, because we include the current node name in the toc
+    % entry, and \donoderef resets it to empty.
+    \writetocentry{\toctype}{#1}{#3}%
+    %
+    % For pdftex, we have to write out the node definition (aka, make
+    % the pdfdest) after any page break, but before the actual text has
+    % been typeset.  If the destination for the pdf outline is after the
+    % text, then jumping from the outline may wind up with the text not
+    % being visible, for instance under high magnification.
+    \donoderef{#2}%
+    %
+    % Typeset the actual heading.
+    \nobreak % Avoid page breaks at the interline glue.
+    \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+          \hangindent=\wd0 \centerparametersmaybe
+          \unhbox0 #1\par}%
+  }%
+  \nobreak\bigskip % no page break after a chapter title
+  \nobreak
+}
+
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+  \advance\rightskip by 3\rightskip
+  \leftskip = \rightskip
+  \parfillskip = 0pt
+}
+
+
+% I don't think this chapter style is supported any more, so I'm not
+% updating it with the new noderef stuff.  We'll see.  --karl, 11aug03.
+%
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+%
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+                       \parindent=0pt\raggedright
+                       \rmisbold #1\hfill}}\bigskip \par\nobreak
+}
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+\def\centerchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+                       \parindent=0pt
+                       \hfill {\rmisbold #1}\hfill}}\bigskip \par\nobreak
+}
+\def\CHAPFopen{%
+  \global\let\chapmacro=\chfopen
+  \global\let\centerchapmacro=\centerchfopen}
+
+
+% Section titles.  These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
+
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
+
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
+
+
+% Print any size, any type, section title.
+%
+% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is
+% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the
+% section number.
+%
+\def\seckeyword{sec}
+%
+\def\sectionheading#1#2#3#4{%
+  {%
+    % Switch to the right set of fonts.
+    \csname #2fonts\endcsname \rmisbold
+    %
+    \def\sectionlevel{#2}%
+    \def\temptype{#3}%
+    %
+    % Insert first mark before the heading break (see notes for \domark).
+    \let\prevsectiondefs=\lastsectiondefs
+    \ifx\temptype\Ynothingkeyword
+      \ifx\sectionlevel\seckeyword
+        \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}%
+                              \gdef\thissection{\thissectionname}}%
+      \fi
+    \else\ifx\temptype\Yomitfromtockeyword
+      % Don't redefine \thissection.
+    \else\ifx\temptype\Yappendixkeyword
+      \ifx\sectionlevel\seckeyword
+        \toks0={#1}%
+        \xdef\lastsectiondefs{%
+          \gdef\noexpand\thissectionname{\the\toks0}%
+          \gdef\noexpand\thissectionnum{#4}%
+          \gdef\noexpand\thissection{\putwordSection{} \noexpand\thissectionnum:
+                                     \noexpand\thissectionname}%
+        }%
+      \fi
+    \else
+      \ifx\sectionlevel\seckeyword
+        \toks0={#1}%
+        \xdef\lastsectiondefs{%
+          \gdef\noexpand\thissectionname{\the\toks0}%
+          \gdef\noexpand\thissectionnum{#4}%
+          \gdef\noexpand\thissection{\putwordSection{} \noexpand\thissectionnum:
+                                     \noexpand\thissectionname}%
+        }%
+      \fi
+    \fi\fi\fi
+    %
+    % Output the mark.  Pass it through \safewhatsit, to take care of
+    % the preceding space.
+    \safewhatsit\domark
+    %
+    % Insert space above the heading.
+    \csname #2headingbreak\endcsname
+    %
+    % Now the second mark, after the heading break.  No break points
+    % between here and the heading.
+    \let\prevsectiondefs=\lastsectiondefs
+    \domark
+    %
+    % Only insert the space after the number if we have a section number.
+    \ifx\temptype\Ynothingkeyword
+      \setbox0 = \hbox{}%
+      \def\toctype{unn}%
+      \gdef\lastsection{#1}%
+    \else\ifx\temptype\Yomitfromtockeyword
+      % for @headings -- no section number, don't include in toc,
+      % and don't redefine \lastsection.
+      \setbox0 = \hbox{}%
+      \def\toctype{omit}%
+      \let\sectionlevel=\empty
+    \else\ifx\temptype\Yappendixkeyword
+      \setbox0 = \hbox{#4\enspace}%
+      \def\toctype{app}%
+      \gdef\lastsection{#1}%
+    \else
+      \setbox0 = \hbox{#4\enspace}%
+      \def\toctype{num}%
+      \gdef\lastsection{#1}%
+    \fi\fi\fi
+    %
+    % Write the toc entry (before \donoderef).  See comments in \chapmacro.
+    \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+    %
+    % Write the node reference (= pdf destination for pdftex).
+    % Again, see comments in \chapmacro.
+    \donoderef{#3}%
+    %
+    % Interline glue will be inserted when the vbox is completed.
+    % That glue will be a valid breakpoint for the page, since it'll be
+    % preceded by a whatsit (usually from the \donoderef, or from the
+    % \writetocentry if there was no node).  We don't want to allow that
+    % break, since then the whatsits could end up on page n while the
+    % section is on page n+1, thus toc/etc. are wrong.  Debian bug 276000.
+    \nobreak
+    %
+    % Output the actual section heading.
+    \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+          \hangindent=\wd0  % zero if no section number
+          \unhbox0 #1}%
+  }%
+  % Add extra space after the heading -- half of whatever came above it.
+  % Don't allow stretch, though.
+  \kern .5 \csname #2headingskip\endcsname
+  %
+  % Do not let the kern be a potential breakpoint, as it would be if it
+  % was followed by glue.
+  \nobreak
+  %
+  % We'll almost certainly start a paragraph next, so don't let that
+  % glue accumulate.  (Not a breakpoint because it's preceded by a
+  % discardable item.)
+  \vskip-\parskip
+  % 
+  % This is purely so the last item on the list is a known \penalty >
+  % 10000.  This is so \startdefun can avoid allowing breakpoints after
+  % section headings.  Otherwise, it would insert a valid breakpoint between:
+  % 
+  %   @section sec-whatever
+  %   @deffn def-whatever
+  \penalty 10001
+}
+
+
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
+
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this.  The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything.  This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+  \edef\writetoctype{#1}%
+  \ifx\writetoctype\omitkeyword \else
+    \iftocfileopened\else
+      \immediate\openout\tocfile = \jobname.toc
+      \global\tocfileopenedtrue
+    \fi
+    %
+    \iflinks
+      {\atdummies
+       \edef\temp{%
+         \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+       \temp
+      }%
+    \fi
+  \fi
+  %
+  % Tell \shipout to create a pdf destination on each page, if we're
+  % writing pdf.  These are used in the table of contents.  We can't
+  % just write one on every page because the title pages are numbered
+  % 1 and 2 (the page numbers aren't printed), and so are the first
+  % two pages of the document.  Thus, we'd have two destinations named
+  % `1', and two named `2'.
+  \ifpdf \global\pdfmakepagedesttrue \fi
+}
+
+
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care.  This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+% 
+\def\activecatcodes{%
+  \catcode`\"=\active
+  \catcode`\$=\active
+  \catcode`\<=\active
+  \catcode`\>=\active
+  \catcode`\\=\active
+  \catcode`\^=\active
+  \catcode`\_=\active
+  \catcode`\|=\active
+  \catcode`\~=\active
+}
+
+
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+  \setupdatafile
+  \activecatcodes
+  \input \tocreadfilename
+}
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
+
+% Prepare to read what we've written to \tocfile.
+%
+\def\startcontents#1{%
+  % If @setchapternewpage on, and @headings double, the contents should
+  % start on an odd page, unlike chapters.  Thus, we maintain
+  % \contentsalignmacro in parallel with \pagealignmacro.
+  % From: Torbjorn Granlund <tege@matematik.su.se>
+  \contentsalignmacro
+  \immediate\closeout\tocfile
+  %
+  % Don't need to put `Contents' or `Short Contents' in the headline.
+  % It is abundantly clear what they are.
+  \chapmacro{#1}{Yomitfromtoc}{}%
+  %
+  \savepageno = \pageno
+  \begingroup                  % Set up to handle contents files properly.
+    \raggedbottom              % Worry more about breakpoints than the bottom.
+    \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+    %
+    % Roman numerals for page numbers.
+    \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
+}
+
+% redefined for the two-volume lispref.  We always output on
+% \jobname.toc even if this is redefined.
+% 
+\def\tocreadfilename{\jobname.toc}
+
+% Normal (long) toc.
+%
+\def\contents{%
+  \startcontents{\putwordTOC}%
+    \openin 1 \tocreadfilename\space
+    \ifeof 1 \else
+      \readtocfile
+    \fi
+    \vfill \eject
+    \contentsalignmacro % in case @setchapternewpage odd is in effect
+    \ifeof 1 \else
+      \pdfmakeoutlines
+    \fi
+    \closein 1
+  \endgroup
+  \lastnegativepageno = \pageno
+  \global\pageno = \savepageno
+}
+
+% And just the chapters.
+\def\summarycontents{%
+  \startcontents{\putwordShortTOC}%
+    %
+    \let\numchapentry = \shortchapentry
+    \let\appentry = \shortchapentry
+    \let\unnchapentry = \shortunnchapentry
+    % We want a true roman here for the page numbers.
+    \secfonts
+    \let\rm=\shortcontrm \let\bf=\shortcontbf
+    \let\sl=\shortcontsl \let\tt=\shortconttt
+    \rm
+    \hyphenpenalty = 10000
+    \advance\baselineskip by 1pt % Open it up a little.
+    \def\numsecentry##1##2##3##4{}
+    \let\appsecentry = \numsecentry
+    \let\unnsecentry = \numsecentry
+    \let\numsubsecentry = \numsecentry
+    \let\appsubsecentry = \numsecentry
+    \let\unnsubsecentry = \numsecentry
+    \let\numsubsubsecentry = \numsecentry
+    \let\appsubsubsecentry = \numsecentry
+    \let\unnsubsubsecentry = \numsecentry
+    \openin 1 \tocreadfilename\space
+    \ifeof 1 \else
+      \readtocfile
+    \fi
+    \closein 1
+    \vfill \eject
+    \contentsalignmacro % in case @setchapternewpage odd is in effect
+  \endgroup
+  \lastnegativepageno = \pageno
+  \global\pageno = \savepageno
+}
+\let\shortcontents = \summarycontents
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+  % This space should be enough, since a single number is .5em, and the
+  % widest letter (M) is 1em, at least in the Computer Modern fonts.
+  % But use \hss just in case.
+  % (This space doesn't include the extra space that gets added after
+  % the label; that gets put in by \shortchapentry above.)
+  %
+  % We'd like to right-justify chapter numbers, but that looks strange
+  % with appendix letters.  And right-justifying numbers and
+  % left-justifying letters looks strange when there is less than 10
+  % chapters.  Have to read the whole toc once to know how many chapters
+  % there are before deciding ...
+  \hbox to 1em{#1\hss}%
+}
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+%
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+  \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
+}
+
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+  % We use M since it's probably the widest letter.
+  \setbox0 = \hbox{\putwordAppendix{} M}%
+  \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}}
+
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
+
+% Sections.
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
+
+% Subsections.
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
+
+% And subsubsections.
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
+
+% This parameter controls the indentation of the various levels.
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we want it to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+   \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
+   \begingroup
+     \chapentryfonts
+     \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+   \endgroup
+   \nobreak\vskip .25\baselineskip plus.1\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+  \secentryfonts \leftskip=\tocindent
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+  \subsecentryfonts \leftskip=2\tocindent
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+  \subsubsecentryfonts \leftskip=3\tocindent
+  \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
+
+
+\message{environments,}
+% @foo ... @end foo.
+
+% Markup style infrastructure.  \defmarkupstylesetup\INITMACRO will
+% define and register \INITMACRO to be called on markup style changes.
+% \INITMACRO can check \currentmarkupstyle for the innermost
+% style and the set of \ifmarkupSTYLE switches for all styles
+% currently in effect.
+\newif\ifmarkupvar
+\newif\ifmarkupsamp
+\newif\ifmarkupkey
+%\newif\ifmarkupfile % @file == @samp.
+%\newif\ifmarkupoption % @option == @samp.
+\newif\ifmarkupcode
+\newif\ifmarkupkbd
+%\newif\ifmarkupenv % @env == @code.
+%\newif\ifmarkupcommand % @command == @code.
+\newif\ifmarkuptex % @tex (and part of @math, for now).
+\newif\ifmarkupexample
+\newif\ifmarkupverb
+\newif\ifmarkupverbatim
+
+\let\currentmarkupstyle\empty
+
+\def\setupmarkupstyle#1{%
+  \csname markup#1true\endcsname
+  \def\currentmarkupstyle{#1}%
+  \markupstylesetup
+}
+
+\let\markupstylesetup\empty
+
+\def\defmarkupstylesetup#1{%
+  \expandafter\def\expandafter\markupstylesetup
+    \expandafter{\markupstylesetup #1}%
+  \def#1%
+}
+
+% Markup style setup for left and right quotes.
+\defmarkupstylesetup\markupsetuplq{%
+  \expandafter\let\expandafter \temp \csname markupsetuplq\currentmarkupstyle\endcsname
+  \ifx\temp\relax \markupsetuplqdefault \else \temp \fi
+}
+
+\defmarkupstylesetup\markupsetuprq{%
+  \expandafter\let\expandafter \temp \csname markupsetuprq\currentmarkupstyle\endcsname
+  \ifx\temp\relax \markupsetuprqdefault \else \temp \fi
+}
+
+{
+\catcode`\'=\active
+\catcode`\`=\active
+
+\gdef\markupsetuplqdefault{\let`\lq}
+\gdef\markupsetuprqdefault{\let'\rq}
+
+\gdef\markupsetcodequoteleft{\let`\codequoteleft}
+\gdef\markupsetcodequoteright{\let'\codequoteright}
+
+\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft}
+}
+
+\let\markupsetuplqcode \markupsetcodequoteleft
+\let\markupsetuprqcode \markupsetcodequoteright
+\let\markupsetuplqexample \markupsetcodequoteleft
+\let\markupsetuprqexample \markupsetcodequoteright
+\let\markupsetuplqverb \markupsetcodequoteleft
+\let\markupsetuprqverb \markupsetcodequoteright
+\let\markupsetuplqverbatim \markupsetcodequoteleft
+\let\markupsetuprqverbatim \markupsetcodequoteright
+
+\let\markupsetuplqsamp \markupsetnoligaturesquoteleft
+\let\markupsetuplqkbd \markupsetnoligaturesquoteleft
+
+% Allow an option to not replace quotes with a regular directed right
+% quote/apostrophe (char 0x27), but instead use the undirected quote
+% from cmtt (char 0x0d).  The undirected quote is ugly, so don't make it
+% the default, but it works for pasting with more pdf viewers (at least
+% evince), the lilypond developers report.  xpdf does work with the
+% regular 0x27.  
+% 
+\def\codequoteright{%
+  \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax
+    \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax
+      '%
+    \else \char'15 \fi
+  \else \char'15 \fi
+}
+%
+% and a similar option for the left quote char vs. a grave accent.
+% Modern fonts display ASCII 0x60 as a grave accent, so some people like
+% the code environments to do likewise.
+% 
+\def\codequoteleft{%
+  \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax
+    \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax
+      % [Knuth] pp. 380,381,391
+      % \relax disables Spanish ligatures ?` and !` of \tt font.
+      \relax`%
+    \else \char'22 \fi
+  \else \char'22 \fi
+}
+
+% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font.
+\def\noligaturesquoteleft{\relax\lq}
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
+% Since these characters are used in examples, they should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+%
+\def\point{$\star$}
+\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}}
+\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% The @error{} command.
+% Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \reducedsf error\kern-1.5pt}
+%
+\setbox\errorbox=\hbox to \dimen0{\hfil
+   \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+   \advance\hsize by -2\dimen2 % Rules.
+   \vbox{%
+      \hrule height\dimen2
+      \hbox{\vrule width\dimen2 \kern3pt          % Space to left of text.
+         \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+         \kern3pt\vrule width\dimen2}% Space to right.
+      \hrule height\dimen2}
+    \hfil}
+%
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @tex ... @end tex    escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\envdef\tex{%
+  \setupmarkupstyle{tex}%
+  \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+  \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+  \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+  \catcode `\%=14
+  \catcode `\+=\other
+  \catcode `\"=\other
+  \catcode `\|=\other
+  \catcode `\<=\other
+  \catcode `\>=\other
+  \catcode`\`=\other
+  \catcode`\'=\other
+  \escapechar=`\\
+  %
+  \let\b=\ptexb
+  \let\bullet=\ptexbullet
+  \let\c=\ptexc
+  \let\,=\ptexcomma
+  \let\.=\ptexdot
+  \let\dots=\ptexdots
+  \let\equiv=\ptexequiv
+  \let\!=\ptexexclam
+  \let\i=\ptexi
+  \let\indent=\ptexindent
+  \let\noindent=\ptexnoindent
+  \let\{=\ptexlbrace
+  \let\+=\tabalign
+  \let\}=\ptexrbrace
+  \let\/=\ptexslash
+  \let\*=\ptexstar
+  \let\t=\ptext
+  \expandafter \let\csname top\endcsname=\ptextop  % outer
+  \let\frenchspacing=\plainfrenchspacing
+  %
+  \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+  \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+  \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments.  \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical.  We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip.
+%
+\def\aboveenvbreak{{%
+  % =10000 instead of <10000 because of a special case in \itemzzz and
+  % \sectionheading, q.v.
+  \ifnum \lastpenalty=10000 \else
+    \advance\envskipamount by \parskip
+    \endgraf
+    \ifdim\lastskip<\envskipamount
+      \removelastskip
+      % it's not a good place to break if the last penalty was \nobreak
+      % or better ...
+      \ifnum\lastpenalty<10000 \penalty-50 \fi
+      \vskip\envskipamount
+    \fi
+  \fi
+}}
+
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag.  If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
+\let\nonarrowing=\relax
+
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+        \ctl\leaders\hrule height\circthick\hfil\ctr
+        \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+        \cbl\leaders\hrule height\circthick\hfil\cbr
+        \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\envdef\cartouche{%
+  \ifhmode\par\fi  % can't be in the midst of a paragraph.
+  \startsavinginserts
+  \lskip=\leftskip \rskip=\rightskip
+  \leftskip=0pt\rightskip=0pt % we want these *outside*.
+  \cartinner=\hsize \advance\cartinner by-\lskip
+  \advance\cartinner by-\rskip
+  \cartouter=\hsize
+  \advance\cartouter by 18.4pt % allow for 3pt kerns on either
+                               % side, and for 6pt waste from
+                               % each corner char, and rule thickness
+  \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+  % Flag to tell @lisp, etc., not to narrow margin.
+  \let\nonarrowing = t%
+  \vbox\bgroup
+      \baselineskip=0pt\parskip=0pt\lineskip=0pt
+      \carttop
+      \hbox\bgroup
+         \hskip\lskip
+         \vrule\kern3pt
+         \vbox\bgroup
+             \kern3pt
+             \hsize=\cartinner
+             \baselineskip=\normbskip
+             \lineskip=\normlskip
+             \parskip=\normpskip
+             \vskip -\parskip
+             \comment % For explanation, see the end of \def\group.
+}
+\def\Ecartouche{%
+              \ifhmode\par\fi
+             \kern3pt
+         \egroup
+         \kern3pt\vrule
+         \hskip\rskip
+      \egroup
+      \cartbot
+  \egroup
+  \checkinserts
+}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\def\nonfillstart{%
+  \aboveenvbreak
+  \hfuzz = 12pt % Don't be fussy
+  \sepspaces % Make spaces be word-separators rather than space tokens.
+  \let\par = \lisppar % don't ignore blank lines
+  \obeylines % each line of input is a line of output
+  \parskip = 0pt
+  \parindent = 0pt
+  \emergencystretch = 0pt % don't try to avoid overfull boxes
+  \ifx\nonarrowing\relax
+    \advance \leftskip by \lispnarrowing
+    \exdentamount=\lispnarrowing
+  \else
+    \let\nonarrowing = \relax
+  \fi
+  \let\exdent=\nofillexdent
+}
+
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+%    @example, @display, @format, @lisp
+%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+  \ifx\SETdispenvsize\smallword
+    % end paragraph for sake of leading, in case document has no blank
+    % line.  This is redundant with what happens in \aboveenvbreak, but
+    % we need to do it before changing the fonts, and it's inconvenient
+    % to change the fonts afterward.
+    \ifnum \lastpenalty=10000 \else \endgraf \fi
+    \smallexamplefonts \rm
+  \fi
+}
+\def\setsmalldispenv{%
+  \ifx\SETdispenvsize\nosmallword
+  \else
+    \ifnum \lastpenalty=10000 \else \endgraf \fi
+    \smallexamplefonts \rm
+  \fi
+}
+
+% We often define two environments, @foo and @smallfoo.
+% Let's do it by one command:
+\def\makedispenv #1#2{
+  \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}
+  \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}
+  \expandafter\let\csname E#1\endcsname \afterenvbreak
+  \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
+
+% Define two synonyms:
+\def\maketwodispenvs #1#2#3{
+  \makedispenv{#1}{#3}
+  \makedispenv{#2}{#3}
+}
+
+% @lisp: indented, narrowed, typewriter font; @example: same as @lisp.
+%
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
+%
+\maketwodispenvs {lisp}{example}{%
+  \nonfillstart
+  \tt\setupmarkupstyle{example}%
+  \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+  \gobble       % eat return
+}
+% @display/@smalldisplay: same as @lisp except keep current font.
+%
+\makedispenv {display}{%
+  \nonfillstart
+  \gobble
+}
+
+% @format/@smallformat: same as @display except don't narrow margins.
+%
+\makedispenv{format}{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \gobble
+}
+
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \gobble
+}
+\let\Eflushleft = \afterenvbreak
+
+% @flushright.
+%
+\envdef\flushright{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  \advance\leftskip by 0pt plus 1fill
+  \gobble
+}
+\let\Eflushright = \afterenvbreak
+
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins.  We keep \parskip nonzero in general, since
+% we're doing normal filling.  So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
+%
+\def\quotationstart{%
+  {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+  \parindent=0pt
+  %
+  % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+  \ifx\nonarrowing\relax
+    \advance\leftskip by \lispnarrowing
+    \advance\rightskip by \lispnarrowing
+    \exdentamount = \lispnarrowing
+  \else
+    \let\nonarrowing = \relax
+  \fi
+  \parsearg\quotationlabel
+}
+
+\envdef\quotation{%
+  \setnormaldispenv
+  \quotationstart
+}
+
+\envdef\smallquotation{%
+  \setsmalldispenv
+  \quotationstart
+}
+\let\Esmallquotation = \Equotation
+
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+  \par
+  \ifx\quotationauthor\undefined\else
+    % indent a bit.
+    \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+  \fi
+  {\parskip=0pt \afterenvbreak}%
+}
+
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+  \def\temp{#1}%
+  \ifx\temp\empty \else
+    {\bf #1: }%
+  \fi
+}
+
+
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command.  --janneke@gnu.org
+%
+% [Knuth]: Donald Ervin Knuth, 1996.  The TeXbook.
+%
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too.  Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+  \do\ \do\\\do\{\do\}\do\$\do\&%
+  \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+  \do\<\do\>\do\|\do\@\do+\do\"%
+  % Don't do the quotes -- if we do, @set txicodequoteundirected and
+  % @set txicodequotebacktick will not have effect on @verb and
+  % @verbatim, and ?` and !` ligatures won't get disabled.
+  %\do\`\do\'%
+}
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+  \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+  \catcode`\^^I=\active
+  \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+  \tt  % easiest (and conventionally used) font for verbatim
+  \def\par{\leavevmode\endgraf}%
+  \setupmarkupstyle{verb}%
+  \tabeightspaces
+  % Respect line breaks,
+  % print special symbols as themselves, and
+  % make each space count
+  % must do in this order:
+  \obeylines \uncatcodespecials \sepspaces
+}
+
+% Setup for the @verbatim environment
+%
+% Real tab expansion
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
+%
+\def\starttabbox{\setbox0=\hbox\bgroup}
+%
+\begingroup
+  \catcode`\^^I=\active
+  \gdef\tabexpand{%
+    \catcode`\^^I=\active
+    \def^^I{\leavevmode\egroup
+      \dimen0=\wd0 % the width so far, or since the previous tab
+      \divide\dimen0 by\tabw
+      \multiply\dimen0 by\tabw % compute previous multiple of \tabw
+      \advance\dimen0 by\tabw  % advance to next multiple of \tabw
+      \wd0=\dimen0 \box0 \starttabbox
+    }%
+  }
+\endgroup
+
+% start the verbatim environment.
+\def\setupverbatim{%
+  \let\nonarrowing = t%
+  \nonfillstart
+  % Easiest (and conventionally used) font for verbatim
+  \tt
+  \def\par{\leavevmode\egroup\box0\endgraf}%
+  \tabexpand
+  \setupmarkupstyle{verbatim}%
+  % Respect line breaks,
+  % print special symbols as themselves, and
+  % make each space count
+  % must do in this order:
+  \obeylines \uncatcodespecials \sepspaces
+  \everypar{\starttabbox}%
+}
+
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters.  Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+%    \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+  \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+  \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+%     \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+  \catcode`\ =\active
+  \obeylines %
+  % ignore everything up to the first ^^M, that's the newline at the end
+  % of the @verbatim input line itself.  Otherwise we get an extra blank
+  % line in the output.
+  \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+  % We really want {...\end verbatim} in the body of the macro, but
+  % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+    \setupverbatim\doverbatim
+}
+\let\Everbatim = \afterenvbreak
+
+
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+  {%
+    \makevalueexpandable
+    \setupverbatim
+    \indexnofonts       % Allow `@@' and other weird things in file names.
+    \input #1
+    \afterenvbreak
+  }%
+}
+
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is very desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+  \begingroup
+    \parindent = 0pt  % paragraph indentation looks wrong on title page
+    \scanexp\copyingtext
+  \endgroup
+}
+
+
+\message{defuns,}
+% @defun etc.
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+\newcount\defunpenalty
+
+% Start the processing of @deffn:
+\def\startdefun{%
+  \ifnum\lastpenalty<10000
+    \medbreak
+    \defunpenalty=10003 % Will keep this @deffn together with the
+                        % following @def command, see below.
+  \else
+    % If there are two @def commands in a row, we'll have a \nobreak,
+    % which is there to keep the function description together with its
+    % header.  But if there's nothing but headers, we need to allow a
+    % break somewhere.  Check specifically for penalty 10002, inserted
+    % by \printdefunline, instead of 10000, since the sectioning
+    % commands also insert a nobreak penalty, and we don't want to allow
+    % a break between a section heading and a defun.
+    %
+    % As a minor refinement, we avoid "club" headers by signalling
+    % with penalty of 10003 after the very first @deffn in the
+    % sequence (see above), and penalty of 10002 after any following
+    % @def command.
+    \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi
+    %
+    % Similarly, after a section heading, do not allow a break.
+    % But do insert the glue.
+    \medskip  % preceded by discardable penalty, so not a breakpoint
+  \fi
+  %
+  \parindent=0in
+  \advance\leftskip by \defbodyindent
+  \exdentamount=\defbodyindent
+}
+
+\def\dodefunx#1{%
+  % First, check whether we are in the right environment:
+  \checkenv#1%
+  %
+  % As above, allow line break if we have multiple x headers in a row.
+  % It's not a great place, though.
+  \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi
+  %
+  % And now, it's time to reuse the body of the original defun:
+  \expandafter\gobbledefun#1%
+}
+\def\gobbledefun#1\startdefun{}
+
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+  \begingroup
+    % call \deffnheader:
+    #1#2 \endheader
+    % common ending:
+    \interlinepenalty = 10000
+    \advance\rightskip by 0pt plus 1fil
+    \endgraf
+    \nobreak\vskip -\parskip
+    \penalty\defunpenalty  % signal to \startdefun and \dodefunx
+    % Some of the @defun-type tags do not enable magic parentheses,
+    % rendering the following check redundant.  But we don't optimize.
+    \checkparencounts
+  \endgroup
+}
+
+\def\Edefun{\endgraf\medbreak}
+
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remaining is to define \deffnheader.
+%
+\def\makedefun#1{%
+  \expandafter\let\csname E#1\endcsname = \Edefun
+  \edef\temp{\noexpand\domakedefun
+    \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+  \temp
+}
+
+% \domakedefun \deffn \deffnx \deffnheader
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+  \envdef#1{%
+    \startdefun
+    \parseargusing\activeparens{\printdefunline#3}%
+  }%
+  \def#2{\dodefunx#1}%
+  \def#3%
+}
+
+%%% Untyped functions:
+
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
+
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
+
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+  % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
+  \dosubind{fn}{\code{#3}}{#1}%
+  \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
+}
+
+%%% Typed functions:
+
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
+
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
+
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+  \dosubind{fn}{\code{#4}}{#1}%
+  \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+%%% Typed variables:
+
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
+
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
+
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+  \dosubind{vr}{\code{#4}}{#1}%
+  \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+%%% Untyped variables:
+
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
+
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
+
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
+
+%%% Type:
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+  \doind{tp}{\code{#2}}%
+  \defname{#1}{}{#2}\defunargs{#3\unskip}%
+}
+
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+  % Get the values of \leftskip and \rightskip as they were outside the @def...
+  \advance\leftskip by -\defbodyindent
+  %
+  % How we'll format the type name.  Putting it in brackets helps
+  % distinguish it from the body text that may end up on the next line
+  % just below it.
+  \def\temp{#1}%
+  \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+  %
+  % Figure out line sizes for the paragraph shape.
+  % The first line needs space for \box0; but if \rightskip is nonzero,
+  % we need only space for the part of \box0 which exceeds it:
+  \dimen0=\hsize  \advance\dimen0 by -\wd0  \advance\dimen0 by \rightskip
+  % The continuations:
+  \dimen2=\hsize  \advance\dimen2 by -\defargsindent
+  % (plain.tex says that \dimen1 should be used only as global.)
+  \parshape 2 0in \dimen0 \defargsindent \dimen2
+  %
+  % Put the type name to the right margin.
+  \noindent
+  \hbox to 0pt{%
+    \hfil\box0 \kern-\hsize
+    % \hsize has to be shortened this way:
+    \kern\leftskip
+    % Intentionally do not respect \rightskip, since we need the space.
+  }%
+  %
+  % Allow all lines to be underfull without complaint:
+  \tolerance=10000 \hbadness=10000
+  \exdentamount=\defbodyindent
+  {%
+    % defun fonts. We use typewriter by default (used to be bold) because:
+    % . we're printing identifiers, they should be in tt in principle.
+    % . in languages with many accents, such as Czech or French, it's
+    %   common to leave accents off identifiers.  The result looks ok in
+    %   tt, but exceedingly strange in rm.
+    % . we don't want -- and --- to be treated as ligatures.
+    % . this still does not fix the ?` and !` ligatures, but so far no
+    %   one has made identifiers using them :).
+    \df \tt
+    \def\temp{#2}% return value type
+    \ifx\temp\empty\else \tclose{\temp} \fi
+    #3% output function name
+  }%
+  {\rm\enskip}% hskip 0.5 em of \tenrm
+  %
+  \boldbrax
+  % arguments will be output next, if any.
+}
+
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name.  This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable.  Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+  % use sl by default (not ttsl),
+  % tt for the names.
+  \df \sl \hyphenchar\font=0
+  %
+  % On the other hand, if an argument has two dashes (for instance), we
+  % want a way to get ttsl.  Let's try @var for that.
+  \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}%
+  #1%
+  \sl\hyphenchar\font=45
+}
+
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+  \catcode`\(=\active \catcode`\)=\active
+  \catcode`\[=\active \catcode`\]=\active
+  \catcode`\&=\active
+}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+% Be sure that we always have a definition for `(', etc.  For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+{
+  \activeparens
+  \global\let(=\lparen \global\let)=\rparen
+  \global\let[=\lbrack \global\let]=\rbrack
+  \global\let& = \&
+
+  \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+  \gdef\magicamp{\let&=\amprm}
+}
+
+\newcount\parencount
+
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\&#1 }}
+
+\def\parenfont{%
+  \ifampseen
+    % At the first level, print parens in roman,
+    % otherwise use the default font.
+    \ifnum \parencount=1 \rm \fi
+  \else
+    % The \sf parens (in \boldbrax) actually are a little bolder than
+    % the contained text.  This is especially needed for [ and ] .
+    \sf
+  \fi
+}
+\def\infirstlevel#1{%
+  \ifampseen
+    \ifnum\parencount=1
+      #1%
+    \fi
+  \fi
+}
+\def\bfafterword#1 {#1 \bf}
+
+\def\opnr{%
+  \global\advance\parencount by 1
+  {\parenfont(}%
+  \infirstlevel \bfafterword
+}
+\def\clnr{%
+  {\parenfont)}%
+  \infirstlevel \sl
+  \global\advance\parencount by -1
+}
+
+\newcount\brackcount
+\def\lbrb{%
+  \global\advance\brackcount by 1
+  {\bf[}%
+}
+\def\rbrb{%
+  {\bf]}%
+  \global\advance\brackcount by -1
+}
+
+\def\checkparencounts{%
+  \ifnum\parencount=0 \else \badparencount \fi
+  \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+% these should not use \errmessage; the glibc manual, at least, actually
+% has such constructs (when documenting function pointers).
+\def\badparencount{%
+  \message{Warning: unbalanced parentheses in @def...}%
+  \global\parencount=0
+}
+\def\badbrackcount{%
+  \message{Warning: unbalanced square brackets in @def...}%
+  \global\brackcount=0
+}
+
+
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\undefined
+  \newwrite\macscribble
+  \def\scantokens#1{%
+    \toks0={#1}%
+    \immediate\openout\macscribble=\jobname.tmp
+    \immediate\write\macscribble{\the\toks0}%
+    \immediate\closeout\macscribble
+    \input \jobname.tmp
+  }
+\fi
+
+\def\scanmacro#1{%
+  \begingroup
+    \newlinechar`\^^M
+    \let\xeatspaces\eatspaces
+    % Undo catcode changes of \startcontents and \doprintindex
+    % When called from @insertcopying or (short)caption, we need active
+    % backslash to get it printed correctly.  Previously, we had
+    % \catcode`\\=\other instead.  We'll see whether a problem appears
+    % with macro expansion.                            --kasal, 19aug04
+    \catcode`\@=0 \catcode`\\=\active \escapechar=`\@
+    % ... and \example
+    \spaceisspace
+    %
+    % Append \endinput to make sure that TeX does not see the ending newline.
+    % I've verified that it is necessary both for e-TeX and for ordinary TeX
+    %                                                  --kasal, 29nov03
+    \scantokens{#1\endinput}%
+  \endgroup
+}
+
+\def\scanexp#1{%
+  \edef\temp{\noexpand\scanmacro{#1}}%
+  \temp
+}
+
+\newcount\paramno   % Count of parameters
+\newtoks\macname    % Macro name
+\newif\ifrecursive  % Is it recursive?
+
+% List of all defined macros in the form
+%    \definedummyword\macro1\definedummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+     \toks0 = \expandafter{\macrolist\definedummyword#1}%
+     \xdef\macrolist{\the\toks0}%
+}
+
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+%   \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+% 
+\def\cslet#1#2{%
+  \expandafter\let
+  \csname#1\expandafter\endcsname
+  \csname#2\endcsname
+}
+
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
+}
+
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
+
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \.
+
+% Non-ASCII encodings make 8-bit characters active, so un-activate
+% them to avoid their expansion.  Must do this non-globally, to
+% confine the change to the current group.
+
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by  making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+
+\def\scanctxt{%
+  \catcode`\"=\other
+  \catcode`\+=\other
+  \catcode`\<=\other
+  \catcode`\>=\other
+  \catcode`\@=\other
+  \catcode`\^=\other
+  \catcode`\_=\other
+  \catcode`\|=\other
+  \catcode`\~=\other
+  \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi
+}
+
+\def\scanargctxt{%
+  \scanctxt
+  \catcode`\\=\other
+  \catcode`\^^M=\other
+}
+
+\def\macrobodyctxt{%
+  \scanctxt
+  \catcode`\{=\other
+  \catcode`\}=\other
+  \catcode`\^^M=\other
+  \usembodybackslash
+}
+
+\def\macroargctxt{%
+  \scanctxt
+  \catcode`\\=\other
+}
+
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
+
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
+
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
+
+\def\macroxxx#1{%
+  \getargs{#1}%           now \macname is the macname and \argl the arglist
+  \ifx\argl\empty       % no arguments
+     \paramno=0%
+  \else
+     \expandafter\parsemargdef \argl;%
+  \fi
+  \if1\csname ismacro.\the\macname\endcsname
+     \message{Warning: redefining \the\macname}%
+  \else
+     \expandafter\ifx\csname \the\macname\endcsname \relax
+     \else \errmessage{Macro name \the\macname\space already defined}\fi
+     \global\cslet{macsave.\the\macname}{\the\macname}%
+     \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+     \addtomacrolist{\the\macname}%
+  \fi
+  \begingroup \macrobodyctxt
+  \ifrecursive \expandafter\parsermacbody
+  \else \expandafter\parsemacbody
+  \fi}
+
+\parseargdef\unmacro{%
+  \if1\csname ismacro.#1\endcsname
+    \global\cslet{#1}{macsave.#1}%
+    \global\expandafter\let \csname ismacro.#1\endcsname=0%
+    % Remove the macro name from \macrolist:
+    \begingroup
+      \expandafter\let\csname#1\endcsname \relax
+      \let\definedummyword\unmacrodo
+      \xdef\macrolist{\macrolist}%
+    \endgroup
+  \else
+    \errmessage{Macro #1 not defined}%
+  \fi
+}
+
+% Called by \do from \dounmacro on each macro.  The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+  \ifx #1\relax
+    % remove this
+  \else
+    \noexpand\definedummyword \noexpand#1%
+  \fi
+}
+
+% This makes use of the obscure feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname #1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+
+% Parse the optional {params} list.  Set up \paramno and \paramlist
+% so \defmacro knows what to do.  Define \macarg.blah for each blah
+% in the params list, to be ##N where N is the position in that list.
+% That gets used by \mbodybackslash (above).
+
+% We need to get `macro parameter char #' into several definitions.
+% The technique used is stolen from LaTeX:  let \hash be something
+% unexpandable, insert that wherever you need a #, and then redefine
+% it to # just before using the token list produced.
+%
+% The same technique is used to protect \eatspaces till just before
+% the macro is used.
+
+\def\parsemargdef#1;{\paramno=0\def\paramlist{}%
+        \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,}
+\def\parsemargdefxxx#1,{%
+  \if#1;\let\next=\relax
+  \else \let\next=\parsemargdefxxx
+    \advance\paramno by 1%
+    \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+        {\xeatspaces{\hash\the\paramno}}%
+    \edef\paramlist{\paramlist\hash\the\paramno,}%
+  \fi\next}
+
+% These two commands read recursive and nonrecursive macro bodies.
+% (They're different since rec and nonrec macros end differently.)
+
+\long\def\parsemacbody#1@end macro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+\long\def\parsermacbody#1@end rmacro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+
+% This defines the macro itself. There are six cases: recursive and
+% nonrecursive macros of zero, one, and many arguments.
+% Much magic with \expandafter here.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in; @include reads the file inside a group.
+\def\defmacro{%
+  \let\hash=##% convert placeholders to macro parameter chars
+  \ifrecursive
+    \ifcase\paramno
+    % 0
+      \expandafter\xdef\csname\the\macname\endcsname{%
+        \noexpand\scanmacro{\temp}}%
+    \or % 1
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \noexpand\braceorline
+         \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+      \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+         \egroup\noexpand\scanmacro{\temp}}%
+    \else % many
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \noexpand\csname\the\macname xx\endcsname}%
+      \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+          \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+      \expandafter\expandafter
+      \expandafter\xdef
+      \expandafter\expandafter
+        \csname\the\macname xxx\endcsname
+          \paramlist{\egroup\noexpand\scanmacro{\temp}}%
+    \fi
+  \else
+    \ifcase\paramno
+    % 0
+      \expandafter\xdef\csname\the\macname\endcsname{%
+        \noexpand\norecurse{\the\macname}%
+        \noexpand\scanmacro{\temp}\egroup}%
+    \or % 1
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \noexpand\braceorline
+         \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+      \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+        \egroup
+        \noexpand\norecurse{\the\macname}%
+        \noexpand\scanmacro{\temp}\egroup}%
+    \else % many
+      \expandafter\xdef\csname\the\macname\endcsname{%
+         \bgroup\noexpand\macroargctxt
+         \expandafter\noexpand\csname\the\macname xx\endcsname}%
+      \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+          \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+      \expandafter\expandafter
+      \expandafter\xdef
+      \expandafter\expandafter
+      \csname\the\macname xxx\endcsname
+      \paramlist{%
+          \egroup
+          \noexpand\norecurse{\the\macname}%
+          \noexpand\scanmacro{\temp}\egroup}%
+    \fi
+  \fi}
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+% \braceorline decides whether the next nonwhitespace character is a
+% {.  If so it reads up to the closing }, if not, it reads the whole
+% line.  Whatever was read is then fed to the next control sequence
+% as an argument (by \parsebrace or \parsearg)
+\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+  \ifx\nchar\bgroup\else
+    \expandafter\parsearg
+  \fi \macnamexxx}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign.  Just make them active and then expand them all to nothing.
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+  {%
+    \expandafter\let\obeyedspace=\empty
+    \addtomacrolist{#1}%
+    \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+  }%
+  \next
+}
+
+
+\message{cross references,}
+
+\newwrite\auxfile
+\newif\ifhavexrefs    % True if xref values are known.
+\newif\ifwarnedxrefs  % True if we warned once that they aren't known.
+
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+  node \samp{\ignorespaces#1{}}}
+
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references.  The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross,  ,  , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}}
+
+\let\nwnode=\node
+\let\lastnode=\empty
+
+% Write a cross-reference definition for the current node.  #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+  \ifx\lastnode\empty\else
+    \setref{\lastnode}{#1}%
+    \global\let\lastnode=\empty
+  \fi
+}
+
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \lastsection,
+%                 or the anchor name.
+% 2) NAME-snt   - section number and type, passed as the SNT arg, or
+%                 empty for anchors.
+% 3) NAME-pg    - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat.  In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof   - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+  \pdfmkdest{#1}%
+  \iflinks
+    {%
+      \atdummies  % preserve commands, but don't expand them
+      \edef\writexrdef##1##2{%
+       \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+         ##1}{##2}}% these are parameters of \writexrdef
+      }%
+      \toks0 = \expandafter{\lastsection}%
+      \immediate \writexrdef{title}{\the\toks0 }%
+      \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+      \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, during \shipout
+    }%
+  \fi
+}
+
+% @xref, @pxref, and @ref generate cross-references.  For \xrefX, #1 is
+% the node name, #2 the name of the Info cross-reference, #3 the printed
+% node name, #4 the name of the Info file, #5 the name of the printed
+% manual.  All but the node name can be omitted.
+%
+\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
+\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
+\def\ref#1{\xrefX[#1,,,,,,,]}
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+  \unsepspaces
+  \def\printedmanual{\ignorespaces #5}%
+  \def\printedrefname{\ignorespaces #3}%
+  \setbox1=\hbox{\printedmanual\unskip}%
+  \setbox0=\hbox{\printedrefname\unskip}%
+  \ifdim \wd0 = 0pt
+    % No printed node name was explicitly given.
+    \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
+      % Use the node name inside the square brackets.
+      \def\printedrefname{\ignorespaces #1}%
+    \else
+      % Use the actual chapter/section title appear inside
+      % the square brackets.  Use the real section title if we have it.
+      \ifdim \wd1 > 0pt
+        % It is in another manual, so we don't have it.
+        \def\printedrefname{\ignorespaces #1}%
+      \else
+        \ifhavexrefs
+          % We know the real title if we have the xref values.
+          \def\printedrefname{\refx{#1-title}{}}%
+        \else
+          % Otherwise just copy the Info node name.
+          \def\printedrefname{\ignorespaces #1}%
+        \fi%
+      \fi
+    \fi
+  \fi
+  %
+  % Make link in pdf output.
+  \ifpdf
+    {\indexnofonts
+     \turnoffactive
+     % This expands tokens, so do it after making catcode changes, so _
+     % etc. don't get their TeX definitions.
+     \getfilename{#4}%
+     %
+     % See comments at \activebackslashdouble.
+     {\activebackslashdouble \xdef\pdfxrefdest{#1}%
+      \backslashparens\pdfxrefdest}%
+     %
+     \leavevmode
+     \startlink attr{/Border [0 0 0]}%
+     \ifnum\filenamelength>0
+       goto file{\the\filename.pdf} name{\pdfxrefdest}%
+     \else
+       goto name{\pdfmkpgn{\pdfxrefdest}}%
+     \fi
+    }%
+    \setcolor{\linkcolor}%
+  \fi
+  %
+  % Float references are printed completely differently: "Figure 1.2"
+  % instead of "[somenode], p.3".  We distinguish them by the
+  % LABEL-title being set to a magic string.
+  {%
+    % Have to otherify everything special to allow the \csname to
+    % include an _ in the xref name, etc.
+    \indexnofonts
+    \turnoffactive
+    \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+      \csname XR#1-title\endcsname
+  }%
+  \iffloat\Xthisreftitle
+    % If the user specified the print name (third arg) to the ref,
+    % print it instead of our usual "Figure 1.2".
+    \ifdim\wd0 = 0pt
+      \refx{#1-snt}{}%
+    \else
+      \printedrefname
+    \fi
+    %
+    % if the user also gave the printed manual name (fifth arg), append
+    % "in MANUALNAME".
+    \ifdim \wd1 > 0pt
+      \space \putwordin{} \cite{\printedmanual}%
+    \fi
+  \else
+    % node/anchor (non-float) references.
+    %
+    % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+    % insert empty discretionaries after hyphens, which means that it will
+    % not find a line break at a hyphen in a node names.  Since some manuals
+    % are best written with fairly long node names, containing hyphens, this
+    % is a loss.  Therefore, we give the text of the node name again, so it
+    % is as if TeX is seeing it for the first time.
+    \ifdim \wd1 > 0pt
+      \putwordSection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}%
+    \else
+      % _ (for example) has to be the character _ for the purposes of the
+      % control sequence corresponding to the node, but it has to expand
+      % into the usual \leavevmode...\vrule stuff for purposes of
+      % printing. So we \turnoffactive for the \refx-snt, back on for the
+      % printing, back off for the \refx-pg.
+      {\turnoffactive
+       % Only output a following space if the -snt ref is nonempty; for
+       % @unnumbered and @anchor, it won't be.
+       \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+       \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+      }%
+      % output the `[mynode]' via a macro so it can be overridden.
+      \xrefprintnodename\printedrefname
+      %
+      % But we always want a comma and a space:
+      ,\space
+      %
+      % output the `page 3'.
+      \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+    \fi
+  \fi
+  \endlink
+\endgroup}
+
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output.  It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents.  Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
+
+% Things referred to by \setref.
+%
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+  \ifnum\secno=0
+    \putwordChapter@tie \the\chapno
+  \else \ifnum\subsecno=0
+    \putwordSection@tie \the\chapno.\the\secno
+  \else \ifnum\subsubsecno=0
+    \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+  \else
+    \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+  \fi\fi\fi
+}
+\def\Yappendix{%
+  \ifnum\secno=0
+     \putwordAppendix@tie @char\the\appendixno{}%
+  \else \ifnum\subsecno=0
+     \putwordSection@tie @char\the\appendixno.\the\secno
+  \else \ifnum\subsubsecno=0
+    \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+  \else
+    \putwordSection@tie
+      @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+  \fi\fi\fi
+}
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+%
+\def\refx#1#2{%
+  {%
+    \indexnofonts
+    \otherbackslash
+    \expandafter\global\expandafter\let\expandafter\thisrefX
+      \csname XR#1\endcsname
+  }%
+  \ifx\thisrefX\relax
+    % If not defined, say something at least.
+    \angleleft un\-de\-fined\angleright
+    \iflinks
+      \ifhavexrefs
+        \message{\linenumber Undefined cross reference `#1'.}%
+      \else
+        \ifwarnedxrefs\else
+          \global\warnedxrefstrue
+          \message{Cross reference values unknown; you must run TeX again.}%
+        \fi
+      \fi
+    \fi
+  \else
+    % It's defined, so just use it.
+    \thisrefX
+  \fi
+  #2% Output the suffix in any case.
+}
+
+% This is the macro invoked by entries in the aux file.  Usually it's
+% just a \def (we prepend XR to the control sequence name to avoid
+% collisions).  But if this is a float type, we have more work to do.
+%
+\def\xrdef#1#2{%
+  {% The node name might contain 8-bit characters, which in our current
+   % implementation are changed to commands like @'e.  Don't let these
+   % mess up the control sequence name.
+    \indexnofonts
+    \turnoffactive
+    \xdef\safexrefname{#1}%
+  }%
+  %
+  \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref
+  %
+  % Was that xref control sequence that we just defined for a float?
+  \expandafter\iffloat\csname XR\safexrefname\endcsname
+    % it was a float, and we have the (safe) float type in \iffloattype.
+    \expandafter\let\expandafter\floatlist
+      \csname floatlist\iffloattype\endcsname
+    %
+    % Is this the first time we've seen this float type?
+    \expandafter\ifx\floatlist\relax
+      \toks0 = {\do}% yes, so just \do
+    \else
+      % had it before, so preserve previous elements in list.
+      \toks0 = \expandafter{\floatlist\do}%
+    \fi
+    %
+    % Remember this xref in the control sequence \floatlistFLOATTYPE,
+    % for later use in \listoffloats.
+    \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0
+      {\safexrefname}}%
+  \fi
+}
+
+% Read the last existing aux file, if any.  No error if none exists.
+%
+\def\tryauxfile{%
+  \openin 1 \jobname.aux
+  \ifeof 1 \else
+    \readdatafile{aux}%
+    \global\havexrefstrue
+  \fi
+  \closein 1
+}
+
+\def\setupdatafile{%
+  \catcode`\^^@=\other
+  \catcode`\^^A=\other
+  \catcode`\^^B=\other
+  \catcode`\^^C=\other
+  \catcode`\^^D=\other
+  \catcode`\^^E=\other
+  \catcode`\^^F=\other
+  \catcode`\^^G=\other
+  \catcode`\^^H=\other
+  \catcode`\^^K=\other
+  \catcode`\^^L=\other
+  \catcode`\^^N=\other
+  \catcode`\^^P=\other
+  \catcode`\^^Q=\other
+  \catcode`\^^R=\other
+  \catcode`\^^S=\other
+  \catcode`\^^T=\other
+  \catcode`\^^U=\other
+  \catcode`\^^V=\other
+  \catcode`\^^W=\other
+  \catcode`\^^X=\other
+  \catcode`\^^Z=\other
+  \catcode`\^^[=\other
+  \catcode`\^^\=\other
+  \catcode`\^^]=\other
+  \catcode`\^^^=\other
+  \catcode`\^^_=\other
+  % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc.
+  % in xref tags, i.e., node names.  But since ^^e4 notation isn't
+  % supported in the main text, it doesn't seem desirable.  Furthermore,
+  % that is not enough: for node names that actually contain a ^
+  % character, we would end up writing a line like this: 'xrdef {'hat
+  % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
+  % argument, and \hat is not an expandable control sequence.  It could
+  % all be worked out, but why?  Either we support ^^ or we don't.
+  %
+  % The other change necessary for this was to define \auxhat:
+  % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
+  % and then to call \auxhat in \setq.
+  %
+  \catcode`\^=\other
+  %
+  % Special characters.  Should be turned off anyway, but...
+  \catcode`\~=\other
+  \catcode`\[=\other
+  \catcode`\]=\other
+  \catcode`\"=\other
+  \catcode`\_=\other
+  \catcode`\|=\other
+  \catcode`\<=\other
+  \catcode`\>=\other
+  \catcode`\$=\other
+  \catcode`\#=\other
+  \catcode`\&=\other
+  \catcode`\%=\other
+  \catcode`+=\other % avoid \+ for paranoia even though we've turned it off
+  %
+  % This is to support \ in node names and titles, since the \
+  % characters end up in a \csname.  It's easier than
+  % leaving it active and making its active definition an actual \
+  % character.  What I don't understand is why it works in the *value*
+  % of the xrdef.  Seems like it should be a catcode12 \, and that
+  % should not typeset properly.  But it works, so I'm moving on for
+  % now.  --karl, 15jan04.
+  \catcode`\\=\other
+  %
+  % Make the characters 128-255 be printing characters.
+  {%
+    \count1=128
+    \def\loop{%
+      \catcode\count1=\other
+      \advance\count1 by 1
+      \ifnum \count1<256 \loop \fi
+    }%
+  }%
+  %
+  % @ is our escape character in .aux files, and we need braces.
+  \catcode`\{=1
+  \catcode`\}=2
+  \catcode`\@=0
+}
+
+\def\readdatafile#1{%
+\begingroup
+  \setupdatafile
+  \input\jobname.#1
+\endgroup}
+
+
+\message{insertions,}
+% including footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for info output only.
+\let\footnotestyle=\comment
+
+{\catcode `\@=11
+%
+% Auto-number footnotes.  Otherwise like plain.
+\gdef\footnote{%
+  \let\indent=\ptexindent
+  \let\noindent=\ptexnoindent
+  \global\advance\footnoteno by \@ne
+  \edef\thisfootno{$^{\the\footnoteno}$}%
+  %
+  % In case the footnote comes at the end of a sentence, preserve the
+  % extra spacing after we do the footnote number.
+  \let\@sf\empty
+  \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi
+  %
+  % Remove inadvertent blank space before typesetting the footnote number.
+  \unskip
+  \thisfootno\@sf
+  \dofootnote
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter.  Our footnotes don't need to be so general.
+%
+% Oh yes, they do; otherwise, @ifset (and anything else that uses
+% \parseargline) fails inside footnotes because the tokens are fixed when
+% the footnote is read.  --karl, 16nov96.
+%
+\gdef\dofootnote{%
+  \insert\footins\bgroup
+  % We want to typeset this text as a normal paragraph, even if the
+  % footnote reference occurs in (for example) a display environment.
+  % So reset some parameters.
+  \hsize=\pagewidth
+  \interlinepenalty\interfootnotelinepenalty
+  \splittopskip\ht\strutbox % top baseline for broken footnotes
+  \splitmaxdepth\dp\strutbox
+  \floatingpenalty\@MM
+  \leftskip\z@skip
+  \rightskip\z@skip
+  \spaceskip\z@skip
+  \xspaceskip\z@skip
+  \parindent\defaultparindent
+  %
+  \smallfonts \rm
+  %
+  % Because we use hanging indentation in footnotes, a @noindent appears
+  % to exdent this text, so make it be a no-op.  makeinfo does not use
+  % hanging indentation so @noindent can still be needed within footnote
+  % text after an @example or the like (not that this is good style).
+  \let\noindent = \relax
+  %
+  % Hang the footnote text off the number.  Use \everypar in case the
+  % footnote extends for more than one paragraph.
+  \everypar = {\hang}%
+  \textindent{\thisfootno}%
+  %
+  % Don't crash into the line above the footnote text.  Since this
+  % expands into a box, it must come within the paragraph, lest it
+  % provide a place where TeX can split the footnote.
+  \footstrut
+  \futurelet\next\fo@t
+}
+}%end \catcode `\@=11
+
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished.  Otherwise, the insertion
+% would be lost.
+% Similarly, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes.  --kasal, 16nov03.
+
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
+%
+\def\startsavinginserts{%
+  \ifx \insert\ptexinsert
+    \let\insert\saveinsert
+  \else
+    \let\checkinserts\relax
+  \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
+%
+\def\saveinsert#1{%
+  \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+  \afterassignment\next
+  % swallow the left brace
+  \let\temp =
+}
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
+
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+  \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+    {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+  \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials  %  ;-)
+  \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+  \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+  \next
+}
+\def\newsaveinsX #1{%
+  \csname newbox\endcsname #1%
+  \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+    \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
+
+
+% @image.  We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front.  If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+  % Do not bother showing banner with epsf.tex v2.7k (available in
+  % doc/epsf.tex and on ctan).
+  \def\epsfannounce{\toks0 = }%
+  \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+  work.  It is also included in the Texinfo distribution, or you can get
+  it from ftp://tug.org/tex/epsf.tex.}
+%
+\def\image#1{%
+  \ifx\epsfbox\undefined
+    \ifwarnednoepsf \else
+      \errhelp = \noepsfhelp
+      \errmessage{epsf.tex not found, images will be ignored}%
+      \global\warnednoepsftrue
+    \fi
+  \else
+    \imagexxx #1,,,,,\finish
+  \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing this stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+  \catcode`\^^M = 5     % in case we're inside an example
+  \normalturnoffactive  % allow _ et al. in names
+  % If the image is by itself, center it.
+  \ifvmode
+    \imagevmodetrue
+    \nobreak\medskip
+    % Usually we'll have text after the image which will insert
+    % \parskip glue, so insert it here too to equalize the space
+    % above and below.
+    \nobreak\vskip\parskip
+    \nobreak
+  \fi
+  %
+  % Leave vertical mode so that indentation from an enclosing
+  % environment such as @quotation is respected.  On the other hand, if
+  % it's at the top level, we don't want the normal paragraph indentation.
+  \noindent
+  %
+  % Output the image.
+  \ifpdf
+    \dopdfimage{#1}{#2}{#3}%
+  \else
+    % \epsfbox itself resets \epsf?size at each figure.
+    \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+    \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+    \epsfbox{#1.eps}%
+  \fi
+  %
+  \ifimagevmode \medskip \fi  % space after the standalone image
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc.  We don't actually implement floating yet, we always include the
+% float "here".  But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
+
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc.  Can't contain commas.  If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label.  Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored.  It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+  \let\thiscaption=\empty
+  \let\thisshortcaption=\empty
+  %
+  % don't lose footnotes inside @float.
+  %
+  % BEWARE: when the floats start float, we have to issue warning whenever an
+  % insert appears inside a float which could possibly float. --kasal, 26may04
+  %
+  \startsavinginserts
+  %
+  % We can't be used inside a paragraph.
+  \par
+  %
+  \vtop\bgroup
+    \def\floattype{#1}%
+    \def\floatlabel{#2}%
+    \def\floatloc{#3}% we do nothing with this yet.
+    %
+    \ifx\floattype\empty
+      \let\safefloattype=\empty
+    \else
+      {%
+        % the floattype might have accents or other special characters,
+        % but we need to use it in a control sequence name.
+        \indexnofonts
+        \turnoffactive
+        \xdef\safefloattype{\floattype}%
+      }%
+    \fi
+    %
+    % If label is given but no type, we handle that as the empty type.
+    \ifx\floatlabel\empty \else
+      % We want each FLOATTYPE to be numbered separately (Figure 1,
+      % Table 1, Figure 2, ...).  (And if no label, no number.)
+      %
+      \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+      \global\advance\floatno by 1
+      %
+      {%
+        % This magic value for \lastsection is output by \setref as the
+        % XREFLABEL-title value.  \xrefX uses it to distinguish float
+        % labels (which have a completely different output format) from
+        % node and anchor labels.  And \xrdef uses it to construct the
+        % lists of floats.
+        %
+        \edef\lastsection{\floatmagic=\safefloattype}%
+        \setref{\floatlabel}{Yfloat}%
+      }%
+    \fi
+    %
+    % start with \parskip glue, I guess.
+    \vskip\parskip
+    %
+    % Don't suppress indentation if a float happens to start a section.
+    \restorefirstparagraphindent
+}
+
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption:    Foo 1.1
+% @float Foo & @caption{Cap}:     Foo: Cap
+% @float Foo & no caption:        Foo
+% @float ,lbl & Caption{Cap}:     1.1: Cap
+% @float ,lbl & no caption:       1.1
+% @float & @caption{Cap}:         Cap
+% @float & no caption:
+%
+\def\Efloat{%
+    \let\floatident = \empty
+    %
+    % In all cases, if we have a float type, it comes first.
+    \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+    %
+    % If we have an xref label, the number comes next.
+    \ifx\floatlabel\empty \else
+      \ifx\floattype\empty \else % if also had float type, need tie first.
+        \appendtomacro\floatident{\tie}%
+      \fi
+      % the number.
+      \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+    \fi
+    %
+    % Start the printed caption with what we've constructed in
+    % \floatident, but keep it separate; we need \floatident again.
+    \let\captionline = \floatident
+    %
+    \ifx\thiscaption\empty \else
+      \ifx\floatident\empty \else
+       \appendtomacro\captionline{: }% had ident, so need a colon between
+      \fi
+      %
+      % caption text.
+      \appendtomacro\captionline{\scanexp\thiscaption}%
+    \fi
+    %
+    % If we have anything to print, print it, with space before.
+    % Eventually this needs to become an \insert.
+    \ifx\captionline\empty \else
+      \vskip.5\parskip
+      \captionline
+      %
+      % Space below caption.
+      \vskip\parskip
+    \fi
+    %
+    % If have an xref label, write the list of floats info.  Do this
+    % after the caption, to avoid chance of it being a breakpoint.
+    \ifx\floatlabel\empty \else
+      % Write the text that goes in the lof to the aux file as
+      % \floatlabel-lof.  Besides \floatident, we include the short
+      % caption if specified, else the full caption if specified, else nothing.
+      {%
+        \atdummies
+        %
+        % since we read the caption text in the macro world, where ^^M
+        % is turned into a normal character, we have to scan it back, so
+        % we don't write the literal three characters "^^M" into the aux file.
+       \scanexp{%
+         \xdef\noexpand\gtemp{%
+           \ifx\thisshortcaption\empty
+             \thiscaption
+           \else
+             \thisshortcaption
+           \fi
+         }%
+       }%
+        \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+         \ifx\gtemp\empty \else : \gtemp \fi}}%
+      }%
+    \fi
+  \egroup  % end of \vtop
+  %
+  % place the captured inserts
+  %
+  % BEWARE: when the floats start floating, we have to issue warning
+  % whenever an insert appears inside a float which could possibly
+  % float. --kasal, 26may04
+  %
+  \checkinserts
+}
+
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+  \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
+
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use.  Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+  \ifx#1\relax
+      % Haven't seen this figure type before.
+      \csname newcount\endcsname #1%
+      %
+      % Remember to reset this floatno at the next chap.
+      \expandafter\gdef\expandafter\resetallfloatnos
+        \expandafter{\resetallfloatnos #1=0 }%
+  \fi
+  \let\floatno#1%
+}
+
+% \setref calls this to get the XREFLABEL-snt value.  We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1".  We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
+
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref.  That is, the magic
+% \lastsection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string.  If so, #2 will be the
+% (safe) float type for this float.  We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+  \def\temp{#1}%
+  \def\iffloattype{#2}%
+  \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+  \def\floattype{#1}% floattype
+  {%
+    % the floattype might have accents or other special characters,
+    % but we need to use it in a control sequence name.
+    \indexnofonts
+    \turnoffactive
+    \xdef\safefloattype{\floattype}%
+  }%
+  %
+  % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+  \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+    \ifhavexrefs
+      % if the user said @listoffloats foo but never @float foo.
+      \message{\linenumber No `\safefloattype' floats to list.}%
+    \fi
+  \else
+    \begingroup
+      \leftskip=\tocindent  % indent these entries like a toc
+      \let\do=\listoffloatsdo
+      \csname floatlist\safefloattype\endcsname
+    \endgroup
+  \fi
+}
+
+% This is called on each entry in a list of floats.  We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file.  We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+  % Can't fully expand XR#1-lof because it can contain anything.  Just
+  % pass the control sequence.  On the other hand, XR#1-pg is just the
+  % page number, and we want to fully expand that so we can get a link
+  % in pdf output.
+  \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+  %
+  % use the same \entry macro we use to generate the TOC and index.
+  \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+  \writeentry
+}}
+
+
+\message{localization,}
+
+% For single-language documents, @documentlanguage is usually given very
+% early, just after @documentencoding.  Single argument is the language
+% (de) or locale (de_DE) abbreviation.
+%
+{
+  \catcode`\_ = \active
+  \globaldefs=1
+\parseargdef\documentlanguage{\begingroup
+  \let_=\normalunderscore  % normal _ character for filenames
+  \tex % read txi-??.tex file in plain TeX.
+    % Read the file by the name they passed if it exists.
+    \openin 1 txi-#1.tex
+    \ifeof 1
+      \documentlanguagetrywithoutunderscore{#1_\finish}%
+    \else
+      \globaldefs = 1  % everything in the txi-LL files needs to persist
+      \input txi-#1.tex
+    \fi
+    \closein 1
+  \endgroup % end raw TeX
+\endgroup}
+}
+%
+% If they passed de_DE, and txi-de_DE.tex doesn't exist,
+% try txi-de.tex.
+% 
+\def\documentlanguagetrywithoutunderscore#1_#2\finish{%
+  \openin 1 txi-#1.tex
+  \ifeof 1
+    \errhelp = \nolanghelp
+    \errmessage{Cannot read language file txi-#1.tex}%
+  \else
+    \input txi-#1.tex
+  \fi
+  \closein 1
+}
+%
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty.  Maybe you need to install it?  Putting it in the current
+directory should work if nowhere else does.}
+
+% This macro is called from txi-??.tex files; the first argument is the
+% \language name to set (without the "\lang@" prefix), the second and
+% third args are \{left,right}hyphenmin.
+% 
+% The language names to pass are determined when the format is built.
+% See the etex.log file created at that time, e.g.,
+% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log.
+% 
+% With TeX Live 2008, etex now includes hyphenation patterns for all
+% available languages.  This means we can support hyphenation in
+% Texinfo, at least to some extent.  (This still doesn't solve the
+% accented characters problem.)
+% 
+\catcode`@=11
+\def\txisetlanguage#1#2#3{%
+  % do not set the language if the name is undefined in the current TeX.
+  \expandafter\ifx\csname lang@#1\endcsname \relax
+    \message{no patterns for #1}%
+  \else
+    \global\language = \csname lang@#1\endcsname
+  \fi
+  % but there is no harm in adjusting the hyphenmin values regardless.
+  \global\lefthyphenmin = #2\relax
+  \global\righthyphenmin = #3\relax
+}
+
+% Helpers for encodings.
+% Set the catcode of characters 128 through 255 to the specified number.
+%
+\def\setnonasciicharscatcode#1{%
+   \count255=128
+   \loop\ifnum\count255<256
+      \global\catcode\count255=#1\relax
+      \advance\count255 by 1
+   \repeat
+}
+
+\def\setnonasciicharscatcodenonglobal#1{%
+   \count255=128
+   \loop\ifnum\count255<256
+      \catcode\count255=#1\relax
+      \advance\count255 by 1
+   \repeat
+}
+
+% @documentencoding sets the definition of non-ASCII characters
+% according to the specified encoding.
+%
+\parseargdef\documentencoding{%
+  % Encoding being declared for the document.
+  \def\declaredencoding{\csname #1.enc\endcsname}%
+  %
+  % Supported encodings: names converted to tokens in order to be able
+  % to compare them with \ifx.
+  \def\ascii{\csname US-ASCII.enc\endcsname}%
+  \def\latnine{\csname ISO-8859-15.enc\endcsname}%
+  \def\latone{\csname ISO-8859-1.enc\endcsname}%
+  \def\lattwo{\csname ISO-8859-2.enc\endcsname}%
+  \def\utfeight{\csname UTF-8.enc\endcsname}%
+  %
+  \ifx \declaredencoding \ascii
+     \asciichardefs
+  %
+  \else \ifx \declaredencoding \lattwo
+     \setnonasciicharscatcode\active
+     \lattwochardefs
+  %
+  \else \ifx \declaredencoding \latone 
+     \setnonasciicharscatcode\active
+     \latonechardefs
+  %
+  \else \ifx \declaredencoding \latnine
+     \setnonasciicharscatcode\active
+     \latninechardefs
+  %
+  \else \ifx \declaredencoding \utfeight
+     \setnonasciicharscatcode\active
+     \utfeightchardefs
+  %
+  \else 
+    \message{Unknown document encoding #1, ignoring.}%
+  %
+  \fi % utfeight
+  \fi % latnine
+  \fi % latone
+  \fi % lattwo
+  \fi % ascii
+}
+
+% A message to be logged when using a character that isn't available
+% the default font encoding (OT1).
+% 
+\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}}
+
+% Take account of \c (plain) vs. \, (Texinfo) difference.
+\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi}
+
+% First, make active non-ASCII characters in order for them to be
+% correctly categorized when TeX reads the replacement text of
+% macros containing the character definitions.
+\setnonasciicharscatcode\active
+%
+% Latin1 (ISO-8859-1) character definitions.
+\def\latonechardefs{%
+  \gdef^^a0{~} 
+  \gdef^^a1{\exclamdown}
+  \gdef^^a2{\missingcharmsg{CENT SIGN}} 
+  \gdef^^a3{{\pounds}}
+  \gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
+  \gdef^^a5{\missingcharmsg{YEN SIGN}}
+  \gdef^^a6{\missingcharmsg{BROKEN BAR}} 
+  \gdef^^a7{\S}
+  \gdef^^a8{\"{}} 
+  \gdef^^a9{\copyright} 
+  \gdef^^aa{\ordf}
+  \gdef^^ab{\guillemetleft}
+  \gdef^^ac{$\lnot$}
+  \gdef^^ad{\-} 
+  \gdef^^ae{\registeredsymbol} 
+  \gdef^^af{\={}}
+  %
+  \gdef^^b0{\textdegree}
+  \gdef^^b1{$\pm$}
+  \gdef^^b2{$^2$}
+  \gdef^^b3{$^3$}
+  \gdef^^b4{\'{}}
+  \gdef^^b5{$\mu$}
+  \gdef^^b6{\P}
+  %
+  \gdef^^b7{$^.$}
+  \gdef^^b8{\cedilla\ }
+  \gdef^^b9{$^1$}
+  \gdef^^ba{\ordm}
+  %
+  \gdef^^bb{\guilletright}
+  \gdef^^bc{$1\over4$}
+  \gdef^^bd{$1\over2$}
+  \gdef^^be{$3\over4$}
+  \gdef^^bf{\questiondown}
+  %
+  \gdef^^c0{\`A}
+  \gdef^^c1{\'A}
+  \gdef^^c2{\^A}
+  \gdef^^c3{\~A}
+  \gdef^^c4{\"A}
+  \gdef^^c5{\ringaccent A} 
+  \gdef^^c6{\AE}
+  \gdef^^c7{\cedilla C}
+  \gdef^^c8{\`E}
+  \gdef^^c9{\'E}
+  \gdef^^ca{\^E}
+  \gdef^^cb{\"E}
+  \gdef^^cc{\`I}
+  \gdef^^cd{\'I}
+  \gdef^^ce{\^I}
+  \gdef^^cf{\"I}
+  %
+  \gdef^^d0{\missingcharmsg{LATIN CAPITAL LETTER ETH}}
+  \gdef^^d1{\~N}
+  \gdef^^d2{\`O}
+  \gdef^^d3{\'O}
+  \gdef^^d4{\^O}
+  \gdef^^d5{\~O}
+  \gdef^^d6{\"O}
+  \gdef^^d7{$\times$}
+  \gdef^^d8{\O}
+  \gdef^^d9{\`U}
+  \gdef^^da{\'U}
+  \gdef^^db{\^U}
+  \gdef^^dc{\"U}
+  \gdef^^dd{\'Y}
+  \gdef^^de{\missingcharmsg{LATIN CAPITAL LETTER THORN}}
+  \gdef^^df{\ss}
+  %
+  \gdef^^e0{\`a}
+  \gdef^^e1{\'a}
+  \gdef^^e2{\^a}
+  \gdef^^e3{\~a}
+  \gdef^^e4{\"a}
+  \gdef^^e5{\ringaccent a}
+  \gdef^^e6{\ae}
+  \gdef^^e7{\cedilla c}
+  \gdef^^e8{\`e}
+  \gdef^^e9{\'e}
+  \gdef^^ea{\^e}
+  \gdef^^eb{\"e}
+  \gdef^^ec{\`{\dotless i}}
+  \gdef^^ed{\'{\dotless i}}
+  \gdef^^ee{\^{\dotless i}}
+  \gdef^^ef{\"{\dotless i}}
+  %
+  \gdef^^f0{\missingcharmsg{LATIN SMALL LETTER ETH}}
+  \gdef^^f1{\~n}
+  \gdef^^f2{\`o}
+  \gdef^^f3{\'o}
+  \gdef^^f4{\^o}
+  \gdef^^f5{\~o}
+  \gdef^^f6{\"o}
+  \gdef^^f7{$\div$}
+  \gdef^^f8{\o}
+  \gdef^^f9{\`u}
+  \gdef^^fa{\'u}
+  \gdef^^fb{\^u}
+  \gdef^^fc{\"u}
+  \gdef^^fd{\'y}
+  \gdef^^fe{\missingcharmsg{LATIN SMALL LETTER THORN}}
+  \gdef^^ff{\"y}
+}
+
+% Latin9 (ISO-8859-15) encoding character definitions.
+\def\latninechardefs{%
+  % Encoding is almost identical to Latin1.
+  \latonechardefs
+  %
+  \gdef^^a4{\euro}
+  \gdef^^a6{\v S}
+  \gdef^^a8{\v s}
+  \gdef^^b4{\v Z}
+  \gdef^^b8{\v z}
+  \gdef^^bc{\OE}
+  \gdef^^bd{\oe}
+  \gdef^^be{\"Y}
+}
+
+% Latin2 (ISO-8859-2) character definitions.
+\def\lattwochardefs{%
+  \gdef^^a0{~}
+  \gdef^^a1{\ogonek{A}}
+  \gdef^^a2{\u{}}
+  \gdef^^a3{\L}
+  \gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
+  \gdef^^a5{\v L}
+  \gdef^^a6{\'S}
+  \gdef^^a7{\S}
+  \gdef^^a8{\"{}}
+  \gdef^^a9{\v S}
+  \gdef^^aa{\cedilla S}
+  \gdef^^ab{\v T}
+  \gdef^^ac{\'Z}
+  \gdef^^ad{\-}
+  \gdef^^ae{\v Z}
+  \gdef^^af{\dotaccent Z}
+  %
+  \gdef^^b0{\textdegree}
+  \gdef^^b1{\ogonek{a}}
+  \gdef^^b2{\ogonek{ }}
+  \gdef^^b3{\l}
+  \gdef^^b4{\'{}}
+  \gdef^^b5{\v l}
+  \gdef^^b6{\'s}
+  \gdef^^b7{\v{}}
+  \gdef^^b8{\cedilla\ }
+  \gdef^^b9{\v s}
+  \gdef^^ba{\cedilla s}
+  \gdef^^bb{\v t}
+  \gdef^^bc{\'z}
+  \gdef^^bd{\H{}}
+  \gdef^^be{\v z}
+  \gdef^^bf{\dotaccent z}
+  %
+  \gdef^^c0{\'R}
+  \gdef^^c1{\'A}
+  \gdef^^c2{\^A}
+  \gdef^^c3{\u A}
+  \gdef^^c4{\"A}
+  \gdef^^c5{\'L}
+  \gdef^^c6{\'C}
+  \gdef^^c7{\cedilla C}
+  \gdef^^c8{\v C}
+  \gdef^^c9{\'E}
+  \gdef^^ca{\ogonek{E}}
+  \gdef^^cb{\"E}
+  \gdef^^cc{\v E}
+  \gdef^^cd{\'I}
+  \gdef^^ce{\^I}
+  \gdef^^cf{\v D}
+  %
+  \gdef^^d0{\missingcharmsg{LATIN CAPITAL LETTER D WITH STROKE}}
+  \gdef^^d1{\'N}
+  \gdef^^d2{\v N}
+  \gdef^^d3{\'O}
+  \gdef^^d4{\^O}
+  \gdef^^d5{\H O}
+  \gdef^^d6{\"O}
+  \gdef^^d7{$\times$}
+  \gdef^^d8{\v R}
+  \gdef^^d9{\ringaccent U} 
+  \gdef^^da{\'U}
+  \gdef^^db{\H U}
+  \gdef^^dc{\"U}
+  \gdef^^dd{\'Y}
+  \gdef^^de{\cedilla T}
+  \gdef^^df{\ss}
+  %
+  \gdef^^e0{\'r}
+  \gdef^^e1{\'a}
+  \gdef^^e2{\^a}
+  \gdef^^e3{\u a}
+  \gdef^^e4{\"a}
+  \gdef^^e5{\'l}
+  \gdef^^e6{\'c}
+  \gdef^^e7{\cedilla c}
+  \gdef^^e8{\v c}
+  \gdef^^e9{\'e}
+  \gdef^^ea{\ogonek{e}}
+  \gdef^^eb{\"e}
+  \gdef^^ec{\v e}
+  \gdef^^ed{\'\i}
+  \gdef^^ee{\^\i}
+  \gdef^^ef{\v d}
+  %
+  \gdef^^f0{\missingcharmsg{LATIN SMALL LETTER D WITH STROKE}}
+  \gdef^^f1{\'n}
+  \gdef^^f2{\v n}
+  \gdef^^f3{\'o}
+  \gdef^^f4{\^o}
+  \gdef^^f5{\H o}
+  \gdef^^f6{\"o}
+  \gdef^^f7{$\div$}
+  \gdef^^f8{\v r}
+  \gdef^^f9{\ringaccent u}
+  \gdef^^fa{\'u}
+  \gdef^^fb{\H u}
+  \gdef^^fc{\"u}
+  \gdef^^fd{\'y}
+  \gdef^^fe{\cedilla t}
+  \gdef^^ff{\dotaccent{}}
+}
+
+% UTF-8 character definitions.
+% 
+% This code to support UTF-8 is based on LaTeX's utf8.def, with some
+% changes for Texinfo conventions.  It is included here under the GPL by
+% permission from Frank Mittelbach and the LaTeX team.
+% 
+\newcount\countUTFx
+\newcount\countUTFy
+\newcount\countUTFz
+
+\gdef\UTFviiiTwoOctets#1#2{\expandafter
+   \UTFviiiDefined\csname u8:#1\string #2\endcsname}
+%
+\gdef\UTFviiiThreeOctets#1#2#3{\expandafter
+   \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname}
+%
+\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter
+   \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname}
+
+\gdef\UTFviiiDefined#1{%
+  \ifx #1\relax
+    \message{\linenumber Unicode char \string #1 not defined for Texinfo}%
+  \else
+    \expandafter #1%
+  \fi
+}
+
+\begingroup
+  \catcode`\~13
+  \catcode`\"12
+
+  \def\UTFviiiLoop{%
+    \global\catcode\countUTFx\active
+    \uccode`\~\countUTFx
+    \uppercase\expandafter{\UTFviiiTmp}%
+    \advance\countUTFx by 1
+    \ifnum\countUTFx < \countUTFy
+      \expandafter\UTFviiiLoop
+    \fi}
+
+  \countUTFx = "C2
+  \countUTFy = "E0
+  \def\UTFviiiTmp{%
+    \xdef~{\noexpand\UTFviiiTwoOctets\string~}}
+  \UTFviiiLoop
+
+  \countUTFx = "E0
+  \countUTFy = "F0
+  \def\UTFviiiTmp{%
+    \xdef~{\noexpand\UTFviiiThreeOctets\string~}}
+  \UTFviiiLoop
+
+  \countUTFx = "F0
+  \countUTFy = "F4
+  \def\UTFviiiTmp{%
+    \xdef~{\noexpand\UTFviiiFourOctets\string~}}
+  \UTFviiiLoop
+\endgroup
+
+\begingroup
+  \catcode`\"=12
+  \catcode`\<=12
+  \catcode`\.=12
+  \catcode`\,=12
+  \catcode`\;=12
+  \catcode`\!=12
+  \catcode`\~=13
+
+  \gdef\DeclareUnicodeCharacter#1#2{%
+    \countUTFz = "#1\relax
+    \wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}%
+    \begingroup
+      \parseXMLCharref
+      \def\UTFviiiTwoOctets##1##2{%
+        \csname u8:##1\string ##2\endcsname}%
+      \def\UTFviiiThreeOctets##1##2##3{%
+        \csname u8:##1\string ##2\string ##3\endcsname}%
+      \def\UTFviiiFourOctets##1##2##3##4{%
+        \csname u8:##1\string ##2\string ##3\string ##4\endcsname}%
+      \expandafter\expandafter\expandafter\expandafter
+       \expandafter\expandafter\expandafter
+       \gdef\UTFviiiTmp{#2}%
+    \endgroup}
+
+  \gdef\parseXMLCharref{%
+    \ifnum\countUTFz < "A0\relax
+      \errhelp = \EMsimple
+      \errmessage{Cannot define Unicode char value < 00A0}%
+    \else\ifnum\countUTFz < "800\relax
+      \parseUTFviiiA,%
+      \parseUTFviiiB C\UTFviiiTwoOctets.,%
+    \else\ifnum\countUTFz < "10000\relax
+      \parseUTFviiiA;%
+      \parseUTFviiiA,%
+      \parseUTFviiiB E\UTFviiiThreeOctets.{,;}%
+    \else
+      \parseUTFviiiA;%
+      \parseUTFviiiA,%
+      \parseUTFviiiA!%
+      \parseUTFviiiB F\UTFviiiFourOctets.{!,;}%
+    \fi\fi\fi
+  }
+
+  \gdef\parseUTFviiiA#1{%
+    \countUTFx = \countUTFz
+    \divide\countUTFz by 64
+    \countUTFy = \countUTFz
+    \multiply\countUTFz by 64
+    \advance\countUTFx by -\countUTFz
+    \advance\countUTFx by 128
+    \uccode `#1\countUTFx
+    \countUTFz = \countUTFy}
+
+  \gdef\parseUTFviiiB#1#2#3#4{%
+    \advance\countUTFz by "#10\relax
+    \uccode `#3\countUTFz
+    \uppercase{\gdef\UTFviiiTmp{#2#3#4}}}
+\endgroup
+
+\def\utfeightchardefs{%
+  \DeclareUnicodeCharacter{00A0}{\tie}
+  \DeclareUnicodeCharacter{00A1}{\exclamdown}
+  \DeclareUnicodeCharacter{00A3}{\pounds}
+  \DeclareUnicodeCharacter{00A8}{\"{ }}
+  \DeclareUnicodeCharacter{00A9}{\copyright}
+  \DeclareUnicodeCharacter{00AA}{\ordf}
+  \DeclareUnicodeCharacter{00AB}{\guillemetleft}
+  \DeclareUnicodeCharacter{00AD}{\-}
+  \DeclareUnicodeCharacter{00AE}{\registeredsymbol}
+  \DeclareUnicodeCharacter{00AF}{\={ }}
+
+  \DeclareUnicodeCharacter{00B0}{\ringaccent{ }}
+  \DeclareUnicodeCharacter{00B4}{\'{ }}
+  \DeclareUnicodeCharacter{00B8}{\cedilla{ }}
+  \DeclareUnicodeCharacter{00BA}{\ordm}
+  \DeclareUnicodeCharacter{00BB}{\guillemetright}
+  \DeclareUnicodeCharacter{00BF}{\questiondown}
+
+  \DeclareUnicodeCharacter{00C0}{\`A}
+  \DeclareUnicodeCharacter{00C1}{\'A}
+  \DeclareUnicodeCharacter{00C2}{\^A}
+  \DeclareUnicodeCharacter{00C3}{\~A}
+  \DeclareUnicodeCharacter{00C4}{\"A}
+  \DeclareUnicodeCharacter{00C5}{\AA}
+  \DeclareUnicodeCharacter{00C6}{\AE}
+  \DeclareUnicodeCharacter{00C7}{\cedilla{C}}
+  \DeclareUnicodeCharacter{00C8}{\`E}
+  \DeclareUnicodeCharacter{00C9}{\'E}
+  \DeclareUnicodeCharacter{00CA}{\^E}
+  \DeclareUnicodeCharacter{00CB}{\"E}
+  \DeclareUnicodeCharacter{00CC}{\`I}
+  \DeclareUnicodeCharacter{00CD}{\'I}
+  \DeclareUnicodeCharacter{00CE}{\^I}
+  \DeclareUnicodeCharacter{00CF}{\"I}
+
+  \DeclareUnicodeCharacter{00D1}{\~N}
+  \DeclareUnicodeCharacter{00D2}{\`O}
+  \DeclareUnicodeCharacter{00D3}{\'O}
+  \DeclareUnicodeCharacter{00D4}{\^O}
+  \DeclareUnicodeCharacter{00D5}{\~O}
+  \DeclareUnicodeCharacter{00D6}{\"O}
+  \DeclareUnicodeCharacter{00D8}{\O}
+  \DeclareUnicodeCharacter{00D9}{\`U}
+  \DeclareUnicodeCharacter{00DA}{\'U}
+  \DeclareUnicodeCharacter{00DB}{\^U}
+  \DeclareUnicodeCharacter{00DC}{\"U}
+  \DeclareUnicodeCharacter{00DD}{\'Y}
+  \DeclareUnicodeCharacter{00DF}{\ss}
+
+  \DeclareUnicodeCharacter{00E0}{\`a}
+  \DeclareUnicodeCharacter{00E1}{\'a}
+  \DeclareUnicodeCharacter{00E2}{\^a}
+  \DeclareUnicodeCharacter{00E3}{\~a}
+  \DeclareUnicodeCharacter{00E4}{\"a}
+  \DeclareUnicodeCharacter{00E5}{\aa}
+  \DeclareUnicodeCharacter{00E6}{\ae}
+  \DeclareUnicodeCharacter{00E7}{\cedilla{c}}
+  \DeclareUnicodeCharacter{00E8}{\`e}
+  \DeclareUnicodeCharacter{00E9}{\'e}
+  \DeclareUnicodeCharacter{00EA}{\^e}
+  \DeclareUnicodeCharacter{00EB}{\"e}
+  \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}}
+  \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}}
+  \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}}
+  \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}}
+
+  \DeclareUnicodeCharacter{00F1}{\~n}
+  \DeclareUnicodeCharacter{00F2}{\`o}
+  \DeclareUnicodeCharacter{00F3}{\'o}
+  \DeclareUnicodeCharacter{00F4}{\^o}
+  \DeclareUnicodeCharacter{00F5}{\~o}
+  \DeclareUnicodeCharacter{00F6}{\"o}
+  \DeclareUnicodeCharacter{00F8}{\o}
+  \DeclareUnicodeCharacter{00F9}{\`u}
+  \DeclareUnicodeCharacter{00FA}{\'u}
+  \DeclareUnicodeCharacter{00FB}{\^u}
+  \DeclareUnicodeCharacter{00FC}{\"u}
+  \DeclareUnicodeCharacter{00FD}{\'y}
+  \DeclareUnicodeCharacter{00FF}{\"y}
+
+  \DeclareUnicodeCharacter{0100}{\=A}
+  \DeclareUnicodeCharacter{0101}{\=a}
+  \DeclareUnicodeCharacter{0102}{\u{A}}
+  \DeclareUnicodeCharacter{0103}{\u{a}}
+  \DeclareUnicodeCharacter{0104}{\ogonek{A}}
+  \DeclareUnicodeCharacter{0105}{\ogonek{a}}
+  \DeclareUnicodeCharacter{0106}{\'C}
+  \DeclareUnicodeCharacter{0107}{\'c}
+  \DeclareUnicodeCharacter{0108}{\^C}
+  \DeclareUnicodeCharacter{0109}{\^c}
+  \DeclareUnicodeCharacter{0118}{\ogonek{E}}
+  \DeclareUnicodeCharacter{0119}{\ogonek{e}}
+  \DeclareUnicodeCharacter{010A}{\dotaccent{C}}
+  \DeclareUnicodeCharacter{010B}{\dotaccent{c}}
+  \DeclareUnicodeCharacter{010C}{\v{C}}
+  \DeclareUnicodeCharacter{010D}{\v{c}}
+  \DeclareUnicodeCharacter{010E}{\v{D}}
+
+  \DeclareUnicodeCharacter{0112}{\=E}
+  \DeclareUnicodeCharacter{0113}{\=e}
+  \DeclareUnicodeCharacter{0114}{\u{E}}
+  \DeclareUnicodeCharacter{0115}{\u{e}}
+  \DeclareUnicodeCharacter{0116}{\dotaccent{E}}
+  \DeclareUnicodeCharacter{0117}{\dotaccent{e}}
+  \DeclareUnicodeCharacter{011A}{\v{E}}
+  \DeclareUnicodeCharacter{011B}{\v{e}}
+  \DeclareUnicodeCharacter{011C}{\^G}
+  \DeclareUnicodeCharacter{011D}{\^g}
+  \DeclareUnicodeCharacter{011E}{\u{G}}
+  \DeclareUnicodeCharacter{011F}{\u{g}}
+
+  \DeclareUnicodeCharacter{0120}{\dotaccent{G}}
+  \DeclareUnicodeCharacter{0121}{\dotaccent{g}}
+  \DeclareUnicodeCharacter{0124}{\^H}
+  \DeclareUnicodeCharacter{0125}{\^h}
+  \DeclareUnicodeCharacter{0128}{\~I}
+  \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}
+  \DeclareUnicodeCharacter{012A}{\=I}
+  \DeclareUnicodeCharacter{012B}{\={\dotless{i}}}
+  \DeclareUnicodeCharacter{012C}{\u{I}}
+  \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}
+
+  \DeclareUnicodeCharacter{0130}{\dotaccent{I}}
+  \DeclareUnicodeCharacter{0131}{\dotless{i}}
+  \DeclareUnicodeCharacter{0132}{IJ}
+  \DeclareUnicodeCharacter{0133}{ij}
+  \DeclareUnicodeCharacter{0134}{\^J}
+  \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}
+  \DeclareUnicodeCharacter{0139}{\'L}
+  \DeclareUnicodeCharacter{013A}{\'l}
+
+  \DeclareUnicodeCharacter{0141}{\L}
+  \DeclareUnicodeCharacter{0142}{\l}
+  \DeclareUnicodeCharacter{0143}{\'N}
+  \DeclareUnicodeCharacter{0144}{\'n}
+  \DeclareUnicodeCharacter{0147}{\v{N}}
+  \DeclareUnicodeCharacter{0148}{\v{n}}
+  \DeclareUnicodeCharacter{014C}{\=O}
+  \DeclareUnicodeCharacter{014D}{\=o}
+  \DeclareUnicodeCharacter{014E}{\u{O}}
+  \DeclareUnicodeCharacter{014F}{\u{o}}
+
+  \DeclareUnicodeCharacter{0150}{\H{O}}
+  \DeclareUnicodeCharacter{0151}{\H{o}}
+  \DeclareUnicodeCharacter{0152}{\OE}
+  \DeclareUnicodeCharacter{0153}{\oe}
+  \DeclareUnicodeCharacter{0154}{\'R}
+  \DeclareUnicodeCharacter{0155}{\'r}
+  \DeclareUnicodeCharacter{0158}{\v{R}}
+  \DeclareUnicodeCharacter{0159}{\v{r}}
+  \DeclareUnicodeCharacter{015A}{\'S}
+  \DeclareUnicodeCharacter{015B}{\'s}
+  \DeclareUnicodeCharacter{015C}{\^S}
+  \DeclareUnicodeCharacter{015D}{\^s}
+  \DeclareUnicodeCharacter{015E}{\cedilla{S}}
+  \DeclareUnicodeCharacter{015F}{\cedilla{s}}
+
+  \DeclareUnicodeCharacter{0160}{\v{S}}
+  \DeclareUnicodeCharacter{0161}{\v{s}}
+  \DeclareUnicodeCharacter{0162}{\cedilla{t}}
+  \DeclareUnicodeCharacter{0163}{\cedilla{T}}
+  \DeclareUnicodeCharacter{0164}{\v{T}}
+
+  \DeclareUnicodeCharacter{0168}{\~U}
+  \DeclareUnicodeCharacter{0169}{\~u}
+  \DeclareUnicodeCharacter{016A}{\=U}
+  \DeclareUnicodeCharacter{016B}{\=u}
+  \DeclareUnicodeCharacter{016C}{\u{U}}
+  \DeclareUnicodeCharacter{016D}{\u{u}}
+  \DeclareUnicodeCharacter{016E}{\ringaccent{U}}
+  \DeclareUnicodeCharacter{016F}{\ringaccent{u}}
+
+  \DeclareUnicodeCharacter{0170}{\H{U}}
+  \DeclareUnicodeCharacter{0171}{\H{u}}
+  \DeclareUnicodeCharacter{0174}{\^W}
+  \DeclareUnicodeCharacter{0175}{\^w}
+  \DeclareUnicodeCharacter{0176}{\^Y}
+  \DeclareUnicodeCharacter{0177}{\^y}
+  \DeclareUnicodeCharacter{0178}{\"Y}
+  \DeclareUnicodeCharacter{0179}{\'Z}
+  \DeclareUnicodeCharacter{017A}{\'z}
+  \DeclareUnicodeCharacter{017B}{\dotaccent{Z}}
+  \DeclareUnicodeCharacter{017C}{\dotaccent{z}}
+  \DeclareUnicodeCharacter{017D}{\v{Z}}
+  \DeclareUnicodeCharacter{017E}{\v{z}}
+
+  \DeclareUnicodeCharacter{01C4}{D\v{Z}}
+  \DeclareUnicodeCharacter{01C5}{D\v{z}}
+  \DeclareUnicodeCharacter{01C6}{d\v{z}}
+  \DeclareUnicodeCharacter{01C7}{LJ}
+  \DeclareUnicodeCharacter{01C8}{Lj}
+  \DeclareUnicodeCharacter{01C9}{lj}
+  \DeclareUnicodeCharacter{01CA}{NJ}
+  \DeclareUnicodeCharacter{01CB}{Nj}
+  \DeclareUnicodeCharacter{01CC}{nj}
+  \DeclareUnicodeCharacter{01CD}{\v{A}}
+  \DeclareUnicodeCharacter{01CE}{\v{a}}
+  \DeclareUnicodeCharacter{01CF}{\v{I}}
+
+  \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}}
+  \DeclareUnicodeCharacter{01D1}{\v{O}}
+  \DeclareUnicodeCharacter{01D2}{\v{o}}
+  \DeclareUnicodeCharacter{01D3}{\v{U}}
+  \DeclareUnicodeCharacter{01D4}{\v{u}}
+
+  \DeclareUnicodeCharacter{01E2}{\={\AE}}
+  \DeclareUnicodeCharacter{01E3}{\={\ae}}
+  \DeclareUnicodeCharacter{01E6}{\v{G}}
+  \DeclareUnicodeCharacter{01E7}{\v{g}}
+  \DeclareUnicodeCharacter{01E8}{\v{K}}
+  \DeclareUnicodeCharacter{01E9}{\v{k}}
+
+  \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}}
+  \DeclareUnicodeCharacter{01F1}{DZ}
+  \DeclareUnicodeCharacter{01F2}{Dz}
+  \DeclareUnicodeCharacter{01F3}{dz}
+  \DeclareUnicodeCharacter{01F4}{\'G}
+  \DeclareUnicodeCharacter{01F5}{\'g}
+  \DeclareUnicodeCharacter{01F8}{\`N}
+  \DeclareUnicodeCharacter{01F9}{\`n}
+  \DeclareUnicodeCharacter{01FC}{\'{\AE}}
+  \DeclareUnicodeCharacter{01FD}{\'{\ae}}
+  \DeclareUnicodeCharacter{01FE}{\'{\O}}
+  \DeclareUnicodeCharacter{01FF}{\'{\o}}
+
+  \DeclareUnicodeCharacter{021E}{\v{H}}
+  \DeclareUnicodeCharacter{021F}{\v{h}}
+
+  \DeclareUnicodeCharacter{0226}{\dotaccent{A}}
+  \DeclareUnicodeCharacter{0227}{\dotaccent{a}}
+  \DeclareUnicodeCharacter{0228}{\cedilla{E}}
+  \DeclareUnicodeCharacter{0229}{\cedilla{e}}
+  \DeclareUnicodeCharacter{022E}{\dotaccent{O}}
+  \DeclareUnicodeCharacter{022F}{\dotaccent{o}}
+
+  \DeclareUnicodeCharacter{0232}{\=Y}
+  \DeclareUnicodeCharacter{0233}{\=y}
+  \DeclareUnicodeCharacter{0237}{\dotless{j}}
+
+  \DeclareUnicodeCharacter{02DB}{\ogonek{ }}
+
+  \DeclareUnicodeCharacter{1E02}{\dotaccent{B}}
+  \DeclareUnicodeCharacter{1E03}{\dotaccent{b}}
+  \DeclareUnicodeCharacter{1E04}{\udotaccent{B}}
+  \DeclareUnicodeCharacter{1E05}{\udotaccent{b}}
+  \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}}
+  \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}}
+  \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}}
+  \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}}
+  \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}}
+  \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}}
+  \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}}
+  \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}}
+
+  \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}}
+  \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}}
+
+  \DeclareUnicodeCharacter{1E20}{\=G}
+  \DeclareUnicodeCharacter{1E21}{\=g}
+  \DeclareUnicodeCharacter{1E22}{\dotaccent{H}}
+  \DeclareUnicodeCharacter{1E23}{\dotaccent{h}}
+  \DeclareUnicodeCharacter{1E24}{\udotaccent{H}}
+  \DeclareUnicodeCharacter{1E25}{\udotaccent{h}}
+  \DeclareUnicodeCharacter{1E26}{\"H}
+  \DeclareUnicodeCharacter{1E27}{\"h}
+
+  \DeclareUnicodeCharacter{1E30}{\'K}
+  \DeclareUnicodeCharacter{1E31}{\'k}
+  \DeclareUnicodeCharacter{1E32}{\udotaccent{K}}
+  \DeclareUnicodeCharacter{1E33}{\udotaccent{k}}
+  \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}}
+  \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}}
+  \DeclareUnicodeCharacter{1E36}{\udotaccent{L}}
+  \DeclareUnicodeCharacter{1E37}{\udotaccent{l}}
+  \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}}
+  \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}}
+  \DeclareUnicodeCharacter{1E3E}{\'M}
+  \DeclareUnicodeCharacter{1E3F}{\'m}
+
+  \DeclareUnicodeCharacter{1E40}{\dotaccent{M}}
+  \DeclareUnicodeCharacter{1E41}{\dotaccent{m}}
+  \DeclareUnicodeCharacter{1E42}{\udotaccent{M}}
+  \DeclareUnicodeCharacter{1E43}{\udotaccent{m}}
+  \DeclareUnicodeCharacter{1E44}{\dotaccent{N}}
+  \DeclareUnicodeCharacter{1E45}{\dotaccent{n}}
+  \DeclareUnicodeCharacter{1E46}{\udotaccent{N}}
+  \DeclareUnicodeCharacter{1E47}{\udotaccent{n}}
+  \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}}
+  \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}}
+
+  \DeclareUnicodeCharacter{1E54}{\'P}
+  \DeclareUnicodeCharacter{1E55}{\'p}
+  \DeclareUnicodeCharacter{1E56}{\dotaccent{P}}
+  \DeclareUnicodeCharacter{1E57}{\dotaccent{p}}
+  \DeclareUnicodeCharacter{1E58}{\dotaccent{R}}
+  \DeclareUnicodeCharacter{1E59}{\dotaccent{r}}
+  \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}}
+  \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}}
+  \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}}
+  \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}}
+
+  \DeclareUnicodeCharacter{1E60}{\dotaccent{S}}
+  \DeclareUnicodeCharacter{1E61}{\dotaccent{s}}
+  \DeclareUnicodeCharacter{1E62}{\udotaccent{S}}
+  \DeclareUnicodeCharacter{1E63}{\udotaccent{s}}
+  \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}}
+  \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}}
+  \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}}
+  \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}}
+  \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}}
+  \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}}
+
+  \DeclareUnicodeCharacter{1E7C}{\~V}
+  \DeclareUnicodeCharacter{1E7D}{\~v}
+  \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}}
+  \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}}
+
+  \DeclareUnicodeCharacter{1E80}{\`W}
+  \DeclareUnicodeCharacter{1E81}{\`w}
+  \DeclareUnicodeCharacter{1E82}{\'W}
+  \DeclareUnicodeCharacter{1E83}{\'w}
+  \DeclareUnicodeCharacter{1E84}{\"W}
+  \DeclareUnicodeCharacter{1E85}{\"w}
+  \DeclareUnicodeCharacter{1E86}{\dotaccent{W}}
+  \DeclareUnicodeCharacter{1E87}{\dotaccent{w}}
+  \DeclareUnicodeCharacter{1E88}{\udotaccent{W}}
+  \DeclareUnicodeCharacter{1E89}{\udotaccent{w}}
+  \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}}
+  \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}}
+  \DeclareUnicodeCharacter{1E8C}{\"X}
+  \DeclareUnicodeCharacter{1E8D}{\"x}
+  \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}}
+  \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}}
+
+  \DeclareUnicodeCharacter{1E90}{\^Z}
+  \DeclareUnicodeCharacter{1E91}{\^z}
+  \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}}
+  \DeclareUnicodeCharacter{1E93}{\udotaccent{z}}
+  \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}}
+  \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}}
+  \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}}
+  \DeclareUnicodeCharacter{1E97}{\"t}
+  \DeclareUnicodeCharacter{1E98}{\ringaccent{w}}
+  \DeclareUnicodeCharacter{1E99}{\ringaccent{y}}
+
+  \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}}
+  \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}}
+
+  \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}}
+  \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}}
+  \DeclareUnicodeCharacter{1EBC}{\~E}
+  \DeclareUnicodeCharacter{1EBD}{\~e}
+
+  \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}}
+  \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}}
+  \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}}
+  \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}}
+
+  \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}}
+  \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}}
+
+  \DeclareUnicodeCharacter{1EF2}{\`Y}
+  \DeclareUnicodeCharacter{1EF3}{\`y}
+  \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}}
+
+  \DeclareUnicodeCharacter{1EF8}{\~Y}
+  \DeclareUnicodeCharacter{1EF9}{\~y}
+
+  \DeclareUnicodeCharacter{2013}{--}
+  \DeclareUnicodeCharacter{2014}{---}
+  \DeclareUnicodeCharacter{2018}{\quoteleft}
+  \DeclareUnicodeCharacter{2019}{\quoteright}
+  \DeclareUnicodeCharacter{201A}{\quotesinglbase}
+  \DeclareUnicodeCharacter{201C}{\quotedblleft}
+  \DeclareUnicodeCharacter{201D}{\quotedblright}
+  \DeclareUnicodeCharacter{201E}{\quotedblbase}
+  \DeclareUnicodeCharacter{2022}{\bullet}
+  \DeclareUnicodeCharacter{2026}{\dots}
+  \DeclareUnicodeCharacter{2039}{\guilsinglleft}
+  \DeclareUnicodeCharacter{203A}{\guilsinglright}
+  \DeclareUnicodeCharacter{20AC}{\euro}
+
+  \DeclareUnicodeCharacter{2192}{\expansion}
+  \DeclareUnicodeCharacter{21D2}{\result}
+
+  \DeclareUnicodeCharacter{2212}{\minus}
+  \DeclareUnicodeCharacter{2217}{\point}
+  \DeclareUnicodeCharacter{2261}{\equiv}
+}% end of \utfeightchardefs
+
+
+% US-ASCII character definitions.
+\def\asciichardefs{% nothing need be done
+   \relax
+}
+
+% Make non-ASCII characters printable again for compatibility with
+% existing Texinfo documents that may use them, even without declaring a
+% document encoding.
+%
+\setnonasciicharscatcode \other
+
+
+\message{formatting,}
+
+\newdimen\defaultparindent \defaultparindent = 15pt
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
+
+% Prevent underfull vbox error messages.
+\vbadness = 10000
+
+% Don't be so finicky about underfull hboxes, either.
+\hbadness = 2000
+
+% Following George Bush, get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything.  We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize.  We call this whenever the paper size is set.
+%
+\def\setemergencystretch{%
+  \ifx\emergencystretch\thisisundefined
+    % Allow us to assign to \emergencystretch anyway.
+    \def\emergencystretch{\dimen0}%
+  \else
+    \emergencystretch = .15\hsize
+  \fi
+}
+
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading.  The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+  \voffset = #3\relax
+  \topskip = #6\relax
+  \splittopskip = \topskip
+  %
+  \vsize = #1\relax
+  \advance\vsize by \topskip
+  \outervsize = \vsize
+  \advance\outervsize by 2\topandbottommargin
+  \pageheight = \vsize
+  %
+  \hsize = #2\relax
+  \outerhsize = \hsize
+  \advance\outerhsize by 0.5in
+  \pagewidth = \hsize
+  %
+  \normaloffset = #4\relax
+  \bindingoffset = #5\relax
+  %
+  \ifpdf
+    \pdfpageheight #7\relax
+    \pdfpagewidth #8\relax
+    % if we don't reset these, they will remain at "1 true in" of
+    % whatever layout pdftex was dumped with.
+    \pdfhorigin = 1 true in
+    \pdfvorigin = 1 true in
+  \fi
+  %
+  \setleading{\textleading}
+  %
+  \parindent = \defaultparindent
+  \setemergencystretch
+}
+
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+  \parskip = 3pt plus 2pt minus 1pt
+  \textleading = 13.2pt
+  %
+  % If page is nothing but text, make it come out even.
+  \internalpagesizes{607.2pt}{6in}% that's 46 lines
+                    {\voffset}{.25in}%
+                    {\bindingoffset}{36pt}%
+                    {11in}{8.5in}%
+}}
+
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+  \parskip = 2pt plus 1pt
+  \textleading = 12pt
+  %
+  \internalpagesizes{7.5in}{5in}%
+                    {-.2in}{0in}%
+                    {\bindingoffset}{16pt}%
+                    {9.25in}{7in}%
+  %
+  \lispnarrowing = 0.3in
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = .5cm
+}}
+
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+  \parskip = 1.5pt plus 1pt
+  \textleading = 12pt
+  %
+  \internalpagesizes{7.4in}{4.8in}%
+                    {-.2in}{-.4in}%
+                    {0pt}{14pt}%
+                    {9in}{6in}%
+  %
+  \lispnarrowing = 0.25in
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = .4cm
+}}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{{\globaldefs = 1
+  \parskip = 3pt plus 2pt minus 1pt
+  \textleading = 13.2pt
+  %
+  % Double-side printing via postscript on Laserjet 4050
+  % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+  % To change the settings for a different printer or situation, adjust
+  % \normaloffset until the front-side and back-side texts align.  Then
+  % do the same for \bindingoffset.  You can set these for testing in
+  % your texinfo source file like this:
+  % @tex
+  % \global\normaloffset = -6mm
+  % \global\bindingoffset = 10mm
+  % @end tex
+  \internalpagesizes{673.2pt}{160mm}% that's 51 lines
+                    {\voffset}{\hoffset}%
+                    {\bindingoffset}{44pt}%
+                    {297mm}{210mm}%
+  %
+  \tolerance = 700
+  \hfuzz = 1pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+  \parskip = 2pt plus 1pt minus 0.1pt
+  \textleading = 12.5pt
+  %
+  \internalpagesizes{160mm}{120mm}%
+                    {\voffset}{\hoffset}%
+                    {\bindingoffset}{8pt}%
+                    {210mm}{148mm}%
+  %
+  \lispnarrowing = 0.2in
+  \tolerance = 800
+  \hfuzz = 1.2pt
+  \contentsrightmargin = 0pt
+  \defbodyindent = 2mm
+  \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+  \afourpaper
+  \internalpagesizes{237mm}{150mm}%
+                    {\voffset}{4.6mm}%
+                    {\bindingoffset}{7mm}%
+                    {297mm}{210mm}%
+  %
+  % Must explicitly reset to 0 because we call \afourpaper.
+  \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+  \afourpaper
+  \internalpagesizes{241mm}{165mm}%
+                    {\voffset}{-2.95mm}%
+                    {\bindingoffset}{7mm}%
+                    {297mm}{210mm}%
+  \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+  \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+  \globaldefs = 1
+  %
+  \parskip = 3pt plus 2pt minus 1pt
+  \setleading{\textleading}%
+  %
+  \dimen0 = #1\relax
+  \advance\dimen0 by \voffset
+  %
+  \dimen2 = \hsize
+  \advance\dimen2 by \normaloffset
+  %
+  \internalpagesizes{#1}{\hsize}%
+                    {\voffset}{\normaloffset}%
+                    {\bindingoffset}{44pt}%
+                    {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+
+\message{and turning on texinfo input format.}
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other
+\catcode`\~=\other
+\catcode`\^=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode`\+=\other
+\catcode`\$=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+\def\normaldollar{$}%$ font-lock fix
+
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise.  Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font.  Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts.  But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary).
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt\char34}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt\char126}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def^{{\tt \hat}}
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+\let\realunder=_
+% Subroutine for the previous macro.
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
+
+\catcode`\|=\active
+\def|{{\tt\char124}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+\catcode`\$=\active
+\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have \everyjob (or @setfilename) turn them on.
+% \otherifyactive is called near the end of this file.
+\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+  \normalturnoffactive
+  \otherbackslash
+}
+
+\catcode`\@=0
+
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+\global\let\rawbackslashxx=\backslashcurfont  % let existing .??s files work
+
+% \realbackslash is an actual character `\' with catcode other, and
+% \doublebackslash is two of them (for the pdf outlines).
+{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
+
+% In texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
+\catcode`\\=\active
+@def@normalbackslash{{@tt@backslashcurfont}}
+% On startup, @fixbackslash assigns:
+%  @let \ = @normalbackslash
+
+% \rawbackslash defines an active \ to do \backslashcurfont.
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.
+@gdef@rawbackslash{@let\=@backslashcurfont}
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+% 
+@def@normalturnoffactive{%
+  @let\=@normalbackslash
+  @let"=@normaldoublequote
+  @let~=@normaltilde
+  @let^=@normalcaret
+  @let_=@normalunderscore
+  @let|=@normalverticalbar
+  @let<=@normalless
+  @let>=@normalgreater
+  @let+=@normalplus
+  @let$=@normaldollar %$ font-lock fix
+  @markupsetuplqdefault
+  @markupsetuprqdefault
+  @unsepspaces
+}
+
+% Make _ and + \other characters, temporarily.
+% This is canceled by @fixbackslash.
+@otherifyactive
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\' in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+% Also turn back on active characters that might appear in the input
+% file name, in case not using a pre-dumped format.
+%
+@gdef@fixbackslash{%
+  @ifx\@eatinput @let\ = @normalbackslash @fi
+  @catcode`+=@active
+  @catcode`@_=@active
+}
+
+% Say @foo, not \foo, in error messages.
+@escapechar = `@@
+
+% These look ok in all fonts, so just make them not special.
+@catcode`@& = @other
+@catcode`@# = @other
+@catcode`@% = @other
+
+@c Finally, make ` and ' active, so that txicodequoteundirected and
+@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}.  If we
+@c don't make ` and ' active, @code will not get them as active chars.
+@c Do this last of all since we use ` in the previous @catcode assignments.
+@catcode`@'=@active
+@catcode`@`=@active
+@markupsetuplqdefault
+@markupsetuprqdefault
+@c Local variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
+@c page-delimiter: "^\\\\message"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
+@c End:
+
+@c vim:sw=2:
+
+@ignore
+   arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
+@end ignore