From: John Darrington Date: Tue, 19 Jan 2016 19:31:27 +0000 (+0100) Subject: Recode Dialog: Convert to PsppireDialogAction X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=eb98330d56ce2c1947d179648b01d4e6779c665b Recode Dialog: Convert to PsppireDialogAction --- diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 82590e6e1a..43068f2876 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -239,6 +239,8 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-dialog-action-paired.h \ src/ui/gui/psppire-dialog-action-rank.c \ 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-regression.c \ src/ui/gui/psppire-dialog-action-regression.h \ src/ui/gui/psppire-dialog-action-reliability.c \ @@ -309,8 +311,6 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-window-base.h \ src/ui/gui/psppire-window-register.c \ src/ui/gui/psppire-window-register.h \ - src/ui/gui/recode-dialog.c \ - src/ui/gui/recode-dialog.h \ src/ui/gui/t-test-options.c \ src/ui/gui/t-test-options.h \ src/ui/gui/val-labs-dialog.c \ diff --git a/src/ui/gui/data-editor.ui b/src/ui/gui/data-editor.ui index ebf87c5a94..23f3ad8864 100644 --- a/src/ui/gui/data-editor.ui +++ b/src/ui/gui/data-editor.ui @@ -262,14 +262,18 @@ - + 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/psppire-data-window.c b/src/ui/gui/psppire-data-window.c index 6cf3a8f617..d7b8ae6f83 100644 --- a/src/ui/gui/psppire-data-window.c +++ b/src/ui/gui/psppire-data-window.c @@ -37,7 +37,6 @@ #include "ui/gui/psppire-syntax-window.h" #include "ui/gui/psppire-window.h" #include "ui/gui/psppire.h" -#include "ui/gui/recode-dialog.h" #include "ui/syntax-gen.h" #include "gl/c-strcase.h" @@ -1041,9 +1040,6 @@ psppire_data_window_finish_init (PsppireDataWindow *de, g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de); - connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog)); - connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog)); - { GtkWidget *recent_data = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data"); diff --git a/src/ui/gui/psppire-dialog-action-recode.c b/src/ui/gui/psppire-dialog-action-recode.c new file mode 100644 index 0000000000..efbd6e6042 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-recode.c @@ -0,0 +1,1085 @@ +/* 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.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 + + +/* This might need to be changed to something less naive. + In particular, what happends with dates, etc? + */ +static gchar * +num_to_string (gdouble x) +{ + return g_strdup_printf ("%.*g", DBL_DIG + 1, x); +} + +/* Define a boxed type to represent a value which is a candidate + to replace an existing value */ + +enum new_value_type + { + NV_NUMERIC, + NV_STRING, + NV_SYSMIS, + NV_COPY + }; + + +struct new_value +{ + enum new_value_type type; + union { + double v; + gchar *s; + } v; +}; + + +static struct new_value * +new_value_copy (struct new_value *nv) +{ + struct new_value *copy = g_memdup (nv, sizeof (*copy)); + + if ( nv->type == NV_STRING ) + copy->v.s = xstrdup (nv->v.s); + + return copy; +} + + +static void +new_value_free (struct new_value *nv) +{ + if ( nv->type == NV_STRING ) + g_free (nv->v.s); + + g_free (nv); +} + + +static void +new_value_to_string (const GValue *src, GValue *dest) +{ + const struct new_value *nv = g_value_get_boxed (src); + + g_assert (nv); + + switch (nv->type) + { + case NV_NUMERIC: + { + gchar *text = g_strdup_printf ("%.*g", DBL_DIG + 1, nv->v.v); + g_value_set_string (dest, text); + g_free (text); + } + break; + case NV_STRING: + g_value_set_string (dest, nv->v.s); + break; + case NV_COPY: + g_value_set_string (dest, "COPY"); + break; + case NV_SYSMIS: + g_value_set_string (dest, "SYSMIS"); + break; + default: + /* Shouldn't ever happen */ + g_warning ("Invalid type in new recode value"); + g_value_set_string (dest, "???"); + break; + } +} + +static GType +new_value_get_type (void) +{ + static GType t = 0; + + if (t == 0 ) + { + t = g_boxed_type_register_static ("psppire-recode-new-values", + (GBoxedCopyFunc) new_value_copy, + (GBoxedFreeFunc) new_value_free); + + g_value_register_transform_func (t, G_TYPE_STRING, + new_value_to_string); + } + + return t; +} + + + +static void +on_string_toggled (GtkToggleButton *b, PsppireDialogActionRecode *rd) +{ + gboolean active; + if (! rd->input_var_is_string ) + return ; + + active = gtk_toggle_button_get_active (b); + 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) +{ + gboolean active; + + g_return_if_fail (rd->input_var_is_string); + + active = gtk_toggle_button_get_active (b); + 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) +{ + if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) + gtk_widget_grab_focus (rd->new_value_entry); +} + + +/* Callback for the new_value_entry and new_value_togglebutton widgets. + It's used to enable/disable the acr. */ +static void +set_acr (PsppireDialogActionRecode *rd) +{ + const gchar *text; + + if ( !gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]))) + { + psppire_acr_set_enabled (PSPPIRE_ACR (rd->acr), TRUE); + return; + } + + text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry)); + + 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; +} + + +/* Callback which gets called when a new row is selected + in the acr's variable treeview. + We use if to set the togglebuttons and entries to correspond to the + selected row. +*/ +static void +on_acr_selection_change (GtkTreeSelection *selection, gpointer data) +{ + PsppireDialogActionRecode *rd = data; + GtkTreeModel *model = NULL; + GtkTreeIter iter; + + GValue ov_value = {0}; + GValue nv_value = {0}; + struct old_value *ov = NULL; + struct new_value *nv = NULL; + + if ( ! gtk_tree_selection_get_selected (selection, &model, &iter) ) + return; + + + gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter, + COL_VALUE_OLD, &ov_value); + + gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter, + COL_VALUE_NEW, &nv_value); + + ov = g_value_get_boxed (&ov_value); + nv = g_value_get_boxed (&nv_value); + + if (nv) + { + switch (nv->type) + { + case NV_NUMERIC: + { + gchar *str = num_to_string (nv->v.v); + + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE); + + gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), str); + g_free (str); + } + break; + case NV_STRING: + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE); + + gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), nv->v.s); + break; + case NV_SYSMIS: + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]), TRUE); + + break; + case NV_COPY: + gtk_toggle_button_set_active + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]), TRUE); + + break; + default: + g_warning ("Invalid new value type"); + break; + } + + g_value_unset (&nv_value); + } + + psppire_val_chooser_set_status (PSPPIRE_VAL_CHOOSER (rd->old_value_chooser), ov); +} + +/* Initialise VAL to reflect the current status of RD */ +static gboolean +set_old_value (GValue *val, const PsppireDialogActionRecode *rd) +{ + PsppireValChooser *vc = PSPPIRE_VAL_CHOOSER (rd->old_value_chooser); + + struct old_value ov; + + psppire_val_chooser_get_status (vc, &ov); + + g_value_init (val, old_value_get_type ()); + g_value_set_boxed (val, &ov); + + return TRUE; +} + + +/* Initialse VAL to reflect the current status of RD */ +static gboolean +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]))) + { + 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 ( 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 + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]))) + { + nv.type = NV_COPY; + } + + else if ( gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]))) + { + nv.type = NV_SYSMIS; + } + else + return FALSE; + + g_value_init (val, new_value_get_type ()); + g_value_set_boxed (val, &nv); + + return TRUE; +} + +/* A function to set a value in a column in the ACR */ +static gboolean +set_value (gint col, GValue *val, gpointer data) +{ + PsppireDialogActionRecode *rd = data; + + switch ( col ) + { + case COL_VALUE_OLD: + set_old_value (val, rd); + break; + case COL_VALUE_NEW: + set_new_value (val, rd); + break; + default: + return FALSE; + } + + return TRUE; +} + +static void +run_old_and_new_dialog (PsppireDialogActionRecode *rd) +{ + gint response; + GtkListStore *local_store = clone_list_store (rd->value_map); + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (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) */ + const struct variable *v; + GtkTreeIter iter; + GtkTreeModel *model = + gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); + + gboolean not_empty = gtk_tree_model_get_iter_first (model, &iter); + + g_return_if_fail (not_empty); + + gtk_tree_model_get (model, &iter, 0, &v, -1); + + rd->input_var_is_string = var_is_alpha (v); + + g_object_set (rd->old_value_chooser, "is-string", rd->input_var_is_string, NULL); + + gtk_widget_set_sensitive (rd->toggle [BUTTON_NEW_SYSMIS], + var_is_numeric (v)); + + gtk_widget_set_sensitive (rd->convert_button, var_is_alpha (v)); + } + + + response = psppire_dialog_run (rd->old_and_new_dialog); + psppire_acr_set_model (PSPPIRE_ACR (rd->acr), NULL); + + + if ( response == PSPPIRE_RESPONSE_CONTINUE ) + { + g_object_unref (rd->value_map); + rd->value_map = clone_list_store (local_store); + } + else + g_object_unref (local_store); + + + 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 */ +static void +toggle_sensitivity (GtkToggleButton *button, GtkWidget *target) +{ + gboolean state = gtk_toggle_button_get_active (button); + + /* g_print ("%s Setting %p (%s) to %d because of %p\n", + __FUNCTION__, target, gtk_widget_get_name (target), state, button); */ + + 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); +} + + + + + +static void +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_) +{ + PsppireDialogActionRecode *rd = PSPPIRE_DIALOG_ACTION_RECODE (rd_); + + GtkTreeModel *vars = + gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); + + gtk_list_store_clear (GTK_LIST_STORE (vars)); + + gtk_widget_set_sensitive (rd->change_button, FALSE); + gtk_widget_set_sensitive (rd->new_name_entry, FALSE); + gtk_widget_set_sensitive (rd->new_label_entry, FALSE); + + if ( rd->different && 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) +{ + PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (a); + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + + GHashTable *thing = psppire_dialog_action_get_hash_table (pda); + GtkBuilder *xml = g_hash_table_lookup (thing, a); + if (!xml) + { + xml = builder_new ("recode.ui"); + g_hash_table_insert (thing, a, xml); + + pda->dialog = get_widget_assert (xml, "recode-dialog"); + pda->source = get_widget_assert (xml, "treeview1"); + + + GtkWidget *selector = get_widget_assert (xml, "psppire-selector1"); + GtkWidget *oldandnew = get_widget_assert (xml, "button1"); + + + GtkWidget *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"); + + act->value_map = gtk_list_store_new (2, + 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); + } + + + psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types); + + /* Set up the Old & New Values subdialog */ + { + act->string_button = get_widget_assert (xml, "checkbutton1"); + act->width_entry = get_widget_assert (xml, "spinbutton1"); + + act->convert_button = get_widget_assert (xml, "checkbutton2"); + + act->old_value_chooser = get_widget_assert (xml, "val-chooser"); + + act->new_value_entry = get_widget_assert (xml, "entry1"); + + + act->toggle[BUTTON_NEW_VALUE] = get_widget_assert (xml, "radiobutton1"); + act->toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (xml, "radiobutton2"); + act->toggle[BUTTON_NEW_COPY] = get_widget_assert (xml, "radiobutton3"); + + act->new_copy_label = get_widget_assert (xml, "label3"); + act->strings_box = get_widget_assert (xml, "table3"); + + act->old_and_new_dialog = + PSPPIRE_DIALOG (get_widget_assert (xml, "old-new-values-dialog")); + + act->acr = get_widget_assert (xml, "psppire-acr1"); + + g_signal_connect_swapped (act->toggle[BUTTON_NEW_VALUE], "toggled", + G_CALLBACK (set_acr), act); + + g_signal_connect_after (act->toggle[BUTTON_NEW_VALUE], "toggled", + G_CALLBACK (focus_value_entry), act); + + g_signal_connect_swapped (act->new_value_entry, "changed", + G_CALLBACK (set_acr), act); + + { + GtkTreeSelection *sel; + /* Remove the ACR's default column. We don't like it */ + GtkTreeViewColumn *column = gtk_tree_view_get_column (PSPPIRE_ACR(act->acr)->tv, 0); + gtk_tree_view_remove_column (PSPPIRE_ACR (act->acr)->tv, column); + + + column = + gtk_tree_view_column_new_with_attributes (_("Old"), + gtk_cell_renderer_text_new (), + "text", 0, + NULL); + + gtk_tree_view_append_column (PSPPIRE_ACR (act->acr)->tv, column); + + column = + gtk_tree_view_column_new_with_attributes (_("New"), + gtk_cell_renderer_text_new (), + "text", 1, + NULL); + + gtk_tree_view_append_column (PSPPIRE_ACR(act->acr)->tv, column); + g_object_set (PSPPIRE_ACR (act->acr)->tv, "headers-visible", TRUE, NULL); + + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (PSPPIRE_ACR(act->acr)->tv)); + g_signal_connect (sel, "changed", + G_CALLBACK (on_acr_selection_change), act); + } + + + g_signal_connect_swapped (oldandnew, "clicked", + G_CALLBACK (run_old_and_new_dialog), act); + + + g_signal_connect (act->toggle[BUTTON_NEW_VALUE], "toggled", + G_CALLBACK (toggle_sensitivity), act->new_value_entry); + + g_signal_connect (act->string_button, "toggled", + G_CALLBACK (toggle_sensitivity), act->width_entry); + + g_signal_connect (act->string_button, "toggled", + G_CALLBACK (on_string_toggled), act); + + 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 */ +static void +new_value_append_syntax (struct string *dds, const struct new_value *nv) +{ + switch (nv->type) + { + case NV_NUMERIC: + ds_put_c_format (dds, "%.*g", DBL_DIG + 1, nv->v.v); + break; + case NV_STRING: + syntax_gen_string (dds, ss_cstr (nv->v.s)); + break; + case NV_COPY: + ds_put_cstr (dds, "COPY"); + break; + case NV_SYSMIS: + ds_put_cstr (dds, "SYSMIS"); + break; + default: + /* Shouldn't ever happen */ + g_warning ("Invalid type in new recode value"); + ds_put_cstr (dds, "???"); + break; + } +} + +static char * +generate_syntax (const PsppireDialogActionRecode *rd) +{ + gboolean ok; + GtkTreeIter iter; + gchar *text; + struct string dds; + + 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) ) + ); + } + } + + ds_put_cstr (&dds, "\nRECODE "); + + psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &dds); + + ds_put_cstr (&dds, "\n\t"); + + if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->convert_button))) + { + ds_put_cstr (&dds, "(CONVERT) "); + } + + for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map), + &iter); + ok; + ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->value_map), &iter)) + { + GValue ov_value = {0}; + GValue nv_value = {0}; + struct old_value *ov; + struct new_value *nv; + gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter, + COL_VALUE_OLD, &ov_value); + + gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter, + COL_VALUE_NEW, &nv_value); + + ov = g_value_get_boxed (&ov_value); + nv = g_value_get_boxed (&nv_value); + + ds_put_cstr (&dds, "("); + + old_value_append_syntax (&dds, ov); + ds_put_cstr (&dds, " = "); + new_value_append_syntax (&dds, nv); + + ds_put_cstr (&dds, ") "); + g_value_unset (&ov_value); + 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, " "); + } + } + + 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); + } + } + } + + ds_put_cstr (&dds, "\nEXECUTE.\n"); + + + text = ds_steal_cstr (&dds); + + ds_destroy (&dds); + + return text; +} + + +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); +} + + +static void +psppire_dialog_action_recode_init (PsppireDialogActionRecode *act) +{ +} + diff --git a/src/ui/gui/psppire-dialog-action-recode.h b/src/ui/gui/psppire-dialog-action-recode.h new file mode 100644 index 0000000000..37d18d5c46 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-recode.h @@ -0,0 +1,113 @@ +/* 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.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_RECODE_H__ +#define __PSPPIRE_DIALOG_ACTION_RECODE_H__ + +G_BEGIN_DECLS + +enum + { + BUTTON_NEW_VALUE, + BUTTON_NEW_COPY, + BUTTON_NEW_SYSMIS, + n_BUTTONS + }; + +#define PSPPIRE_TYPE_DIALOG_ACTION_RECODE (psppire_dialog_action_recode_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_RECODE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE, PsppireDialogActionRecode)) + +#define PSPPIRE_DIALOG_ACTION_RECODE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE, \ + PsppireDialogActionRecodeClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_RECODE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_RECODE)) + +#define PSPPIRE_IS_DIALOG_ACTION_RECODE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_RECODE)) + + +#define PSPPIRE_DIALOG_ACTION_RECODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_RECODE, \ + PsppireDialogActionRecodeClass)) + +typedef struct _PsppireDialogActionRecode PsppireDialogActionRecode; +typedef struct _PsppireDialogActionRecodeClass PsppireDialogActionRecodeClass; + + +struct _PsppireDialogActionRecode +{ + PsppireDialogAction parent; + + PsppireDialog *old_and_new_dialog; + + GtkWidget *dict_treeview; + GtkWidget *variable_treeview; + GtkWidget *toggle[n_BUTTONS]; + + GtkWidget *strings_box; + 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; + + GtkWidget *new_name_entry; + GtkWidget *new_label_entry; + GtkWidget *change_button; + + GtkWidget *string_button; + GtkWidget *width_entry; + + /* A hash table of struct nlp's indexed by variable */ + GHashTable *varmap; +}; + + +struct _PsppireDialogActionRecodeClass +{ + PsppireDialogActionClass parent_class; +}; + + +GType psppire_dialog_action_recode_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_RECODE_H__ */ diff --git a/src/ui/gui/recode-dialog.c b/src/ui/gui/recode-dialog.c deleted file mode 100644 index 4ef8ae216d..0000000000 --- a/src/ui/gui/recode-dialog.c +++ /dev/null @@ -1,1107 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2007, 2009, 2010, 2011, 2012, 2014 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 . */ - -/* This module implements the RECODE dialog. - - It has two forms. One for recoding values into the same variable. - The second for recoding into different variables. -*/ - -#include - -#include "recode-dialog.h" - -#include "executor.h" - -#include "psppire-var-view.h" - -#include - -#include -#include -#include -#include -#include -#include -#include "helper.h" -#include - -#include "psppire-val-chooser.h" - -#include - -#include "psppire-acr.h" - -#include "gettext.h" -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - - -/* Define a boxed type to represent a value which is a candidate - to replace an existing value */ - -enum new_value_type - { - NV_NUMERIC, - NV_STRING, - NV_SYSMIS, - NV_COPY - }; - - -struct new_value -{ - enum new_value_type type; - union { - double v; - gchar *s; - } v; -}; - - -static struct new_value * -new_value_copy (struct new_value *nv) -{ - struct new_value *copy = g_memdup (nv, sizeof (*copy)); - - if ( nv->type == NV_STRING ) - copy->v.s = xstrdup (nv->v.s); - - return copy; -} - - -static void -new_value_free (struct new_value *nv) -{ - if ( nv->type == NV_STRING ) - g_free (nv->v.s); - - g_free (nv); -} - - -static void -new_value_to_string (const GValue *src, GValue *dest) -{ - const struct new_value *nv = g_value_get_boxed (src); - - g_assert (nv); - - switch (nv->type) - { - case NV_NUMERIC: - { - gchar *text = g_strdup_printf ("%.*g", DBL_DIG + 1, nv->v.v); - g_value_set_string (dest, text); - g_free (text); - } - break; - case NV_STRING: - g_value_set_string (dest, nv->v.s); - break; - case NV_COPY: - g_value_set_string (dest, "COPY"); - break; - case NV_SYSMIS: - g_value_set_string (dest, "SYSMIS"); - break; - default: - /* Shouldn't ever happen */ - g_warning ("Invalid type in new recode value"); - g_value_set_string (dest, "???"); - break; - } -} - -static GType -new_value_get_type (void) -{ - static GType t = 0; - - if (t == 0 ) - { - t = g_boxed_type_register_static ("psppire-recode-new-values", - (GBoxedCopyFunc) new_value_copy, - (GBoxedFreeFunc) new_value_free); - - g_value_register_transform_func (t, G_TYPE_STRING, - new_value_to_string); - } - - return t; -} - - - - -enum - { - BUTTON_NEW_VALUE, - BUTTON_NEW_COPY, - BUTTON_NEW_SYSMIS, - n_BUTTONS - }; - -struct recode_dialog -{ - PsppireDict *dict; - - GtkWidget *dialog; - PsppireDialog *old_and_new_dialog; - - GtkWidget *dict_treeview; - GtkWidget *variable_treeview; - GtkWidget *toggle[n_BUTTONS]; - - GtkWidget *strings_box; - 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; - - PsppireAcr *acr; - - gboolean input_var_is_string; - - GtkWidget *new_name_entry; - GtkWidget *new_label_entry; - GtkWidget *change_button; - - GtkWidget *string_button; - GtkWidget *width_entry; - - /* A hash table of struct nlp's indexed by variable */ - GHashTable *varmap; -}; - - -static void run_old_and_new_dialog (struct recode_dialog *rd); - -static void -refresh (PsppireDialog *dialog, struct recode_dialog *rd) -{ - GtkTreeModel *vars = - gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); - - gtk_list_store_clear (GTK_LIST_STORE (vars)); - - gtk_widget_set_sensitive (rd->change_button, FALSE); - gtk_widget_set_sensitive (rd->new_name_entry, FALSE); - gtk_widget_set_sensitive (rd->new_label_entry, FALSE); - - if ( rd->different && rd->varmap ) - g_hash_table_remove_all (rd->varmap); - - gtk_list_store_clear (GTK_LIST_STORE (rd->value_map)); -} - -static char * generate_syntax (const struct recode_dialog *rd); - -enum { - COL_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; - struct recode_dialog *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; -} - -static void -on_old_new_show (struct recode_dialog *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 */ -static void -toggle_sensitivity (GtkToggleButton *button, GtkWidget *target) -{ - gboolean state = gtk_toggle_button_get_active (button); - - /* g_print ("%s Setting %p (%s) to %d because of %p\n", - __FUNCTION__, target, gtk_widget_get_name (target), state, button); */ - - gtk_widget_set_sensitive (target, state); -} - -static void recode_dialog (PsppireDataWindow *de, gboolean diff); - - -/* Pops up the Recode Same version of the dialog box */ -void -recode_same_dialog (PsppireDataWindow *de) -{ - recode_dialog (de, FALSE); -} - -/* Pops up the Recode Different version of the dialog box */ -void -recode_different_dialog (PsppireDataWindow *de) -{ - recode_dialog (de, TRUE); -} - - -/* This might need to be changed to something less naive. - In particular, what happends with dates, etc? - */ -static gchar * -num_to_string (gdouble x) -{ - return g_strdup_printf ("%.*g", DBL_DIG + 1, x); -} - -/* Callback which gets called when a new row is selected - in the acr's variable treeview. - We use if to set the togglebuttons and entries to correspond to the - selected row. -*/ -static void -on_acr_selection_change (GtkTreeSelection *selection, gpointer data) -{ - struct recode_dialog *rd = data; - GtkTreeModel *model = NULL; - GtkTreeIter iter; - - GValue ov_value = {0}; - GValue nv_value = {0}; - struct old_value *ov = NULL; - struct new_value *nv = NULL; - - if ( ! gtk_tree_selection_get_selected (selection, &model, &iter) ) - return; - - - gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter, - COL_VALUE_OLD, &ov_value); - - gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter, - COL_VALUE_NEW, &nv_value); - - ov = g_value_get_boxed (&ov_value); - nv = g_value_get_boxed (&nv_value); - - if (nv) - { - switch (nv->type) - { - case NV_NUMERIC: - { - gchar *str = num_to_string (nv->v.v); - - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE); - - gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), str); - g_free (str); - } - break; - case NV_STRING: - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE); - - gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), nv->v.s); - break; - case NV_SYSMIS: - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]), TRUE); - - break; - case NV_COPY: - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]), TRUE); - - break; - default: - g_warning ("Invalid new value type"); - break; - } - - g_value_unset (&nv_value); - } - - psppire_val_chooser_set_status (PSPPIRE_VAL_CHOOSER (rd->old_value_chooser), ov); -} - -/* 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) -{ - struct recode_dialog *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_string_toggled (GtkToggleButton *b, struct recode_dialog *rd) -{ - gboolean active; - if (! rd->input_var_is_string ) - return ; - - active = gtk_toggle_button_get_active (b); - gtk_widget_set_sensitive (rd->convert_button, !active); -} - - -static void -on_convert_toggled (GtkToggleButton *b, struct recode_dialog *rd) -{ - gboolean active; - - g_return_if_fail (rd->input_var_is_string); - - active = gtk_toggle_button_get_active (b); - gtk_widget_set_sensitive (rd->string_button, !active); -} - -static void -on_change_clicked (GObject *obj, gpointer data) -{ - struct recode_dialog *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, struct recode_dialog *rd) -{ - if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) - gtk_widget_grab_focus (rd->new_value_entry); -} - - -/* Callback for the new_value_entry and new_value_togglebutton widgets. - It's used to enable/disable the acr. */ -static void -set_acr (struct recode_dialog *rd) -{ - const gchar *text; - - if ( !gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]))) - { - psppire_acr_set_enabled (rd->acr, TRUE); - return; - } - - text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry)); - - psppire_acr_set_enabled (rd->acr, !g_str_equal (text, "")); -} - -static void -render_new_var_name (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - struct nlp *nlp = NULL; - struct recode_dialog *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); -} - - - -static void -recode_dialog (PsppireDataWindow *de, gboolean diff) -{ - gint response; - - struct recode_dialog rd; - - GtkBuilder *builder = builder_new ("recode.ui"); - - GtkWidget *selector = get_widget_assert (builder, "psppire-selector1"); - - GtkWidget *oldandnew = get_widget_assert (builder, "button1"); - - - GtkWidget *output_variable_box = get_widget_assert (builder,"frame4"); - - rd.change_button = get_widget_assert (builder, "change-button"); - rd.varmap = NULL; - rd.dialog = get_widget_assert (builder, "recode-dialog"); - rd.dict_treeview = get_widget_assert (builder, "treeview1"); - rd.variable_treeview = get_widget_assert (builder, "treeview2"); - rd.new_name_entry = get_widget_assert (builder, "dest-name-entry"); - rd.new_label_entry = get_widget_assert (builder, "dest-label-entry"); - - g_object_get (de->data_editor, "dictionary", &rd.dict, NULL); - - rd.value_map = gtk_list_store_new (2, - old_value_get_type (), - new_value_get_type () - ); - - g_object_set (output_variable_box, "visible", diff, NULL); - - if ( diff ) - gtk_window_set_title (GTK_WINDOW (rd.dialog), - _("Recode into Different Variables")); - else - gtk_window_set_title (GTK_WINDOW (rd.dialog), - _("Recode into Same Variables")); - - rd.different = diff; - - gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), GTK_WINDOW (de)); - - g_object_set (rd.dict_treeview, "model", rd.dict, NULL); - - if (rd.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, - &rd, NULL); - - - gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col); - - - col = gtk_tree_view_get_column (GTK_TREE_VIEW (rd.variable_treeview), 0); - - g_object_set (col, "title", _("Old"), NULL); - - g_object_set (rd.variable_treeview, "headers-visible", TRUE, NULL); - - rd.varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy); - - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.variable_treeview)); - - g_signal_connect (sel, "changed", - G_CALLBACK (on_selection_change), &rd); - - g_signal_connect (rd.change_button, "clicked", - G_CALLBACK (on_change_clicked), &rd); - - } - - psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types); - - /* Set up the Old & New Values subdialog */ - { - rd.string_button = get_widget_assert (builder, "checkbutton1"); - rd.width_entry = get_widget_assert (builder, "spinbutton1"); - - rd.convert_button = get_widget_assert (builder, "checkbutton2"); - - rd.old_value_chooser = get_widget_assert (builder, "val-chooser"); - - rd.new_value_entry = get_widget_assert (builder, "entry1"); - - - rd.toggle[BUTTON_NEW_VALUE] = get_widget_assert (builder, "radiobutton1"); - rd.toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (builder, "radiobutton2"); - rd.toggle[BUTTON_NEW_COPY] = get_widget_assert (builder, "radiobutton3"); - - rd.new_copy_label = get_widget_assert (builder, "label3"); - rd.strings_box = get_widget_assert (builder, "table3"); - - rd.old_and_new_dialog = - PSPPIRE_DIALOG (get_widget_assert (builder, "old-new-values-dialog")); - - gtk_window_set_transient_for (GTK_WINDOW (rd.old_and_new_dialog), - GTK_WINDOW (de)); - - rd.acr = PSPPIRE_ACR (get_widget_assert (builder, "psppire-acr1")); - - g_signal_connect_swapped (rd.toggle[BUTTON_NEW_VALUE], "toggled", - G_CALLBACK (set_acr), &rd); - - g_signal_connect_after (rd.toggle[BUTTON_NEW_VALUE], "toggled", - G_CALLBACK (focus_value_entry), &rd); - - g_signal_connect_swapped (rd.new_value_entry, "changed", - G_CALLBACK (set_acr), &rd); - - { - GtkTreeSelection *sel; - /* Remove the ACR's default column. We don't like it */ - GtkTreeViewColumn *column = gtk_tree_view_get_column (rd.acr->tv, 0); - gtk_tree_view_remove_column (rd.acr->tv, column); - - - column = - gtk_tree_view_column_new_with_attributes (_("Old"), - gtk_cell_renderer_text_new (), - "text", 0, - NULL); - - gtk_tree_view_append_column (rd.acr->tv, column); - - column = - gtk_tree_view_column_new_with_attributes (_("New"), - gtk_cell_renderer_text_new (), - "text", 1, - NULL); - - gtk_tree_view_append_column (rd.acr->tv, column); - g_object_set (rd.acr->tv, "headers-visible", TRUE, NULL); - - - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.acr->tv)); - g_signal_connect (sel, "changed", - G_CALLBACK (on_acr_selection_change), &rd); - } - - - g_signal_connect_swapped (oldandnew, "clicked", - G_CALLBACK (run_old_and_new_dialog), &rd); - - - g_signal_connect (rd.toggle[BUTTON_NEW_VALUE], "toggled", - G_CALLBACK (toggle_sensitivity), rd.new_value_entry); - - g_signal_connect (rd.string_button, "toggled", - G_CALLBACK (toggle_sensitivity), rd.width_entry); - - g_signal_connect (rd.string_button, "toggled", - G_CALLBACK (on_string_toggled), &rd); - - g_signal_connect (rd.convert_button, "toggled", - G_CALLBACK (on_convert_toggled), &rd); - - g_signal_connect_swapped (rd.old_and_new_dialog, "show", - G_CALLBACK (on_old_new_show), &rd); - } - - g_signal_connect (rd.dialog, "refresh", G_CALLBACK (refresh), &rd); - - - psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (rd.dialog), - dialog_state_valid, &rd); - - response = psppire_dialog_run (PSPPIRE_DIALOG (rd.dialog)); - - switch (response) - { - case GTK_RESPONSE_OK: - g_free (execute_syntax_string (de, generate_syntax (&rd))); - break; - case PSPPIRE_RESPONSE_PASTE: - g_free (paste_syntax_to_window (generate_syntax (&rd))); - break; - default: - break; - } - - if (rd.varmap) - g_hash_table_destroy (rd.varmap); - - gtk_list_store_clear (GTK_LIST_STORE (rd.value_map)); - g_object_unref (rd.value_map); - - g_object_unref (builder); -} - -/* Initialise VAL to reflect the current status of RD */ -static gboolean -set_old_value (GValue *val, const struct recode_dialog *rd) -{ - PsppireValChooser *vc = PSPPIRE_VAL_CHOOSER (rd->old_value_chooser); - - struct old_value ov; - - psppire_val_chooser_get_status (vc, &ov); - - g_value_init (val, old_value_get_type ()); - g_value_set_boxed (val, &ov); - - return TRUE; -} - - -/* Initialse VAL to reflect the current status of RD */ -static gboolean -set_new_value (GValue *val, const struct recode_dialog *rd) -{ - const gchar *text = NULL; - struct new_value nv; - - 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 ( 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 - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]))) - { - nv.type = NV_COPY; - } - - else if ( gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]))) - { - nv.type = NV_SYSMIS; - } - else - return FALSE; - - g_value_init (val, new_value_get_type ()); - g_value_set_boxed (val, &nv); - - return TRUE; -} - - -/* A function to set a value in a column in the ACR */ -static gboolean -set_value (gint col, GValue *val, gpointer data) -{ - struct recode_dialog *rd = data; - - switch ( col ) - { - case COL_VALUE_OLD: - set_old_value (val, rd); - break; - case COL_VALUE_NEW: - set_new_value (val, rd); - break; - default: - return FALSE; - } - - return TRUE; -} - -static void -run_old_and_new_dialog (struct recode_dialog *rd) -{ - gint response; - GtkListStore *local_store = clone_list_store (rd->value_map); - - psppire_acr_set_model (rd->acr, local_store); - psppire_acr_set_get_value_func (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) */ - const struct variable *v; - GtkTreeIter iter; - GtkTreeModel *model = - gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview)); - - gboolean not_empty = gtk_tree_model_get_iter_first (model, &iter); - - g_return_if_fail (not_empty); - - gtk_tree_model_get (model, &iter, 0, &v, -1); - - rd->input_var_is_string = var_is_alpha (v); - - g_object_set (rd->old_value_chooser, "is-string", rd->input_var_is_string, NULL); - - gtk_widget_set_sensitive (rd->toggle [BUTTON_NEW_SYSMIS], - var_is_numeric (v)); - - gtk_widget_set_sensitive (rd->convert_button, var_is_alpha (v)); - } - - - response = psppire_dialog_run (rd->old_and_new_dialog); - psppire_acr_set_model (rd->acr, NULL); - - - if ( response == PSPPIRE_RESPONSE_CONTINUE ) - { - g_object_unref (rd->value_map); - rd->value_map = clone_list_store (local_store); - } - else - g_object_unref (local_store); - - - psppire_dialog_notify_change (PSPPIRE_DIALOG (rd->dialog)); -} - - -/* Generate a syntax fragment for NV and append it to STR */ -static void -new_value_append_syntax (struct string *dds, const struct new_value *nv) -{ - switch (nv->type) - { - case NV_NUMERIC: - ds_put_c_format (dds, "%.*g", DBL_DIG + 1, nv->v.v); - break; - case NV_STRING: - syntax_gen_string (dds, ss_cstr (nv->v.s)); - break; - case NV_COPY: - ds_put_cstr (dds, "COPY"); - break; - case NV_SYSMIS: - ds_put_cstr (dds, "SYSMIS"); - break; - default: - /* Shouldn't ever happen */ - g_warning ("Invalid type in new recode value"); - ds_put_cstr (dds, "???"); - break; - } -} - - -static char * -generate_syntax (const struct recode_dialog *rd) -{ - gboolean ok; - GtkTreeIter iter; - gchar *text; - struct string dds; - - 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) ) - ); - } - } - - ds_put_cstr (&dds, "\nRECODE "); - - psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &dds); - - ds_put_cstr (&dds, "\n\t"); - - if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->convert_button))) - { - ds_put_cstr (&dds, "(CONVERT) "); - } - - for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map), - &iter); - ok; - ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->value_map), &iter)) - { - GValue ov_value = {0}; - GValue nv_value = {0}; - struct old_value *ov; - struct new_value *nv; - gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter, - COL_VALUE_OLD, &ov_value); - - gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter, - COL_VALUE_NEW, &nv_value); - - ov = g_value_get_boxed (&ov_value); - nv = g_value_get_boxed (&nv_value); - - ds_put_cstr (&dds, "("); - - old_value_append_syntax (&dds, ov); - ds_put_cstr (&dds, " = "); - new_value_append_syntax (&dds, nv); - - ds_put_cstr (&dds, ") "); - g_value_unset (&ov_value); - 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, " "); - } - } - - 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); - } - } - } - - ds_put_cstr (&dds, "\nEXECUTE.\n"); - - - text = ds_steal_cstr (&dds); - - ds_destroy (&dds); - - return text; -} diff --git a/src/ui/gui/recode-dialog.h b/src/ui/gui/recode-dialog.h deleted file mode 100644 index 03e4a348e0..0000000000 --- a/src/ui/gui/recode-dialog.h +++ /dev/null @@ -1,26 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2007 Free Software Foundation - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef __RECODE_DIALOG_H -#define __RECODE_DIALOG_H - - -#include "psppire-data-window.h" - -void recode_same_dialog (PsppireDataWindow * data); -void recode_different_dialog (PsppireDataWindow * data); - -#endif diff --git a/src/ui/gui/recode.ui b/src/ui/gui/recode.ui index 954023454e..c25065f5e1 100644 --- a/src/ui/gui/recode.ui +++ b/src/ui/gui/recode.ui @@ -372,6 +372,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Recode into Same Variables True + RECODE True diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c index 78e67382da..488ec94e1a 100644 --- a/src/ui/gui/widgets.c +++ b/src/ui/gui/widgets.c @@ -42,6 +42,7 @@ #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-regression.h" #include "psppire-dialog-action-reliability.h" #include "psppire-dialog-action-roc.h" @@ -90,6 +91,7 @@ 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_reliability_get_type, psppire_dialog_action_regression_get_type, psppire_dialog_action_roc_get_type,