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