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