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