Applied patch #6428
[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
330   /* Indicates that the INTO {new variables} form of the dialog
331      is being used */
332   gboolean different;
333
334   PsppireAcr *acr;
335
336   gboolean input_var_is_string;
337
338   GtkListStore *var_map;
339   GtkWidget *new_name_entry;
340   GtkWidget *new_label_entry;
341   GtkWidget *change_button;
342
343   GtkWidget *string_button;
344   GtkWidget *width_entry;
345 };
346
347
348 static void run_old_and_new_dialog (struct recode_dialog *rd);
349
350 static void
351 refresh (PsppireDialog *dialog, struct recode_dialog *rd)
352 {
353   gtk_widget_set_sensitive (rd->change_button, FALSE);
354   gtk_widget_set_sensitive (rd->new_name_entry, FALSE);
355   gtk_widget_set_sensitive (rd->new_label_entry, FALSE);
356
357
358   if ( rd->different )
359     gtk_list_store_clear (GTK_LIST_STORE (rd->var_map));
360   else
361     {
362       GtkTreeModel *vars =
363         gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
364
365       gtk_list_store_clear (GTK_LIST_STORE (vars));
366     }
367
368   gtk_list_store_clear (GTK_LIST_STORE (rd->value_map));
369 }
370
371 static char * generate_syntax (const struct recode_dialog *rd);
372
373 enum {
374   COL_OLD,
375   COL_NEW_NAME,
376   COL_NEW_LABEL,
377   n_COL_VARS
378 };
379
380 enum {
381   COL_VALUE_OLD,
382   COL_VALUE_NEW,
383   n_COL_VALUES
384 };
385
386 /* Dialog is valid iff at least one variable has been selected,
387    AND the list of mappings is not empty.
388  */
389 static gboolean
390 dialog_state_valid (gpointer data)
391 {
392   GtkTreeIter not_used;
393   struct recode_dialog *rd = data;
394
395   if ( ! rd->value_map )
396     return FALSE;
397
398   if ( ! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
399                                         &not_used) )
400     return FALSE;
401
402   if ( rd->different )
403     {
404       GtkTreeIter iter;
405
406       gboolean ok;
407
408       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
409                                                &iter);
410            ok;
411            ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map),
412                                           &iter))
413         {
414           gchar *name = NULL;
415
416           gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
417                               COL_NEW_NAME, &name, -1);
418
419           if ( name == NULL )
420             return FALSE;
421
422           g_free (name);
423         }
424     }
425   else
426     {
427       GtkTreeModel *vars =
428         gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
429
430       if ( !gtk_tree_model_get_iter_first (vars, &not_used))
431         return FALSE;
432
433     }
434
435   return TRUE;
436 }
437
438 static void
439 on_old_new_show (struct recode_dialog *rd)
440 {
441   gtk_toggle_button_set_active
442     (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_OLD_VALUE]), TRUE);
443
444   g_signal_emit_by_name (rd->toggle[BUTTON_OLD_VALUE], "toggled");
445
446   gtk_toggle_button_set_active
447     (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE);
448
449   g_signal_emit_by_name (rd->toggle[BUTTON_NEW_VALUE], "toggled");
450
451   g_object_set (rd->toggle[BUTTON_NEW_COPY],
452                 "visible", rd->different, NULL);
453
454   g_object_set (rd->new_copy_label,
455                 "visible", rd->different, NULL);
456
457   g_object_set (rd->strings_box,
458                 "visible", rd->different, NULL);
459 }
460
461 /* Sets the sensitivity of TARGET dependent upon the active status
462    of BUTTON */
463 static void
464 toggle_sensitivity (GtkToggleButton *button, GtkWidget *target)
465 {
466   gboolean state = gtk_toggle_button_get_active (button);
467
468   /*  g_print ("%s Setting %p (%s) to %d because of %p\n",
469       __FUNCTION__, target, gtk_widget_get_name (target), state, button); */
470
471   gtk_widget_set_sensitive (target, state);
472 }
473
474 static void recode_dialog (struct data_editor *de, gboolean diff);
475
476
477 /* Pops up the Recode Same version of the dialog box */
478 void
479 recode_same_dialog (GObject *o, gpointer data)
480 {
481   struct data_editor *de = data;
482
483   recode_dialog (de, FALSE);
484 }
485
486 /* Pops up the Recode Different version of the dialog box */
487 void
488 recode_different_dialog (GObject *o, gpointer data)
489 {
490   struct data_editor *de = data;
491
492   recode_dialog (de, TRUE);
493 }
494
495 static void
496 render_new_var_name (GtkTreeViewColumn *tree_column,
497                      GtkCellRenderer *cell,
498                      GtkTreeModel *tree_model,
499                      GtkTreeIter *iter,
500                      gpointer data)
501 {
502   gchar *new_var_name = NULL;
503
504   gtk_tree_model_get (tree_model, iter, COL_NEW_NAME, &new_var_name, -1);
505
506   g_object_set (cell, "text", new_var_name, NULL);
507
508   g_free (new_var_name);
509 }
510
511
512 /* This might need to be changed to something less naive.
513    In particular, what happends with dates, etc?
514  */
515 static gchar *
516 num_to_string (gdouble x)
517 {
518   return g_strdup_printf ("%g", x);
519 }
520
521 /* Callback which gets called when a new row is selected
522    in the acr's variable treeview.
523    We use if to set the togglebuttons and entries to correspond to the
524    selected row.
525 */
526 static void
527 on_acr_selection_change (GtkTreeSelection *selection, gpointer data)
528 {
529   struct recode_dialog *rd = data;
530   GtkTreeModel *model = NULL;
531   GtkTreeIter iter;
532
533   GValue ov_value = {0};
534   GValue nv_value = {0};
535   struct old_value *ov = NULL;
536   struct new_value *nv = NULL;
537
538   if ( ! gtk_tree_selection_get_selected (selection, &model, &iter) )
539     return;
540
541
542   gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter,
543                             COL_VALUE_OLD, &ov_value);
544
545   gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter,
546                             COL_VALUE_NEW, &nv_value);
547
548   ov = g_value_get_boxed (&ov_value);
549   nv = g_value_get_boxed (&nv_value);
550
551   if (nv)
552     {
553       switch (nv->type)
554         {
555         case NV_NUMERIC:
556           {
557             gchar *str = num_to_string (nv->v.v);
558
559             gtk_toggle_button_set_active
560               (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE);
561
562             gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), str);
563             g_free (str);
564           }
565           break;
566         case NV_STRING:
567           gtk_toggle_button_set_active
568             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE]), TRUE);
569
570           gtk_entry_set_text (GTK_ENTRY (rd->new_value_entry), nv->v.s);
571           break;
572         case NV_SYSMIS:
573           gtk_toggle_button_set_active
574             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS]), TRUE);
575
576           break;
577         case NV_COPY:
578           gtk_toggle_button_set_active
579             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY]), TRUE);
580
581           break;
582         default:
583           g_warning ("Invalid new value type");
584           break;
585         }
586
587       g_value_unset (&nv_value);
588     }
589
590   if ( ov )
591     {
592     switch (ov->type)
593       {
594       case OV_STRING:
595           gtk_toggle_button_set_active
596             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_VALUE]), TRUE);
597
598           gtk_entry_set_text (GTK_ENTRY (rd->ov_value_entry), ov->v.s);
599         break;
600
601       case OV_NUMERIC:
602         {
603           gchar *str;
604
605           gtk_toggle_button_set_active
606             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_VALUE]), TRUE);
607
608           str = num_to_string (ov->v.v);
609
610           gtk_entry_set_text (GTK_ENTRY (rd->ov_value_entry), str);
611
612           g_free (str);
613         }
614         break;
615
616       case OV_SYSMIS:
617         gtk_toggle_button_set_active
618           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_SYSMIS]), TRUE);
619         break;
620
621       case OV_MISSING:
622         gtk_toggle_button_set_active
623           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_MISSING]), TRUE);
624         break;
625
626       case OV_RANGE:
627         {
628           gchar *str;
629
630           gtk_toggle_button_set_active
631             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_RANGE]), TRUE);
632
633           str = num_to_string (ov->v.range[0]);
634
635           gtk_entry_set_text (GTK_ENTRY (rd->ov_range_lower_entry), str);
636
637           g_free (str);
638
639
640           str = num_to_string (ov->v.range[1]);
641
642           gtk_entry_set_text (GTK_ENTRY (rd->ov_range_upper_entry), str);
643
644           g_free (str);
645         }
646         break;
647
648       case OV_LOW_UP:
649         {
650           gchar *str = num_to_string (ov->v.range[1]);
651
652           gtk_toggle_button_set_active
653             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_LOW_UP]), TRUE);
654
655           gtk_entry_set_text (GTK_ENTRY (rd->ov_low_up_entry), str);
656
657           g_free (str);
658         }
659         break;
660
661       case OV_HIGH_DOWN:
662         {
663           gchar *str = num_to_string (ov->v.range[0]);
664
665           gtk_toggle_button_set_active
666             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_HIGH_DOWN]), TRUE);
667
668           gtk_entry_set_text (GTK_ENTRY (rd->ov_high_down_entry), str);
669
670           g_free (str);
671         }
672         break;
673
674       case OV_ELSE:
675         gtk_toggle_button_set_active
676           (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_OLD_ELSE]), TRUE);
677         break;
678
679       default:
680         g_warning ("Unknown old value type");
681         break;
682       };
683     g_value_unset (&ov_value);
684     }
685 }
686
687 /* Callback which gets called when a new row is selected
688    in the variable treeview.
689    It sets the name and label entry widgets to reflect the
690    currently selected row.
691  */
692 static void
693 on_selection_change (GtkTreeSelection *selection, gpointer data)
694 {
695   struct recode_dialog *rd = data;
696   GtkTreeModel *model = GTK_TREE_MODEL (rd->var_map);
697
698   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
699
700   if ( rows && !rows->next)
701     {
702       /* Exactly one row is selected */
703
704       gboolean ok;
705       GtkTreeIter iter;
706       gchar *name = NULL;
707       gchar *label = NULL;
708
709       gtk_widget_set_sensitive  (rd->change_button, TRUE);
710       gtk_widget_set_sensitive  (rd->new_name_entry, TRUE);
711       gtk_widget_set_sensitive  (rd->new_label_entry, TRUE);
712
713       ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data);
714
715       gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
716                           COL_NEW_NAME, &name,
717                           COL_NEW_LABEL, &label,
718                           -1);
719
720       gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), name ? name : "");
721       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), label ? label : "");
722
723       g_free (name);
724       g_free (label);
725     }
726   else
727     {
728       gtk_widget_set_sensitive  (rd->change_button, FALSE);
729       gtk_widget_set_sensitive  (rd->new_name_entry, FALSE);
730       gtk_widget_set_sensitive  (rd->new_label_entry, FALSE);
731
732       gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
733       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
734     }
735
736   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
737   g_list_free (rows);
738 }
739
740 static void
741 on_string_toggled (GtkToggleButton *b, struct recode_dialog *rd)
742 {
743   gboolean active;
744   if (! rd->input_var_is_string )
745     return ;
746
747   active = gtk_toggle_button_get_active (b);
748   gtk_widget_set_sensitive (rd->convert_button, !active);
749 }
750
751
752 static void
753 on_convert_toggled (GtkToggleButton *b, struct recode_dialog *rd)
754 {
755   gboolean active;
756
757   g_return_if_fail (rd->input_var_is_string);
758
759   active = gtk_toggle_button_get_active (b);
760   gtk_widget_set_sensitive (rd->string_button, !active);
761 }
762
763
764 static void
765 on_change_clicked (GObject *obj, gpointer data)
766 {
767   struct recode_dialog *rd = data;
768   GtkTreeModel *model = GTK_TREE_MODEL (rd->var_map);
769   GtkTreeIter iter;
770   GtkTreeSelection *selection =
771     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
772
773   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
774
775   const gchar *dest_var_name =
776     gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry));
777
778   const gchar *dest_var_label =
779     gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry));
780
781   if ( NULL == rows )
782     return;
783
784   gtk_tree_model_get_iter (model, &iter, rows->data);
785
786   gtk_list_store_set (rd->var_map, &iter,
787                       COL_NEW_NAME, dest_var_name,
788                       COL_NEW_LABEL, dest_var_label,
789                       -1);
790
791   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
792   g_list_free (rows);
793 }
794
795
796 /* If there's nothing selected in the variable treeview,
797    then automatically select the first item */
798 static void
799 select_something (GtkTreeModel *treemodel,
800                   GtkTreePath  *arg1,
801                   GtkTreeIter  *arg2,
802                   gpointer      data)
803 {
804   struct recode_dialog *rd = data;
805   GtkTreeSelection *sel;
806
807   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
808
809   if ( gtk_tree_selection_count_selected_rows (sel) < 1)
810     {
811       GtkTreeIter iter;
812
813       gtk_tree_model_get_iter_first   (treemodel, &iter);
814
815       gtk_tree_selection_select_iter  (sel, &iter);
816     }
817 }
818
819
820 /* Callback for the new_value_entry and new_value_togglebutton widgets.
821    It's used to enable/disable the acr. */
822 static void
823 set_acr (struct recode_dialog *rd)
824 {
825   const gchar *text;
826
827   if ( !gtk_toggle_button_get_active
828        (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE])))
829     {
830       psppire_acr_set_enabled (rd->acr, TRUE);
831       return;
832     }
833
834   text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry));
835
836   psppire_acr_set_enabled (rd->acr, !g_str_equal (text, ""));
837 }
838
839 static void
840 recode_dialog (struct data_editor *de, gboolean diff)
841 {
842   gint response;
843
844   struct recode_dialog rd;
845
846   GladeXML *xml = XML_NEW ("recode.glade");
847
848
849   GtkWidget *selector = get_widget_assert (xml, "psppire-selector1");
850
851   GtkWidget *oldandnew = get_widget_assert (xml, "button1");
852
853
854   GtkWidget *output_variable_box = get_widget_assert (xml,"frame4");
855
856
857   PsppireVarStore *vs = NULL;
858
859   g_object_get (de->data_editor, "var-store", &vs, NULL);
860
861   rd.change_button = get_widget_assert (xml, "change-button");
862
863   rd.dialog = get_widget_assert   (xml, "recode-dialog");
864   rd.dict_treeview = get_widget_assert (xml, "treeview1");
865   rd.variable_treeview =   get_widget_assert (xml, "treeview2");
866   rd.new_name_entry = get_widget_assert (xml, "dest-name-entry");
867   rd.new_label_entry = get_widget_assert (xml, "dest-label-entry");
868
869   rd.dict = vs->dict;
870
871   rd.value_map = gtk_list_store_new (2,
872                                      old_value_get_type (),
873                                      new_value_get_type ()
874                                      );
875
876   g_object_set (output_variable_box, "visible", diff, NULL);
877
878   if ( diff )
879     gtk_window_set_title (GTK_WINDOW (rd.dialog),
880                           _("Recode into Different Variables"));
881   else
882     gtk_window_set_title (GTK_WINDOW (rd.dialog),
883                           _("Recode into Same Variables"));
884
885   rd.different = diff;
886
887   gtk_window_set_transient_for (GTK_WINDOW (rd.dialog), de->parent.window);
888
889
890   attach_dictionary_to_treeview (GTK_TREE_VIEW (rd.dict_treeview),
891                                  vs->dict,
892                                  GTK_SELECTION_MULTIPLE, NULL);
893
894
895   if ( ! rd.different )
896     {
897       set_dest_model (GTK_TREE_VIEW (rd.variable_treeview), vs->dict);
898     }
899   else
900     {
901       GtkTreeSelection *sel;
902       GtkTreeViewColumn *col;
903       GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
904
905       rd.var_map = gtk_list_store_new (n_COL_VARS, G_TYPE_INT,
906                                                     G_TYPE_STRING,
907                                                     G_TYPE_STRING);
908
909
910
911       gtk_tree_view_set_model (GTK_TREE_VIEW (rd.variable_treeview),
912                                GTK_TREE_MODEL (rd.var_map));
913
914       col = gtk_tree_view_column_new_with_attributes (_("Old"),
915                                                   renderer,
916                                                   "text", NULL,
917                                                   NULL);
918
919       gtk_tree_view_column_set_cell_data_func (col, renderer,
920                                                cell_var_name,
921                                                vs->dict, 0);
922
923
924       gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
925
926
927       renderer = gtk_cell_renderer_text_new ();
928
929       col = gtk_tree_view_column_new_with_attributes (_("New"),
930                                                   renderer,
931                                                   "text", NULL,
932                                                   NULL);
933
934       gtk_tree_view_column_set_cell_data_func (col, renderer,
935                                                render_new_var_name,
936                                                NULL, NULL);
937
938
939       gtk_tree_view_append_column (GTK_TREE_VIEW (rd.variable_treeview), col);
940
941       g_object_set (rd.variable_treeview, "headers-visible", TRUE, NULL);
942
943       g_signal_connect (rd.change_button, "clicked",
944                         G_CALLBACK (on_change_clicked),  &rd);
945
946       sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.variable_treeview));
947       g_signal_connect (sel, "changed",
948                         G_CALLBACK (on_selection_change), &rd);
949
950       g_signal_connect (rd.var_map, "row-inserted",
951                         G_CALLBACK (select_something), &rd);
952     }
953
954
955
956   psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
957                                  rd.dict_treeview,
958                                  rd.variable_treeview,
959                                  insert_source_row_into_tree_view,
960                                  NULL,
961                                  NULL);
962
963   psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), homogeneous_types);
964
965   /* Set up the Old & New Values subdialog */
966   {
967     rd.string_button = get_widget_assert (xml, "checkbutton1");
968     rd.width_entry   = get_widget_assert (xml, "spinbutton1");
969
970     rd.convert_button           = get_widget_assert (xml, "checkbutton2");
971
972     rd.ov_range_lower_entry = get_widget_assert (xml, "entry5");
973     rd.ov_range_upper_entry  = get_widget_assert (xml, "entry3");
974     rd.ov_low_up_entry       = get_widget_assert (xml, "entry6");
975     rd.ov_high_down_entry    = get_widget_assert (xml, "entry7");
976
977     rd.new_value_entry = get_widget_assert (xml, "entry1");
978     rd.ov_value_entry  = get_widget_assert (xml, "entry2");
979
980     rd.toggle[BUTTON_NEW_VALUE]  = get_widget_assert (xml, "radiobutton1");
981     rd.toggle[BUTTON_NEW_SYSMIS] = get_widget_assert (xml, "radiobutton2");
982     rd.toggle[BUTTON_NEW_COPY]   = get_widget_assert (xml, "radiobutton3");
983     rd.toggle[BUTTON_OLD_VALUE]  = get_widget_assert (xml, "radiobutton4");
984     rd.toggle[BUTTON_OLD_SYSMIS] = get_widget_assert (xml, "radiobutton6");
985     rd.toggle[BUTTON_OLD_MISSING]= get_widget_assert (xml, "radiobutton7");
986     rd.toggle[BUTTON_OLD_RANGE]  = get_widget_assert (xml, "radiobutton8");
987     rd.toggle[BUTTON_OLD_LOW_UP] = get_widget_assert (xml, "radiobutton10");
988     rd.toggle[BUTTON_OLD_HIGH_DOWN] = get_widget_assert (xml, "radiobutton5");
989     rd.toggle[BUTTON_OLD_ELSE]   = get_widget_assert (xml, "radiobutton11");
990
991     rd.new_copy_label = get_widget_assert (xml, "label3");
992     rd.strings_box    = get_widget_assert (xml, "table3");
993
994     rd.old_and_new_dialog =
995       PSPPIRE_DIALOG (get_widget_assert (xml, "old-new-values-dialog"));
996
997     gtk_window_set_transient_for (GTK_WINDOW (rd.old_and_new_dialog),
998                                   de->parent.window);
999
1000     rd.acr = PSPPIRE_ACR (get_widget_assert (xml, "psppire-acr1"));
1001
1002     g_signal_connect_swapped (rd.toggle[BUTTON_NEW_VALUE], "toggled",
1003                       G_CALLBACK (set_acr), &rd);
1004
1005     g_signal_connect_swapped (rd.new_value_entry, "changed",
1006                       G_CALLBACK (set_acr), &rd);
1007
1008     {
1009       GtkTreeSelection *sel;
1010       /* Remove the ACR's default column.  We don't like it */
1011       GtkTreeViewColumn *column = gtk_tree_view_get_column (rd.acr->tv, 0);
1012       gtk_tree_view_remove_column (rd.acr->tv, column);
1013
1014
1015       column =
1016         gtk_tree_view_column_new_with_attributes (_("Old"),
1017                                                   gtk_cell_renderer_text_new (),
1018                                                   "text", 0,
1019                                                   NULL);
1020
1021       gtk_tree_view_append_column (rd.acr->tv, column);
1022
1023       column =
1024         gtk_tree_view_column_new_with_attributes (_("New"),
1025                                                   gtk_cell_renderer_text_new (),
1026                                                   "text", 1,
1027                                                   NULL);
1028
1029       gtk_tree_view_append_column (rd.acr->tv, column);
1030       g_object_set (rd.acr->tv, "headers-visible", TRUE, NULL);
1031
1032
1033       sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (rd.acr->tv));
1034       g_signal_connect (sel, "changed",
1035                         G_CALLBACK (on_acr_selection_change), &rd);
1036     }
1037
1038
1039     g_signal_connect_swapped (oldandnew, "clicked",
1040                               G_CALLBACK (run_old_and_new_dialog), &rd);
1041
1042
1043     g_signal_connect (rd.toggle[BUTTON_NEW_VALUE], "toggled",
1044                       G_CALLBACK (toggle_sensitivity), rd.new_value_entry);
1045
1046     g_signal_connect (rd.toggle[BUTTON_OLD_VALUE], "toggled",
1047                       G_CALLBACK (toggle_sensitivity), rd.ov_value_entry);
1048
1049     g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
1050                       G_CALLBACK (toggle_sensitivity),
1051                       get_widget_assert (xml, "entry3"));
1052
1053     g_signal_connect (rd.toggle[BUTTON_OLD_RANGE], "toggled",
1054                       G_CALLBACK (toggle_sensitivity),
1055                       get_widget_assert (xml, "entry5"));
1056
1057     g_signal_connect (rd.toggle[BUTTON_OLD_LOW_UP], "toggled",
1058                       G_CALLBACK (toggle_sensitivity), rd.ov_low_up_entry);
1059
1060     g_signal_connect (rd.toggle[BUTTON_OLD_HIGH_DOWN], "toggled",
1061                       G_CALLBACK (toggle_sensitivity), rd.ov_high_down_entry);
1062
1063     g_signal_connect (rd.string_button, "toggled",
1064                       G_CALLBACK (toggle_sensitivity), rd.width_entry);
1065
1066     g_signal_connect (rd.string_button, "toggled",
1067                       G_CALLBACK (on_string_toggled), &rd);
1068
1069     g_signal_connect (rd.convert_button, "toggled",
1070                       G_CALLBACK (on_convert_toggled), &rd);
1071
1072     g_signal_connect_swapped (rd.old_and_new_dialog, "show",
1073                               G_CALLBACK (on_old_new_show), &rd);
1074   }
1075
1076   g_signal_connect (rd.dialog, "refresh", G_CALLBACK (refresh),  &rd);
1077
1078
1079   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (rd.dialog),
1080                                       dialog_state_valid, &rd);
1081
1082   response = psppire_dialog_run (PSPPIRE_DIALOG (rd.dialog));
1083
1084   switch (response)
1085     {
1086     case GTK_RESPONSE_OK:
1087       {
1088         gchar *syntax = generate_syntax (&rd);
1089         struct getl_interface *sss = create_syntax_string_source (syntax);
1090         execute_syntax (sss);
1091
1092         g_free (syntax);
1093       }
1094       break;
1095     case PSPPIRE_RESPONSE_PASTE:
1096       {
1097         gchar *syntax = generate_syntax (&rd);
1098
1099         struct syntax_editor *se =
1100           (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
1101
1102         gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
1103
1104         g_free (syntax);
1105       }
1106       break;
1107     default:
1108       break;
1109     }
1110
1111
1112   gtk_list_store_clear (GTK_LIST_STORE (rd.value_map));
1113   g_object_unref (rd.value_map);
1114
1115   g_object_unref (xml);
1116 }
1117
1118 /* Initialise VAL to reflect the current status of RD */
1119 static gboolean
1120 set_old_value (GValue *val, const struct recode_dialog *rd)
1121 {
1122   const gchar *text = NULL;
1123   struct old_value ov;
1124   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1125                                      (rd->toggle [BUTTON_OLD_VALUE])))
1126     {
1127       text = gtk_entry_get_text (GTK_ENTRY (rd->ov_value_entry));
1128       if ( rd->input_var_is_string )
1129         {
1130           ov.type = OV_STRING;
1131           ov.v.s = g_strdup (text);
1132         }
1133       else
1134         {
1135           ov.type = OV_NUMERIC;
1136           ov.v.v = g_strtod (text, 0);
1137         }
1138     }
1139   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1140                                           (rd->toggle [BUTTON_OLD_MISSING])))
1141     {
1142       ov.type = OV_MISSING;
1143     }
1144   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1145                                           (rd->toggle [BUTTON_OLD_SYSMIS])))
1146     {
1147       ov.type = OV_SYSMIS;
1148     }
1149   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1150                                           (rd->toggle [BUTTON_OLD_ELSE])))
1151     {
1152       ov.type = OV_ELSE;
1153     }
1154   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1155                                           (rd->toggle [BUTTON_OLD_RANGE])))
1156     {
1157       const gchar *text;
1158       text = gtk_entry_get_text (GTK_ENTRY (rd->ov_range_lower_entry));
1159
1160       ov.type = OV_RANGE;
1161       ov.v.range[0] = g_strtod (text, 0);
1162
1163       text = gtk_entry_get_text (GTK_ENTRY (rd->ov_range_upper_entry));
1164       ov.v.range[1] = g_strtod (text, 0);
1165     }
1166   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1167                                           (rd->toggle [BUTTON_OLD_LOW_UP])))
1168     {
1169       const gchar *text =
1170         gtk_entry_get_text (GTK_ENTRY (rd->ov_low_up_entry));
1171
1172       ov.type = OV_LOW_UP;
1173       ov.v.range[1] = g_strtod (text, 0);
1174     }
1175   else if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
1176                                           (rd->toggle [BUTTON_OLD_HIGH_DOWN])))
1177     {
1178       const gchar *text =
1179         gtk_entry_get_text (GTK_ENTRY (rd->ov_high_down_entry));
1180
1181       ov.type = OV_HIGH_DOWN;
1182       ov.v.range[0] = g_strtod (text, 0);
1183     }
1184   else
1185     return FALSE;
1186
1187   g_value_init (val, old_value_get_type ());
1188   g_value_set_boxed (val, &ov);
1189
1190   return TRUE;
1191 }
1192
1193
1194 /* Initialse VAL to reflect the current status of RD */
1195 static gboolean
1196 set_new_value (GValue *val, const struct recode_dialog *rd)
1197 {
1198   const gchar *text = NULL;
1199   struct new_value nv;
1200
1201   if ( gtk_toggle_button_get_active
1202        (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_VALUE])))
1203     {
1204       text = gtk_entry_get_text (GTK_ENTRY (rd->new_value_entry));
1205
1206       nv.type = NV_NUMERIC;
1207       if (
1208           (! rd->different && rd->input_var_is_string) ||
1209           ( rd->different &&
1210             gtk_toggle_button_get_active
1211                (GTK_TOGGLE_BUTTON (rd->string_button)))
1212           )
1213         {
1214           nv.type = NV_STRING;
1215         }
1216
1217       if ( nv.type == NV_STRING )
1218         nv.v.s = g_strdup (text);
1219       else
1220         nv.v.v = g_strtod (text, 0);
1221     }
1222   else if ( gtk_toggle_button_get_active
1223             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_COPY])))
1224     {
1225       nv.type = NV_COPY;
1226     }
1227
1228   else if ( gtk_toggle_button_get_active
1229             (GTK_TOGGLE_BUTTON (rd->toggle [BUTTON_NEW_SYSMIS])))
1230     {
1231       nv.type = NV_SYSMIS;
1232     }
1233   else
1234     return FALSE;
1235
1236   g_value_init (val, new_value_get_type ());
1237   g_value_set_boxed (val, &nv);
1238
1239   return TRUE;
1240 }
1241
1242
1243 /* A function to set a value in a column in the ACR */
1244 gboolean
1245 set_value (gint col, GValue  *val, gpointer data)
1246 {
1247   struct recode_dialog *rd = data;
1248
1249   switch ( col )
1250     {
1251     case COL_VALUE_OLD:
1252       set_old_value (val, rd);
1253       break;
1254     case COL_VALUE_NEW:
1255       set_new_value (val, rd);
1256       break;
1257     default:
1258       return FALSE;
1259     }
1260
1261   return TRUE;
1262 }
1263
1264 static void
1265 run_old_and_new_dialog (struct recode_dialog *rd)
1266 {
1267   gint response;
1268   GtkListStore *local_store = clone_list_store (rd->value_map);
1269
1270   psppire_acr_set_model (rd->acr, 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     {
1320       g_object_unref (rd->value_map);
1321       rd->value_map = clone_list_store (local_store);
1322     }
1323   else
1324     g_object_unref (local_store);
1325
1326
1327   psppire_dialog_notify_change (PSPPIRE_DIALOG (rd->dialog));
1328 }
1329
1330
1331 /* Generate a syntax fragment for NV and append it to STR */
1332 static void
1333 new_value_append_syntax (GString *str, const struct new_value *nv)
1334 {
1335   switch (nv->type)
1336     {
1337     case NV_NUMERIC:
1338       g_string_append_printf (str, "%g", nv->v.v);
1339       break;
1340     case NV_STRING:
1341       {
1342         struct string ds;
1343         ds_init_cstr (&ds, nv->v.s);
1344         gen_quoted_string (&ds);
1345         g_string_append (str, ds_cstr (&ds));
1346         ds_destroy (&ds);
1347       }
1348       break;
1349     case NV_COPY:
1350       g_string_append (str, "COPY");
1351       break;
1352     case NV_SYSMIS:
1353       g_string_append (str, "SYSMIS");
1354       break;
1355     default:
1356       /* Shouldn't ever happen */
1357       g_warning ("Invalid type in new recode value");
1358       g_string_append (str, "???");
1359       break;
1360     }
1361 }
1362
1363
1364 /* Generate a syntax fragment for NV and append it to STR */
1365 static void
1366 old_value_append_syntax (GString *str, const struct old_value *ov)
1367 {
1368   switch (ov->type)
1369     {
1370     case OV_NUMERIC:
1371       g_string_append_printf (str, "%g", ov->v.v);
1372       break;
1373     case OV_STRING:
1374       {
1375         struct string ds;
1376         ds_init_cstr (&ds, ov->v.s);
1377         gen_quoted_string (&ds);
1378         g_string_append (str, ds_cstr (&ds));
1379         ds_destroy (&ds);
1380       }
1381       break;
1382     case OV_MISSING:
1383       g_string_append (str, "MISSING");
1384       break;
1385     case OV_SYSMIS:
1386       g_string_append (str, "SYSMIS");
1387       break;
1388     case OV_ELSE:
1389       g_string_append (str, "ELSE");
1390       break;
1391     case OV_RANGE:
1392       g_string_append_printf (str, "%g THRU %g",
1393                               ov->v.range[0],
1394                               ov->v.range[1]);
1395       break;
1396     case OV_LOW_UP:
1397       g_string_append_printf (str, "LOWEST THRU %g",
1398                               ov->v.range[1]);
1399       break;
1400     case OV_HIGH_DOWN:
1401       g_string_append_printf (str, "%g THRU HIGHEST",
1402                               ov->v.range[0]);
1403       break;
1404     default:
1405       g_warning ("Invalid type in old recode value");
1406       g_string_append (str, "???");
1407       break;
1408     };
1409 }
1410
1411
1412
1413 static char *
1414 generate_syntax (const struct recode_dialog *rd)
1415 {
1416   gboolean ok;
1417   GtkTreeIter iter;
1418   gchar *text;
1419
1420   GString *str = g_string_sized_new (100);
1421
1422   /* Declare new string variables if applicable */
1423   if ( rd->different &&
1424        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)))
1425     {
1426       GtkTreeIter iter;
1427
1428
1429       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
1430                                                &iter);
1431            ok;
1432            ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
1433         {
1434           gchar *name = NULL;
1435
1436           gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
1437                               COL_NEW_NAME, &name, -1);
1438
1439           g_string_append (str, "\nSTRING ");
1440           g_string_append (str, name);
1441           g_string_append_printf (str, " (A%d).",
1442                                   (int)
1443                                   gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry) )
1444                                   );
1445
1446           g_free (name);
1447         }
1448     }
1449
1450   g_string_append (str, "\nRECODE ");
1451
1452   append_variable_names (str, rd->dict, GTK_TREE_VIEW (rd->variable_treeview), 0);
1453
1454   g_string_append (str, "\n\t");
1455
1456   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->convert_button)))
1457     {
1458       g_string_append (str, "(CONVERT) ");
1459     }
1460
1461   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
1462                                            &iter);
1463        ok;
1464        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->value_map), &iter))
1465     {
1466       GValue ov_value = {0};
1467       GValue nv_value = {0};
1468       struct old_value *ov;
1469       struct new_value *nv;
1470       gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter,
1471                                 COL_VALUE_OLD, &ov_value);
1472
1473       gtk_tree_model_get_value (GTK_TREE_MODEL (rd->value_map), &iter,
1474                                 COL_VALUE_NEW, &nv_value);
1475
1476       ov = g_value_get_boxed (&ov_value);
1477       nv = g_value_get_boxed (&nv_value);
1478
1479       g_string_append (str, "(");
1480
1481       old_value_append_syntax (str, ov);
1482       g_string_append (str, " = ");
1483       new_value_append_syntax (str, nv);
1484
1485       g_string_append (str, ") ");
1486       g_value_unset (&ov_value);
1487       g_value_unset (&nv_value);
1488     }
1489
1490
1491   if ( rd->different )
1492     {
1493       GtkTreeIter iter;
1494       g_string_append (str, "\n\tINTO ");
1495
1496       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
1497                                                &iter);
1498            ok;
1499            ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
1500         {
1501           gchar *name = NULL;
1502
1503           gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
1504                               COL_NEW_NAME, &name, -1);
1505
1506           g_string_append (str, name);
1507           g_string_append (str, " ");
1508
1509           g_free (name);
1510         }
1511     }
1512
1513   g_string_append (str, ".");
1514
1515
1516   /* If applicable, set labels for the new variables. */
1517   if ( rd->different )
1518     {
1519       GtkTreeIter iter;
1520
1521       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->var_map),
1522                                                &iter);
1523            ok;
1524            ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (rd->var_map), &iter))
1525         {
1526           struct string ls;
1527           gchar *label = NULL;
1528           gchar *name = NULL;
1529
1530           gtk_tree_model_get (GTK_TREE_MODEL (rd->var_map), &iter,
1531                               COL_NEW_NAME, &name,
1532                               COL_NEW_LABEL, &label, -1);
1533
1534           if ( 0 == strcmp (label, "") )
1535             {
1536               g_free (name);
1537               g_free (label);
1538               continue;
1539             }
1540
1541           ds_init_cstr (&ls, label);
1542           g_free (label);
1543           gen_quoted_string (&ls);
1544
1545           g_string_append_printf (str, "\nVARIABLE LABELS %s %s.",
1546                                   name, ds_cstr (&ls));
1547
1548           g_free (name);
1549           ds_destroy (&ls);
1550         }
1551     }
1552
1553
1554   g_string_append (str, "\nEXECUTE.\n");
1555
1556
1557   text = str->str;
1558
1559   g_string_free (str, FALSE);
1560
1561   return text;
1562 }