Change many %g format specifiers to %.*g with precision DBL_DIG + 1.
[pspp] / src / ui / gui / recode-dialog.c
index 041566cf290bdc2ea30a401e81ba00e224373388..4ef8ae216d88f3a9825bf3555a5b0d81cbc36221 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   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
 
 #include "recode-dialog.h"
 
+#include "executor.h"
+
+#include "psppire-var-view.h"
+
 #include <gtk/gtk.h>
 
-#include <language/syntax-string-source.h>
-#include <ui/gui/data-editor.h>
+#include <float.h>
+#include <xalloc.h>
+#include <ui/gui/psppire-data-window.h>
 #include <ui/gui/dialog-common.h>
 #include <ui/gui/dict-display.h>
-#include <ui/gui/helper.h>
+#include <ui/gui/builder-wrapper.h>
+#include "helper.h"
 #include <ui/gui/psppire-dialog.h>
-#include <ui/gui/psppire-var-store.h>
-#include <ui/gui/syntax-editor.h>
-#include <libpspp/syntax-gen.h>
+
+#include "psppire-val-chooser.h"
+
+#include <ui/syntax-gen.h>
 
 #include "psppire-acr.h"
 
@@ -71,7 +78,7 @@ new_value_copy (struct new_value *nv)
   struct new_value *copy = g_memdup (nv, sizeof (*copy));
 
   if ( nv->type == NV_STRING )
-    copy->v.s = strdup (nv->v.s);
+    copy->v.s = xstrdup (nv->v.s);
 
   return copy;
 }
@@ -98,7 +105,7 @@ new_value_to_string (const GValue *src, GValue *dest)
     {
     case NV_NUMERIC:
       {
-       gchar *text = g_strdup_printf ("%g", nv->v.v);
+       gchar *text = g_strdup_printf ("%.*g", DBL_DIG + 1, nv->v.v);
        g_value_set_string (dest, text);
        g_free (text);
       }
@@ -139,151 +146,6 @@ new_value_get_type (void)
 }
 
 
-\f
-
-
-/* A boxed type representing a value, or a range of values which may
-   potentially be replaced by something */
-
-enum old_value_type
- {
-   OV_NUMERIC,
-   OV_STRING,
-   OV_SYSMIS,
-   OV_MISSING,
-   OV_RANGE,
-   OV_LOW_UP,
-   OV_HIGH_DOWN,
-   OV_ELSE
- };
-
-struct old_value
- {
-   enum old_value_type type;
-   union {
-     double v;
-     gchar *s;
-     double range[2];
-   } v;
- };
-
-
-static struct old_value *
-old_value_copy (struct old_value *ov)
-{
-  struct old_value *copy = g_memdup (ov, sizeof (*copy));
-
-  if ( ov->type == OV_STRING )
-    copy->v.s = g_strdup (ov->v.s);
-
-  return copy;
-}
-
-
-static void
-old_value_free (struct old_value *ov)
-{
-  if (ov->type == OV_STRING)
-    g_free (ov->v.s);
-  g_free (ov);
-}
-
-static void
-old_value_to_string (const GValue *src, GValue *dest)
-{
-  const struct old_value *ov = g_value_get_boxed (src);
-
-  switch (ov->type)
-    {
-    case OV_NUMERIC:
-      {
-       gchar *text = g_strdup_printf ("%g", ov->v.v);
-       g_value_set_string (dest, text);
-       g_free (text);
-      }
-      break;
-    case OV_STRING:
-      g_value_set_string (dest, ov->v.s);
-      break;
-    case OV_MISSING:
-      g_value_set_string (dest, "MISSING");
-      break;
-    case OV_SYSMIS:
-      g_value_set_string (dest, "SYSMIS");
-      break;
-    case OV_ELSE:
-      g_value_set_string (dest, "ELSE");
-      break;
-    case OV_RANGE:
-      {
-       gchar *text;
-       char en_dash[6] = {0,0,0,0,0,0};
-
-       g_unichar_to_utf8 (0x2013, en_dash);
-
-       text = g_strdup_printf ("%g %s %g",
-                                      ov->v.range[0],
-                                      en_dash,
-                                      ov->v.range[1]);
-       g_value_set_string (dest, text);
-       g_free (text);
-      }
-      break;
-    case OV_LOW_UP:
-      {
-       gchar *text;
-       char en_dash[6] = {0,0,0,0,0,0};
-
-       g_unichar_to_utf8 (0x2013, en_dash);
-
-       text = g_strdup_printf ("LOWEST %s %g",
-                               en_dash,
-                               ov->v.range[1]);
-
-       g_value_set_string (dest, text);
-       g_free (text);
-      }
-      break;
-    case OV_HIGH_DOWN:
-      {
-       gchar *text;
-       char en_dash[6] = {0,0,0,0,0,0};
-
-       g_unichar_to_utf8 (0x2013, en_dash);
-
-       text = g_strdup_printf ("%g %s HIGHEST",
-                               ov->v.range[0],
-                               en_dash);
-
-       g_value_set_string (dest, text);
-       g_free (text);
-      }
-      break;
-    default:
-      g_warning ("Invalid type in old recode value");
-      g_value_set_string (dest, "???");
-      break;
-    };
-}
-
-static GType
-old_value_get_type (void)
-{
-  static GType t = 0;
-
-  if (t == 0 )
-    {
-      t = g_boxed_type_register_static  ("psppire-recode-old-values",
-                                        (GBoxedCopyFunc) old_value_copy,
-                                        (GBoxedFreeFunc) old_value_free);
-
-      g_value_register_transform_func     (t, G_TYPE_STRING,
-                                          old_value_to_string);
-    }
-
-  return t;
-}
-
 \f
 
 enum
@@ -291,13 +153,6 @@ enum
     BUTTON_NEW_VALUE,
     BUTTON_NEW_COPY,
     BUTTON_NEW_SYSMIS,
-    BUTTON_OLD_VALUE,
-    BUTTON_OLD_SYSMIS,
-    BUTTON_OLD_MISSING,
-    BUTTON_OLD_RANGE,
-    BUTTON_OLD_LOW_UP,
-    BUTTON_OLD_HIGH_DOWN,
-    BUTTON_OLD_ELSE,
     n_BUTTONS
   };
 
@@ -316,13 +171,9 @@ struct recode_dialog
   GtkWidget *convert_button;
   GtkWidget *new_copy_label;
 
-  GtkWidget *ov_value_entry;
   GtkWidget *new_value_entry;
 
-  GtkWidget *ov_range_lower_entry;
-  GtkWidget *ov_range_upper_entry;
-  GtkWidget *ov_low_up_entry;
-  GtkWidget *ov_high_down_entry;
+  GtkWidget *old_value_chooser;
 
   GtkListStore *value_map;
 
@@ -334,13 +185,15 @@ struct recode_dialog
 
   gboolean input_var_is_string;
 
-  GtkListStore *var_map;
   GtkWidget *new_name_entry;
   GtkWidget *new_label_entry;
   GtkWidget *change_button;
 
   GtkWidget *string_button;
   GtkWidget *width_entry;
+
+  /* A hash table of struct nlp's indexed by variable */
+  GHashTable *varmap;
 };
 
 
@@ -349,33 +202,23 @@ static void run_old_and_new_dialog (struct recode_dialog *rd);
 static void
 refresh (PsppireDialog *dialog, struct recode_dialog *rd)
 {
+  GtkTreeModel *vars =
+    gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
+
+  gtk_list_store_clear (GTK_LIST_STORE (vars));
+
   gtk_widget_set_sensitive (rd->change_button, FALSE);
   gtk_widget_set_sensitive (rd->new_name_entry, FALSE);
   gtk_widget_set_sensitive (rd->new_label_entry, FALSE);
 
-
-  if ( rd->different )
-    gtk_list_store_clear (GTK_LIST_STORE (rd->var_map));
-  else
-    {
-      GtkTreeModel *vars =
-       gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
-
-      gtk_list_store_clear (GTK_LIST_STORE (vars));
-    }
+  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_OLD,
-  COL_NEW_NAME,
-  COL_NEW_LABEL,
-  n_COL_VARS
-};
-
 enum {
   COL_VALUE_OLD,
   COL_VALUE_NEW,
@@ -400,26 +243,10 @@ dialog_state_valid (gpointer data)
 
   if ( rd->different )
     {
-      GtkTreeIter iter;
-
-      gboolean ok;
-
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
-          ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map),
-                                         &iter))
-       {
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name, -1);
+      GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
 
-         if ( name == NULL )
-           return FALSE;
-
-         g_free (name);
-       }
+      if (g_hash_table_size (rd->varmap) != gtk_tree_model_iter_n_children (model, NULL) )
+       return FALSE;
     }
   else
     {
@@ -428,7 +255,6 @@ dialog_state_valid (gpointer data)
 
       if ( !gtk_tree_model_get_iter_first (vars, &not_used))
        return FALSE;
-
     }
 
   return TRUE;
@@ -437,11 +263,6 @@ dialog_state_valid (gpointer data)
 static void
 on_old_new_show (struct recode_dialog *rd)
 {
-  gtk_toggle_button_set_active
-    (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_OLD_VALUE]), TRUE);
-
-  g_signal_emit_by_name (rd->toggle[BUTTON_OLD_VALUE], "toggled");
-
   gtk_toggle_button_set_active
     (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE);
 
@@ -470,43 +291,23 @@ toggle_sensitivity (GtkToggleButton *button, GtkWidget *target)
   gtk_widget_set_sensitive (target, state);
 }
 
-static void recode_dialog (struct data_editor *de, gboolean diff);
+static void recode_dialog (PsppireDataWindow *de, gboolean diff);
 
 
 /* Pops up the Recode Same version of the dialog box */
 void
-recode_same_dialog (GObject *o, gpointer data)
+recode_same_dialog (PsppireDataWindow *de)
 {
-  struct data_editor *de = data;
-
   recode_dialog (de, FALSE);
 }
 
 /* Pops up the Recode Different version of the dialog box */
 void
-recode_different_dialog (GObject *o, gpointer data)
+recode_different_dialog (PsppireDataWindow *de)
 {
-  struct data_editor *de = data;
-
   recode_dialog (de, TRUE);
 }
 
-static void
-render_new_var_name (GtkTreeViewColumn *tree_column,
-                    GtkCellRenderer *cell,
-                    GtkTreeModel *tree_model,
-                    GtkTreeIter *iter,
-                    gpointer data)
-{
-  gchar *new_var_name = NULL;
-
-  gtk_tree_model_get (tree_model, iter, COL_NEW_NAME, &new_var_name, -1);
-
-  g_object_set (cell, "text", new_var_name, NULL);
-
-  g_free (new_var_name);
-}
-
 
 /* This might need to be changed to something less naive.
    In particular, what happends with dates, etc?
@@ -514,7 +315,7 @@ render_new_var_name (GtkTreeViewColumn *tree_column,
 static gchar *
 num_to_string (gdouble x)
 {
-  return g_strdup_printf ("%g", x);
+  return g_strdup_printf ("%.*g", DBL_DIG + 1, x);
 }
 
 /* Callback which gets called when a new row is selected
@@ -586,103 +387,44 @@ on_acr_selection_change (GtkTreeSelection *selection, gpointer data)
       g_value_unset (&nv_value);
     }
 
-  if ( ov )
-    {
-    switch (ov->type)
-      {
-      case OV_STRING:
-         gtk_toggle_button_set_active
-           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_VALUE]), TRUE);
-
-         gtk_entry_set_text (GTK_ENTRY (rd->ov_value_entry), ov->v.s);
-       break;
-
-      case OV_NUMERIC:
-       {
-         gchar *str;
-
-         gtk_toggle_button_set_active
-           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_VALUE]), TRUE);
-
-         str = num_to_string (ov->v.v);
-
-         gtk_entry_set_text (GTK_ENTRY (rd->ov_value_entry), str);
-
-         g_free (str);
-       }
-       break;
-
-      case OV_SYSMIS:
-       gtk_toggle_button_set_active
-         (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_SYSMIS]), TRUE);
-       break;
-
-      case OV_MISSING:
-       gtk_toggle_button_set_active
-         (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_MISSING]), TRUE);
-       break;
-
-      case OV_RANGE:
-       {
-         gchar *str;
-
-         gtk_toggle_button_set_active
-           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_RANGE]), TRUE);
-
-         str = num_to_string (ov->v.range[0]);
-
-         gtk_entry_set_text (GTK_ENTRY (rd->ov_range_lower_entry), str);
-
-         g_free (str);
-
-
-         str = num_to_string (ov->v.range[1]);
-
-         gtk_entry_set_text (GTK_ENTRY (rd->ov_range_upper_entry), str);
-
-         g_free (str);
-       }
-       break;
+  psppire_val_chooser_set_status (PSPPIRE_VAL_CHOOSER (rd->old_value_chooser), ov);
+}
 
-      case OV_LOW_UP:
-       {
-         gchar *str = num_to_string (ov->v.range[1]);
+/* Name-Label pair */
+struct nlp
+{
+  char *name;
+  char *label;
+};
 
-         gtk_toggle_button_set_active
-           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_LOW_UP]), TRUE);
+static struct nlp *
+nlp_create (const char *name, const char *label)
+{
+  struct nlp *nlp = xmalloc (sizeof *nlp);
 
-         gtk_entry_set_text (GTK_ENTRY (rd->ov_low_up_entry), str);
+  nlp->name = g_strdup (name);
 
-         g_free (str);
-       }
-       break;
+  nlp->label = NULL;
 
-      case OV_HIGH_DOWN:
-       {
-         gchar *str = num_to_string (ov->v.range[0]);
+  if ( 0 != strcmp ("", label))
+    nlp->label = g_strdup (label);
 
-         gtk_toggle_button_set_active
-           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_HIGH_DOWN]), TRUE);
+  return nlp;
+}
 
-         gtk_entry_set_text (GTK_ENTRY (rd->ov_high_down_entry), str);
+static void
+nlp_destroy (gpointer data)
+{
+  struct nlp *nlp = data ;
+  if ( ! nlp )
+    return;
 
-         g_free (str);
-       }
-       break;
-
-      case OV_ELSE:
-       gtk_toggle_button_set_active
-         (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_ELSE]), TRUE);
-       break;
-
-      default:
-       g_warning ("Unknown old value type");
-       break;
-      };
-    g_value_unset (&ov_value);
-    }
+  g_free (nlp->name);
+  g_free (nlp->label);
+  g_free (nlp);
 }
 
+
 /* Callback which gets called when a new row is selected
    in the variable treeview.
    It sets the name and label entry widgets to reflect the
@@ -692,35 +434,42 @@ static void
 on_selection_change (GtkTreeSelection *selection, gpointer data)
 {
   struct recode_dialog *rd = data;
-  GtkTreeModel *model = GTK_TREE_MODEL (rd->var_map);
+
+  GtkTreeModel *model = gtk_tree_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;
-      gchar *name = NULL;
-      gchar *label = NULL;
 
       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 (GTK_TREE_MODEL (rd->var_map), &iter,
-                         COL_NEW_NAME, &name,
-                         COL_NEW_LABEL, &label,
+      gtk_tree_model_get (model, &iter,
+                         0, &var, 
                          -1);
 
-      gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), name ? name : "");
-      gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), label ? label : "");
+      nlp = g_hash_table_lookup (rd->varmap, var);
 
-      g_free (name);
-      g_free (label);
+      if (nlp)
+       {
+         gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), nlp->name ? nlp->name : "");
+         gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), nlp->label ? nlp->label : "");
+       }
+      else
+       {
+         gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
+         gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
+       }
     }
   else
     {
@@ -732,6 +481,7 @@ on_selection_change (GtkTreeSelection *selection, gpointer data)
       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
     }
 
+
   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
   g_list_free (rows);
 }
@@ -759,12 +509,15 @@ on_convert_toggled (GtkToggleButton *b, struct recode_dialog *rd)
   gtk_widget_set_sensitive (rd->string_button, !active);
 }
 
-
 static void
 on_change_clicked (GObject *obj, gpointer data)
 {
   struct recode_dialog *rd = data;
-  GtkTreeModel *model = GTK_TREE_MODEL (rd->var_map);
+  struct variable *var = NULL;
+  struct nlp *nlp;
+
+  GtkTreeModel *model =  gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
+
   GtkTreeIter iter;
   GtkTreeSelection *selection =
     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
@@ -777,42 +530,32 @@ on_change_clicked (GObject *obj, gpointer data)
   const gchar *dest_var_label =
     gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry));
 
-  if ( NULL == rows )
-    return;
+  if ( NULL == rows || rows->next != NULL)
+    goto finish;
 
   gtk_tree_model_get_iter (model, &iter, rows->data);
 
-  gtk_list_store_set (rd->var_map, &iter,
-                     COL_NEW_NAME, dest_var_name,
-                     COL_NEW_LABEL, dest_var_label,
-                     -1);
+  gtk_tree_model_get (model, &iter, 0, &var, -1);
+
+  g_hash_table_remove (rd->varmap, var);
+
+  nlp = nlp_create (dest_var_name, dest_var_label);
+
+  g_hash_table_insert (rd->varmap, var, nlp);
 
+  gtk_tree_model_row_changed (model, rows->data, &iter);
+
+ finish:
   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
   g_list_free (rows);
 }
 
 
-/* If there's nothing selected in the variable treeview,
-   then automatically select the first item */
 static void
-select_something (GtkTreeModel *treemodel,
-                 GtkTreePath  *arg1,
-                 GtkTreeIter  *arg2,
-                 gpointer      data)
+focus_value_entry (GtkWidget *w, struct recode_dialog *rd)
 {
-  struct recode_dialog *rd = data;
-  GtkTreeSelection *sel;
-
-  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
-
-  if ( gtk_tree_selection_count_selected_rows (sel) < 1)
-    {
-      GtkTreeIter iter;
-
-      gtk_tree_model_get_iter_first   (treemodel, &iter);
-
-      gtk_tree_selection_select_iter  (sel, &iter);
-    }
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
+    gtk_widget_grab_focus (rd->new_value_entry);
 }
 
 
@@ -836,36 +579,56 @@ set_acr (struct recode_dialog *rd)
 }
 
 static void
-recode_dialog (struct data_editor *de, gboolean diff)
+render_new_var_name (GtkTreeViewColumn *tree_column,
+                    GtkCellRenderer *cell,
+                    GtkTreeModel *tree_model,
+                    GtkTreeIter *iter,
+                    gpointer data)
 {
-  gint response;
+  struct nlp *nlp = NULL;
+  struct recode_dialog *rd = data;
 
-  struct recode_dialog rd;
+  struct variable *var = NULL;
+
+  gtk_tree_model_get (tree_model, iter, 
+                     0, &var,
+                     -1);
+
+  nlp = g_hash_table_lookup (rd->varmap, var);
 
-  GladeXML *xml = XML_NEW ("recode.glade");
+  if ( nlp )
+    g_object_set (cell, "text", nlp->name, NULL);
+  else
+    g_object_set (cell, "text", "", NULL);
+}
 
 
-  GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
 
-  GtkWidget *oldandnew = get_widget_assert (xml, "button1");
+static void
+recode_dialog (PsppireDataWindow *de, gboolean diff)
+{
+  gint response;
 
+  struct recode_dialog rd;
 
-  GtkWidget *output_variable_box = get_widget_assert (xml,"frame4");
+  GtkBuilder *builder = builder_new ("recode.ui");
 
+  GtkWidget *selector = get_widget_assert (builder, "psppire-selector1");
 
-  PsppireVarStore *vs = NULL;
+  GtkWidget *oldandnew = get_widget_assert (builder, "button1");
 
-  g_object_get (de->data_editor, "var-store", &vs, NULL);
 
-  rd.change_button = get_widget_assert (xml, "change-button");
+  GtkWidget *output_variable_box = get_widget_assert (builder,"frame4");
 
-  rd.dialog = get_widget_assert   (xml, "recode-dialog");
-  rd.dict_treeview = get_widget_assert (xml, "treeview1");
-  rd.variable_treeview =   get_widget_assert (xml, "treeview2");
-  rd.new_name_entry = get_widget_assert (xml, "dest-name-entry");
-  rd.new_label_entry = get_widget_assert (xml, "dest-label-entry");
+  rd.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");
 
-  rd.dict = vs->dict;
+  g_object_get (de->data_editor, "dictionary", &rd.dict, NULL);
 
   rd.value_map = gtk_list_store_new (2,
                                     old_value_get_type (),
@@ -883,124 +646,82 @@ recode_dialog (struct data_editor *de, gboolean diff)
 
   rd.different = diff;
 
-  gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), de->parent.window);
-
-
-  attach_dictionary_to_treeview (GTK_TREE_VIEW (rd.dict_treeview),
-                                vs->dict,
-                                GTK_SELECTION_MULTIPLE, NULL);
+  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 )
-    {
-      set_dest_model (GTK_TREE_VIEW (rd.variable_treeview), vs->dict);
-    }
-  else
+  if (rd.different)
     {
       GtkTreeSelection *sel;
-      GtkTreeViewColumn *col;
-      GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
-
-      rd.var_map = gtk_list_store_new (n_COL_VARS, G_TYPE_INT,
-                                                   G_TYPE_STRING,
-                                                   G_TYPE_STRING);
-
 
+      GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
 
-      gtk_tree_view_set_model (GTK_TREE_VIEW (rd.variable_treeview),
-                              GTK_TREE_MODEL (rd.var_map));
-
-      col = gtk_tree_view_column_new_with_attributes (_("Old"),
-                                                 renderer,
-                                                 "text", NULL,
-                                                 NULL);
+      GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("New"),
+                                                                        renderer,
+                                                                        "text", NULL,
+                                                                        NULL);
 
       gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                              cell_var_name,
-                                              vs->dict, 0);
+                                              render_new_var_name,
+                                              &rd, NULL);
 
 
       gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
 
 
-      renderer = gtk_cell_renderer_text_new ();
-
-      col = gtk_tree_view_column_new_with_attributes (_("New"),
-                                                 renderer,
-                                                 "text", NULL,
-                                                 NULL);
-
-      gtk_tree_view_column_set_cell_data_func (col, renderer,
-                                              render_new_var_name,
-                                              NULL, NULL);
-
+      col = gtk_tree_view_get_column (GTK_TREE_VIEW (rd.variable_treeview), 0);
 
-      gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
+      g_object_set (col, "title", _("Old"), NULL);
 
       g_object_set (rd.variable_treeview, "headers-visible", TRUE, NULL);
 
-      g_signal_connect (rd.change_button, "clicked",
-                       G_CALLBACK (on_change_clicked),  &rd);
+      rd.varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy);
 
       sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.variable_treeview));
+
       g_signal_connect (sel, "changed",
                        G_CALLBACK (on_selection_change), &rd);
 
-      g_signal_connect (rd.var_map, "row-inserted",
-                       G_CALLBACK (select_something), &rd);
-    }
-
-
+      g_signal_connect (rd.change_button, "clicked",
+                       G_CALLBACK (on_change_clicked),  &rd);
 
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                rd.dict_treeview,
-                                rd.variable_treeview,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
+    }
 
   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types);
 
   /* Set up the Old & New Values subdialog */
   {
-    rd.string_button = get_widget_assert (xml, "checkbutton1");
-    rd.width_entry   = get_widget_assert (xml, "spinbutton1");
+    rd.string_button = get_widget_assert (builder, "checkbutton1");
+    rd.width_entry   = get_widget_assert (builder, "spinbutton1");
 
-    rd.convert_button           = get_widget_assert (xml, "checkbutton2");
+    rd.convert_button           = get_widget_assert (builder, "checkbutton2");
 
-    rd.ov_range_lower_entry = get_widget_assert (xml, "entry5");
-    rd.ov_range_upper_entry  = get_widget_assert (xml, "entry3");
-    rd.ov_low_up_entry       = get_widget_assert (xml, "entry6");
-    rd.ov_high_down_entry    = get_widget_assert (xml, "entry7");
+    rd.old_value_chooser = get_widget_assert (builder, "val-chooser");
 
-    rd.new_value_entry = get_widget_assert (xml, "entry1");
-    rd.ov_value_entry  = get_widget_assert (xml, "entry2");
+    rd.new_value_entry = get_widget_assert (builder, "entry1");
 
-    rd.toggle[BUTTON_NEW_VALUE]  = get_widget_assert (xml, "radiobutton1");
-    rd.toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (xml, "radiobutton2");
-    rd.toggle[BUTTON_NEW_COPY]   = get_widget_assert (xml, "radiobutton3");
-    rd.toggle[BUTTON_OLD_VALUE]  = get_widget_assert (xml, "radiobutton4");
-    rd.toggle[BUTTON_OLD_SYSMIS] = get_widget_assert (xml, "radiobutton6");
-    rd.toggle[BUTTON_OLD_MISSING]= get_widget_assert (xml, "radiobutton7");
-    rd.toggle[BUTTON_OLD_RANGE]  = get_widget_assert (xml, "radiobutton8");
-    rd.toggle[BUTTON_OLD_LOW_UP] = get_widget_assert (xml, "radiobutton10");
-    rd.toggle[BUTTON_OLD_HIGH_DOWN] = get_widget_assert (xml, "radiobutton5");
-    rd.toggle[BUTTON_OLD_ELSE]   = get_widget_assert (xml, "radiobutton11");
 
-    rd.new_copy_label = get_widget_assert (xml, "label3");
-    rd.strings_box    = get_widget_assert (xml, "table3");
+    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 (xml, "old-new-values-dialog"));
+      PSPPIRE_DIALOG (get_widget_assert (builder, "old-new-values-dialog"));
 
     gtk_window_set_transient_for (GTK_WINDOW (rd.old_and_new_dialog),
-                                 de->parent.window);
+                                 GTK_WINDOW (de));
 
-    rd.acr = PSPPIRE_ACR (get_widget_assert (xml, "psppire-acr1"));
+    rd.acr = PSPPIRE_ACR (get_widget_assert (builder, "psppire-acr1"));
 
     g_signal_connect_swapped (rd.toggle[BUTTON_NEW_VALUE], "toggled",
                      G_CALLBACK (set_acr), &rd);
 
+    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);
 
@@ -1042,23 +763,6 @@ recode_dialog (struct data_editor *de, gboolean diff)
     g_signal_connect (rd.toggle[BUTTON_NEW_VALUE], "toggled",
                      G_CALLBACK (toggle_sensitivity), rd.new_value_entry);
 
-    g_signal_connect (rd.toggle[BUTTON_OLD_VALUE], "toggled",
-                     G_CALLBACK (toggle_sensitivity), rd.ov_value_entry);
-
-    g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
-                     G_CALLBACK (toggle_sensitivity),
-                     get_widget_assert (xml, "entry3"));
-
-    g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
-                     G_CALLBACK (toggle_sensitivity),
-                     get_widget_assert (xml, "entry5"));
-
-    g_signal_connect (rd.toggle[BUTTON_OLD_LOW_UP], "toggled",
-                     G_CALLBACK (toggle_sensitivity), rd.ov_low_up_entry);
-
-    g_signal_connect (rd.toggle[BUTTON_OLD_HIGH_DOWN], "toggled",
-                     G_CALLBACK (toggle_sensitivity), rd.ov_high_down_entry);
-
     g_signal_connect (rd.string_button, "toggled",
                      G_CALLBACK (toggle_sensitivity), rd.width_entry);
 
@@ -1083,105 +787,33 @@ recode_dialog (struct data_editor *de, gboolean diff)
   switch (response)
     {
     case GTK_RESPONSE_OK:
-      {
-       gchar *syntax = generate_syntax (&rd);
-       struct getl_interface *sss = create_syntax_string_source (syntax);
-       execute_syntax (sss);
-
-       g_free (syntax);
-      }
+      g_free (execute_syntax_string (de, generate_syntax (&rd)));
       break;
     case PSPPIRE_RESPONSE_PASTE:
-      {
-       gchar *syntax = generate_syntax (&rd);
-
-       struct syntax_editor *se =
-         (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
-
-       gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
-
-       g_free (syntax);
-      }
+      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 (xml);
+  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)
 {
-  const gchar *text = NULL;
-  struct old_value ov;
-  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                    (rd->toggle [BUTTON_OLD_VALUE])))
-    {
-      text = gtk_entry_get_text (GTK_ENTRY (rd->ov_value_entry));
-      if ( rd->input_var_is_string )
-       {
-         ov.type = OV_STRING;
-         ov.v.s = g_strdup (text);
-       }
-      else
-       {
-         ov.type = OV_NUMERIC;
-         ov.v.v = g_strtod (text, 0);
-       }
-    }
-  else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                         (rd->toggle [BUTTON_OLD_MISSING])))
-    {
-      ov.type = OV_MISSING;
-    }
-  else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                         (rd->toggle [BUTTON_OLD_SYSMIS])))
-    {
-      ov.type = OV_SYSMIS;
-    }
-  else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                         (rd->toggle [BUTTON_OLD_ELSE])))
-    {
-      ov.type = OV_ELSE;
-    }
-  else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                         (rd->toggle [BUTTON_OLD_RANGE])))
-    {
-      const gchar *text;
-      text = gtk_entry_get_text (GTK_ENTRY (rd->ov_range_lower_entry));
+  PsppireValChooser *vc = PSPPIRE_VAL_CHOOSER (rd->old_value_chooser);
 
-      ov.type = OV_RANGE;
-      ov.v.range[0] = g_strtod (text, 0);
-
-      text = gtk_entry_get_text (GTK_ENTRY (rd->ov_range_upper_entry));
-      ov.v.range[1] = g_strtod (text, 0);
-    }
-  else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                         (rd->toggle [BUTTON_OLD_LOW_UP])))
-    {
-      const gchar *text =
-       gtk_entry_get_text (GTK_ENTRY (rd->ov_low_up_entry));
-
-      ov.type = OV_LOW_UP;
-      ov.v.range[1] = g_strtod (text, 0);
-    }
-  else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
-                                         (rd->toggle [BUTTON_OLD_HIGH_DOWN])))
-    {
-      const gchar *text =
-       gtk_entry_get_text (GTK_ENTRY (rd->ov_high_down_entry));
+  struct old_value ov;
 
-      ov.type = OV_HIGH_DOWN;
-      ov.v.range[0] = g_strtod (text, 0);
-    }
-  else
-    return FALSE;
+  psppire_val_chooser_get_status (vc, &ov);
 
   g_value_init (val, old_value_get_type ());
   g_value_set_boxed (val, &ov);
@@ -1240,7 +872,7 @@ set_new_value (GValue *val, const struct recode_dialog *rd)
 
 
 /* A function to set a value in a column in the ACR */
-gboolean
+static gboolean
 set_value (gint col, GValue  *val, gpointer data)
 {
   struct recode_dialog *rd = data;
@@ -1280,7 +912,6 @@ run_old_and_new_dialog (struct recode_dialog *rd)
     /* Find the type of the first variable (it's invariant that
        all variables are of the same type) */
     const struct variable *v;
-    gint idx;
     GtkTreeIter iter;
     GtkTreeModel *model =
       gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
@@ -1289,20 +920,12 @@ run_old_and_new_dialog (struct recode_dialog *rd)
 
     g_return_if_fail (not_empty);
 
-    gtk_tree_model_get (model, &iter, 0, &idx, -1);
-
-    v = psppire_dict_get_variable (rd->dict, idx);
+    gtk_tree_model_get (model, &iter, 0, &v, -1);
 
     rd->input_var_is_string = var_is_alpha (v);
 
-    gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_SYSMIS],
-                             var_is_numeric (v));
-    gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_RANGE],
-                             var_is_numeric (v));
-    gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_LOW_UP],
-                             var_is_numeric (v));
-    gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_HIGH_DOWN],
-                             var_is_numeric (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));
 
@@ -1329,132 +952,72 @@ run_old_and_new_dialog (struct recode_dialog *rd)
 
 /* Generate a syntax fragment for NV and append it to STR */
 static void
-new_value_append_syntax (GString *str, const struct new_value *nv)
+new_value_append_syntax (struct string *dds, const struct new_value *nv)
 {
   switch (nv->type)
     {
     case NV_NUMERIC:
-      g_string_append_printf (str, "%g", nv->v.v);
+      ds_put_c_format (dds, "%.*g", DBL_DIG + 1, nv->v.v);
       break;
     case NV_STRING:
-      {
-       struct string ds;
-       ds_init_cstr (&ds, nv->v.s);
-       gen_quoted_string (&ds);
-       g_string_append (str, ds_cstr (&ds));
-       ds_destroy (&ds);
-      }
+      syntax_gen_string (dds, ss_cstr (nv->v.s));
       break;
     case NV_COPY:
-      g_string_append (str, "COPY");
+      ds_put_cstr (dds, "COPY");
       break;
     case NV_SYSMIS:
-      g_string_append (str, "SYSMIS");
+      ds_put_cstr (dds, "SYSMIS");
       break;
     default:
       /* Shouldn't ever happen */
       g_warning ("Invalid type in new recode value");
-      g_string_append (str, "???");
+      ds_put_cstr (dds, "???");
       break;
     }
 }
 
 
-/* Generate a syntax fragment for NV and append it to STR */
-static void
-old_value_append_syntax (GString *str, const struct old_value *ov)
-{
-  switch (ov->type)
-    {
-    case OV_NUMERIC:
-      g_string_append_printf (str, "%g", ov->v.v);
-      break;
-    case OV_STRING:
-      {
-       struct string ds;
-       ds_init_cstr (&ds, ov->v.s);
-       gen_quoted_string (&ds);
-       g_string_append (str, ds_cstr (&ds));
-       ds_destroy (&ds);
-      }
-      break;
-    case OV_MISSING:
-      g_string_append (str, "MISSING");
-      break;
-    case OV_SYSMIS:
-      g_string_append (str, "SYSMIS");
-      break;
-    case OV_ELSE:
-      g_string_append (str, "ELSE");
-      break;
-    case OV_RANGE:
-      g_string_append_printf (str, "%g THRU %g",
-                             ov->v.range[0],
-                             ov->v.range[1]);
-      break;
-    case OV_LOW_UP:
-      g_string_append_printf (str, "LOWEST THRU %g",
-                             ov->v.range[1]);
-      break;
-    case OV_HIGH_DOWN:
-      g_string_append_printf (str, "%g THRU HIGHEST",
-                             ov->v.range[0]);
-      break;
-    default:
-      g_warning ("Invalid type in old recode value");
-      g_string_append (str, "???");
-      break;
-    };
-}
-
-
-
 static char *
 generate_syntax (const struct recode_dialog *rd)
 {
   gboolean ok;
   GtkTreeIter iter;
   gchar *text;
+  struct string dds;
+
+  ds_init_empty (&dds);
 
-  GString *str = g_string_sized_new (100);
 
   /* Declare new string variables if applicable */
   if ( rd->different &&
        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)))
     {
-      GtkTreeIter iter;
+      GHashTableIter iter;
 
+      struct variable *var = NULL;
+      struct nlp *nlp = NULL;
 
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
-          ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
+      g_hash_table_iter_init (&iter, rd->varmap);
+      while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
        {
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name, -1);
-
-         g_string_append (str, "\nSTRING ");
-         g_string_append (str, name);
-         g_string_append_printf (str, " (A%d).",
+         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) )
                                  );
-
-         g_free (name);
        }
     }
 
-  g_string_append (str, "\nRECODE ");
+  ds_put_cstr (&dds, "\nRECODE ");
 
-  append_variable_names (str, rd->dict, GTK_TREE_VIEW (rd->variable_treeview), 0);
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &dds);
 
-  g_string_append (str, "\n\t");
+  ds_put_cstr (&dds, "\n\t");
 
   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->convert_button)))
     {
-      g_string_append (str, "(CONVERT) ");
+      ds_put_cstr (&dds, "(CONVERT) ");
     }
 
   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
@@ -1475,13 +1038,13 @@ generate_syntax (const struct recode_dialog *rd)
       ov = g_value_get_boxed (&ov_value);
       nv = g_value_get_boxed (&nv_value);
 
-      g_string_append (str, "(");
+      ds_put_cstr (&dds, "(");
 
-      old_value_append_syntax (str, ov);
-      g_string_append (str, " = ");
-      new_value_append_syntax (str, nv);
+      old_value_append_syntax (&dds, ov);
+      ds_put_cstr (&dds, " = ");
+      new_value_append_syntax (&dds, nv);
 
-      g_string_append (str, ") ");
+      ds_put_cstr (&dds, ") ");
       g_value_unset (&ov_value);
       g_value_unset (&nv_value);
     }
@@ -1489,73 +1052,56 @@ generate_syntax (const struct recode_dialog *rd)
 
   if ( rd->different )
     {
+
       GtkTreeIter iter;
-      g_string_append (str, "\n\tINTO ");
+      ds_put_cstr (&dds, "\n\tINTO ");
 
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
+      for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter);
           ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
-       {
-         gchar *name = NULL;
-
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name, -1);
-
-         g_string_append (str, name);
-         g_string_append (str, " ");
+          ok = psppire_var_view_get_iter_next (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter))
+         {
+           struct nlp *nlp = NULL;
+           const struct variable *var = psppire_var_view_get_variable (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &iter);
 
-         g_free (name);
-       }
+           nlp = g_hash_table_lookup (rd->varmap, var);
+           
+           ds_put_cstr (&dds, nlp->name);
+           ds_put_cstr (&dds, " ");
+         }
     }
 
-  g_string_append (str, ".");
-
+  ds_put_cstr (&dds, ".");
 
   /* If applicable, set labels for the new variables. */
   if ( rd->different )
     {
-      GtkTreeIter iter;
+      GHashTableIter iter;
 
-      for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
-                                              &iter);
-          ok;
-          ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
-       {
-         struct string ls;
-         gchar *label = NULL;
-         gchar *name = NULL;
+      struct variable *var = NULL;
+      struct nlp *nlp = NULL;
 
-         gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
-                             COL_NEW_NAME, &name,
-                             COL_NEW_LABEL, &label, -1);
-
-         if ( 0 == strcmp (label, "") )
+      g_hash_table_iter_init (&iter, rd->varmap);
+      while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
+       {
+         if (nlp->label)
            {
-             g_free (name);
-             g_free (label);
-             continue;
-           }
-
-         ds_init_cstr (&ls, label);
-         g_free (label);
-         gen_quoted_string (&ls);
-
-         g_string_append_printf (str, "\nVARIABLE LABELS %s %s.",
-                                 name, ds_cstr (&ls));
+             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));
 
-         g_free (name);
-         ds_destroy (&ls);
+             ds_destroy (&sl);
+           }
        }
     }
 
-
-  g_string_append (str, "\nEXECUTE.\n");
+  ds_put_cstr (&dds, "\nEXECUTE.\n");
 
 
-  text = str->str;
+  text = ds_steal_cstr (&dds);
 
-  g_string_free (str, FALSE);
+  ds_destroy (&dds);
 
   return text;
 }