From: John Darrington Date: Sun, 10 Jul 2016 05:47:34 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/master' into sheet X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f9b2322d7b0662bd313d9c63450638c39b88be70;hp=55c55aa33d0f90d1b3b58f8b33b3fc54062c553e;p=pspp Merge remote-tracking branch 'origin/master' into sheet --- diff --git a/NEWS b/NEWS index 3b37cd78cf..7391ff4cc5 100644 --- a/NEWS +++ b/NEWS @@ -3,10 +3,29 @@ Copyright (C) 1996-2000, 2008-2016 Free Software Foundation, Inc. See the end for copying conditions. Please send PSPP bug reports to bug-gnu-pspp@gnu.org. - + Changes from 0.10.1 to 0.10.2: - (nothing yet) + * CROSSTABS implements a new COUNT subcommand to round case or cell + weights. + + * Help will be opened as HTML in default browser if yelp + is not available. + + * When plotting scatterplots with only one dataset (the simple case) + the colour used for the dataset is now black. The previous default + from the Tango palette was too faint to see easily. + + * Bug fixes, including the following: + + - T-test with independent samples GUI crashed with string + type variable as group variable. + + - The variable info dialog showed the previous selected variable + superimposed with the currently selected one. + + - The GLM command did not properly deal with missing values. This + has been fixed. Changes from 0.10.0 to 0.10.1: diff --git a/README.Git b/README.Git index 8333600457..123665095b 100644 --- a/README.Git +++ b/README.Git @@ -36,14 +36,11 @@ 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 c5c4f53b31f1ad04f4033a6124120ffec9257847 - Author: Pádraig Brady - Date: Sat Dec 6 01:14:02 2014 +0000 + commit baef0a4b9433d00e59c586b9eaad67d8461d7324 + Author: Karl Berry + Date: Tue Jun 14 09:05:27 2016 -0700 - vasnprintf: fix potential use after free - - * lib/vasnprintf.c (VASNPRINTF): Fix free-memory read, - flagged by clang-analyzer 3.4.2. + autoupdate To clone Gnulib into a directory named "gnulib" using Git, and then check out this particular commit, run these commands: diff --git a/doc/statistics.texi b/doc/statistics.texi index 9774c514aa..e18c6109db 100644 --- a/doc/statistics.texi +++ b/doc/statistics.texi @@ -597,6 +597,8 @@ CROSSTABS @{BOX,NOBOX@} /CELLS=@{COUNT,ROW,COLUMN,TOTAL,EXPECTED,RESIDUAL,SRESIDUAL, ASRESIDUAL,ALL,NONE@} + /COUNT=@{ASIS,CASE,CELL@} + @{ROUND,TRUNCATE@} /STATISTICS=@{CHISQ,PHI,CC,LAMBDA,UC,BTAU,CTAU,RISK,GAMMA,D, KAPPA,ETA,CORR,ALL,NONE@} /BARCHART @@ -696,6 +698,15 @@ Suppress cells entirely. If @subcmd{CELLS} is not specified at all then only @subcmd{COUNT} will be selected. +By default, crosstabulation and statistics use raw case weights, +without rounding. Use the @subcmd{/COUNT} subcommand to perform +rounding: CASE rounds the weights of individual weights as cases are +read, CELL rounds the weights of cells within each crosstabulation +table after it has been constructed, and ASIS explicitly specifies the +default non-rounding behavior. When rounding is requested, ROUND, the +default, rounds to the nearest integer and TRUNCATE rounds toward +zero. + The @subcmd{STATISTICS} subcommand selects statistics for computation: @table @asis @@ -940,12 +951,13 @@ implies the model The @subcmd{MISSING} subcommand determines the handling of missing variables. -If @subcmd{INCLUDE} is set, then user-missing values are included in the -calculations, but system-missing values are not. -If @subcmd{EXCLUDE} is set, which is the default, user-missing -values are excluded as well as system-missing values. -This is the default. - +If @subcmd{INCLUDE} is set then, for the purposes of GLM analysis, +only system-missing values are considered +to be missing; user-missing values are not regarded as missing. +If @subcmd{EXCLUDE} is set, which is the default, then user-missing +values are considered to be missing as well as system-missing values. +A case for which any dependent variable or any factor +variable has a missing value is excluded from the analysis. @node LOGISTIC REGRESSION @section LOGISTIC REGRESSION diff --git a/src/data/casereader-filter.c b/src/data/casereader-filter.c index 15fa4a5e67..5b6cf06b72 100644 --- a/src/data/casereader-filter.c +++ b/src/data/casereader-filter.c @@ -266,7 +266,7 @@ static bool casereader_filter_missing_destroy (void *); is destroyed. If N_MISSING is non-null, then after reading, it will be filled - with the totla number of dropped cases. + with the total number of dropped cases. After this function is called, READER must not ever again be referenced directly. It will be destroyed automatically diff --git a/src/data/gnumeric-reader.c b/src/data/gnumeric-reader.c index 7033448d8b..ba4218ff77 100644 --- a/src/data/gnumeric-reader.c +++ b/src/data/gnumeric-reader.c @@ -38,6 +38,19 @@ gnumeric_open_reader (const struct spreadsheet_read_options *opts, struct dictio return NULL; } +struct casereader * +gnumeric_make_reader (struct spreadsheet *spreadsheet, + const struct spreadsheet_read_options *opts) +{ + return NULL; +} + +void +gnumeric_unref (struct spreadsheet *r) +{ +} + + #else #include "data/gnumeric-reader.h" diff --git a/src/data/ods-reader.c b/src/data/ods-reader.c index 2f058b044d..c2e7a0e354 100644 --- a/src/data/ods-reader.c +++ b/src/data/ods-reader.c @@ -43,6 +43,19 @@ ods_open_reader (const struct spreadsheet_read_options *opts, return NULL; } +struct casereader * +ods_make_reader (struct spreadsheet *spreadsheet, + const struct spreadsheet_read_options *opts) +{ + return NULL; +} + + +void +ods_unref (struct spreadsheet *r) +{ +} + #else #include "libpspp/zip-reader.h" diff --git a/src/language/data-io/.gitignore b/src/language/data-io/.gitignore deleted file mode 100644 index cd4131076c..0000000000 --- a/src/language/data-io/.gitignore +++ /dev/null @@ -1 +0,0 @@ -file-handle.c diff --git a/src/language/data-io/automake.mk b/src/language/data-io/automake.mk index 906a8ed158..770300ee77 100644 --- a/src/language/data-io/automake.mk +++ b/src/language/data-io/automake.mk @@ -1,8 +1,5 @@ ## Process this file with automake to produce Makefile.in -*- makefile -*- -src_language_data_io_built_sources = \ - src/language/data-io/file-handle.c - language_data_io_sources = \ src/language/data-io/combine-files.c \ src/language/data-io/data-list.c \ @@ -13,6 +10,7 @@ language_data_io_sources = \ src/language/data-io/data-writer.c \ src/language/data-io/data-writer.h \ src/language/data-io/dataset.c \ + src/language/data-io/file-handle.c \ src/language/data-io/file-handle.h \ src/language/data-io/get-data.c \ src/language/data-io/get.c \ @@ -27,7 +25,3 @@ language_data_io_sources = \ 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) -CLEANFILES += $(src_language_data_io_built_sources) diff --git a/src/language/data-io/file-handle.c b/src/language/data-io/file-handle.c new file mode 100644 index 0000000000..8d65418db7 --- /dev/null +++ b/src/language/data-io/file-handle.c @@ -0,0 +1,378 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-2000, 2006, 2010-2013, 2016 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "data/file-handle-def.h" + +#include +#include +#include + +#include "data/file-name.h" +#include "data/session.h" +#include "data/variable.h" +#include "language/command.h" +#include "language/data-io/file-handle.h" +#include "language/lexer/lexer.h" +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/message.h" +#include "libpspp/str.h" + +#include "gl/xalloc.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +int +cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED) +{ + enum cmd_result result = CMD_CASCADING_FAILURE; + char *handle_name = NULL; + char *file_name = NULL; + int lrecl = 0; + int tabwidth = -1; + enum { MODE_DEFAULT, MODE_CHARACTER, MODE_BINARY, MODE_IMAGE, MODE_360 } + mode = MODE_DEFAULT; + int ends = -1; + enum { RECFORM_FIXED = 1, RECFORM_VARIABLE, RECFORM_SPANNED } recform = 0; + char *encoding = NULL; + + if (!lex_force_id (lexer)) + goto exit; + + handle_name = xstrdup (lex_tokcstr (lexer)); + if (fh_from_id (handle_name)) + { + msg (SE, _("File handle %s is already defined. " + "Use %s before redefining a file handle."), + handle_name, "CLOSE FILE HANDLE"); + goto exit; + } + + lex_get (lexer); + if (!lex_force_match (lexer, T_SLASH)) + goto exit; + + while (lex_token (lexer) != T_ENDCMD) + { + if (lex_match_id (lexer, "NAME")) + { + if (file_name) + { + lex_sbc_only_once ("NAME"); + goto exit; + } + + lex_match (lexer, T_EQUALS); + if (!lex_force_string (lexer)) + goto exit; + free (file_name); + file_name = ss_xstrdup (lex_tokss (lexer)); + lex_get (lexer); + } + else if (lex_match_id (lexer, "LRECL")) + { + if (lrecl) + { + lex_sbc_only_once ("LRECL"); + goto exit; + } + + lex_match (lexer, T_EQUALS); + if (!lex_force_int (lexer)) + goto exit; + lrecl = lex_integer (lexer); + lex_get (lexer); + } + else if (lex_match_id (lexer, "TABWIDTH")) + { + if (tabwidth >= 0) + { + lex_sbc_only_once ("TABWIDTH"); + goto exit; + } + lex_match (lexer, T_EQUALS); + + if (!lex_force_int (lexer)) + goto exit; + tabwidth = lex_integer (lexer); + lex_get (lexer); + } + else if (lex_match_id (lexer, "MODE")) + { + if (mode != MODE_DEFAULT) + { + lex_sbc_only_once ("MODE"); + goto exit; + } + lex_match (lexer, T_EQUALS); + + if (lex_match_id (lexer, "CHARACTER")) + mode = MODE_CHARACTER; + else if (lex_match_id (lexer, "BINARY")) + mode = MODE_BINARY; + else if (lex_match_id (lexer, "IMAGE")) + mode = MODE_IMAGE; + else if (lex_match_int (lexer, 360)) + mode = MODE_360; + else + { + lex_error (lexer, NULL); + goto exit; + } + } + else if (lex_match_id (lexer, "ENDS")) + { + if (ends >= 0) + { + lex_sbc_only_once ("ENDS"); + goto exit; + } + lex_match (lexer, T_EQUALS); + + if (lex_match_id (lexer, "LF")) + ends = FH_END_LF; + else if (lex_match_id (lexer, "CRLF")) + ends = FH_END_CRLF; + else + { + lex_error (lexer, NULL); + goto exit; + } + } + else if (lex_match_id (lexer, "RECFORM")) + { + if (recform) + { + lex_sbc_only_once ("RECFORM"); + goto exit; + } + lex_match (lexer, T_EQUALS); + if (lex_match_id (lexer, "FIXED") || lex_match_id (lexer, "F")) + recform = RECFORM_FIXED; + else if (lex_match_id (lexer, "VARIABLE") + || lex_match_id (lexer, "V")) + recform = RECFORM_VARIABLE; + else if (lex_match_id (lexer, "SPANNED") + || lex_match_id (lexer, "VS")) + recform = RECFORM_SPANNED; + else + { + lex_error (lexer, NULL); + goto exit; + } + } + else if (lex_match_id (lexer, "ENCODING")) + { + if (encoding) + { + lex_sbc_only_once ("ENCODING"); + goto exit; + } + + lex_match (lexer, T_EQUALS); + if (!lex_force_string (lexer)) + goto exit; + free (encoding); + encoding = ss_xstrdup (lex_tokss (lexer)); + lex_get (lexer); + } + if (!lex_match (lexer, T_SLASH)) + break; + } + + if (lex_end_of_command (lexer) != CMD_SUCCESS) + goto exit; + + struct fh_properties properties = *fh_default_properties (); + if (file_name == NULL) + { + lex_sbc_missing ("NAME"); + goto exit; + } + + switch (mode) + { + case MODE_DEFAULT: + case MODE_CHARACTER: + properties.mode = FH_MODE_TEXT; + if (tabwidth >= 0) + properties.tab_width = tabwidth; + if (ends) + properties.line_ends = ends; + break; + case MODE_IMAGE: + properties.mode = FH_MODE_FIXED; + break; + case MODE_BINARY: + properties.mode = FH_MODE_VARIABLE; + break; + case MODE_360: + properties.encoding = CONST_CAST (char *, "EBCDIC-US"); + if (recform == RECFORM_FIXED) + properties.mode = FH_MODE_FIXED; + else if (recform == RECFORM_VARIABLE) + { + properties.mode = FH_MODE_360_VARIABLE; + properties.record_width = 8192; + } + else if (recform == RECFORM_SPANNED) + { + properties.mode = FH_MODE_360_SPANNED; + properties.record_width = 8192; + } + else + { + msg (SE, _("%s must be specified with %s."), "RECFORM", "MODE=360"); + goto exit; + } + break; + default: + NOT_REACHED (); + } + + if (properties.mode == FH_MODE_FIXED || lrecl) + { + if (!lrecl) + msg (SE, _("The specified file mode requires LRECL. " + "Assuming %zu-character records."), + properties.record_width); + else if (lrecl < 1 || lrecl >= (1UL << 31)) + msg (SE, _("Record length (%d) must be between 1 and %lu bytes. " + "Assuming %zu-character records."), + lrecl, (1UL << 31) - 1, properties.record_width); + else + properties.record_width = lrecl; + } + + if (encoding) + properties.encoding = encoding; + + fh_create_file (handle_name, file_name, lex_get_encoding (lexer), + &properties); + + result = CMD_SUCCESS; + +exit: + free (handle_name); + free (file_name); + free (encoding); + return result; +} + +int +cmd_close_file_handle (struct lexer *lexer, struct dataset *ds UNUSED) +{ + struct file_handle *handle; + + if (!lex_force_id (lexer)) + return CMD_CASCADING_FAILURE; + handle = fh_from_id (lex_tokcstr (lexer)); + if (handle == NULL) + return CMD_CASCADING_FAILURE; + + fh_unname (handle); + return CMD_SUCCESS; +} + +/* Returns the name for REFERENT. */ +static const char * +referent_name (enum fh_referent referent) +{ + switch (referent) + { + case FH_REF_FILE: + return _("file"); + case FH_REF_INLINE: + return _("inline file"); + case FH_REF_DATASET: + return _("dataset"); + default: + NOT_REACHED (); + } +} + +/* Parses a file handle name: + + - If SESSION is nonnull, then the parsed syntax may be the name of a + dataset within SESSION. Dataset names take precedence over file handle + names. + + - If REFERENT_MASK includes FH_REF_FILE, the parsed syntax may be a file + name as a string or a file handle name as an identifier. + + - If REFERENT_MASK includes FH_REF_INLINE, the parsed syntax may be the + identifier INLINE to represent inline data. + + Returns the file handle when successful, a null pointer on failure. + + The caller is responsible for fh_unref()'ing the returned file handle when + it is no longer needed. */ +struct file_handle * +fh_parse (struct lexer *lexer, enum fh_referent referent_mask, + struct session *session) +{ + struct file_handle *handle; + + if (session != NULL && lex_token (lexer) == T_ID) + { + struct dataset *ds; + + ds = session_lookup_dataset (session, lex_tokcstr (lexer)); + if (ds != NULL) + { + lex_get (lexer); + return fh_create_dataset (ds); + } + } + + if (lex_match_id (lexer, "INLINE")) + handle = fh_inline_file (); + else + { + if (lex_token (lexer) != T_ID && !lex_is_string (lexer)) + { + lex_error (lexer, _("expecting a file name or handle name")); + return NULL; + } + + handle = NULL; + if (lex_token (lexer) == T_ID) + handle = fh_from_id (lex_tokcstr (lexer)); + if (handle == NULL) + handle = fh_create_file (NULL, lex_tokcstr (lexer), lex_get_encoding (lexer), + fh_default_properties ()); + lex_get (lexer); + } + + if (!(fh_get_referent (handle) & referent_mask)) + { + msg (SE, _("Handle for %s not allowed here."), + referent_name (fh_get_referent (handle))); + fh_unref (handle); + return NULL; + } + + return handle; +} + +/* + Local variables: + mode: c + End: +*/ diff --git a/src/language/data-io/file-handle.q b/src/language/data-io/file-handle.q deleted file mode 100644 index 7ac20a080c..0000000000 --- a/src/language/data-io/file-handle.q +++ /dev/null @@ -1,272 +0,0 @@ -/* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#include - -#include "data/file-handle-def.h" - -#include -#include -#include - -#include "data/file-name.h" -#include "data/session.h" -#include "data/variable.h" -#include "language/command.h" -#include "language/data-io/file-handle.h" -#include "language/lexer/lexer.h" -#include "libpspp/assertion.h" -#include "libpspp/cast.h" -#include "libpspp/message.h" -#include "libpspp/str.h" - -#include "gl/xalloc.h" - -#include "gettext.h" -#define _(msgid) gettext (msgid) - -/* (headers) */ - - -/* (specification) - "FILE HANDLE" (fh_): - name=string; - lrecl=integer; - tabwidth=integer; - mode=mode:!character/binary/image/360; - ends=ends:lf/crlf; - recform=recform:fixed/f/variable/v/spanned/vs; - encoding=string. -*/ -/* (declarations) */ -/* (functions) */ - -int -cmd_file_handle (struct lexer *lexer, struct dataset *ds) -{ - struct fh_properties properties; - struct cmd_file_handle cmd; - struct file_handle *handle; - enum cmd_result result; - char *handle_name; - - result = CMD_CASCADING_FAILURE; - if (!lex_force_id (lexer)) - goto exit; - - handle_name = xstrdup (lex_tokcstr (lexer)); - handle = fh_from_id (handle_name); - if (handle != NULL) - { - msg (SE, _("File handle %s is already defined. " - "Use %s before redefining a file handle."), - handle_name, "CLOSE FILE HANDLE"); - goto exit_free_handle_name; - } - - lex_get (lexer); - if (!lex_force_match (lexer, T_SLASH)) - goto exit_free_handle_name; - - if (!parse_file_handle (lexer, ds, &cmd, NULL)) - goto exit_free_handle_name; - - if (lex_end_of_command (lexer) != CMD_SUCCESS) - goto exit_free_cmd; - - properties = *fh_default_properties (); - if (cmd.s_name == NULL) - { - lex_sbc_missing ("NAME"); - goto exit_free_cmd; - } - - switch (cmd.mode) - { - case FH_CHARACTER: - properties.mode = FH_MODE_TEXT; - if (cmd.sbc_tabwidth) - { - if (cmd.n_tabwidth[0] >= 0) - properties.tab_width = cmd.n_tabwidth[0]; - else - msg (SE, _("%s must not be negative."), "TABWIDTH"); - } - if (cmd.ends == FH_LF) - properties.line_ends = FH_END_LF; - else if (cmd.ends == FH_CRLF) - properties.line_ends = FH_END_CRLF; - break; - case FH_IMAGE: - properties.mode = FH_MODE_FIXED; - break; - case FH_BINARY: - properties.mode = FH_MODE_VARIABLE; - break; - case FH_360: - properties.encoding = CONST_CAST (char *, "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) - { - properties.mode = FH_MODE_360_VARIABLE; - properties.record_width = 8192; - } - else if (cmd.recform == FH_SPANNED || cmd.recform == FH_VS) - { - properties.mode = FH_MODE_360_SPANNED; - properties.record_width = 8192; - } - else - { - msg (SE, _("%s must be specified with %s."), "RECFORM", "MODE=360"); - goto exit_free_cmd; - } - break; - default: - NOT_REACHED (); - } - - if (properties.mode == FH_MODE_FIXED || cmd.n_lrecl[0] != LONG_MIN) - { - if (cmd.n_lrecl[0] == LONG_MIN) - msg (SE, _("The specified file mode requires LRECL. " - "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. " - "Assuming %zu-character records."), - cmd.n_lrecl[0], (1UL << 31) - 1, properties.record_width); - else - properties.record_width = cmd.n_lrecl[0]; - } - - if (cmd.s_encoding != NULL) - properties.encoding = cmd.s_encoding; - - fh_create_file (handle_name, cmd.s_name, lex_get_encoding (lexer), &properties); - - result = CMD_SUCCESS; - -exit_free_cmd: - free_file_handle (&cmd); -exit_free_handle_name: - free (handle_name); -exit: - return result; -} - -int -cmd_close_file_handle (struct lexer *lexer, struct dataset *ds UNUSED) -{ - struct file_handle *handle; - - if (!lex_force_id (lexer)) - return CMD_CASCADING_FAILURE; - handle = fh_from_id (lex_tokcstr (lexer)); - if (handle == NULL) - return CMD_CASCADING_FAILURE; - - fh_unname (handle); - return CMD_SUCCESS; -} - -/* Returns the name for REFERENT. */ -static const char * -referent_name (enum fh_referent referent) -{ - switch (referent) - { - case FH_REF_FILE: - return _("file"); - case FH_REF_INLINE: - return _("inline file"); - case FH_REF_DATASET: - return _("dataset"); - default: - NOT_REACHED (); - } -} - -/* Parses a file handle name: - - - If SESSION is nonnull, then the parsed syntax may be the name of a - dataset within SESSION. Dataset names take precedence over file handle - names. - - - If REFERENT_MASK includes FH_REF_FILE, the parsed syntax may be a file - name as a string or a file handle name as an identifier. - - - If REFERENT_MASK includes FH_REF_INLINE, the parsed syntax may be the - identifier INLINE to represent inline data. - - Returns the file handle when successful, a null pointer on failure. - - The caller is responsible for fh_unref()'ing the returned file handle when - it is no longer needed. */ -struct file_handle * -fh_parse (struct lexer *lexer, enum fh_referent referent_mask, - struct session *session) -{ - struct file_handle *handle; - - if (session != NULL && lex_token (lexer) == T_ID) - { - struct dataset *ds; - - ds = session_lookup_dataset (session, lex_tokcstr (lexer)); - if (ds != NULL) - { - lex_get (lexer); - return fh_create_dataset (ds); - } - } - - if (lex_match_id (lexer, "INLINE")) - handle = fh_inline_file (); - else - { - if (lex_token (lexer) != T_ID && !lex_is_string (lexer)) - { - lex_error (lexer, _("expecting a file name or handle name")); - return NULL; - } - - handle = NULL; - if (lex_token (lexer) == T_ID) - handle = fh_from_id (lex_tokcstr (lexer)); - if (handle == NULL) - handle = fh_create_file (NULL, lex_tokcstr (lexer), lex_get_encoding (lexer), - fh_default_properties ()); - lex_get (lexer); - } - - if (!(fh_get_referent (handle) & referent_mask)) - { - msg (SE, _("Handle for %s not allowed here."), - referent_name (fh_get_referent (handle))); - fh_unref (handle); - return NULL; - } - - return handle; -} - -/* - Local variables: - mode: c - End: -*/ diff --git a/src/language/data-io/get-data.c b/src/language/data-io/get-data.c index e3a93b1738..13eebe6c3a 100644 --- a/src/language/data-io/get-data.c +++ b/src/language/data-io/get-data.c @@ -52,9 +52,6 @@ static const bool odf_read_support = true; #else static const bool odf_read_support = false; struct spreadsheet *ods_probe (const char *filename, bool report_errors){} -struct casereader * ods_make_reader (struct spreadsheet *spreadsheet, - const struct spreadsheet_read_options *opts){} -void ods_unref (struct spreadsheet *r){} #endif #ifdef GNM_READ_SUPPORT @@ -62,10 +59,6 @@ static const bool gnm_read_support = true; #else static const bool gnm_read_support = false; struct spreadsheet *gnumeric_probe (const char *filename, bool report_errors){} -struct casereader * gnumeric_make_reader (struct spreadsheet *spreadsheet, - const struct spreadsheet_read_options *opts){} -void gnumeric_unref (struct spreadsheet *r){} - #endif static bool parse_spreadsheet (struct lexer *lexer, char **filename, @@ -617,18 +610,20 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) lex_get (lexer); } - if (!lex_force_id (lexer) - || !dict_id_is_valid (dict, lex_tokcstr (lexer), true)) - goto error; name = xstrdup (lex_tokcstr (lexer)); + if (!lex_force_id (lexer) + || !dict_id_is_valid (dict, name, true)) + { + goto error; + } lex_get (lexer); - if (type == DP_DELIMITED) { if (!parse_format_specifier (lexer, &input) - || !fmt_check_input (&input)) - goto error; - + || !fmt_check_input (&input)) + { + goto error; + } output = fmt_for_output_from_input (&input); } else @@ -648,14 +643,12 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) msg (SE, _("Unknown format type `%s'."), fmt_type_name); goto error; } - /* Compose input format. */ input.type = fmt_type; input.w = lc - fc + 1; input.d = 0; if (!fmt_check_input (&input)) goto error; - /* Compose output format. */ if (w != 0) { @@ -668,7 +661,6 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) else output = fmt_for_output_from_input (&input); } - v = dict_create_var (dict, name, fmt_var_width (&input)); if (v == NULL) { @@ -676,7 +668,6 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds) goto error; } var_set_both_formats (v, &output); - if (type == DP_DELIMITED) data_parser_add_delimited_field (parser, &input, var_get_case_index (v), diff --git a/src/language/expressions/helpers.c b/src/language/expressions/helpers.c index 6e73887285..c69e440e4b 100644 --- a/src/language/expressions/helpers.c +++ b/src/language/expressions/helpers.c @@ -664,7 +664,7 @@ npdf_beta (double x, double a, double b, double lambda) } } -double +static double round__ (double x, double mult, double fuzzbits, double adjustment) { if (fuzzbits <= 0) diff --git a/src/language/lexer/format-parser.c b/src/language/lexer/format-parser.c index 78af09da31..2c05335ff9 100644 --- a/src/language/lexer/format-parser.c +++ b/src/language/lexer/format-parser.c @@ -40,7 +40,7 @@ parse_abstract_format_specifier__ (struct lexer *lexer, struct substring type_ss, width_ss, decimals_ss; bool has_decimals; - if (lex_token (lexer) != T_ID) + if (lex_token (lexer) != T_ID && lex_token (lexer) != T_STRING) goto error; /* Extract pieces. */ diff --git a/src/language/stats/crosstabs.q b/src/language/stats/crosstabs.q index 7b0c6c345e..6e87c0260b 100644 --- a/src/language/stats/crosstabs.q +++ b/src/language/stats/crosstabs.q @@ -74,6 +74,8 @@ *^tables=custom; +variables=custom; missing=miss:!table/include/report; + count=roundwhat:asis/case/!cell, + roundhow:!round/truncate; +write[wr_]=none,cells,all; +format=val:!avalue/dvalue, indx:!noindex/index, @@ -182,6 +184,11 @@ struct crosstabs_proc 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. */ + /* Rounding of cells. */ + bool round_case_weights; /* Round case weights? */ + bool round_cells; /* If !round_case_weights, round cells? */ + bool round_down; /* Round down? (otherwise to nearest) */ + /* STATISTICS. */ unsigned int statistics; /* Bit k is 1 if statistic k is requested. */ @@ -200,6 +207,12 @@ static void tabulate_integer_case (struct pivot_table *, const struct ccase *, static void postcalc (struct crosstabs_proc *); static void submit (struct pivot_table *, struct tab_table *); +static double +round_weight (const struct crosstabs_proc *proc, double weight) +{ + return proc->round_down ? floor (weight) : floor (weight + 0.5); +} + /* Parses and executes the CROSSTABS procedure. */ int cmd_crosstabs (struct lexer *lexer, struct dataset *ds) @@ -236,6 +249,10 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds) proc.descending = cmd.val == CRS_DVALUE; + proc.round_case_weights = cmd.sbc_count && cmd.roundwhat == CRS_CASE; + proc.round_cells = cmd.sbc_count && cmd.roundwhat == CRS_CELL; + proc.round_down = cmd.roundhow == CRS_TRUNCATE; + /* CELLS. */ if (!cmd.sbc_cells) proc.cells = 1u << CRS_CL_COUNT; @@ -316,6 +333,12 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds) { double weight = dict_get_case_weight (dataset_dict (ds), c, &proc.bad_warn); + if (cmd.roundwhat == CRS_CASE) + { + weight = round_weight (&proc, weight); + if (weight == 0.) + continue; + } if (should_tabulate_case (pt, c, proc.exclude)) { if (proc.mode == GENERAL) @@ -677,17 +700,36 @@ static bool find_crosstab (struct pivot_table *, size_t *row0p, size_t *row1p); static void postcalc (struct crosstabs_proc *proc) { - struct pivot_table *pt; + + /* Round hash table entries, if requested + + If this causes any of the cell counts to fall to zero, delete those + cells. */ + if (proc->round_cells) + for (struct pivot_table *pt = proc->pivots; + pt < &proc->pivots[proc->n_pivots]; pt++) + { + struct freq *e, *next; + HMAP_FOR_EACH_SAFE (e, next, struct freq, node, &pt->data) + { + e->count = round_weight (proc, e->count); + if (e->count == 0.0) + { + hmap_delete (&pt->data, &e->node); + free (e); + } + } + } /* Convert hash tables into sorted arrays of entries. */ - for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++) + for (struct pivot_table *pt = proc->pivots; + pt < &proc->pivots[proc->n_pivots]; pt++) { struct freq *e; - size_t i; pt->n_entries = hmap_count (&pt->data); pt->entries = xnmalloc (pt->n_entries, sizeof *pt->entries); - i = 0; + size_t i = 0; HMAP_FOR_EACH (e, struct freq, node, &pt->data) pt->entries[i++] = e; hmap_destroy (&pt->data); @@ -701,7 +743,8 @@ postcalc (struct crosstabs_proc *proc) make_summary_table (proc); /* Output each pivot table. */ - for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++) + for (struct pivot_table *pt = proc->pivots; + pt < &proc->pivots[proc->n_pivots]; pt++) { if (proc->pivot || pt->n_vars == 2) output_pivot_table (proc, pt); @@ -721,10 +764,9 @@ postcalc (struct crosstabs_proc *proc) } /* Free output and prepare for next split file. */ - for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++) + for (struct pivot_table *pt = proc->pivots; + pt < &proc->pivots[proc->n_pivots]; pt++) { - size_t i; - pt->missing = 0.0; /* Free the members that were allocated in this function(and the values @@ -734,7 +776,7 @@ postcalc (struct crosstabs_proc *proc) 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_vars; i++) + for (size_t i = 0; i < pt->n_vars; i++) { int width = var_get_width (pt->vars[i]); if (value_needs_init (width)) @@ -746,7 +788,7 @@ postcalc (struct crosstabs_proc *proc) } } - for (i = 0; i < pt->n_entries; i++) + for (size_t i = 0; i < pt->n_entries; i++) free (pt->entries[i]); free (pt->entries); } diff --git a/src/language/stats/frequencies.c b/src/language/stats/frequencies.c index 34da71c8aa..675a02319a 100644 --- a/src/language/stats/frequencies.c +++ b/src/language/stats/frequencies.c @@ -1124,6 +1124,7 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) frq.percentiles[frq.n_percentiles].show = true; frq.n_percentiles++; + frq.n_show_percentiles++; } @@ -1212,19 +1213,20 @@ cmd_frequencies (struct lexer *lexer, struct dataset *ds) frq.n_show_percentiles = 0; for (i = o = 0; i < frq.n_percentiles; ++i) { - frq.percentiles[o].p = frq.percentiles[i].p; - - if (frq.percentiles[i].show) - frq.percentiles[o].show = true; - - if (frq.percentiles[i].p != previous_p) - { - if (frq.percentiles[i].show) - frq.n_show_percentiles++; - - o++; - } - + if (frq.percentiles[i].p != previous_p) + { + frq.percentiles[o].p = frq.percentiles[i].p; + frq.percentiles[o].show = frq.percentiles[i].show; + if (frq.percentiles[i].show) + frq.n_show_percentiles++; + o++; + } + else if (frq.percentiles[i].show && + !frq.percentiles[o].show) + { + frq.percentiles[o].show = true; + frq.n_show_percentiles++; + } previous_p = frq.percentiles[i].p; } diff --git a/src/language/stats/glm.c b/src/language/stats/glm.c index 625de2d2a9..9ac3150c0a 100644 --- a/src/language/stats/glm.c +++ b/src/language/stats/glm.c @@ -587,6 +587,16 @@ run_glm (struct glm_spec *cmd, struct casereader *input, struct glm_workspace ws; struct covariance *cov; + input = casereader_create_filter_missing (input, + cmd->dep_vars, cmd->n_dep_vars, + cmd->exclude, + NULL, NULL); + + input = casereader_create_filter_missing (input, + cmd->factor_vars, cmd->n_factor_vars, + cmd->exclude, + NULL, NULL); + ws.cats = categoricals_create (cmd->interactions, cmd->n_interactions, cmd->wv, cmd->exclude, MV_ANY); diff --git a/src/output/charts/scatterplot-cairo.c b/src/output/charts/scatterplot-cairo.c index 736d5e2ae7..211d2d60dc 100644 --- a/src/output/charts/scatterplot-cairo.c +++ b/src/output/charts/scatterplot-cairo.c @@ -28,6 +28,7 @@ #include "gettext.h" #define _(msgid) gettext (msgid) +static const struct xrchart_colour black = {0,0,0}; void xrchart_draw_scatterplot (const struct chart_item *chart_item, cairo_t *cr, @@ -87,8 +88,11 @@ xrchart_draw_scatterplot (const struct chart_item *chart_item, cairo_t *cr, i--; } } + colour = &data_colour[i % XRCHART_N_COLOURS]; } - colour = &data_colour[i % XRCHART_N_COLOURS]; + else + colour = &black; + cairo_set_source_rgb (cr, colour->red / 255.0, colour->green / 255.0, diff --git a/src/output/tab.c b/src/output/tab.c index db384bdfc9..733cc9854f 100644 --- a/src/output/tab.c +++ b/src/output/tab.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013, 2014, 2016 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,6 +44,13 @@ #include "gettext.h" #define _(msgid) gettext (msgid) + +#if DEBUGGING +static const bool debugging = true; +#else +static const bool debugging = false; +#endif + /* Cell options. */ #define TAB_JOIN (1u << TAB_FIRST_AVAILABLE) #define TAB_SUBTABLE (1u << (TAB_FIRST_AVAILABLE + 1)) @@ -51,28 +58,27 @@ /* Joined cell. */ struct tab_joined_cell +{ + int d[TABLE_N_AXES][2]; /* Table region, same as struct table_cell. */ + union { - int d[TABLE_N_AXES][2]; /* Table region, same as struct table_cell. */ - union - { - char *text; - struct table_item *subtable; - } + char *text; + struct table_item *subtable; + } u; - size_t n_footnotes; - char **footnotes; - }; + size_t n_footnotes; + char **footnotes; +}; static const struct table_class tab_table_class; -struct fmt_spec ugly [n_RC] = - { - {FMT_F, 8, 0}, /* INTEGER */ - {FMT_F, 8, 3}, /* WEIGHT (ignored) */ - {FMT_F, 8, 3}, /* PVALUE */ - {FMT_F, 8, 3} /* OTHER (ignored) */ - }; +struct fmt_spec ugly[n_RC] = { + {FMT_F, 8, 0}, /* INTEGER */ + {FMT_F, 8, 3}, /* WEIGHT (ignored) */ + {FMT_F, 8, 3}, /* PVALUE */ + {FMT_F, 8, 3} /* OTHER (ignored) */ +}; /* Creates and returns a new table with NC columns and NR rows and initially no @@ -112,8 +118,9 @@ tab_create (int nc, int nr) } -void -tab_set_format (struct tab_table *t, enum result_class rc, const struct fmt_spec *fmt) +void +tab_set_format (struct tab_table *t, enum result_class rc, + const struct fmt_spec *fmt) { t->fmtmap[rc] = *fmt; } @@ -175,11 +182,12 @@ tab_realloc (struct tab_table *t, int nc, int nr) new_cc = pool_calloc (t->container, nr * nc, sizeof *new_cc); new_ct = pool_malloc (t->container, nr * nc); for (r = 0; r < mr1; r++) - { - memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc); - memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1); - memset (&new_ct[r * nc + tab_nc (t)], 0, nc - tab_nc (t)); - } + { + memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], + mc1 * sizeof *t->cc); + memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1); + memset (&new_ct[r * nc + tab_nc (t)], 0, nc - tab_nc (t)); + } pool_free (t->container, t->cc); pool_free (t->container, t->ct); t->cc = new_cc; @@ -195,11 +203,12 @@ tab_realloc (struct tab_table *t, int nc, int nr) t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1); if (nr > tab_nr (t)) - { - memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc); - memset (&t->rv[(nc + 1) * tab_nr (t)], TAL_GAP, + { + memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, + (nr - tab_nr (t)) * nc); + memset (&t->rv[(nc + 1) * tab_nr (t)], TAL_GAP, (nr - tab_nr (t)) * (nc + 1)); - } + } } memset (&t->ct[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t))); @@ -232,20 +241,20 @@ tab_headers (struct tab_table *table, int l, int r, int t, int b) void tab_vline (struct tab_table *t, int style, int x, int y1, int y2) { -#if DEBUGGING - if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t) - || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t) - || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t)) + if (debugging) { - printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in " - "table size (%d,%d)\n"), - x, t->col_ofs, x + t->col_ofs, - y1, t->row_ofs, y1 + t->row_ofs, - y2, t->row_ofs, y2 + t->row_ofs, - tab_nc (t), tab_nr (t)); - return; + if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t) + || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t) + || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t)) + { + printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in " + "table size (%d,%d)\n"), + x, t->col_ofs, x + t->col_ofs, + y1, t->row_ofs, y1 + t->row_ofs, + y2, t->row_ofs, y2 + t->row_ofs, tab_nc (t), tab_nr (t)); + return; + } } -#endif x += t->col_ofs; y1 += t->row_ofs; @@ -268,22 +277,22 @@ tab_vline (struct tab_table *t, int style, int x, int y1, int y2) /* Draws a horizontal line above cells at vertical position Y from X1 to X2 inclusive in style STYLE, if style is not -1. */ void -tab_hline (struct tab_table * t, int style, int x1, int x2, int y) +tab_hline (struct tab_table *t, int style, int x1, int x2, int y) { -#if DEBUGGING - if (y + t->row_ofs < 0 || y + t->row_ofs > tab_nr (t) - || x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t) - || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)) + if (debugging) { - printf (_("bad hline: x=(%d+%d=%d,%d+%d=%d) y=%d+%d=%d in " - "table size (%d,%d)\n"), - x1, t->col_ofs, x1 + t->col_ofs, - x2, t->col_ofs, x2 + t->col_ofs, - y, t->row_ofs, y + t->row_ofs, - tab_nc (t), tab_nr (t)); - return; + if (y + t->row_ofs < 0 || y + t->row_ofs > tab_nr (t) + || x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t) + || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)) + { + printf (_("bad hline: x=(%d+%d=%d,%d+%d=%d) y=%d+%d=%d in " + "table size (%d,%d)\n"), + x1, t->col_ofs, x1 + t->col_ofs, + x2, t->col_ofs, x2 + t->col_ofs, + y, t->row_ofs, y + t->row_ofs, tab_nc (t), tab_nr (t)); + return; + } } -#endif x1 += t->col_ofs; x2 += t->col_ofs; @@ -291,8 +300,8 @@ tab_hline (struct tab_table * t, int style, int x1, int x2, int y) assert (y >= 0); assert (y <= tab_nr (t)); - assert (x2 >= x1 ); - assert (x1 >= 0 ); + assert (x2 >= x1); + assert (x1 >= 0); assert (x2 < tab_nc (t)); if (style != -1) @@ -311,24 +320,24 @@ tab_hline (struct tab_table * t, int style, int x1, int x2, int y) line. */ void tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, - int x1, int y1, int x2, int y2) + int x1, int y1, int x2, int y2) { -#if DEBUGGING - if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t) - || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t) - || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t) - || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t)) + if (debugging) { - printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) " - "in table size (%d,%d)\n"), - x1, t->col_ofs, x1 + t->col_ofs, - y1, t->row_ofs, y1 + t->row_ofs, - x2, t->col_ofs, x2 + t->col_ofs, - y2, t->row_ofs, y2 + t->row_ofs, - tab_nc (t), tab_nr (t)); - NOT_REACHED (); + if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t) + || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t) + || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t) + || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t)) + { + printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) " + "in table size (%d,%d)\n"), + x1, t->col_ofs, x1 + t->col_ofs, + y1, t->row_ofs, y1 + t->row_ofs, + x2, t->col_ofs, x2 + t->col_ofs, + y2, t->row_ofs, y2 + t->row_ofs, tab_nc (t), tab_nr (t)); + NOT_REACHED (); + } } -#endif x1 += t->col_ofs; x2 += t->col_ofs; @@ -366,24 +375,24 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, int y; for (y = y1 + 1; y <= y2; y++) - { - int x; + { + int x; for (x = x1; x <= x2; x++) t->rh[x + t->cf * y] = i_h; - } + } } if (i_v != -1) { int x; for (x = x1 + 1; x <= x2; x++) - { - int y; + { + int y; for (y = y1; y <= y2; y++) t->rv[x + (t->cf + 1) * y] = i_v; - } + } } } @@ -393,24 +402,25 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, 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 variable *var, - const struct fmt_spec *f) + const union value *v, const struct variable *var, + const struct fmt_spec *f) { char *contents; -#if DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= tab_nc (table) - || r + table->row_ofs >= tab_nr (table)) + if (debugging) { - printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - tab_nc (table), tab_nr (table)); - return; + if (c + table->col_ofs < 0 || r + table->row_ofs < 0 + || c + table->col_ofs >= tab_nc (table) + || r + table->row_ofs >= tab_nr (table)) + { + printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size " + "(%d,%d)\n", + c, table->col_ofs, c + table->col_ofs, + r, table->row_ofs, r + table->row_ofs, + tab_nc (table), tab_nr (table)); + return; + } } -#endif contents = data_out_stretchy (v, var_get_encoding (var), f != NULL ? f : var_get_print_format (var), @@ -426,9 +436,9 @@ tab_value (struct tab_table *table, int c, int r, unsigned char opt, */ void tab_double (struct tab_table *table, int c, int r, unsigned char opt, - double val, const struct fmt_spec *fmt, enum result_class rc) + double val, const struct fmt_spec *fmt, enum result_class rc) { - union value double_value ; + union value double_value; char *s; assert (c >= 0); @@ -438,22 +448,23 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt, if (fmt == NULL) fmt = &table->fmtmap[rc]; - + fmt_check_output (fmt); -#if DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= tab_nc (table) - || r + table->row_ofs >= tab_nr (table)) + if (debugging) { - printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - tab_nc (table), tab_nr (table)); - return; + if (c + table->col_ofs < 0 || r + table->row_ofs < 0 + || c + table->col_ofs >= tab_nc (table) + || r + table->row_ofs >= tab_nr (table)) + { + printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size " + "(%d,%d)\n", + c, table->col_ofs, c + table->col_ofs, + r, table->row_ofs, r + table->row_ofs, + tab_nc (table), tab_nr (table)); + return; + } } -#endif double_value.f = val; s = data_out_stretchy (&double_value, C_ENCODING, fmt, table->container); @@ -465,24 +476,25 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt, static void do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text) { - assert (c >= 0 ); - assert (r >= 0 ); + assert (c >= 0); + assert (r >= 0); assert (c < tab_nc (table)); assert (r < tab_nr (table)); -#if DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= tab_nc (table) - || r + table->row_ofs >= tab_nr (table)) + if (debugging) { - printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - tab_nc (table), tab_nr (table)); - return; + if (c + table->col_ofs < 0 || r + table->row_ofs < 0 + || c + table->col_ofs >= tab_nc (table) + || r + table->row_ofs >= tab_nr (table)) + { + printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size " + "(%d,%d)\n", + c, table->col_ofs, c + table->col_ofs, + r, table->row_ofs, r + table->row_ofs, + tab_nc (table), tab_nr (table)); + return; + } } -#endif table->cc[c + r * table->cf] = text; table->ct[c + r * table->cf] = opt; @@ -524,22 +536,23 @@ add_joined_cell (struct tab_table *table, int x1, int y1, int x2, int y2, assert (y2 + table->row_ofs < tab_nr (table)); assert (x2 + table->col_ofs < tab_nc (table)); -#if DEBUGGING - if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table) - || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table) - || x2 < x1 || x2 + table->col_ofs >= tab_nc (table) - || y2 < y2 || y2 + table->row_ofs >= tab_nr (table)) + if (debugging) { - printf ("tab_joint_text(): bad cell " - "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n", - x1, table->col_ofs, x1 + table->col_ofs, - y1, table->row_ofs, y1 + table->row_ofs, - x2, table->col_ofs, x2 + table->col_ofs, - y2, table->row_ofs, y2 + table->row_ofs, - tab_nc (table), tab_nr (table)); - return; + if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table) + || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table) + || x2 < x1 || x2 + table->col_ofs >= tab_nc (table) + || y2 < y1 || y2 + table->row_ofs >= tab_nr (table)) + { + printf ("tab_joint_text(): bad cell " + "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n", + x1, table->col_ofs, x1 + table->col_ofs, + y1, table->row_ofs, y1 + table->row_ofs, + x2, table->col_ofs, x2 + table->col_ofs, + y2, table->row_ofs, y2 + table->row_ofs, + tab_nc (table), tab_nr (table)); + return NULL; + } } -#endif tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2); @@ -560,16 +573,16 @@ add_joined_cell (struct tab_table *table, int x1, int y1, int x2, int y2, for (y = y1; y < y2; y++) { - int x; + int x; - for (x = x1; x < x2; x++) - { - *cc++ = j; - *ct++ = opt | TAB_JOIN; - } + for (x = x1; x < x2; x++) + { + *cc++ = j; + *ct++ = opt | TAB_JOIN; + } - cc += ofs; - ct += ofs; + cc += ofs; + ct += ofs; } } @@ -590,8 +603,8 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2, 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, ...) +tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, + int y2, unsigned opt, const char *format, ...) { va_list args; char *s; @@ -625,7 +638,8 @@ tab_footnote (struct tab_table *table, int x, int y, const char *format, ...) (j->n_footnotes + 1) * sizeof *j->footnotes); va_start (args, format); - j->footnotes[j->n_footnotes++] = pool_vasprintf (table->container, format, args); + j->footnotes[j->n_footnotes++] = + pool_vasprintf (table->container, format, args); va_end (args); } @@ -712,18 +726,20 @@ tab_offset (struct tab_table *t, int col, int row) { int diff = 0; -#if DEBUGGING - if (row < -1 || row > tab_nr (t)) + if (debugging) { - printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t)); - NOT_REACHED (); - } - if (col < -1 || col > tab_nc (t)) - { - printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t)); - NOT_REACHED (); + if (row < -1 || row > tab_nr (t)) + { + printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t)); + NOT_REACHED (); + } + if (col < -1 || col > tab_nc (t)) + { + printf ("tab_offset(): col=%d in %d-column table\n", col, + tab_nc (t)); + NOT_REACHED (); + } } -#endif if (row != -1) diff += (row - t->row_ofs) * t->cf, t->row_ofs = row; @@ -790,7 +806,8 @@ tab_destroy (struct table *table) } static void -tab_get_cell (const struct table *table, int x, int y, struct table_cell *cell) +tab_get_cell (const struct table *table, int x, int y, + struct table_cell *cell) { const struct tab_table *t = tab_cast (table); int index = x + y * t->cf; @@ -858,18 +875,16 @@ tab_get_rule (const struct table *table, enum table_axis axis, int x, int y) { const struct tab_table *t = tab_cast (table); return (axis == TABLE_VERT - ? t->rh[x + t->cf * y] - : t->rv[x + (t->cf + 1) * y]); + ? t->rh[x + t->cf * y] : t->rv[x + (t->cf + 1) * y]); } -static const struct table_class tab_table_class = - { - tab_destroy, - tab_get_cell, - tab_get_rule, - NULL, /* paste */ - NULL, /* select */ - }; +static const struct table_class tab_table_class = { + tab_destroy, + tab_get_cell, + tab_get_rule, + NULL, /* paste */ + NULL, /* select */ +}; struct tab_table * tab_cast (const struct table *table) diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 2d6862857c..1155cf70db 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -242,6 +242,10 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-dialog-action-rank.h \ src/ui/gui/psppire-dialog-action-recode.c \ src/ui/gui/psppire-dialog-action-recode.h \ + src/ui/gui/psppire-dialog-action-recode-same.c \ + src/ui/gui/psppire-dialog-action-recode-same.h \ + src/ui/gui/psppire-dialog-action-recode-different.c \ + src/ui/gui/psppire-dialog-action-recode-different.h \ src/ui/gui/psppire-dialog-action-regression.c \ src/ui/gui/psppire-dialog-action-regression.h \ src/ui/gui/psppire-dialog-action-reliability.c \ diff --git a/src/ui/gui/data-editor.ui b/src/ui/gui/data-editor.ui index 27262d972e..048ba0bbb1 100644 --- a/src/ui/gui/data-editor.ui +++ b/src/ui/gui/data-editor.ui @@ -262,18 +262,16 @@ - + transform_recode-same uimanager1 - FALSE Recode into _Same Variables... transform-in-to-same-variables - + uimanager1 - TRUE transform_recode-different Recode into _Different Variables... transform-in-to-different-variables diff --git a/src/ui/gui/help-menu.c b/src/ui/gui/help-menu.c index 61b9140d1e..aeef8c10c4 100644 --- a/src/ui/gui/help-menu.c +++ b/src/ui/gui/help-menu.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013, 2015 Free Software Foundation + Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013, 2015, 2016 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,6 +31,15 @@ #define _(msgid) gettext (msgid) #define N_(msgid) msgid +/* Try to open html documentation uri via the default + browser on the operating system */ +#ifdef __APPLE__ +#define HTMLOPENARGV {"open", 0, 0} +#elif _WIN32 +#define HTMLOPENARGV {"wscript", 0, 0} +#else +#define HTMLOPENARGV {"xdg-open", 0, 0} +#endif static const gchar *artists[] = { "Bastián Díaz", "Hugo Alejandro", NULL}; @@ -80,32 +89,130 @@ about_new (GtkMenuItem *mmm, GtkWindow *parent) gtk_widget_hide (about); } -/* Open the manual at PAGE */ + +/* Opening the htmluri in windows via cmd /start uri opens + the windows command shell for a moment. The alternative is + to start a script via wscript. This will not be visible*/ +#ifdef _WIN32 +static gboolean open_windows_help (const gchar *helpuri, + GError **err) +{ + gchar *vbsfilename = NULL; + gchar *vbs = NULL; + gboolean result; + vbsfilename = g_build_filename (g_get_tmp_dir (), + "pspp-help-open.vbs", + NULL); + vbs = g_strdup_printf("CreateObject(\"WScript.Shell\").Run \"%s\"", + helpuri); + result = g_file_set_contents (vbsfilename, + vbs, + strlen(vbs), + err); + g_free (vbs); + if (!result) + goto error; + + gchar *argv[] = {"wscript",vbsfilename,0}; + + result = g_spawn_async (NULL, argv, + NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, err); + error: + g_free (vbsfilename); + return result; +} +#endif + +/* Open the manual at PAGE with the following priorities + First: local yelp help system + Second: browser with local html doc dir in path pspp.html/.html + Third: browers with Internet html help at gnu.org */ void online_help (const char *page) { GError *err = NULL; - gchar *cmd = NULL; - + GError *htmlerr = NULL; gchar *argv[3] = { "yelp", 0, 0}; + gchar *htmlargv[3] = HTMLOPENARGV; + gchar *htmlfilename = NULL; + gchar *htmlfullname = NULL; + gchar *htmluri = NULL; if (page == NULL) - argv[1] = g_strdup_printf ("file://%s", relocate (DOCDIR "/pspp.xml")); + { + argv[1] = g_strdup_printf ("file://%s", relocate (DOCDIR "/pspp.xml")); + htmlfilename = g_strdup ("index.html"); + } else - argv[1] = g_strdup_printf ("file://%s#%s", relocate (DOCDIR "/pspp.xml"), page); - - if (! g_spawn_async (NULL, argv, - NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, &err)) { - msg (ME, _("Cannot open reference manual: %s. The PSPP user manual is " - "also available at %s"), + gchar **tokens = NULL; + const int maxtokens = 5; + int idx = 0; + argv[1] = g_strdup_printf ("file://%s#%s", + relocate (DOCDIR "/pspp.xml"), page); + /* The page will be translated to the htmlfilename + page htmlfilename + GRAPH#SCATTERPLOT SCATTERPLOT.html + QUICK-CLUSTER QUICK-CLUSTER.html + which is valid for the multiple page html doc*/ + tokens = g_strsplit (page, "#", maxtokens); + for(;tokens[idx] && idx < maxtokens;idx++); + htmlfilename = g_strdup_printf ("%s.html", tokens[idx-1]); + g_strfreev (tokens); + } + /* Hint: pspp.html is a directory...*/ + htmlfullname = g_strdup_printf ("%s/%s", relocate (DOCDIR "/pspp.html"), + htmlfilename); + if (g_file_test (relocate (DOCDIR "/pspp.html"), G_FILE_TEST_IS_DIR)) + { + GError *urierr = NULL; + htmluri = g_filename_to_uri (htmlfullname,NULL, &urierr); + if (!htmluri) + { + msg (ME, _("Help path conversion error: %s"), urierr->message); + htmluri = htmlfullname; + } + g_clear_error (&urierr); + } + else + htmluri = g_strdup_printf (PACKAGE_URL "manual/html_node/%s", + htmlfilename); + g_free (htmlfullname); + g_free (htmlfilename); + htmlargv[1] = htmluri; + + /* The following **SHOULD** work but it does not on 28.5.2016 + g_app_info_launch_default_for_uri (htmluri, NULL, &err); + osx: wine is started to launch the uri... + windows: not so bad, but the first access does not work*/ + + if (! (g_spawn_async (NULL, argv, + NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, &err) || +#ifdef _WIN32 + open_windows_help (htmluri, &htmlerr)) +#else + g_spawn_async (NULL, htmlargv, + NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, &htmlerr)) +#endif + ) + { + msg (ME, _("Cannot open reference manual via yelp: %s. " + "Cannot open via html: %s " + "with uri: %s " + "The PSSP manual is also available at %s"), err->message, + htmlerr->message, + htmluri, PACKAGE_URL "documentation.html"); } - g_free (cmd); + g_free (argv[1]); + g_free (htmluri); g_clear_error (&err); + g_clear_error (&htmlerr); } static void diff --git a/src/ui/gui/psppire-dialog-action-indep-samps.c b/src/ui/gui/psppire-dialog-action-indep-samps.c index 1d647092b5..a1fed40abf 100644 --- a/src/ui/gui/psppire-dialog-action-indep-samps.c +++ b/src/ui/gui/psppire-dialog-action-indep-samps.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2007, 2009, 2010, 2011, 2012 Free Software Foundation + Copyright (C) 2007, 2009, 2010, 2011, 2012, 2016 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 @@ -49,10 +49,10 @@ dialog_state_valid (gpointer data) if (NULL == act->grp_var) return FALSE; - if ( 0 == gtk_tree_model_get_iter_first (vars, ¬used)) + if (0 == gtk_tree_model_get_iter_first (vars, ¬used)) return FALSE; - if ( act->group_defn == GROUPS_UNDEF) + if (act->group_defn == GROUPS_UNDEF) return FALSE; return TRUE; @@ -70,11 +70,12 @@ refresh (PsppireDialogAction *da) if (act->grp_var) { - int width = var_get_width (act->grp_var); + const int width = act->grp_var_width; value_destroy (&act->cut_point, width); value_destroy (&act->grp_val[0], width); value_destroy (&act->grp_val[1], width); act->grp_var = NULL; + act->grp_var_width = -1; } psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (act->dg_grp_entry[0]), NULL); @@ -101,7 +102,7 @@ value_entry_contains_invalid (PsppireValueEntry *ve, const struct variable *var) const int width = var_get_width (var); value_init (&val, width); - if ( psppire_value_entry_get_value (ve, &val, width)) + if (psppire_value_entry_get_value (ve, &val, width)) { if (var_is_value_missing (var, &val, MV_SYSTEM)) { @@ -124,6 +125,9 @@ define_groups_state_valid (gpointer data) { PsppireDialogActionIndepSamps *act = data; + if (act->grp_var == NULL) + return FALSE; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->dg_values_toggle_button))) { @@ -153,14 +157,16 @@ run_define_groups (PsppireDialogActionIndepSamps *act) PsppireDialogAction *da = PSPPIRE_DIALOG_ACTION (act); GtkWidget *parent1 = gtk_widget_get_parent (act->dg_table1); GtkWidget *parent2 = gtk_widget_get_parent (act->dg_table2); - + + g_return_if_fail (act->grp_var); + if (parent1) gtk_container_remove (GTK_CONTAINER (parent1), act->dg_table1); if (parent2) gtk_container_remove (GTK_CONTAINER (parent2), act->dg_table2); - if ( var_is_numeric (act->grp_var)) + if (var_is_numeric (act->grp_var)) { gtk_grid_attach (GTK_GRID (act->dg_table1), act->dg_table2, 1, 1, 1, 1); @@ -181,7 +187,7 @@ run_define_groups (PsppireDialogActionIndepSamps *act) psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (act->dg_grp_entry[1]), act->grp_var); psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (act->dg_cut_point_entry), act->grp_var); - if ( act->group_defn != GROUPS_CUT_POINT ) + if (act->group_defn != GROUPS_CUT_POINT ) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (act->dg_cut_point_toggle_button), TRUE); @@ -237,13 +243,13 @@ on_grp_var_change (GtkEntry *entry, PsppireDialogActionIndepSamps *act) PsppireDialogAction *da = PSPPIRE_DIALOG_ACTION (act); const gchar *text = gtk_entry_get_text (entry); - const struct variable *v = psppire_dict_lookup_var (da->dict, text); + const struct variable *v = da->dict ? psppire_dict_lookup_var (da->dict, text) : NULL; gtk_widget_set_sensitive (act->define_groups_button, v != NULL); if (act->grp_var) { - int width = var_get_width (act->grp_var); + const int width = act->grp_var_width; value_destroy (&act->cut_point, width); value_destroy (&act->grp_val[0], width); value_destroy (&act->grp_val[1], width); @@ -264,13 +270,14 @@ on_grp_var_change (GtkEntry *entry, PsppireDialogActionIndepSamps *act) } else { - act->cut_point.short_string[0] = '\0'; - act->grp_val[0].short_string[0] = '\0'; - act->grp_val[1].short_string[0] = '\0'; + value_str_rw (&act->cut_point, width)[0] = '\0'; + value_str_rw (&act->grp_val[0], width)[0] = '\0'; + value_str_rw (&act->grp_val[1], width)[0] = '\0'; } } act->grp_var = v; + act->grp_var_width = v ? var_get_width (v) : -1; } static void @@ -345,6 +352,8 @@ psppire_dialog_action_indep_samps_activate (PsppireDialogAction *a) g_signal_connect (act->group_var_entry, "changed", G_CALLBACK (on_grp_var_change), act); + on_grp_var_change (GTK_ENTRY (act->group_var_entry), act); + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_indep_samps_parent_class)->activate) PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_indep_samps_parent_class)->activate (pda); } @@ -427,6 +436,7 @@ static void psppire_dialog_action_indep_samps_init (PsppireDialogActionIndepSamps *act) { act->grp_var = NULL; + act->grp_var_width = -1; act->group_defn = GROUPS_UNDEF; } diff --git a/src/ui/gui/psppire-dialog-action-indep-samps.h b/src/ui/gui/psppire-dialog-action-indep-samps.h index 359640df7e..672a403074 100644 --- a/src/ui/gui/psppire-dialog-action-indep-samps.h +++ b/src/ui/gui/psppire-dialog-action-indep-samps.h @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2012 Free Software Foundation + Copyright (C) 2012, 2016 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 @@ -76,6 +76,7 @@ struct _PsppireDialogActionIndepSamps /* The variable which determines to which group a datum belongs */ const struct variable *grp_var; + int grp_var_width; /* The GtkEntry which holds the reference to the above variable */ GtkWidget *group_var_entry; diff --git a/src/ui/gui/psppire-dialog-action-recode-different.c b/src/ui/gui/psppire-dialog-action-recode-different.c new file mode 100644 index 0000000000..2d28146c79 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-recode-different.c @@ -0,0 +1,448 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2007, 2009, 2010, 2011, 2012, 2014, 2016 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 . */ + +#include + +#include "psppire-var-view.h" + +#include "psppire-dialog-action-recode-different.h" +#include "builder-wrapper.h" +#include + +#include "psppire-acr.h" + +#include "psppire-selector.h" +#include "psppire-val-chooser.h" + +#include "helper.h" +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + + + +static gboolean +difx_variable_treeview_is_populated (PsppireDialogActionRecode *rd) +{ + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); + + if (g_hash_table_size (rdd->varmap) != gtk_tree_model_iter_n_children (model, NULL) ) + return FALSE; + + return TRUE; +} + + +/* Dialog is valid iff at least one variable has been selected, + AND the list of mappings is not empty. +*/ +static gboolean +dialog_state_valid (gpointer data) +{ + PsppireDialogActionRecode *rd = data; + GtkTreeIter not_used; + + if ( ! rd->value_map ) + return FALSE; + + if ( ! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map), + ¬_used) ) + return FALSE; + + + return difx_variable_treeview_is_populated (rd); +} + + + + +static void +psppire_dialog_action_recode_different_class_init (PsppireDialogActionRecodeDifferentClass *class); + +G_DEFINE_TYPE (PsppireDialogActionRecodeDifferent, psppire_dialog_action_recode_different, PSPPIRE_TYPE_DIALOG_ACTION_RECODE); + +static void +refresh (PsppireDialogAction *act) +{ + PsppireDialogActionRecode *rd = PSPPIRE_DIALOG_ACTION_RECODE (act); + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + psppire_dialog_action_recode_refresh (act); + + if (rdd->varmap) + g_hash_table_remove_all (rdd->varmap); +} + + +static void +on_old_new_show (PsppireDialogActionRecode *rd) +{ + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE); + + g_signal_emit_by_name (rd->toggle[BUTTON_NEW_VALUE], "toggled"); + + gtk_widget_show (rd->toggle[BUTTON_NEW_COPY]); + gtk_widget_show (rd->new_copy_label); + gtk_widget_show (rd->strings_box); +} + + + +/* 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); +} + + + +static void +render_new_var_name (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + struct nlp *nlp = NULL; + PsppireDialogActionRecode *rd = data; + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + struct variable *var = NULL; + + gtk_tree_model_get (tree_model, iter, + 0, &var, + -1); + + nlp = g_hash_table_lookup (rdd->varmap, var); + + if ( nlp ) + g_object_set (cell, "text", nlp->name, NULL); + else + g_object_set (cell, "text", "", NULL); +} + +static void +on_change_clicked (GObject *obj, gpointer data) +{ + PsppireDialogActionRecode *rd = data; + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + struct variable *var = NULL; + struct nlp *nlp; + + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); + + GtkTreeIter iter; + GtkTreeSelection *selection = + gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview)); + + GList *rows = gtk_tree_selection_get_selected_rows (selection, &model); + + const gchar *dest_var_name = + gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry)); + + const gchar *dest_var_label = + gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry)); + + if ( NULL == rows || rows->next != NULL) + goto finish; + + gtk_tree_model_get_iter (model, &iter, rows->data); + + gtk_tree_model_get (model, &iter, 0, &var, -1); + + g_hash_table_remove (rdd->varmap, var); + + nlp = nlp_create (dest_var_name, dest_var_label); + + g_hash_table_insert (rdd->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); +} + + + +/* 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 + currently selected row. +*/ +static void +on_selection_change (GtkTreeSelection *selection, gpointer data) +{ + PsppireDialogActionRecode *rd = data; + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); + + 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; + + gtk_widget_set_sensitive (rd->change_button, TRUE); + gtk_widget_set_sensitive (rd->new_name_entry, TRUE); + gtk_widget_set_sensitive (rd->new_label_entry, TRUE); + + ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data); + g_return_if_fail (ok); + + gtk_tree_model_get (model, &iter, + 0, &var, + -1); + + nlp = g_hash_table_lookup (rdd->varmap, var); + + 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 + { + 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); + + gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), ""); + gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), ""); + } + + + g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL); + g_list_free (rows); +} + + + + +static void +populate_treeview (PsppireDialogActionRecode *act) +{ + GtkTreeSelection *sel; + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (act); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *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, + act, NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (act->variable_treeview), col); + + col = gtk_tree_view_get_column (GTK_TREE_VIEW (act->variable_treeview), 0); + + g_object_set (col, "title", _("Old"), NULL); + + g_object_set (act->variable_treeview, "headers-visible", TRUE, NULL); + + rdd->varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (act->variable_treeview)); + + g_signal_connect (sel, "changed", + G_CALLBACK (on_selection_change), act); + + g_signal_connect (act->change_button, "clicked", + G_CALLBACK (on_change_clicked), act); +} + + +static void +psppire_dialog_action_recode_different_activate (PsppireDialogAction *a) +{ + PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (a); + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + + psppire_dialog_action_recode_pre_activate (act, populate_treeview); + + gtk_window_set_title (GTK_WINDOW (pda->dialog), + _("Recode into Different Variables")); + + gtk_window_set_title (GTK_WINDOW (act->old_and_new_dialog), + _("Recode into Different Variables: Old and New Values ")); + + gtk_widget_show (act->output_variable_box); + + g_signal_connect_swapped (act->old_and_new_dialog, "show", + G_CALLBACK (on_old_new_show), act); + + psppire_dialog_action_set_refresh (pda, refresh); + + psppire_dialog_action_set_valid_predicate (pda, + dialog_state_valid); + + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_different_parent_class)->activate) + PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_different_parent_class)->activate (pda); +} + +static void +append_into_clause (const PsppireDialogActionRecode *rd, struct string *dds) +{ + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + /* If applicable set the INTO clause which determines into which variables the new values go */ + GtkTreeIter iter; + ds_put_cstr (dds, "\n\tINTO "); + gboolean ok; + + for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter); + ok; + 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); + + nlp = g_hash_table_lookup (rdd->varmap, var); + + ds_put_cstr (dds, nlp->name); + ds_put_cstr (dds, " "); + } +} + +static void +append_string_declarations (const PsppireDialogActionRecode *rd, struct string *dds) +{ + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + /* Declare new string variables if applicable */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button))) + { + GHashTableIter iter; + + struct variable *var = NULL; + struct nlp *nlp = NULL; + + g_hash_table_iter_init (&iter, rdd->varmap); + while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp)) + { + ds_put_cstr (dds, "\nSTRING "); + ds_put_cstr (dds, nlp->name); + ds_put_c_format (dds, " (A%d).", + (int) + gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry))); + } + } +} + +static void +append_new_value_labels (const PsppireDialogActionRecode *rd, struct string *dds) +{ + PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd); + + /* If applicable, set labels for the new variables. */ + GHashTableIter iter; + + struct variable *var = NULL; + struct nlp *nlp = NULL; + + g_hash_table_iter_init (&iter, rdd->varmap); + while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp)) + { + if (nlp->label) + { + struct string sl; + ds_init_empty (&sl); + syntax_gen_string (&sl, ss_cstr (nlp->label)); + ds_put_c_format (dds, "\nVARIABLE LABELS %s %s.", + nlp->name, ds_cstr (&sl)); + + ds_destroy (&sl); + } + } +} + +static char * +diff_generate_syntax (const PsppireDialogAction *act) +{ + return psppire_dialog_action_recode_generate_syntax (act, + append_string_declarations, + append_into_clause, + append_new_value_labels); +} + +static gboolean +target_is_string (const PsppireDialogActionRecode *rd) +{ + return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)); +} + +static void +psppire_dialog_action_recode_different_class_init (PsppireDialogActionRecodeDifferentClass *class) +{ + psppire_dialog_action_set_activation (class, psppire_dialog_action_recode_different_activate); + + PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = diff_generate_syntax; + PSPPIRE_DIALOG_ACTION_RECODE_CLASS (class)->target_is_string = target_is_string; +} + + +static void +psppire_dialog_action_recode_different_init (PsppireDialogActionRecodeDifferent *act) +{ +} + diff --git a/src/ui/gui/psppire-dialog-action-recode-different.h b/src/ui/gui/psppire-dialog-action-recode-different.h new file mode 100644 index 0000000000..03e199f5f5 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-recode-different.h @@ -0,0 +1,75 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2012 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 . */ + + +#include +#include + +#include "psppire-dialog-action-recode.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT_H__ +#define __PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT_H__ + +G_BEGIN_DECLS + + +#define PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT (psppire_dialog_action_recode_different_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT, PsppireDialogActionRecodeDifferent)) + +#define PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT, \ + PsppireDialogActionRecodeDifferentClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_RECODE_DIFFERENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT)) + +#define PSPPIRE_IS_DIALOG_ACTION_RECODE_DIFFERENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT)) + + +#define PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT, \ + PsppireDialogActionRecodeDifferentClass)) + +typedef struct _PsppireDialogActionRecodeDifferent PsppireDialogActionRecodeDifferent; +typedef struct _PsppireDialogActionRecodeDifferentClass PsppireDialogActionRecodeDifferentClass; + + +struct _PsppireDialogActionRecodeDifferent +{ + PsppireDialogActionRecode parent; + + /* A hash table of struct nlp's indexed by variable */ + GHashTable *varmap; +}; + + +struct _PsppireDialogActionRecodeDifferentClass +{ + PsppireDialogActionRecodeClass parent_class; +}; + + +GType psppire_dialog_action_recode_different_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT_H__ */ diff --git a/src/ui/gui/psppire-dialog-action-recode-same.c b/src/ui/gui/psppire-dialog-action-recode-same.c new file mode 100644 index 0000000000..975ff8cead --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-recode-same.c @@ -0,0 +1,159 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2007, 2009, 2010, 2011, 2012, 2014, 2016 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 . */ + + +#include + +#include "psppire-var-view.h" + +#include "psppire-dialog-action-recode-same.h" +#include "builder-wrapper.h" +#include + +#include "psppire-acr.h" + +#include "psppire-selector.h" +#include "psppire-val-chooser.h" + +#include "helper.h" +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +static gboolean +difx_variable_treeview_is_populated (PsppireDialogActionRecode *rd) +{ + GtkTreeIter not_used; + + GtkTreeModel *vars = + gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); + + if ( !gtk_tree_model_get_iter_first (vars, ¬_used)) + return FALSE; + + return TRUE; +} + + +/* Dialog is valid iff at least one variable has been selected, + AND the list of mappings is not empty. + */ +static gboolean +dialog_state_valid (gpointer data) +{ + PsppireDialogActionRecode *rd = data; + GtkTreeIter not_used; + + if ( ! rd->value_map ) + return FALSE; + + if ( ! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map), + ¬_used) ) + return FALSE; + + return difx_variable_treeview_is_populated (rd); +} + + + + +static void +psppire_dialog_action_recode_same_class_init (PsppireDialogActionRecodeSameClass *class); + +G_DEFINE_TYPE (PsppireDialogActionRecodeSame, psppire_dialog_action_recode_same, PSPPIRE_TYPE_DIALOG_ACTION_RECODE); + +static void +refresh (PsppireDialogAction *rd) +{ + psppire_dialog_action_recode_refresh (rd); +} + +static void +on_old_new_show (PsppireDialogActionRecode *rd) +{ + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE); + + g_signal_emit_by_name (rd->toggle[BUTTON_NEW_VALUE], "toggled"); + + gtk_widget_hide (rd->toggle[BUTTON_NEW_COPY]); + gtk_widget_hide (rd->new_copy_label); + gtk_widget_hide (rd->strings_box); +} + +static void +psppire_dialog_action_recode_same_activate (PsppireDialogAction *a) +{ + PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (a); + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + + psppire_dialog_action_recode_pre_activate (act, NULL); + + gtk_window_set_title (GTK_WINDOW (pda->dialog), + _("Recode into Same Variables")); + + g_signal_connect_swapped (act->old_and_new_dialog, "show", + G_CALLBACK (on_old_new_show), act); + + gtk_window_set_title (GTK_WINDOW (act->old_and_new_dialog), + _("Recode into Same Variables: Old and New Values")); + + gtk_widget_hide (act->output_variable_box); + + psppire_dialog_action_set_refresh (pda, refresh); + + psppire_dialog_action_set_valid_predicate (pda, + dialog_state_valid); + + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_same_parent_class)->activate) + PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_same_parent_class)->activate (pda); +} + +static void +null_op (const PsppireDialogActionRecode *rd, struct string *dds) +{ +} + +static char * +same_generate_syntax (const PsppireDialogAction *act) +{ + return psppire_dialog_action_recode_generate_syntax (act, null_op, null_op, null_op); +} + +static gboolean +target_is_string (const PsppireDialogActionRecode *rd) +{ + return rd->input_var_is_string; +} + + +static void +psppire_dialog_action_recode_same_class_init (PsppireDialogActionRecodeSameClass *class) +{ + psppire_dialog_action_set_activation (class, psppire_dialog_action_recode_same_activate); + + PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = same_generate_syntax; + PSPPIRE_DIALOG_ACTION_RECODE_CLASS (class)->target_is_string = target_is_string; +} + + +static void +psppire_dialog_action_recode_same_init (PsppireDialogActionRecodeSame *act) +{ +} + diff --git a/src/ui/gui/psppire-dialog-action-recode-same.h b/src/ui/gui/psppire-dialog-action-recode-same.h new file mode 100644 index 0000000000..f2a9be54cf --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-recode-same.h @@ -0,0 +1,71 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2012, 2016 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 . */ + + +#include +#include + +#include "psppire-dialog-action-recode.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_RECODE_SAME_H__ +#define __PSPPIRE_DIALOG_ACTION_RECODE_SAME_H__ + +G_BEGIN_DECLS + +#define PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME (psppire_dialog_action_recode_same_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_RECODE_SAME(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME, PsppireDialogActionRecodeSame)) + +#define PSPPIRE_DIALOG_ACTION_RECODE_SAME_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME, \ + PsppireDialogActionRecodeSameClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_RECODE_SAME(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME)) + +#define PSPPIRE_IS_DIALOG_ACTION_RECODE_SAME_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME)) + + +#define PSPPIRE_DIALOG_ACTION_RECODE_SAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME, \ + PsppireDialogActionRecodeSameClass)) + +typedef struct _PsppireDialogActionRecodeSame PsppireDialogActionRecodeSame; +typedef struct _PsppireDialogActionRecodeSameClass PsppireDialogActionRecodeSameClass; + + +struct _PsppireDialogActionRecodeSame +{ + PsppireDialogActionRecode parent; +}; + + +struct _PsppireDialogActionRecodeSameClass +{ + PsppireDialogActionRecodeClass parent_class; +}; + + +GType psppire_dialog_action_recode_same_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_RECODE_SAME_H__ */ diff --git a/src/ui/gui/psppire-dialog-action-recode.c b/src/ui/gui/psppire-dialog-action-recode.c index d4066dbd06..6e8b222b47 100644 --- a/src/ui/gui/psppire-dialog-action-recode.c +++ b/src/ui/gui/psppire-dialog-action-recode.c @@ -122,7 +122,7 @@ new_value_to_string (const GValue *src, GValue *dest) } } -static GType +GType new_value_get_type (void) { static GType t = 0; @@ -153,102 +153,6 @@ on_string_toggled (GtkToggleButton *b, PsppireDialogActionRecode *rd) gtk_widget_set_sensitive (rd->convert_button, !active); } -/* 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 - currently selected row. - */ -static void -on_selection_change (GtkTreeSelection *selection, gpointer data) -{ - PsppireDialogActionRecode *rd = data; - - GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); - - 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; - - gtk_widget_set_sensitive (rd->change_button, TRUE); - gtk_widget_set_sensitive (rd->new_name_entry, TRUE); - gtk_widget_set_sensitive (rd->new_label_entry, TRUE); - - ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data); - g_return_if_fail (ok); - - gtk_tree_model_get (model, &iter, - 0, &var, - -1); - - nlp = g_hash_table_lookup (rd->varmap, var); - - 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 - { - 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); - - gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), ""); - gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), ""); - } - - - g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL); - g_list_free (rows); -} - static void on_convert_toggled (GtkToggleButton *b, PsppireDialogActionRecode *rd) @@ -261,48 +165,6 @@ on_convert_toggled (GtkToggleButton *b, PsppireDialogActionRecode *rd) gtk_widget_set_sensitive (rd->string_button, !active); } -static void -on_change_clicked (GObject *obj, gpointer data) -{ - PsppireDialogActionRecode *rd = data; - struct variable *var = NULL; - struct nlp *nlp; - - GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); - - GtkTreeIter iter; - GtkTreeSelection *selection = - gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview)); - - GList *rows = gtk_tree_selection_get_selected_rows (selection, &model); - - const gchar *dest_var_name = - gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry)); - - const gchar *dest_var_label = - gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry)); - - if ( NULL == rows || rows->next != NULL) - goto finish; - - gtk_tree_model_get_iter (model, &iter, rows->data); - - 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); -} - - static void focus_value_entry (GtkWidget *w, PsppireDialogActionRecode *rd) { @@ -330,47 +192,12 @@ set_acr (PsppireDialogActionRecode *rd) psppire_acr_set_enabled (PSPPIRE_ACR (rd->acr), !g_str_equal (text, "")); } -enum { - COL_VALUE_OLD, - COL_VALUE_NEW, - n_COL_VALUES -}; - -/* Dialog is valid iff at least one variable has been selected, - AND the list of mappings is not empty. - */ -static gboolean -dialog_state_valid (gpointer data) -{ - GtkTreeIter not_used; - PsppireDialogActionRecode *rd = data; - - if ( ! rd->value_map ) - return FALSE; - - if ( ! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map), - ¬_used) ) - return FALSE; - - if ( rd->different ) - { - GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); - - if (g_hash_table_size (rd->varmap) != gtk_tree_model_iter_n_children (model, NULL) ) - return FALSE; - } - else - { - GtkTreeModel *vars = - gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); - - if ( !gtk_tree_model_get_iter_first (vars, ¬_used)) - return FALSE; - } - - return TRUE; -} - +enum + { + COL_VALUE_OLD, + COL_VALUE_NEW, + n_COL_VALUES + }; /* Callback which gets called when a new row is selected in the acr's variable treeview. @@ -468,34 +295,26 @@ set_new_value (GValue *val, const PsppireDialogActionRecode *rd) const gchar *text = NULL; struct new_value nv; - if ( gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]))) + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]))) { text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry)); - nv.type = NV_NUMERIC; - if ( - (! rd->different && rd->input_var_is_string) || - ( rd->different && - gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON (rd->string_button))) - ) - { - nv.type = NV_STRING; - } + + if (PSPPIRE_DIALOG_ACTION_RECODE_CLASS (G_OBJECT_GET_CLASS (rd))->target_is_string (rd)) + nv.type = NV_STRING; if ( nv.type == NV_STRING ) nv.v.s = g_strdup (text); else nv.v.v = g_strtod (text, 0); } - else if ( gtk_toggle_button_get_active + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]))) { nv.type = NV_COPY; } - - else if ( gtk_toggle_button_get_active + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]))) { nv.type = NV_SYSMIS; @@ -540,13 +359,6 @@ run_old_and_new_dialog (PsppireDialogActionRecode *rd) psppire_acr_set_model (PSPPIRE_ACR (rd->acr), local_store); psppire_acr_set_get_value_func (PSPPIRE_ACR (rd->acr), set_value, rd); - gtk_window_set_title (GTK_WINDOW (rd->old_and_new_dialog), - rd->different - ? _("Recode into Different Variables: Old and New Values ") - : _("Recode into Same Variables: Old and New Values") - ); - - { /* Find the type of the first variable (it's invariant that all variables are of the same type) */ @@ -588,24 +400,6 @@ run_old_and_new_dialog (PsppireDialogActionRecode *rd) psppire_dialog_notify_change (PSPPIRE_DIALOG (pda->dialog)); } -static void -on_old_new_show (PsppireDialogActionRecode *rd) -{ - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE); - - g_signal_emit_by_name (rd->toggle[BUTTON_NEW_VALUE], "toggled"); - - g_object_set (rd->toggle[BUTTON_NEW_COPY], - "visible", rd->different, NULL); - - g_object_set (rd->new_copy_label, - "visible", rd->different, NULL); - - g_object_set (rd->strings_box, - "visible", rd->different, NULL); -} - /* Sets the sensitivity of TARGET dependent upon the active status of BUTTON */ @@ -620,31 +414,6 @@ toggle_sensitivity (GtkToggleButton *button, GtkWidget *target) gtk_widget_set_sensitive (target, state); } -static void -render_new_var_name (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - struct nlp *nlp = NULL; - PsppireDialogActionRecode *rd = data; - - struct variable *var = NULL; - - gtk_tree_model_get (tree_model, iter, - 0, &var, - -1); - - 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); -} - - @@ -653,8 +422,8 @@ psppire_dialog_action_recode_class_init (PsppireDialogActionRecodeClass *class); G_DEFINE_TYPE (PsppireDialogActionRecode, psppire_dialog_action_recode, PSPPIRE_TYPE_DIALOG_ACTION); -static void -refresh (PsppireDialogAction *rd_) +void +psppire_dialog_action_recode_refresh (PsppireDialogAction *rd_) { PsppireDialogActionRecode *rd = PSPPIRE_DIALOG_ACTION_RECODE (rd_); @@ -667,26 +436,29 @@ refresh (PsppireDialogAction *rd_) gtk_widget_set_sensitive (rd->new_name_entry, FALSE); gtk_widget_set_sensitive (rd->new_label_entry, FALSE); - if ( rd->different && rd->varmap ) - g_hash_table_remove_all (rd->varmap); - gtk_list_store_clear (GTK_LIST_STORE (rd->value_map)); } - static void -psppire_dialog_action_recode_activate (PsppireDialogAction *a) +psppire_dialog_action_recode_activate (PsppireDialogAction *act) { - PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (a); - PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_parent_class)->activate) + PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_parent_class)->activate (act); +} + + +void +psppire_dialog_action_recode_pre_activate (PsppireDialogActionRecode *act, void (*populate_treeview) (PsppireDialogActionRecode *)) +{ + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (act); GHashTable *thing = psppire_dialog_action_get_hash_table (pda); - GtkBuilder *xml = g_hash_table_lookup (thing, a); + GtkBuilder *xml = g_hash_table_lookup (thing, act); if (!xml) { xml = builder_new ("recode.ui"); - g_hash_table_insert (thing, a, xml); + g_hash_table_insert (thing, act, xml); pda->dialog = get_widget_assert (xml, "recode-dialog"); pda->source = get_widget_assert (xml, "treeview1"); @@ -696,10 +468,9 @@ psppire_dialog_action_recode_activate (PsppireDialogAction *a) GtkWidget *oldandnew = get_widget_assert (xml, "button1"); - GtkWidget *output_variable_box = get_widget_assert (xml,"frame4"); + act->output_variable_box = get_widget_assert (xml,"frame4"); act->change_button = get_widget_assert (xml, "change-button"); - act->varmap = NULL; act->variable_treeview = get_widget_assert (xml, "treeview2"); act->new_name_entry = get_widget_assert (xml, "dest-name-entry"); act->new_label_entry = get_widget_assert (xml, "dest-label-entry"); @@ -708,51 +479,8 @@ psppire_dialog_action_recode_activate (PsppireDialogAction *a) old_value_get_type (), new_value_get_type ()); - if (act->different) - gtk_window_set_title (GTK_WINDOW (pda->dialog), - _("Recode into Different Variables")); - else - gtk_window_set_title (GTK_WINDOW (pda->dialog), - _("Recode into Same Variables")); - - g_object_set (output_variable_box, "visible", act->different, NULL); - - if (act->different) - { - GtkTreeSelection *sel; - - GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - - GtkTreeViewColumn *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, - act, NULL); - - - gtk_tree_view_append_column (GTK_TREE_VIEW (act->variable_treeview), col); - - - col = gtk_tree_view_get_column (GTK_TREE_VIEW (act->variable_treeview), 0); - - g_object_set (col, "title", _("Old"), NULL); - - g_object_set (act->variable_treeview, "headers-visible", TRUE, NULL); - - act->varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy); - - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (act->variable_treeview)); - - g_signal_connect (sel, "changed", - G_CALLBACK (on_selection_change), act); - - g_signal_connect (act->change_button, "clicked", - G_CALLBACK (on_change_clicked), act); - } - + if (populate_treeview) + populate_treeview (act); psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types); @@ -835,19 +563,8 @@ psppire_dialog_action_recode_activate (PsppireDialogAction *a) g_signal_connect (act->convert_button, "toggled", G_CALLBACK (on_convert_toggled), act); - - g_signal_connect_swapped (act->old_and_new_dialog, "show", - G_CALLBACK (on_old_new_show), act); } } - - psppire_dialog_action_set_refresh (pda, refresh); - - psppire_dialog_action_set_valid_predicate (pda, - dialog_state_valid); - - if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_parent_class)->activate) - PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_recode_parent_class)->activate (pda); } /* Generate a syntax fragment for NV and append it to STR */ @@ -877,8 +594,11 @@ new_value_append_syntax (struct string *dds, const struct new_value *nv) } -static char * -generate_syntax (const PsppireDialogAction *act) +char * +psppire_dialog_action_recode_generate_syntax (const PsppireDialogAction *act, + void (*append_string_decls) (const PsppireDialogActionRecode *, struct string *), + void (*append_into_clause) (const PsppireDialogActionRecode *, struct string *), + void (*append_new_value_labels) (const PsppireDialogActionRecode *, struct string *)) { PsppireDialogActionRecode *rd = PSPPIRE_DIALOG_ACTION_RECODE (act); gboolean ok; @@ -888,28 +608,8 @@ generate_syntax (const PsppireDialogAction *act) ds_init_empty (&dds); - - /* Declare new string variables if applicable */ - if ( rd->different && - gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button))) - { - GHashTableIter 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)) - { - ds_put_cstr (&dds, "\nSTRING "); - ds_put_cstr (&dds, nlp->name); - ds_put_c_format (&dds, " (A%d).", - (int) - gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry) ) - ); - } - } - + append_string_decls (rd, &dds); + ds_put_cstr (&dds, "\nRECODE "); psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &dds); @@ -950,53 +650,12 @@ generate_syntax (const PsppireDialogAction *act) g_value_unset (&nv_value); } - - if ( rd->different ) - { - - GtkTreeIter iter; - ds_put_cstr (&dds, "\n\tINTO "); - - for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter); - ok; - 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); - - nlp = g_hash_table_lookup (rd->varmap, var); - - ds_put_cstr (&dds, nlp->name); - ds_put_cstr (&dds, " "); - } - } + append_into_clause (rd, &dds); ds_put_cstr (&dds, "."); - /* If applicable, set labels for the new variables. */ - if ( rd->different ) - { - GHashTableIter 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)) - { - if (nlp->label) - { - struct string sl; - ds_init_empty (&sl); - syntax_gen_string (&sl, ss_cstr (nlp->label)); - ds_put_c_format (&dds, "\nVARIABLE LABELS %s %s.", - nlp->name, ds_cstr (&sl)); - - ds_destroy (&sl); - } - } - } - + append_new_value_labels (rd, &dds); + ds_put_cstr (&dds, "\nEXECUTE.\n"); @@ -1008,75 +667,10 @@ generate_syntax (const PsppireDialogAction *act) } -enum -{ - PROP_0, - PROP_DIFF -}; - -static void -__set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (object); - - switch (prop_id) - { - case PROP_DIFF: - act->different = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - }; -} - - -static void -__get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (object); - - switch (prop_id) - { - case PROP_DIFF: - g_value_set_boolean (value, act->different); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - }; -} - - static void psppire_dialog_action_recode_class_init (PsppireDialogActionRecodeClass *class) { - GObjectClass *object_class = G_OBJECT_CLASS (class); - psppire_dialog_action_set_activation (class, psppire_dialog_action_recode_activate); - - PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax; - - GParamSpec *diff_spec = - g_param_spec_boolean ("recode-to-new-variable", - "Recode to New Variable", - "True iff the new values should be placed in a new variable", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - - - object_class->set_property = __set_property; - object_class->get_property = __get_property; - - g_object_class_install_property (object_class, - PROP_DIFF, - diff_spec); } diff --git a/src/ui/gui/psppire-dialog-action-recode.h b/src/ui/gui/psppire-dialog-action-recode.h index 37d18d5c46..f46c34aaa6 100644 --- a/src/ui/gui/psppire-dialog-action-recode.h +++ b/src/ui/gui/psppire-dialog-action-recode.h @@ -74,16 +74,13 @@ struct _PsppireDialogActionRecode GtkWidget *convert_button; GtkWidget *new_copy_label; + GtkWidget *new_value_entry; GtkWidget *old_value_chooser; GtkListStore *value_map; - /* Indicates that the INTO {new variables} form of the dialog - is being used */ - gboolean different; - GtkWidget *acr; gboolean input_var_is_string; @@ -92,22 +89,35 @@ struct _PsppireDialogActionRecode GtkWidget *new_label_entry; GtkWidget *change_button; + GtkWidget *output_variable_box; + GtkWidget *string_button; GtkWidget *width_entry; - - /* A hash table of struct nlp's indexed by variable */ - GHashTable *varmap; }; struct _PsppireDialogActionRecodeClass { PsppireDialogActionClass parent_class; -}; + gboolean (*target_is_string) (const PsppireDialogActionRecode *); +}; GType psppire_dialog_action_recode_get_type (void) ; +void psppire_dialog_action_recode_refresh (PsppireDialogAction *); + +void psppire_dialog_action_recode_pre_activate (PsppireDialogActionRecode *act, void (*populate_treeview) (PsppireDialogActionRecode *) ); + + +GType new_value_get_type (void); + + +char *psppire_dialog_action_recode_generate_syntax (const PsppireDialogAction *act, + void (*add_string_decls) (const PsppireDialogActionRecode *, struct string *), + void (*add_into_clause) (const PsppireDialogActionRecode *, struct string *), + void (*add_new_value_labels) (const PsppireDialogActionRecode *, struct string *)); + G_END_DECLS #endif /* __PSPPIRE_DIALOG_ACTION_RECODE_H__ */ diff --git a/src/ui/gui/psppire-dialog-action-var-info.c b/src/ui/gui/psppire-dialog-action-var-info.c index 21f9f0439c..998f7bb45c 100644 --- a/src/ui/gui/psppire-dialog-action-var-info.c +++ b/src/ui/gui/psppire-dialog-action-var-info.c @@ -175,28 +175,29 @@ psppire_dialog_action_var_info_activate (PsppireDialogAction *a) { xml = builder_new ("variable-info.ui"); g_hash_table_insert (thing, a, xml); - } - - act->output = psppire_output_view_new ( - GTK_LAYOUT (get_widget_assert (xml, "layout1")), NULL, NULL, NULL); - pda->dialog = get_widget_assert (xml, "variable-info-dialog"); - pda->source = get_widget_assert (xml, "treeview2"); + act->output = + psppire_output_view_new (GTK_LAYOUT (get_widget_assert (xml, "layout1")), + NULL, NULL, NULL); + + pda->dialog = get_widget_assert (xml, "variable-info-dialog"); + pda->source = get_widget_assert (xml, "treeview2"); - g_object_set (pda->source, - "selection-mode", GTK_SELECTION_MULTIPLE, - NULL); + g_object_set (pda->source, + "selection-mode", GTK_SELECTION_MULTIPLE, + NULL); - g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (pda->source)), - "changed", G_CALLBACK (populate_output), - act); + g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (pda->source)), + "changed", G_CALLBACK (populate_output), + act); - g_signal_connect (pda->dialog, "response", G_CALLBACK (jump_to), - pda); - - psppire_dialog_action_set_valid_predicate (pda, - treeview_item_selected); + g_signal_connect (pda->dialog, "response", G_CALLBACK (jump_to), + pda); + psppire_dialog_action_set_valid_predicate (pda, + treeview_item_selected); + } + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_var_info_parent_class)->activate) PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_var_info_parent_class)->activate (pda); } diff --git a/src/ui/gui/psppire-window.c b/src/ui/gui/psppire-window.c index 6fb0b87c47..f3c360b35e 100644 --- a/src/ui/gui/psppire-window.c +++ b/src/ui/gui/psppire-window.c @@ -111,8 +111,8 @@ psppire_window_set_title (PsppireWindow *window) int minor = 1; sscanf (bare_version, "%*d.%d.%*d", &minor); if (minor % 2) - g_string_append (title, " - UNRELEASED TEST SOFTWARE! NOT FOR PRODUCTION USE."); - + g_string_append_printf (title, " - Test version! Please report bugs to %s", PACKAGE_BUGREPORT); + gtk_window_set_title (GTK_WINDOW (window), title->str); g_string_free (title, TRUE); diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c index 042b8fcee7..3f138291ec 100644 --- a/src/ui/gui/widgets.c +++ b/src/ui/gui/widgets.c @@ -43,7 +43,8 @@ #include "psppire-dialog-action-oneway.h" #include "psppire-means-layer.h" #include "psppire-dialog-action-rank.h" -#include "psppire-dialog-action-recode.h" +#include "psppire-dialog-action-recode-same.h" +#include "psppire-dialog-action-recode-different.h" #include "psppire-dialog-action-regression.h" #include "psppire-dialog-action-reliability.h" #include "psppire-dialog-action-roc.h" @@ -92,7 +93,8 @@ static const get_type_func dialog_action_types[]= psppire_dialog_action_indep_samps_get_type, psppire_dialog_action_var_info_get_type, psppire_dialog_action_rank_get_type, - psppire_dialog_action_recode_get_type, + psppire_dialog_action_recode_same_get_type, + psppire_dialog_action_recode_different_get_type, psppire_dialog_action_reliability_get_type, psppire_dialog_action_regression_get_type, psppire_dialog_action_roc_get_type, diff --git a/tests/language/data-io/get-data-txt.at b/tests/language/data-io/get-data-txt.at index b0b2616f7a..53f5d4fddc 100644 --- a/tests/language/data-io/get-data-txt.at +++ b/tests/language/data-io/get-data-txt.at @@ -343,3 +343,40 @@ AT_CHECK([pspp -o pspp.csv x.sps], [1], [ignore]) AT_CLEANUP + +AT_SETUP([GET DATA /TYPE=txt bug]) + + +AT_DATA([thing.txt], [dnl +foo, title, last +1, this, 1 +2, that, 2 +3, other, 3 +]) + +AT_DATA([x.sps], [dnl +GET DATA + /TYPE=TXT + /FILE="thing.txt" + /ARRANGEMENT=DELIMITED + /DELCASE=LINE + /FIRSTCASE=2 + /DELIMITERS="," + /VARIABLES=foo F1.0 + title A8 + last F2.0. + +list. +]) + +AT_CHECK([pspp -O format=csv x.sps], [0], [dnl +Table: Data List +foo,title,last +1,this ,1 +2,that ,2 +3,other ,3 +]) + +AT_CLEANUP + + diff --git a/tests/language/stats/crosstabs.at b/tests/language/stats/crosstabs.at index f7fccb434d..9cd2a04f09 100644 --- a/tests/language/stats/crosstabs.at +++ b/tests/language/stats/crosstabs.at @@ -431,7 +431,112 @@ z,Category,Statistic,Type,Value,Asymp. Std. Error,Approx. T,Approx. Sig. ]]) AT_CLEANUP +AT_SETUP([CROSSTABS rounding weights with COUNT]) +AT_DATA([crosstabs.sps], + [[DATA LIST NOTABLE LIST /x y w. +BEGIN DATA. +1 1 1.4 +1 1 1.4 +1 2 1.6 +1 2 1.6 +2 1 1 +2 2 2 +END DATA. +WEIGHT BY w. + +* These should have the same effect (no rounding). +CROSSTABS /TABLES x BY y. +CROSSTABS /TABLES x BY y /COUNT ASIS. + +* Round input weights. +CROSSTABS /TABLES x BY y /COUNT CASE ROUND. +CROSSTABS /TABLES x BY y /COUNT CASE TRUNCATE. + +* Round cell weights. +CROSSTABS /TABLES x BY y /COUNT. +CROSSTABS /TABLES x BY y /COUNT TRUNCATE. +]]) + +AT_CHECK([pspp -O format=csv crosstabs.sps], [0], + [[Table: Summary. +,Cases,,,,, +,Valid,,Missing,,Total, +,N,Percent,N,Percent,N,Percent +x * y,9.00,100.0%,.00,0.0%,9.00,100.0% + +Table: x * y [count]. +,y,, +x,1.00,2.00,Total +1.00,2.80,3.20,6.00 +2.00,1.00,2.00,3.00 +Total,3.80,5.20,9.00 + +Table: Summary. +,Cases,,,,, +,Valid,,Missing,,Total, +,N,Percent,N,Percent,N,Percent +x * y,9.00,100.0%,.00,0.0%,9.00,100.0% + +Table: x * y [count]. +,y,, +x,1.00,2.00,Total +1.00,2.80,3.20,6.00 +2.00,1.00,2.00,3.00 +Total,3.80,5.20,9.00 + +Table: Summary. +,Cases,,,,, +,Valid,,Missing,,Total, +,N,Percent,N,Percent,N,Percent +x * y,9.00,100.0%,.00,0.0%,9.00,100.0% +Table: x * y [count]. +,y,, +x,1.00,2.00,Total +1.00,2.00,4.00,6.00 +2.00,1.00,2.00,3.00 +Total,3.00,6.00,9.00 + +Table: Summary. +,Cases,,,,, +,Valid,,Missing,,Total, +,N,Percent,N,Percent,N,Percent +x * y,7.00,100.0%,.00,0.0%,7.00,100.0% + +Table: x * y [count]. +,y,, +x,1.00,2.00,Total +1.00,2.00,2.00,4.00 +2.00,1.00,2.00,3.00 +Total,3.00,4.00,7.00 + +Table: Summary. +,Cases,,,,, +,Valid,,Missing,,Total, +,N,Percent,N,Percent,N,Percent +x * y,9.00,100.0%,.00,0.0%,9.00,100.0% + +Table: x * y [count]. +,y,, +x,1.00,2.00,Total +1.00,3.00,3.00,6.00 +2.00,1.00,2.00,3.00 +Total,4.00,5.00,9.00 + +Table: Summary. +,Cases,,,,, +,Valid,,Missing,,Total, +,N,Percent,N,Percent,N,Percent +x * y,8.00,100.0%,.00,0.0%,8.00,100.0% + +Table: x * y [count]. +,y,, +x,1.00,2.00,Total +1.00,2.00,3.00,5.00 +2.00,1.00,2.00,3.00 +Total,3.00,5.00,8.00 +]]) +AT_CLEANUP AT_SETUP([CROSSTABS descending sort order]) AT_DATA([crosstabs-descending.sps], diff --git a/tests/language/stats/frequencies.at b/tests/language/stats/frequencies.at index f0d81c3210..7c55110c96 100644 --- a/tests/language/stats/frequencies.at +++ b/tests/language/stats/frequencies.at @@ -761,3 +761,27 @@ FREQUENCIES AT_CHECK([pspp empty.sps -O format=csv], [0], [ignore]) AT_CLEANUP + +AT_SETUP([FREQUENCIES percentiles + histogram bug#48128]) +AT_DATA([bug.sps], [dnl +SET FORMAT=F8.0. + +INPUT PROGRAM. + LOOP I=1 TO 10. + COMPUTE SCORE=EXP(NORMAL(1)). + END CASE. + END LOOP. + END FILE. +END INPUT PROGRAM. + +FREQUENCIES VARIABLES=SCORE +/FORMAT=NOTABLE +/STATISTICS=ALL +/PERCENTILES=1 10 20 30 40 50 60 70 80 90 99 +/HISTOGRAM. + +]) + +AT_CHECK([pspp bug.sps], [0], [ignore]) + +AT_CLEANUP diff --git a/tests/language/stats/glm.at b/tests/language/stats/glm.at index de1ad2a4bc..2ac5909fea 100644 --- a/tests/language/stats/glm.at +++ b/tests/language/stats/glm.at @@ -326,3 +326,131 @@ Corrected Total,436.784,19,,, AT_CLEANUP + +AT_SETUP([GLM missing values]) + +AT_DATA([glm.data], [dnl +1 1 6 3.5 +1 2 2 8.9 +1 3 3 9.6 +1 4 4 10.5 +1 5 5 3.1 +1 6 1 5.9 +2 1 2 4.2 +2 2 6 1.9 +2 3 5 3.7 +2 4 3 10.2 +2 5 1 7.2 +2 6 4 7.6 +3 1 1 6.7 +3 2 4 5.8 +3 3 6 -2.7 +3 4 2 4.6 +3 5 3 4.0 +3 6 5 -0.7 +4 1 4 6.6 +4 2 1 4.5 +4 3 2 3.7 +4 4 5 3.7 +4 5 6 -3.3 +4 6 3 3.0 +5 1 3 4.1 +5 2 5 2.4 +5 3 4 6.0 +5 4 1 5.1 +5 5 2 3.5 +5 6 6 4.0 +6 1 5 3.8 +6 2 3 5.8 +6 3 1 7.0 +6 4 6 3.8 +6 5 4 5.0 +6 6 2 8.6 +]) + +AT_DATA([glm-miss.sps], [dnl +set format = F20.3. +data list file='glm.data' notable fixed /a 1 b 3 c 5 y 7-10(2). + +do if a=6. +recode y (else=SYSMIS). +end if. + +glm y by b a c + /criteria=alpha(.05) + /design = a b c + . +]) + +AT_CHECK([pspp -O format=csv glm-miss.sps], [0], [dnl +Table: Tests of Between-Subjects Effects +Source,Type III Sum of Squares,df,Mean Square,F,Sig. +Corrected Model,251.621,14,17.973,4.969,.002 +Intercept,628.376,1,628.376,173.737,.000 +a,72.929,4,18.232,5.041,.009 +b,20.703,5,4.141,1.145,.380 +c,135.179,5,27.036,7.475,.001 +Error,54.253,15,3.617,, +Total,934.250,30,,, +Corrected Total,305.874,29,,, +]) + + + +AT_DATA([glm-miss2.sps], [dnl +set format = F20.3. +data list file='glm.data' notable fixed /a 1 b 3 c 5 y 7-10(2). + +select if a <> 6. + +glm y by b a c + /criteria=alpha(.05) + /design = a b c + . +]) + +AT_CHECK([pspp -O format=csv glm-miss2.sps], [0], [dnl +Table: Tests of Between-Subjects Effects +Source,Type III Sum of Squares,df,Mean Square,F,Sig. +Corrected Model,251.621,14,17.973,4.969,.002 +Intercept,628.376,1,628.376,173.737,.000 +a,72.929,4,18.232,5.041,.009 +b,20.703,5,4.141,1.145,.380 +c,135.179,5,27.036,7.475,.001 +Error,54.253,15,3.617,, +Total,934.250,30,,, +Corrected Total,305.874,29,,, +]) + + +dnl Now for some missing values in the factor variables. + +AT_DATA([glm-miss3.sps], [dnl +set format = F20.3. +data list file=glm.data notable fixed /a 1 b 3 c 5 y 7-10(2). + +do if a=6. +recode a (else=SYSMIS). +end if. + +glm y by b a c + /criteria=alpha(.05) + /design = a b c + . +]) + +AT_CHECK([pspp -O format=csv glm-miss3.sps], [0], [dnl +Table: Tests of Between-Subjects Effects +Source,Type III Sum of Squares,df,Mean Square,F,Sig. +Corrected Model,251.621,14,17.973,4.969,.002 +Intercept,628.376,1,628.376,173.737,.000 +a,72.929,4,18.232,5.041,.009 +b,20.703,5,4.141,1.145,.380 +c,135.179,5,27.036,7.475,.001 +Error,54.253,15,3.617,, +Total,934.250,30,,, +Corrected Total,305.874,29,,, +]) + +AT_CLEANUP + diff --git a/tests/testsuite.in b/tests/testsuite.in index be9aa305f3..ef11e91931 100644 --- a/tests/testsuite.in +++ b/tests/testsuite.in @@ -9,7 +9,7 @@ m4_ifndef([AT_SKIP_IF], m4_divert_text([PREPARE_TESTS], [dnl if test X"$RUNNER" != X; then wrapper_dir=`pwd`/wrappers - rm "$wrapper_dir"/* + rm -f "$wrapper_dir"/* test -d "$wrapper_dir" || mkdir "$wrapper_dir" wrap_dir () {