251e5999e03b0079dfef7fdfaa082d06abcfca75
[pspp] / src / ui / gui / psppire-dialog-action-recode-different.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009, 2010, 2011, 2012, 2014, 2016  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 #include <config.h>
18
19 #include "psppire-var-view.h"
20
21 #include "psppire-dialog-action-recode-different.h"
22 #include "builder-wrapper.h"
23 #include <ui/gui/dialog-common.h>
24
25 #include "psppire-acr.h"
26
27 #include "psppire-selector.h"
28 #include "psppire-val-chooser.h"
29
30 #include "helper.h"
31 #include <ui/syntax-gen.h>
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35 #define N_(msgid) msgid
36
37
38
39 static gboolean
40 difx_variable_treeview_is_populated (PsppireDialogActionRecode *rd)
41 {
42   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
43   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
44
45   if (g_hash_table_size (rdd->varmap) != gtk_tree_model_iter_n_children (model, NULL) )
46     return FALSE;
47
48   return TRUE;
49 }
50
51
52 /* Dialog is valid iff at least one variable has been selected,
53    AND the list of mappings is not empty.
54 */
55 static gboolean
56 dialog_state_valid (gpointer data)
57 {
58   PsppireDialogActionRecode *rd = data;
59   GtkTreeIter not_used;
60
61   if ( ! rd->value_map )
62     return FALSE;
63
64   if ( ! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
65                                         &not_used) )
66     return FALSE;
67
68
69   return difx_variable_treeview_is_populated (rd);
70 }
71
72
73 \f
74
75 static void
76 psppire_dialog_action_recode_different_class_init (PsppireDialogActionRecodeDifferentClass *class);
77
78 G_DEFINE_TYPE (PsppireDialogActionRecodeDifferent, psppire_dialog_action_recode_different, PSPPIRE_TYPE_DIALOG_ACTION_RECODE);
79
80 static void
81 refresh (PsppireDialogAction *act)
82 {
83   PsppireDialogActionRecode *rd = PSPPIRE_DIALOG_ACTION_RECODE (act);
84   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
85
86   psppire_dialog_action_recode_refresh (act);
87
88   if (rdd->varmap)
89     g_hash_table_remove_all (rdd->varmap);
90 }
91
92
93 static void
94 on_old_new_show (PsppireDialogActionRecode *rd)
95 {
96   gtk_toggle_button_set_active
97     (GTK_TOGGLE_BUTTON (rd->toggle[BUTTON_NEW_VALUE]), TRUE);
98
99   g_signal_emit_by_name (rd->toggle[BUTTON_NEW_VALUE], "toggled");
100
101   gtk_widget_show (rd->toggle[BUTTON_NEW_COPY]);
102   gtk_widget_show (rd->new_copy_label);
103   gtk_widget_show (rd->strings_box);
104 }
105
106 \f
107
108 /* Name-Label pair */
109 struct nlp
110 {
111   char *name;
112   char *label;
113 };
114
115
116 static struct nlp *
117 nlp_create (const char *name, const char *label)
118 {
119   struct nlp *nlp = xmalloc (sizeof *nlp);
120
121   nlp->name = g_strdup (name);
122
123   nlp->label = NULL;
124
125   if ( 0 != strcmp ("", label))
126     nlp->label = g_strdup (label);
127
128   return nlp;
129 }
130
131 static void
132 nlp_destroy (gpointer data)
133 {
134   struct nlp *nlp = data ;
135   if ( ! nlp )
136     return;
137
138   g_free (nlp->name);
139   g_free (nlp->label);
140   g_free (nlp);
141 }
142
143 \f
144
145 static void
146 render_new_var_name (GtkTreeViewColumn *tree_column,
147                      GtkCellRenderer *cell,
148                      GtkTreeModel *tree_model,
149                      GtkTreeIter *iter,
150                      gpointer data)
151 {
152   struct nlp *nlp = NULL;
153   PsppireDialogActionRecode *rd = data;
154   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
155
156   struct variable *var = NULL;
157
158   gtk_tree_model_get (tree_model, iter,
159                       0, &var,
160                       -1);
161
162   nlp = g_hash_table_lookup (rdd->varmap, var);
163
164   if ( nlp )
165     g_object_set (cell, "text", nlp->name, NULL);
166   else
167     g_object_set (cell, "text", "", NULL);
168 }
169
170 static void
171 on_change_clicked (GObject *obj, gpointer data)
172 {
173   PsppireDialogActionRecode *rd = data;
174   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
175
176   struct variable *var = NULL;
177   struct nlp *nlp;
178
179   GtkTreeModel *model =  gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
180
181   GtkTreeIter iter;
182   GtkTreeSelection *selection =
183     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
184
185   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
186
187   const gchar *dest_var_name =
188     gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry));
189
190   const gchar *dest_var_label =
191     gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry));
192
193   if ( NULL == rows || rows->next != NULL)
194     goto finish;
195
196   gtk_tree_model_get_iter (model, &iter, rows->data);
197
198   gtk_tree_model_get (model, &iter, 0, &var, -1);
199
200   g_hash_table_remove (rdd->varmap, var);
201
202   nlp = nlp_create (dest_var_name, dest_var_label);
203
204   g_hash_table_insert (rdd->varmap, var, nlp);
205
206   gtk_tree_model_row_changed (model, rows->data, &iter);
207
208  finish:
209   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
210   g_list_free (rows);
211 }
212
213
214
215 /* Callback which gets called when a new row is selected
216    in the variable treeview.
217    It sets the name and label entry widgets to reflect the
218    currently selected row.
219 */
220 static void
221 on_selection_change (GtkTreeSelection *selection, gpointer data)
222 {
223   PsppireDialogActionRecode *rd = data;
224   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
225
226   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
227
228   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
229
230   if ( rows && !rows->next)
231     {
232       /* Exactly one row is selected */
233       struct nlp *nlp;
234       struct variable *var;
235       gboolean ok;
236       GtkTreeIter iter;
237
238       gtk_widget_set_sensitive  (rd->change_button, TRUE);
239       gtk_widget_set_sensitive  (rd->new_name_entry, TRUE);
240       gtk_widget_set_sensitive  (rd->new_label_entry, TRUE);
241
242       ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data);
243       g_return_if_fail (ok);
244
245       gtk_tree_model_get (model, &iter,
246                           0, &var,
247                           -1);
248
249       nlp = g_hash_table_lookup (rdd->varmap, var);
250
251       if (nlp)
252         {
253           gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), nlp->name ? nlp->name : "");
254           gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), nlp->label ? nlp->label : "");
255         }
256       else
257         {
258           gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
259           gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
260         }
261     }
262   else
263     {
264       gtk_widget_set_sensitive  (rd->change_button, FALSE);
265       gtk_widget_set_sensitive  (rd->new_name_entry, FALSE);
266       gtk_widget_set_sensitive  (rd->new_label_entry, FALSE);
267
268       gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
269       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
270     }
271
272
273   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
274   g_list_free (rows);
275 }
276
277
278
279
280 static void
281 populate_treeview (PsppireDialogActionRecode *act)
282 {
283   GtkTreeSelection *sel;
284   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (act);
285   GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
286   GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("New"),
287                                                                      renderer,
288                                                                      "text", NULL,
289                                                                      NULL);
290
291   gtk_tree_view_column_set_cell_data_func (col, renderer,
292                                            render_new_var_name,
293                                            act, NULL);
294
295   gtk_tree_view_append_column (GTK_TREE_VIEW (act->variable_treeview), col);
296
297   col = gtk_tree_view_get_column (GTK_TREE_VIEW (act->variable_treeview), 0);
298
299   g_object_set (col, "title", _("Old"), NULL);
300
301   g_object_set (act->variable_treeview, "headers-visible", TRUE, NULL);
302
303   rdd->varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy);
304
305   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (act->variable_treeview));
306
307   g_signal_connect (sel, "changed",
308                     G_CALLBACK (on_selection_change), act);
309
310   g_signal_connect (act->change_button, "clicked",
311                     G_CALLBACK (on_change_clicked),  act);
312 }
313
314
315 static GtkBuilder *
316 psppire_dialog_action_recode_different_activate (PsppireDialogAction *a, GVariant *param)
317 {
318   PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (a);
319   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
320
321   GtkBuilder *xml = psppire_dialog_action_recode_pre_activate (act,
322                                                                populate_treeview);
323
324   gtk_window_set_title (GTK_WINDOW (pda->dialog),
325                         _("Recode into Different Variables"));
326
327   gtk_window_set_title (GTK_WINDOW (act->old_and_new_dialog),
328                         _("Recode into Different Variables: Old and New Values "));
329
330   gtk_widget_show (act->output_variable_box);
331
332   g_signal_connect_swapped (act->old_and_new_dialog, "show",
333                             G_CALLBACK (on_old_new_show), act);
334
335   psppire_dialog_action_set_refresh (pda, refresh);
336
337   psppire_dialog_action_set_valid_predicate (pda,
338                                              dialog_state_valid);
339   return xml;
340 }
341
342 static void
343 append_into_clause (const PsppireDialogActionRecode *rd, struct string *dds)
344 {
345   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
346
347   /* If applicable set the INTO clause which determines into which variables the new values go */
348   GtkTreeIter iter;
349   ds_put_cstr (dds, "\n\tINTO ");
350   gboolean ok;
351
352   for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter);
353        ok;
354        ok = psppire_var_view_get_iter_next (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter))
355     {
356       struct nlp *nlp = NULL;
357       const struct variable *var = psppire_var_view_get_variable (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &iter);
358
359       nlp = g_hash_table_lookup (rdd->varmap, var);
360
361       ds_put_cstr (dds, nlp->name);
362       ds_put_cstr (dds, " ");
363     }
364 }
365
366 static void
367 append_string_declarations (const PsppireDialogActionRecode *rd, struct string *dds)
368 {
369   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
370
371   /* Declare new string variables if applicable */
372   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)))
373     {
374       GHashTableIter iter;
375
376       struct variable *var = NULL;
377       struct nlp *nlp = NULL;
378
379       g_hash_table_iter_init (&iter, rdd->varmap);
380       while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
381         {
382           ds_put_cstr (dds, "\nSTRING ");
383           ds_put_cstr (dds, nlp->name);
384           ds_put_c_format (dds, " (A%d).",
385                            (int)
386                            gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry)));
387         }
388     }
389 }
390
391 static void
392 append_new_value_labels (const PsppireDialogActionRecode *rd, struct string *dds)
393 {
394   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
395
396   /* If applicable, set labels for the new variables. */
397   GHashTableIter iter;
398
399   struct variable *var = NULL;
400   struct nlp *nlp = NULL;
401
402   g_hash_table_iter_init (&iter, rdd->varmap);
403   while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
404     {
405       if (nlp->label)
406         {
407           struct string sl;
408           ds_init_empty (&sl);
409           syntax_gen_string (&sl, ss_cstr (nlp->label));
410           ds_put_c_format (dds, "\nVARIABLE LABELS %s %s.",
411                            nlp->name, ds_cstr (&sl));
412
413           ds_destroy (&sl);
414         }
415     }
416 }
417
418 static char *
419 diff_generate_syntax (const PsppireDialogAction *act)
420 {
421   return psppire_dialog_action_recode_generate_syntax (act,
422                                                        append_string_declarations,
423                                                        append_into_clause,
424                                                        append_new_value_labels);
425 }
426
427 static gboolean
428 target_is_string (const PsppireDialogActionRecode *rd)
429 {
430   return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button));
431 }
432
433 static void
434 psppire_dialog_action_recode_different_class_init (PsppireDialogActionRecodeDifferentClass *class)
435 {
436   PSPPIRE_DIALOG_ACTION_CLASS (class)->initial_activate = psppire_dialog_action_recode_different_activate;
437
438   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = diff_generate_syntax;
439   PSPPIRE_DIALOG_ACTION_RECODE_CLASS (class)->target_is_string = target_is_string;
440 }
441
442
443 static void
444 psppire_dialog_action_recode_different_init (PsppireDialogActionRecodeDifferent *act)
445 {
446 }
447