Warnings: function type cast for g_list_foreach
[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   var_unref (var);
170 }
171
172 static void
173 on_change_clicked (GObject *obj, gpointer data)
174 {
175   PsppireDialogActionRecode *rd = data;
176   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
177
178   struct variable *var = NULL;
179   struct nlp *nlp;
180
181   GtkTreeModel *model =  gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
182
183   GtkTreeIter iter;
184   GtkTreeSelection *selection =
185     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->variable_treeview));
186
187   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
188
189   const gchar *dest_var_name =
190     gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry));
191
192   const gchar *dest_var_label =
193     gtk_entry_get_text (GTK_ENTRY (rd->new_label_entry));
194
195   if (NULL == rows || rows->next != NULL)
196     goto finish;
197
198   gtk_tree_model_get_iter (model, &iter, rows->data);
199
200   gtk_tree_model_get (model, &iter, 0, &var, -1);
201
202   g_hash_table_remove (rdd->varmap, var);
203
204   nlp = nlp_create (dest_var_name, dest_var_label);
205
206   g_hash_table_insert (rdd->varmap, var, nlp);
207
208   gtk_tree_model_row_changed (model, rows->data, &iter);
209
210  finish:
211   g_list_foreach (rows, (GFunc) (void (*)(void)) gtk_tree_path_free, NULL);
212   g_list_free (rows);
213 }
214
215
216
217 /* Callback which gets called when a new row is selected
218    in the variable treeview.
219    It sets the name and label entry widgets to reflect the
220    currently selected row.
221 */
222 static void
223 on_selection_change (GtkTreeSelection *selection, gpointer data)
224 {
225   PsppireDialogActionRecode *rd = data;
226   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
227
228   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->variable_treeview));
229
230   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
231
232   if (rows && !rows->next)
233     {
234       /* Exactly one row is selected */
235       struct nlp *nlp;
236       struct variable *var;
237       gboolean ok;
238       GtkTreeIter iter;
239
240       gtk_widget_set_sensitive  (rd->change_button, TRUE);
241       gtk_widget_set_sensitive  (rd->new_name_entry, TRUE);
242       gtk_widget_set_sensitive  (rd->new_label_entry, TRUE);
243
244       ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data);
245       g_return_if_fail (ok);
246
247       gtk_tree_model_get (model, &iter,
248                           0, &var,
249                           -1);
250
251       nlp = g_hash_table_lookup (rdd->varmap, var);
252
253       if (nlp)
254         {
255           gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), nlp->name ? nlp->name : "");
256           gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), nlp->label ? nlp->label : "");
257         }
258       else
259         {
260           gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
261           gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
262         }
263     }
264   else
265     {
266       gtk_widget_set_sensitive  (rd->change_button, FALSE);
267       gtk_widget_set_sensitive  (rd->new_name_entry, FALSE);
268       gtk_widget_set_sensitive  (rd->new_label_entry, FALSE);
269
270       gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
271       gtk_entry_set_text (GTK_ENTRY (rd->new_label_entry), "");
272     }
273
274
275   g_list_foreach (rows, (GFunc) (void (*)(void)) gtk_tree_path_free, NULL);
276   g_list_free (rows);
277 }
278
279
280
281
282 static void
283 populate_treeview (PsppireDialogActionRecode *act)
284 {
285   GtkTreeSelection *sel;
286   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (act);
287   GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
288   GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("New"),
289                                                                      renderer,
290                                                                      "text", NULL,
291                                                                      NULL);
292
293   gtk_tree_view_column_set_cell_data_func (col, renderer,
294                                            render_new_var_name,
295                                            act, NULL);
296
297   gtk_tree_view_append_column (GTK_TREE_VIEW (act->variable_treeview), col);
298
299   col = gtk_tree_view_get_column (GTK_TREE_VIEW (act->variable_treeview), 0);
300
301   g_object_set (col, "title", _("Old"), NULL);
302
303   g_object_set (act->variable_treeview, "headers-visible", TRUE, NULL);
304
305   rdd->varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy);
306
307   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (act->variable_treeview));
308
309   g_signal_connect (sel, "changed",
310                     G_CALLBACK (on_selection_change), act);
311
312   g_signal_connect (act->change_button, "clicked",
313                     G_CALLBACK (on_change_clicked),  act);
314 }
315
316
317 static GtkBuilder *
318 psppire_dialog_action_recode_different_activate (PsppireDialogAction *a, GVariant *param)
319 {
320   PsppireDialogActionRecode *act = PSPPIRE_DIALOG_ACTION_RECODE (a);
321   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
322
323   GtkBuilder *xml = psppire_dialog_action_recode_pre_activate (act,
324                                                                populate_treeview);
325
326   gtk_window_set_title (GTK_WINDOW (pda->dialog),
327                         _("Recode into Different Variables"));
328
329   gtk_window_set_title (GTK_WINDOW (act->old_and_new_dialog),
330                         _("Recode into Different Variables: Old and New Values "));
331
332   gtk_widget_show (act->output_variable_box);
333
334   g_signal_connect_swapped (act->old_and_new_dialog, "show",
335                             G_CALLBACK (on_old_new_show), act);
336
337   psppire_dialog_action_set_refresh (pda, refresh);
338
339   psppire_dialog_action_set_valid_predicate (pda,
340                                              dialog_state_valid);
341   return xml;
342 }
343
344 static void
345 append_into_clause (const PsppireDialogActionRecode *rd, struct string *dds)
346 {
347   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
348
349   /* If applicable set the INTO clause which determines into which variables the new values go */
350   GtkTreeIter iter;
351   ds_put_cstr (dds, "\n\tINTO ");
352   gboolean ok;
353
354   for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter);
355        ok;
356        ok = psppire_var_view_get_iter_next (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter))
357     {
358       struct nlp *nlp = NULL;
359       const struct variable *var = psppire_var_view_get_variable (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &iter);
360
361       nlp = g_hash_table_lookup (rdd->varmap, var);
362
363       ds_put_cstr (dds, nlp->name);
364       ds_put_cstr (dds, " ");
365     }
366 }
367
368 static void
369 append_string_declarations (const PsppireDialogActionRecode *rd, struct string *dds)
370 {
371   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
372
373   /* Declare new string variables if applicable */
374   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button)))
375     {
376       GHashTableIter iter;
377
378       struct variable *var = NULL;
379       struct nlp *nlp = NULL;
380
381       g_hash_table_iter_init (&iter, rdd->varmap);
382       while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
383         {
384           ds_put_cstr (dds, "\nSTRING ");
385           ds_put_cstr (dds, nlp->name);
386           ds_put_c_format (dds, " (A%d).",
387                            (int)
388                            gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry)));
389         }
390     }
391 }
392
393 static void
394 append_new_value_labels (const PsppireDialogActionRecode *rd, struct string *dds)
395 {
396   PsppireDialogActionRecodeDifferent *rdd = PSPPIRE_DIALOG_ACTION_RECODE_DIFFERENT (rd);
397
398   /* If applicable, set labels for the new variables. */
399   GHashTableIter iter;
400
401   struct variable *var = NULL;
402   struct nlp *nlp = NULL;
403
404   g_hash_table_iter_init (&iter, rdd->varmap);
405   while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
406     {
407       if (nlp->label)
408         {
409           struct string sl;
410           ds_init_empty (&sl);
411           syntax_gen_string (&sl, ss_cstr (nlp->label));
412           ds_put_c_format (dds, "\nVARIABLE LABELS %s %s.",
413                            nlp->name, ds_cstr (&sl));
414
415           ds_destroy (&sl);
416         }
417     }
418 }
419
420 static char *
421 diff_generate_syntax (const PsppireDialogAction *act)
422 {
423   return psppire_dialog_action_recode_generate_syntax (act,
424                                                        append_string_declarations,
425                                                        append_into_clause,
426                                                        append_new_value_labels);
427 }
428
429 static gboolean
430 target_is_string (const PsppireDialogActionRecode *rd)
431 {
432   return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->string_button));
433 }
434
435 static void
436 psppire_dialog_action_recode_different_class_init (PsppireDialogActionRecodeDifferentClass *class)
437 {
438   PSPPIRE_DIALOG_ACTION_CLASS (class)->initial_activate = psppire_dialog_action_recode_different_activate;
439
440   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = diff_generate_syntax;
441   PSPPIRE_DIALOG_ACTION_RECODE_CLASS (class)->target_is_string = target_is_string;
442 }
443
444
445 static void
446 psppire_dialog_action_recode_different_init (PsppireDialogActionRecodeDifferent *act)
447 {
448 }
449