gui: Make syntax execution functions take a PsppireDataWindow argument.
[pspp-builds.git] / src / ui / gui / recode-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009, 2010, 2011  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 /* This module implements the RECODE dialog.
18
19    It has two forms.  One for recoding values into the same variable.
20    The second for recoding into different variables.
21 */
22
23 #include <config.h>
24
25 #include "recode-dialog.h"
26
27 #include "executor.h"
28
29 #include "psppire-var-view.h"
30
31 #include <gtk/gtk.h>
32
33 #include <xalloc.h>
34 #include <ui/gui/psppire-data-window.h>
35 #include <ui/gui/dialog-common.h>
36 #include <ui/gui/dict-display.h>
37 #include <ui/gui/helper.h>
38 #include <ui/gui/psppire-dialog.h>
39 #include <ui/gui/psppire-var-store.h>
40
41 #include <ui/syntax-gen.h>
42
43 #include "psppire-acr.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) msgid
48
49
50 /* Define a boxed type to represent a value which is a candidate
51    to replace an existing value */
52
53 enum new_value_type
54  {
55    NV_NUMERIC,
56    NV_STRING,
57    NV_SYSMIS,
58    NV_COPY
59  };
60
61
62 struct new_value
63 {
64   enum new_value_type type;
65   union {
66     double v;
67     gchar *s;
68   } v;
69 };
70
71
72 static struct new_value *
73 new_value_copy (struct new_value *nv)
74 {
75   struct new_value *copy = g_memdup (nv, sizeof (*copy));
76
77   if ( nv->type == NV_STRING )
78     copy->v.s = xstrdup (nv->v.s);
79
80   return copy;
81 }
82
83
84 static void
85 new_value_free (struct new_value *nv)
86 {
87   if ( nv->type == NV_STRING )
88     g_free (nv->v.s);
89
90   g_free (nv);
91 }
92
93
94 static void
95 new_value_to_string (const GValue *src, GValue *dest)
96 {
97   const struct new_value *nv = g_value_get_boxed (src);
98
99   g_assert (nv);
100
101   switch (nv->type)
102     {
103     case NV_NUMERIC:
104       {
105         gchar *text = g_strdup_printf ("%g", nv->v.v);
106         g_value_set_string (dest, text);
107         g_free (text);
108       }
109       break;
110     case NV_STRING:
111       g_value_set_string (dest, nv->v.s);
112       break;
113     case NV_COPY:
114       g_value_set_string (dest, "COPY");
115       break;
116     case NV_SYSMIS:
117       g_value_set_string (dest, "SYSMIS");
118       break;
119     default:
120       /* Shouldn't ever happen */
121       g_warning ("Invalid type in new recode value");
122       g_value_set_string (dest, "???");
123       break;
124     }
125 }
126
127 static GType
128 new_value_get_type (void)
129 {
130   static GType t = 0;
131
132   if (t == 0 )
133     {
134       t = g_boxed_type_register_static  ("psppire-recode-new-values",
135                                          (GBoxedCopyFunc) new_value_copy,
136                                          (GBoxedFreeFunc) new_value_free);
137
138       g_value_register_transform_func (t, G_TYPE_STRING,
139                                        new_value_to_string);
140     }
141
142   return t;
143 }
144
145
146 \f
147
148
149 /* A boxed type representing a value, or a range of values which may
150    potentially be replaced by something */
151
152 enum old_value_type
153  {
154    OV_NUMERIC,
155    OV_STRING,
156    OV_SYSMIS,
157    OV_MISSING,
158    OV_RANGE,
159    OV_LOW_UP,
160    OV_HIGH_DOWN,
161    OV_ELSE
162  };
163
164 struct old_value
165  {
166    enum old_value_type type;
167    union {
168      double v;
169      gchar *s;
170      double range[2];
171    } v;
172  };
173
174
175 static struct old_value *
176 old_value_copy (struct old_value *ov)
177 {
178   struct old_value *copy = g_memdup (ov, sizeof (*copy));
179
180   if ( ov->type == OV_STRING )
181     copy->v.s = g_strdup (ov->v.s);
182
183   return copy;
184 }
185
186
187 static void
188 old_value_free (struct old_value *ov)
189 {
190   if (ov->type == OV_STRING)
191     g_free (ov->v.s);
192   g_free (ov);
193 }
194
195 static void
196 old_value_to_string (const GValue *src, GValue *dest)
197 {
198   const struct old_value *ov = g_value_get_boxed (src);
199
200   switch (ov->type)
201     {
202     case OV_NUMERIC:
203       {
204         gchar *text = g_strdup_printf ("%g", ov->v.v);
205         g_value_set_string (dest, text);
206         g_free (text);
207       }
208       break;
209     case OV_STRING:
210       g_value_set_string (dest, ov->v.s);
211       break;
212     case OV_MISSING:
213       g_value_set_string (dest, "MISSING");
214       break;
215     case OV_SYSMIS:
216       g_value_set_string (dest, "SYSMIS");
217       break;
218     case OV_ELSE:
219       g_value_set_string (dest, "ELSE");
220       break;
221     case OV_RANGE:
222       {
223         gchar *text;
224         char en_dash[6] = {0,0,0,0,0,0};
225
226         g_unichar_to_utf8 (0x2013, en_dash);
227
228         text = g_strdup_printf ("%g %s %g",
229                                        ov->v.range[0],
230                                        en_dash,
231                                        ov->v.range[1]);
232         g_value_set_string (dest, text);
233         g_free (text);
234       }
235       break;
236     case OV_LOW_UP:
237       {
238         gchar *text;
239         char en_dash[6] = {0,0,0,0,0,0};
240
241         g_unichar_to_utf8 (0x2013, en_dash);
242
243         text = g_strdup_printf ("LOWEST %s %g",
244                                 en_dash,
245                                 ov->v.range[1]);
246
247         g_value_set_string (dest, text);
248         g_free (text);
249       }
250       break;
251     case OV_HIGH_DOWN:
252       {
253         gchar *text;
254         char en_dash[6] = {0,0,0,0,0,0};
255
256         g_unichar_to_utf8 (0x2013, en_dash);
257
258         text = g_strdup_printf ("%g %s HIGHEST",
259                                 ov->v.range[0],
260                                 en_dash);
261
262         g_value_set_string (dest, text);
263         g_free (text);
264       }
265       break;
266     default:
267       g_warning ("Invalid type in old recode value");
268       g_value_set_string (dest, "???");
269       break;
270     };
271 }
272
273 static GType
274 old_value_get_type (void)
275 {
276   static GType t = 0;
277
278   if (t == 0 )
279     {
280       t = g_boxed_type_register_static  ("psppire-recode-old-values",
281                                          (GBoxedCopyFunc) old_value_copy,
282                                          (GBoxedFreeFunc) old_value_free);
283
284       g_value_register_transform_func     (t, G_TYPE_STRING,
285                                            old_value_to_string);
286     }
287
288   return t;
289 }
290
291 \f
292
293 enum
294   {
295     BUTTON_NEW_VALUE,
296     BUTTON_NEW_COPY,
297     BUTTON_NEW_SYSMIS,
298     BUTTON_OLD_VALUE,
299     BUTTON_OLD_SYSMIS,
300     BUTTON_OLD_MISSING,
301     BUTTON_OLD_RANGE,
302     BUTTON_OLD_LOW_UP,
303     BUTTON_OLD_HIGH_DOWN,
304     BUTTON_OLD_ELSE,
305     n_BUTTONS
306   };
307
308 struct recode_dialog
309 {
310   PsppireDict *dict;
311
312   GtkWidget *dialog;
313   PsppireDialog *old_and_new_dialog;
314
315   GtkWidget *dict_treeview;
316   GtkWidget *variable_treeview;
317   GtkWidget *toggle[n_BUTTONS];
318
319   GtkWidget *strings_box;
320   GtkWidget *convert_button;
321   GtkWidget *new_copy_label;
322
323   GtkWidget *ov_value_entry;
324   GtkWidget *new_value_entry;
325
326   GtkWidget *ov_range_lower_entry;
327   GtkWidget *ov_range_upper_entry;
328   GtkWidget *ov_low_up_entry;
329   GtkWidget *ov_high_down_entry;
330
331   GtkListStore *value_map;
332
333   /* Indicates that the INTO {new variables} form of the dialog
334      is being used */
335   gboolean different;
336
337   PsppireAcr *acr;
338
339   gboolean input_var_is_string;
340
341   GtkWidget *new_name_entry;
342   GtkWidget *new_label_entry;
343   GtkWidget *change_button;
344
345   GtkWidget *string_button;
346   GtkWidget *width_entry;
347
348   /* A hash table of struct nlp's indexed by variable */
349   GHashTable *varmap;
350 };
351
352
353 static void run_old_and_new_dialog (struct recode_dialog *rd);
354
355 static void
356 refresh (PsppireDialog *dialog, struct recode_dialog *rd)
357 {
358   GtkTreeModel *vars =
359     gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
360
361   gtk_list_store_clear (GTK_LIST_STORE (vars));
362
363   gtk_widget_set_sensitive (rd->change_button, FALSE);
364   gtk_widget_set_sensitive (rd->new_name_entry, FALSE);
365   gtk_widget_set_sensitive (rd->new_label_entry, FALSE);
366
367   if ( rd->different )
368     g_hash_table_remove_all (rd->varmap);
369
370   gtk_list_store_clear (GTK_LIST_STORE (rd->value_map));
371 }
372
373 static char * generate_syntax (const struct recode_dialog *rd);
374
375 enum {
376   COL_VALUE_OLD,
377   COL_VALUE_NEW,
378   n_COL_VALUES
379 };
380
381 /* Dialog is valid iff at least one variable has been selected,
382    AND the list of mappings is not empty.
383  */
384 static gboolean
385 dialog_state_valid (gpointer data)
386 {
387   GtkTreeIter not_used;
388   struct recode_dialog *rd = data;
389
390   if ( ! rd->value_map )
391     return FALSE;
392
393   if ( ! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
394                                         &not_used) )
395     return FALSE;
396
397   if ( rd->different )
398     {
399       GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd->variable_treeview)->list);
400
401       if (g_hash_table_size (rd->varmap) != gtk_tree_model_iter_n_children (model, NULL) )
402         return FALSE;
403     }
404   else
405     {
406       GtkTreeModel *vars =
407         gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
408
409       if ( !gtk_tree_model_get_iter_first (vars, &not_used))
410         return FALSE;
411     }
412
413   return TRUE;
414 }
415
416 static void
417 on_old_new_show (struct recode_dialog *rd)
418 {
419   gtk_toggle_button_set_active
420     (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_OLD_VALUE]), TRUE);
421
422   g_signal_emit_by_name (rd->toggle[BUTTON_OLD_VALUE], "toggled");
423
424   gtk_toggle_button_set_active
425     (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE);
426
427   g_signal_emit_by_name (rd->toggle[BUTTON_NEW_VALUE], "toggled");
428
429   g_object_set (rd->toggle[BUTTON_NEW_COPY],
430                 "visible", rd->different, NULL);
431
432   g_object_set (rd->new_copy_label,
433                 "visible", rd->different, NULL);
434
435   g_object_set (rd->strings_box,
436                 "visible", rd->different, NULL);
437 }
438
439 /* Sets the sensitivity of TARGET dependent upon the active status
440    of BUTTON */
441 static void
442 toggle_sensitivity (GtkToggleButton *button, GtkWidget *target)
443 {
444   gboolean state = gtk_toggle_button_get_active (button);
445
446   /*  g_print ("%s Setting %p (%s) to %d because of %p\n",
447       __FUNCTION__, target, gtk_widget_get_name (target), state, button); */
448
449   gtk_widget_set_sensitive (target, state);
450 }
451
452 static void recode_dialog (PsppireDataWindow *de, gboolean diff);
453
454
455 /* Pops up the Recode Same version of the dialog box */
456 void
457 recode_same_dialog (PsppireDataWindow *de)
458 {
459   recode_dialog (de, FALSE);
460 }
461
462 /* Pops up the Recode Different version of the dialog box */
463 void
464 recode_different_dialog (PsppireDataWindow *de)
465 {
466   recode_dialog (de, TRUE);
467 }
468
469
470 /* This might need to be changed to something less naive.
471    In particular, what happends with dates, etc?
472  */
473 static gchar *
474 num_to_string (gdouble x)
475 {
476   return g_strdup_printf ("%g", x);
477 }
478
479 /* Callback which gets called when a new row is selected
480    in the acr's variable treeview.
481    We use if to set the togglebuttons and entries to correspond to the
482    selected row.
483 */
484 static void
485 on_acr_selection_change (GtkTreeSelection *selection, gpointer data)
486 {
487   struct recode_dialog *rd = data;
488   GtkTreeModel *model = NULL;
489   GtkTreeIter iter;
490
491   GValue ov_value = {0};
492   GValue nv_value = {0};
493   struct old_value *ov = NULL;
494   struct new_value *nv = NULL;
495
496   if ( ! gtk_tree_selection_get_selected (selection, &model, &iter) )
497     return;
498
499
500   gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter,
501                             COL_VALUE_OLD, &ov_value);
502
503   gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter,
504                             COL_VALUE_NEW, &nv_value);
505
506   ov = g_value_get_boxed (&ov_value);
507   nv = g_value_get_boxed (&nv_value);
508
509   if (nv)
510     {
511       switch (nv->type)
512         {
513         case NV_NUMERIC:
514           {
515             gchar *str = num_to_string (nv->v.v);
516
517             gtk_toggle_button_set_active
518               (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE);
519
520             gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), str);
521             g_free (str);
522           }
523           break;
524         case NV_STRING:
525           gtk_toggle_button_set_active
526             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE);
527
528           gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), nv->v.s);
529           break;
530         case NV_SYSMIS:
531           gtk_toggle_button_set_active
532             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]), TRUE);
533
534           break;
535         case NV_COPY:
536           gtk_toggle_button_set_active
537             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]), TRUE);
538
539           break;
540         default:
541           g_warning ("Invalid new value type");
542           break;
543         }
544
545       g_value_unset (&nv_value);
546     }
547
548   if ( ov )
549     {
550     switch (ov->type)
551       {
552       case OV_STRING:
553           gtk_toggle_button_set_active
554             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_VALUE]), TRUE);
555
556           gtk_entry_set_text (GTK_ENTRY (rd->ov_value_entry), ov->v.s);
557         break;
558
559       case OV_NUMERIC:
560         {
561           gchar *str;
562
563           gtk_toggle_button_set_active
564             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_VALUE]), TRUE);
565
566           str = num_to_string (ov->v.v);
567
568           gtk_entry_set_text (GTK_ENTRY (rd->ov_value_entry), str);
569
570           g_free (str);
571         }
572         break;
573
574       case OV_SYSMIS:
575         gtk_toggle_button_set_active
576           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_SYSMIS]), TRUE);
577         break;
578
579       case OV_MISSING:
580         gtk_toggle_button_set_active
581           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_MISSING]), TRUE);
582         break;
583
584       case OV_RANGE:
585         {
586           gchar *str;
587
588           gtk_toggle_button_set_active
589             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_RANGE]), TRUE);
590
591           str = num_to_string (ov->v.range[0]);
592
593           gtk_entry_set_text (GTK_ENTRY (rd->ov_range_lower_entry), str);
594
595           g_free (str);
596
597
598           str = num_to_string (ov->v.range[1]);
599
600           gtk_entry_set_text (GTK_ENTRY (rd->ov_range_upper_entry), str);
601
602           g_free (str);
603         }
604         break;
605
606       case OV_LOW_UP:
607         {
608           gchar *str = num_to_string (ov->v.range[1]);
609
610           gtk_toggle_button_set_active
611             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_LOW_UP]), TRUE);
612
613           gtk_entry_set_text (GTK_ENTRY (rd->ov_low_up_entry), str);
614
615           g_free (str);
616         }
617         break;
618
619       case OV_HIGH_DOWN:
620         {
621           gchar *str = num_to_string (ov->v.range[0]);
622
623           gtk_toggle_button_set_active
624             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_HIGH_DOWN]), TRUE);
625
626           gtk_entry_set_text (GTK_ENTRY (rd->ov_high_down_entry), str);
627
628           g_free (str);
629         }
630         break;
631
632       case OV_ELSE:
633         gtk_toggle_button_set_active
634           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_ELSE]), TRUE);
635         break;
636
637       default:
638         g_warning ("Unknown old value type");
639         break;
640       };
641     g_value_unset (&ov_value);
642     }
643 }
644
645 /* Name-Label pair */
646 struct nlp
647 {
648   char *name;
649   char *label;
650 };
651
652 static struct nlp *
653 nlp_create (const char *name, const char *label)
654 {
655   struct nlp *nlp = xmalloc (sizeof *nlp);
656
657   nlp->name = g_strdup (name);
658
659   nlp->label = NULL;
660
661   if ( 0 != strcmp ("", label))
662     nlp->label = g_strdup (label);
663
664   return nlp;
665 }
666
667 static void
668 nlp_destroy (gpointer data)
669 {
670   struct nlp *nlp = data ;
671   if ( ! nlp )
672     return;
673
674   g_free (nlp->name);
675   g_free (nlp->label);
676   g_free (nlp);
677 }
678
679
680 /* Callback which gets called when a new row is selected
681    in the variable treeview.
682    It sets the name and label entry widgets to reflect the
683    currently selected row.
684  */
685 static void
686 on_selection_change (GtkTreeSelection *selection, gpointer data)
687 {
688   struct recode_dialog *rd = data;
689
690   GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd->variable_treeview)->list);
691
692   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
693
694   if ( rows && !rows->next)
695     {
696       /* Exactly one row is selected */
697       struct nlp *nlp;
698       struct variable *var;
699       gboolean ok;
700       GtkTreeIter iter;
701
702       gtk_widget_set_sensitive  (rd->change_button, TRUE);
703       gtk_widget_set_sensitive  (rd->new_name_entry, TRUE);
704       gtk_widget_set_sensitive  (rd->new_label_entry, TRUE);
705
706       ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data);
707
708       gtk_tree_model_get (model, &iter,
709                           0, &var, 
710                           -1);
711
712       nlp = g_hash_table_lookup (rd->varmap, var);
713
714       if (nlp)
715         {
716           gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), nlp->name ? nlp->name : "");
717           gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), nlp->label ? nlp->label : "");
718         }
719       else
720         {
721           gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
722           gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
723         }
724     }
725   else
726     {
727       gtk_widget_set_sensitive  (rd->change_button, FALSE);
728       gtk_widget_set_sensitive  (rd->new_name_entry, FALSE);
729       gtk_widget_set_sensitive  (rd->new_label_entry, FALSE);
730
731       gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
732       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
733     }
734
735
736   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
737   g_list_free (rows);
738 }
739
740 static void
741 on_string_toggled (GtkToggleButton *b, struct recode_dialog *rd)
742 {
743   gboolean active;
744   if (! rd->input_var_is_string )
745     return ;
746
747   active = gtk_toggle_button_get_active (b);
748   gtk_widget_set_sensitive (rd->convert_button, !active);
749 }
750
751
752 static void
753 on_convert_toggled (GtkToggleButton *b, struct recode_dialog *rd)
754 {
755   gboolean active;
756
757   g_return_if_fail (rd->input_var_is_string);
758
759   active = gtk_toggle_button_get_active (b);
760   gtk_widget_set_sensitive (rd->string_button, !active);
761 }
762
763 static void
764 on_change_clicked (GObject *obj, gpointer data)
765 {
766   struct recode_dialog *rd = data;
767   struct variable *var = NULL;
768   struct nlp *nlp;
769   GtkTreeModel *model = GTK_TREE_MODEL (PSPPIRE_VAR_VIEW (rd->variable_treeview)->list);
770   GtkTreeIter iter;
771   GtkTreeSelection *selection =
772     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
773
774   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
775
776   const gchar *dest_var_name =
777     gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry));
778
779   const gchar *dest_var_label =
780     gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry));
781
782   if ( NULL == rows || rows->next != NULL)
783     goto finish;
784
785   gtk_tree_model_get_iter (model, &iter, rows->data);
786
787   gtk_tree_model_get (model, &iter, 0, &var, -1);
788
789   g_hash_table_remove (rd->varmap, var);
790
791   nlp = nlp_create (dest_var_name, dest_var_label);
792
793   g_hash_table_insert (rd->varmap, var, nlp);
794
795   gtk_tree_model_row_changed (model, rows->data, &iter);
796
797  finish:
798   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
799   g_list_free (rows);
800 }
801
802
803 /* Callback for the new_value_entry and new_value_togglebutton widgets.
804    It's used to enable/disable the acr. */
805 static void
806 set_acr (struct recode_dialog *rd)
807 {
808   const gchar *text;
809
810   if ( !gtk_toggle_button_get_active
811        (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE])))
812     {
813       psppire_acr_set_enabled (rd->acr, TRUE);
814       return;
815     }
816
817   text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry));
818
819   psppire_acr_set_enabled (rd->acr, !g_str_equal (text, ""));
820 }
821
822 static void
823 render_new_var_name (GtkTreeViewColumn *tree_column,
824                      GtkCellRenderer *cell,
825                      GtkTreeModel *tree_model,
826                      GtkTreeIter *iter,
827                      gpointer data)
828 {
829   struct nlp *nlp = NULL;
830   struct recode_dialog *rd = data;
831
832   struct variable *var = NULL;
833
834   gtk_tree_model_get (tree_model, iter, 
835                       0, &var,
836                       -1);
837
838   nlp = g_hash_table_lookup (rd->varmap, var);
839
840   if ( nlp )
841     g_object_set (cell, "text", nlp->name, NULL);
842   else
843     g_object_set (cell, "text", "", NULL);
844 }
845
846
847
848 static void
849 recode_dialog (PsppireDataWindow *de, gboolean diff)
850 {
851   gint response;
852
853   struct recode_dialog rd;
854
855   GtkBuilder *builder = builder_new ("recode.ui");
856
857   GtkWidget *selector = get_widget_assert (builder, "psppire-selector1");
858
859   GtkWidget *oldandnew = get_widget_assert (builder, "button1");
860
861
862   GtkWidget *output_variable_box = get_widget_assert (builder,"frame4");
863
864   PsppireVarStore *vs = NULL;
865   g_object_get (de->data_editor, "var-store", &vs, NULL);
866
867   rd.change_button = get_widget_assert (builder, "change-button");
868   rd.varmap = NULL;
869   rd.dialog = get_widget_assert   (builder, "recode-dialog");
870   rd.dict_treeview = get_widget_assert (builder, "treeview1");
871   rd.variable_treeview =   get_widget_assert (builder, "treeview2");
872   rd.new_name_entry = get_widget_assert (builder, "dest-name-entry");
873   rd.new_label_entry = get_widget_assert (builder, "dest-label-entry");
874
875   g_object_get (vs, "dictionary", &rd.dict, NULL);
876
877   rd.value_map = gtk_list_store_new (2,
878                                      old_value_get_type (),
879                                      new_value_get_type ()
880                                      );
881
882   g_object_set (output_variable_box, "visible", diff, NULL);
883
884   if ( diff )
885     gtk_window_set_title (GTK_WINDOW (rd.dialog),
886                           _("Recode into Different Variables"));
887   else
888     gtk_window_set_title (GTK_WINDOW (rd.dialog),
889                           _("Recode into Same Variables"));
890
891   rd.different = diff;
892
893   gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), GTK_WINDOW (de));
894
895   g_object_set (rd.dict_treeview, "model", rd.dict, NULL);
896
897   if (rd.different)
898     {
899       GtkTreeSelection *sel;
900
901       GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
902
903       GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("New"),
904                                                                          renderer,
905                                                                          "text", NULL,
906                                                                          NULL);
907
908       gtk_tree_view_column_set_cell_data_func (col, renderer,
909                                                render_new_var_name,
910                                                &rd, NULL);
911
912
913       gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
914
915
916       col = gtk_tree_view_get_column (GTK_TREE_VIEW (rd.variable_treeview), 0);
917
918       g_object_set (col, "title", _("Old"), NULL);
919
920       g_object_set (rd.variable_treeview, "headers-visible", TRUE, NULL);
921
922       rd.varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy);
923
924       sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.variable_treeview));
925
926       g_signal_connect (sel, "changed",
927                         G_CALLBACK (on_selection_change), &rd);
928
929       g_signal_connect (rd.change_button, "clicked",
930                         G_CALLBACK (on_change_clicked),  &rd);
931
932 #if 0
933       g_signal_connect (model, "row-inserted",
934                         G_CALLBACK (select_something), &rd);
935 #endif
936     }
937
938   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types);
939
940   /* Set up the Old & New Values subdialog */
941   {
942     rd.string_button = get_widget_assert (builder, "checkbutton1");
943     rd.width_entry   = get_widget_assert (builder, "spinbutton1");
944
945     rd.convert_button           = get_widget_assert (builder, "checkbutton2");
946
947     rd.ov_range_lower_entry = get_widget_assert (builder, "entry5");
948     rd.ov_range_upper_entry  = get_widget_assert (builder, "entry3");
949     rd.ov_low_up_entry       = get_widget_assert (builder, "entry6");
950     rd.ov_high_down_entry    = get_widget_assert (builder, "entry7");
951
952     rd.new_value_entry = get_widget_assert (builder, "entry1");
953     rd.ov_value_entry  = get_widget_assert (builder, "entry2");
954
955     rd.toggle[BUTTON_NEW_VALUE]  = get_widget_assert (builder, "radiobutton1");
956     rd.toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (builder, "radiobutton2");
957     rd.toggle[BUTTON_NEW_COPY]   = get_widget_assert (builder, "radiobutton3");
958     rd.toggle[BUTTON_OLD_VALUE]  = get_widget_assert (builder, "radiobutton4");
959     rd.toggle[BUTTON_OLD_SYSMIS] = get_widget_assert (builder, "radiobutton6");
960     rd.toggle[BUTTON_OLD_MISSING]= get_widget_assert (builder, "radiobutton7");
961     rd.toggle[BUTTON_OLD_RANGE]  = get_widget_assert (builder, "radiobutton8");
962     rd.toggle[BUTTON_OLD_LOW_UP] = get_widget_assert (builder, "radiobutton10");
963     rd.toggle[BUTTON_OLD_HIGH_DOWN] = get_widget_assert (builder, "radiobutton5");
964     rd.toggle[BUTTON_OLD_ELSE]   = get_widget_assert (builder, "radiobutton11");
965
966     rd.new_copy_label = get_widget_assert (builder, "label3");
967     rd.strings_box    = get_widget_assert (builder, "table3");
968
969     rd.old_and_new_dialog =
970       PSPPIRE_DIALOG (get_widget_assert (builder, "old-new-values-dialog"));
971
972     gtk_window_set_transient_for (GTK_WINDOW (rd.old_and_new_dialog),
973                                   GTK_WINDOW (de));
974
975     rd.acr = PSPPIRE_ACR (get_widget_assert (builder, "psppire-acr1"));
976
977     g_signal_connect_swapped (rd.toggle[BUTTON_NEW_VALUE], "toggled",
978                       G_CALLBACK (set_acr), &rd);
979
980     g_signal_connect_swapped (rd.new_value_entry, "changed",
981                       G_CALLBACK (set_acr), &rd);
982
983     {
984       GtkTreeSelection *sel;
985       /* Remove the ACR's default column.  We don't like it */
986       GtkTreeViewColumn *column = gtk_tree_view_get_column (rd.acr->tv, 0);
987       gtk_tree_view_remove_column (rd.acr->tv, column);
988
989
990       column =
991         gtk_tree_view_column_new_with_attributes (_("Old"),
992                                                   gtk_cell_renderer_text_new (),
993                                                   "text", 0,
994                                                   NULL);
995
996       gtk_tree_view_append_column (rd.acr->tv, column);
997
998       column =
999         gtk_tree_view_column_new_with_attributes (_("New"),
1000                                                   gtk_cell_renderer_text_new (),
1001                                                   "text", 1,
1002                                                   NULL);
1003
1004       gtk_tree_view_append_column (rd.acr->tv, column);
1005       g_object_set (rd.acr->tv, "headers-visible", TRUE, NULL);
1006
1007
1008       sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.acr->tv));
1009       g_signal_connect (sel, "changed",
1010                         G_CALLBACK (on_acr_selection_change), &rd);
1011     }
1012
1013
1014     g_signal_connect_swapped (oldandnew, "clicked",
1015                               G_CALLBACK (run_old_and_new_dialog), &rd);
1016
1017
1018     g_signal_connect (rd.toggle[BUTTON_NEW_VALUE], "toggled",
1019                       G_CALLBACK (toggle_sensitivity), rd.new_value_entry);
1020
1021     g_signal_connect (rd.toggle[BUTTON_OLD_VALUE], "toggled",
1022                       G_CALLBACK (toggle_sensitivity), rd.ov_value_entry);
1023
1024     g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
1025                       G_CALLBACK (toggle_sensitivity),
1026                       get_widget_assert (builder, "entry3"));
1027
1028     g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
1029                       G_CALLBACK (toggle_sensitivity),
1030                       get_widget_assert (builder, "entry5"));
1031
1032     g_signal_connect (rd.toggle[BUTTON_OLD_LOW_UP], "toggled",
1033                       G_CALLBACK (toggle_sensitivity), rd.ov_low_up_entry);
1034
1035     g_signal_connect (rd.toggle[BUTTON_OLD_HIGH_DOWN], "toggled",
1036                       G_CALLBACK (toggle_sensitivity), rd.ov_high_down_entry);
1037
1038     g_signal_connect (rd.string_button, "toggled",
1039                       G_CALLBACK (toggle_sensitivity), rd.width_entry);
1040
1041     g_signal_connect (rd.string_button, "toggled",
1042                       G_CALLBACK (on_string_toggled), &rd);
1043
1044     g_signal_connect (rd.convert_button, "toggled",
1045                       G_CALLBACK (on_convert_toggled), &rd);
1046
1047     g_signal_connect_swapped (rd.old_and_new_dialog, "show",
1048                               G_CALLBACK (on_old_new_show), &rd);
1049   }
1050
1051   g_signal_connect (rd.dialog, "refresh", G_CALLBACK (refresh),  &rd);
1052
1053
1054   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (rd.dialog),
1055                                       dialog_state_valid, &rd);
1056
1057   response = psppire_dialog_run (PSPPIRE_DIALOG (rd.dialog));
1058
1059   switch (response)
1060     {
1061     case GTK_RESPONSE_OK:
1062       g_free (execute_syntax_string (de, generate_syntax (&rd)));
1063       break;
1064     case PSPPIRE_RESPONSE_PASTE:
1065       g_free (paste_syntax_to_window (generate_syntax (&rd)));
1066       break;
1067     default:
1068       break;
1069     }
1070
1071   g_hash_table_destroy (rd.varmap);
1072
1073   gtk_list_store_clear (GTK_LIST_STORE (rd.value_map));
1074   g_object_unref (rd.value_map);
1075
1076   g_object_unref (builder);
1077 }
1078
1079 /* Initialise VAL to reflect the current status of RD */
1080 static gboolean
1081 set_old_value (GValue *val, const struct recode_dialog *rd)
1082 {
1083   const gchar *text = NULL;
1084   struct old_value ov;
1085   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1086                                      (rd->toggle [BUTTON_OLD_VALUE])))
1087     {
1088       text = gtk_entry_get_text (GTK_ENTRY (rd->ov_value_entry));
1089       if ( rd->input_var_is_string )
1090         {
1091           ov.type = OV_STRING;
1092           ov.v.s = g_strdup (text);
1093         }
1094       else
1095         {
1096           ov.type = OV_NUMERIC;
1097           ov.v.v = g_strtod (text, 0);
1098         }
1099     }
1100   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1101                                           (rd->toggle [BUTTON_OLD_MISSING])))
1102     {
1103       ov.type = OV_MISSING;
1104     }
1105   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1106                                           (rd->toggle [BUTTON_OLD_SYSMIS])))
1107     {
1108       ov.type = OV_SYSMIS;
1109     }
1110   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1111                                           (rd->toggle [BUTTON_OLD_ELSE])))
1112     {
1113       ov.type = OV_ELSE;
1114     }
1115   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1116                                           (rd->toggle [BUTTON_OLD_RANGE])))
1117     {
1118       const gchar *text;
1119       text = gtk_entry_get_text (GTK_ENTRY (rd->ov_range_lower_entry));
1120
1121       ov.type = OV_RANGE;
1122       ov.v.range[0] = g_strtod (text, 0);
1123
1124       text = gtk_entry_get_text (GTK_ENTRY (rd->ov_range_upper_entry));
1125       ov.v.range[1] = g_strtod (text, 0);
1126     }
1127   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1128                                           (rd->toggle [BUTTON_OLD_LOW_UP])))
1129     {
1130       const gchar *text =
1131         gtk_entry_get_text (GTK_ENTRY (rd->ov_low_up_entry));
1132
1133       ov.type = OV_LOW_UP;
1134       ov.v.range[1] = g_strtod (text, 0);
1135     }
1136   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1137                                           (rd->toggle [BUTTON_OLD_HIGH_DOWN])))
1138     {
1139       const gchar *text =
1140         gtk_entry_get_text (GTK_ENTRY (rd->ov_high_down_entry));
1141
1142       ov.type = OV_HIGH_DOWN;
1143       ov.v.range[0] = g_strtod (text, 0);
1144     }
1145   else
1146     return FALSE;
1147
1148   g_value_init (val, old_value_get_type ());
1149   g_value_set_boxed (val, &ov);
1150
1151   return TRUE;
1152 }
1153
1154
1155 /* Initialse VAL to reflect the current status of RD */
1156 static gboolean
1157 set_new_value (GValue *val, const struct recode_dialog *rd)
1158 {
1159   const gchar *text = NULL;
1160   struct new_value nv;
1161
1162   if ( gtk_toggle_button_get_active
1163        (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE])))
1164     {
1165       text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry));
1166
1167       nv.type = NV_NUMERIC;
1168       if (
1169           (! rd->different && rd->input_var_is_string) ||
1170           ( rd->different &&
1171             gtk_toggle_button_get_active
1172                (GTK_TOGGLE_BUTTON (rd->string_button)))
1173           )
1174         {
1175           nv.type = NV_STRING;
1176         }
1177
1178       if ( nv.type == NV_STRING )
1179         nv.v.s = g_strdup (text);
1180       else
1181         nv.v.v = g_strtod (text, 0);
1182     }
1183   else if ( gtk_toggle_button_get_active
1184             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY])))
1185     {
1186       nv.type = NV_COPY;
1187     }
1188
1189   else if ( gtk_toggle_button_get_active
1190             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS])))
1191     {
1192       nv.type = NV_SYSMIS;
1193     }
1194   else
1195     return FALSE;
1196
1197   g_value_init (val, new_value_get_type ());
1198   g_value_set_boxed (val, &nv);
1199
1200   return TRUE;
1201 }
1202
1203
1204 /* A function to set a value in a column in the ACR */
1205 gboolean
1206 set_value (gint col, GValue  *val, gpointer data)
1207 {
1208   struct recode_dialog *rd = data;
1209
1210   switch ( col )
1211     {
1212     case COL_VALUE_OLD:
1213       set_old_value (val, rd);
1214       break;
1215     case COL_VALUE_NEW:
1216       set_new_value (val, rd);
1217       break;
1218     default:
1219       return FALSE;
1220     }
1221
1222   return TRUE;
1223 }
1224
1225 static void
1226 run_old_and_new_dialog (struct recode_dialog *rd)
1227 {
1228   gint response;
1229   GtkListStore *local_store = clone_list_store (rd->value_map);
1230
1231   psppire_acr_set_model (rd->acr, local_store);
1232   psppire_acr_set_get_value_func (rd->acr, set_value, rd);
1233
1234   gtk_window_set_title (GTK_WINDOW (rd->old_and_new_dialog),
1235                         rd->different
1236                         ? _("Recode into Different Variables: Old and New Values ")
1237                         : _("Recode into Same Variables: Old and New Values")
1238                         );
1239
1240
1241   {
1242     /* Find the type of the first variable (it's invariant that
1243        all variables are of the same type) */
1244     const struct variable *v;
1245     GtkTreeIter iter;
1246     GtkTreeModel *model =
1247       gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
1248
1249     gboolean not_empty = gtk_tree_model_get_iter_first (model, &iter);
1250
1251     g_return_if_fail (not_empty);
1252
1253     gtk_tree_model_get (model, &iter, 0, &v, -1);
1254
1255     rd->input_var_is_string = var_is_alpha (v);
1256
1257     gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_SYSMIS],
1258                               var_is_numeric (v));
1259     gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_RANGE],
1260                               var_is_numeric (v));
1261     gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_LOW_UP],
1262                               var_is_numeric (v));
1263     gtk_widget_set_sensitive (rd->toggle [BUTTON_OLD_HIGH_DOWN],
1264                               var_is_numeric (v));
1265     gtk_widget_set_sensitive (rd->toggle [BUTTON_NEW_SYSMIS],
1266                               var_is_numeric (v));
1267
1268     gtk_widget_set_sensitive (rd->convert_button, var_is_alpha (v));
1269   }
1270
1271
1272   response = psppire_dialog_run (rd->old_and_new_dialog);
1273   psppire_acr_set_model (rd->acr, NULL);
1274
1275
1276   if ( response == PSPPIRE_RESPONSE_CONTINUE )
1277     {
1278       g_object_unref (rd->value_map);
1279       rd->value_map = clone_list_store (local_store);
1280     }
1281   else
1282     g_object_unref (local_store);
1283
1284
1285   psppire_dialog_notify_change (PSPPIRE_DIALOG (rd->dialog));
1286 }
1287
1288
1289 /* Generate a syntax fragment for NV and append it to STR */
1290 static void
1291 new_value_append_syntax (GString *str, const struct new_value *nv)
1292 {
1293   switch (nv->type)
1294     {
1295     case NV_NUMERIC:
1296       g_string_append_printf (str, "%g", nv->v.v);
1297       break;
1298     case NV_STRING:
1299       {
1300         struct string ds = DS_EMPTY_INITIALIZER;
1301         syntax_gen_string (&ds, ss_cstr (nv->v.s));
1302         g_string_append (str, ds_cstr (&ds));
1303         ds_destroy (&ds);
1304       }
1305       break;
1306     case NV_COPY:
1307       g_string_append (str, "COPY");
1308       break;
1309     case NV_SYSMIS:
1310       g_string_append (str, "SYSMIS");
1311       break;
1312     default:
1313       /* Shouldn't ever happen */
1314       g_warning ("Invalid type in new recode value");
1315       g_string_append (str, "???");
1316       break;
1317     }
1318 }
1319
1320
1321 /* Generate a syntax fragment for NV and append it to STR */
1322 static void
1323 old_value_append_syntax (GString *str, const struct old_value *ov)
1324 {
1325   switch (ov->type)
1326     {
1327     case OV_NUMERIC:
1328       g_string_append_printf (str, "%g", ov->v.v);
1329       break;
1330     case OV_STRING:
1331       {
1332         struct string ds = DS_EMPTY_INITIALIZER;
1333         syntax_gen_string (&ds, ss_cstr (ov->v.s));
1334         g_string_append (str, ds_cstr (&ds));
1335         ds_destroy (&ds);
1336       }
1337       break;
1338     case OV_MISSING:
1339       g_string_append (str, "MISSING");
1340       break;
1341     case OV_SYSMIS:
1342       g_string_append (str, "SYSMIS");
1343       break;
1344     case OV_ELSE:
1345       g_string_append (str, "ELSE");
1346       break;
1347     case OV_RANGE:
1348       g_string_append_printf (str, "%g THRU %g",
1349                               ov->v.range[0],
1350                               ov->v.range[1]);
1351       break;
1352     case OV_LOW_UP:
1353       g_string_append_printf (str, "LOWEST THRU %g",
1354                               ov->v.range[1]);
1355       break;
1356     case OV_HIGH_DOWN:
1357       g_string_append_printf (str, "%g THRU HIGHEST",
1358                               ov->v.range[0]);
1359       break;
1360     default:
1361       g_warning ("Invalid type in old recode value");
1362       g_string_append (str, "???");
1363       break;
1364     };
1365 }
1366
1367
1368
1369 static char *
1370 generate_syntax (const struct recode_dialog *rd)
1371 {
1372   gboolean ok;
1373   GtkTreeIter iter;
1374   gchar *text;
1375
1376   GString *str = g_string_sized_new (100);
1377
1378   /* Declare new string variables if applicable */
1379   if ( rd->different &&
1380        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)))
1381     {
1382       GHashTableIter iter;
1383
1384       struct variable *var = NULL;
1385       struct nlp *nlp = NULL;
1386
1387       g_hash_table_iter_init (&iter, rd->varmap);
1388       while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
1389         {
1390           g_string_append (str, "\nSTRING ");
1391           g_string_append (str, nlp->name);
1392           g_string_append_printf (str, " (A%d).",
1393                                   (int)
1394                                   gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry) )
1395                                   );
1396         }
1397     }
1398
1399   g_string_append (str, "\nRECODE ");
1400
1401   psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, str);
1402
1403   g_string_append (str, "\n\t");
1404
1405   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->convert_button)))
1406     {
1407       g_string_append (str, "(CONVERT) ");
1408     }
1409
1410   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
1411                                            &iter);
1412        ok;
1413        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->value_map), &iter))
1414     {
1415       GValue ov_value = {0};
1416       GValue nv_value = {0};
1417       struct old_value *ov;
1418       struct new_value *nv;
1419       gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter,
1420                                 COL_VALUE_OLD, &ov_value);
1421
1422       gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter,
1423                                 COL_VALUE_NEW, &nv_value);
1424
1425       ov = g_value_get_boxed (&ov_value);
1426       nv = g_value_get_boxed (&nv_value);
1427
1428       g_string_append (str, "(");
1429
1430       old_value_append_syntax (str, ov);
1431       g_string_append (str, " = ");
1432       new_value_append_syntax (str, nv);
1433
1434       g_string_append (str, ") ");
1435       g_value_unset (&ov_value);
1436       g_value_unset (&nv_value);
1437     }
1438
1439
1440   if ( rd->different )
1441     {
1442
1443       GtkTreeIter iter;
1444       g_string_append (str, "\n\tINTO ");
1445
1446       for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter);
1447            ok;
1448            ok = psppire_var_view_get_iter_next (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter))
1449           {
1450             struct nlp *nlp = NULL;
1451             const struct variable *var = psppire_var_view_get_variable (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &iter);
1452
1453             nlp = g_hash_table_lookup (rd->varmap, var);
1454             
1455             g_string_append (str, nlp->name);
1456             g_string_append (str, " ");
1457           }
1458     }
1459
1460   g_string_append (str, ".");
1461
1462   /* If applicable, set labels for the new variables. */
1463   if ( rd->different )
1464     {
1465       GHashTableIter iter;
1466
1467       struct variable *var = NULL;
1468       struct nlp *nlp = NULL;
1469
1470       g_hash_table_iter_init (&iter, rd->varmap);
1471       while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
1472         {
1473           if (nlp->label)
1474             g_string_append_printf (str, "\nVARIABLE LABELS %s %s.",
1475                                     nlp->name, nlp->label);
1476         }
1477     }
1478
1479   g_string_append (str, "\nEXECUTE.\n");
1480
1481
1482   text = str->str;
1483
1484   g_string_free (str, FALSE);
1485
1486   return text;
1487 }