ed82bce8e293e966e3f468fbbe069f7a61f29a04
[pspp] / src / ui / gui / psppire-dialog-action-autorecode.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2015  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
18 #include <config.h>
19
20 #include "psppire-dialog-action-autorecode.h"
21
22 #include "psppire-var-view.h"
23 #include <stdlib.h>
24 #include "psppire-dialog.h"
25 #include "builder-wrapper.h"
26
27 #include "gettext.h"
28 #define _(msgid) gettext (msgid)
29 #define N_(msgid) msgid
30
31
32 static void psppire_dialog_action_autorecode_init            (PsppireDialogActionAutorecode      *act);
33 static void psppire_dialog_action_autorecode_class_init      (PsppireDialogActionAutorecodeClass *class);
34
35
36 G_DEFINE_TYPE (PsppireDialogActionAutorecode, psppire_dialog_action_autorecode, PSPPIRE_TYPE_DIALOG_ACTION);
37
38
39 static gboolean
40 dialog_state_valid (gpointer pda)
41 {
42   PsppireDialogActionAutorecode *rd = PSPPIRE_DIALOG_ACTION_AUTORECODE (pda);
43
44   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->var_view));
45   const gint n_vars = gtk_tree_model_iter_n_children (model, NULL);
46
47   if (n_vars == 0)
48     return FALSE;
49
50   if (g_hash_table_size (rd->varmap) != n_vars)
51     return FALSE;
52
53   return TRUE;
54 }
55
56 static void
57 refresh (PsppireDialogAction *pda)
58 {
59   PsppireDialogActionAutorecode *rd = PSPPIRE_DIALOG_ACTION_AUTORECODE (pda);
60
61   GtkTreeModel *target_list = gtk_tree_view_get_model (GTK_TREE_VIEW (rd->var_view));
62
63   gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
64   gtk_widget_set_sensitive  (rd->new_name_entry, FALSE);
65   gtk_widget_set_sensitive  (rd->change_button, FALSE);
66
67   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->ascending), TRUE);
68   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->group), FALSE);
69   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->blank), FALSE);
70
71   if (rd->varmap)
72     g_hash_table_remove_all (rd->varmap);
73
74   gtk_list_store_clear (GTK_LIST_STORE (target_list));
75
76
77 }
78
79 /* Name-Label pair */
80 struct nlp
81 {
82   char *name;
83   char *label;
84 };
85
86 static struct nlp *
87 nlp_create (const char *name, const char *label)
88 {
89   struct nlp *nlp = xmalloc (sizeof *nlp);
90
91   nlp->name = g_strdup (name);
92
93   nlp->label = NULL;
94
95   if (label != NULL && 0 != strcmp ("", label))
96     nlp->label = g_strdup (label);
97
98   return nlp;
99 }
100
101 static void
102 nlp_destroy (gpointer data)
103 {
104   struct nlp *nlp = data ;
105   if (! nlp)
106     return;
107
108   g_free (nlp->name);
109   g_free (nlp->label);
110   g_free (nlp);
111 }
112
113
114 static char *
115 generate_syntax (const PsppireDialogAction *act)
116 {
117   PsppireDialogActionAutorecode *rd = PSPPIRE_DIALOG_ACTION_AUTORECODE (act);
118
119   GHashTableIter iter;
120   gpointer key, value;
121   gchar *text;
122
123   GString *string = g_string_new ("AUTORECODE");
124
125   g_string_append (string, "\n\tVARIABLES =");
126
127   g_hash_table_iter_init (&iter, rd->varmap);
128   while (g_hash_table_iter_next (&iter, &key, &value))
129   {
130     struct variable *var = key;
131     g_string_append (string, " ");
132     g_string_append (string, var_get_name (var));
133   }
134
135   g_string_append (string, " INTO");
136
137   g_hash_table_iter_init (&iter, rd->varmap);
138   while (g_hash_table_iter_next (&iter, &key, &value))
139   {
140     struct nlp *nlp  = value;
141     g_string_append (string, " ");
142     g_string_append (string, nlp->name);
143   }
144
145   if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->ascending)))
146     g_string_append (string, "\n\t/DESCENDING");
147
148   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->group)))
149     g_string_append (string, "\n\t/GROUP");
150
151   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->blank)))
152     g_string_append (string, "\n\t/BLANK = MISSING");
153
154   g_string_append (string, ".\n");
155
156   text = string->str;
157
158   g_string_free (string, FALSE);
159
160   return text;
161 }
162
163 static void
164 on_change_clicked (GObject *obj, gpointer data)
165 {
166   PsppireDialogActionAutorecode *rd = PSPPIRE_DIALOG_ACTION_AUTORECODE (data);
167   struct variable *var = NULL;
168   struct nlp *nlp;
169   GtkTreeModel *model = psppire_var_view_get_current_model (PSPPIRE_VAR_VIEW (rd->var_view));
170   GtkTreeIter iter;
171   GtkTreeSelection *selection =
172     gtk_tree_view_get_selection (GTK_TREE_VIEW (rd->var_view));
173
174   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
175
176   const gchar *dest_var_name =
177     gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry));
178
179   if (NULL == rows || rows->next != NULL)
180     goto finish;
181
182   gtk_tree_model_get_iter (model, &iter, rows->data);
183
184   gtk_tree_model_get (model, &iter, 0, &var, -1);
185
186   g_hash_table_remove (rd->varmap, var);
187
188   nlp = nlp_create (dest_var_name, NULL);
189
190   g_hash_table_insert (rd->varmap, var, nlp);
191
192   gtk_tree_model_row_changed (model, rows->data, &iter);
193
194  finish:
195   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
196   g_list_free (rows);
197   var_unref (var);
198 }
199
200
201 static void
202 on_entry_change (PsppireDialogActionAutorecode *rd)
203 {
204   gboolean valid = TRUE;
205   const char *text = gtk_entry_get_text (GTK_ENTRY (rd->new_name_entry));
206
207   if (0 == strcmp ("", text))
208     valid = FALSE;
209   else if (psppire_dict_lookup_var (PSPPIRE_DIALOG_ACTION (rd)->dict, text))
210     valid = FALSE;
211   else
212     {
213       GHashTableIter iter;
214       gpointer key, value;
215
216       g_hash_table_iter_init (&iter, rd->varmap);
217       while (g_hash_table_iter_next (&iter, &key, &value))
218         {
219           struct nlp *nlp = value;
220
221           if (0 == strcmp (nlp->name, text))
222             {
223               valid = FALSE;
224               break;
225             }
226         }
227     }
228
229   gtk_widget_set_sensitive  (rd->change_button, valid);
230 }
231
232
233 /* Callback which gets called when a new row is selected
234    in the variable treeview.
235    It sets the name and label entry widgets to reflect the
236    currently selected row.
237 */
238 static void
239 on_selection_change (GtkTreeSelection *selection, gpointer data)
240 {
241   PsppireDialogActionAutorecode *rd = PSPPIRE_DIALOG_ACTION_AUTORECODE (data);
242
243   GtkTreeModel *model = psppire_var_view_get_current_model (PSPPIRE_VAR_VIEW (rd->var_view));
244
245   GList *rows = gtk_tree_selection_get_selected_rows (selection, &model);
246
247   if (rows && !rows->next)
248     {
249       /* Exactly one row is selected */
250       struct nlp *nlp;
251       struct variable *var;
252       gboolean ok;
253       GtkTreeIter iter;
254
255       gtk_widget_set_sensitive  (rd->new_name_entry, TRUE);
256       gtk_widget_set_sensitive  (rd->change_button, TRUE);
257
258
259       ok = gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) rows->data);
260       g_return_if_fail (ok);
261
262       gtk_tree_model_get (model, &iter, 0, &var, -1);
263
264       nlp = g_hash_table_lookup (rd->varmap, var);
265
266       if (nlp)
267         gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), nlp->name ? nlp->name : "");
268       else
269         gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
270     }
271   else
272     {
273       gtk_entry_set_text (GTK_ENTRY (rd->new_name_entry), "");
274       gtk_widget_set_sensitive  (rd->new_name_entry, FALSE);
275       gtk_widget_set_sensitive  (rd->change_button, FALSE);
276     }
277
278   g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
279   g_list_free (rows);
280 }
281
282
283
284 static void
285 render_new_var_name (GtkTreeViewColumn *tree_column,
286                      GtkCellRenderer *cell,
287                      GtkTreeModel *tree_model,
288                      GtkTreeIter *iter,
289                      gpointer data)
290 {
291   struct nlp *nlp = NULL;
292
293   PsppireDialogActionAutorecode *rd = PSPPIRE_DIALOG_ACTION_AUTORECODE (data);
294
295
296
297   struct variable *var = NULL;
298
299   gtk_tree_model_get (tree_model, iter,
300                       0, &var,
301                       -1);
302
303   nlp = g_hash_table_lookup (rd->varmap, var);
304
305   if (nlp)
306     g_object_set (cell, "text", nlp->name, NULL);
307   else
308     g_object_set (cell, "text", "", NULL);
309 }
310
311
312 static GtkBuilder *
313 psppire_dialog_action_autorecode_activate (PsppireDialogAction *a, GVariant *param)
314 {
315   PsppireDialogActionAutorecode *act = PSPPIRE_DIALOG_ACTION_AUTORECODE (a);
316   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
317
318   GtkBuilder *xml = builder_new ("autorecode.ui");
319
320   pda->dialog = get_widget_assert   (xml, "autorecode-dialog");
321   pda->source = get_widget_assert   (xml, "dict-view");
322
323   act->var_view = get_widget_assert   (xml, "var-view");
324
325   act->new_name_entry = get_widget_assert (xml, "entry1");
326   act->change_button = get_widget_assert (xml, "button1");
327   act->ascending = get_widget_assert (xml, "radiobutton1");
328   act->group = get_widget_assert (xml, "checkbutton1");
329   act->blank = get_widget_assert (xml, "checkbutton2");
330
331   {
332     GtkTreeSelection *sel;
333
334     GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
335
336     GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("New"),
337                                                                        renderer,
338                                                                        "text", NULL,
339                                                                        NULL);
340
341     gtk_tree_view_column_set_cell_data_func (col, renderer,
342                                              render_new_var_name,
343                                              act, NULL);
344
345     gtk_tree_view_append_column (GTK_TREE_VIEW (act->var_view), col);
346
347
348     col = gtk_tree_view_get_column (GTK_TREE_VIEW (act->var_view), 0);
349
350     g_object_set (col, "title", _("Old"), NULL);
351
352     g_object_set (act->var_view, "headers-visible", TRUE, NULL);
353
354     act->varmap = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, nlp_destroy);
355
356
357     sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (act->var_view));
358
359
360     g_signal_connect (sel, "changed",
361                       G_CALLBACK (on_selection_change), act);
362
363     g_signal_connect (act->change_button, "clicked",
364                       G_CALLBACK (on_change_clicked),  act);
365
366     g_signal_connect_swapped (act->new_name_entry, "changed",
367                               G_CALLBACK (on_entry_change),  act);
368
369   }
370
371   psppire_dialog_action_set_refresh (pda, refresh);
372   psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid);
373
374   return xml;
375 }
376
377 static void
378 psppire_dialog_action_autorecode_class_init (PsppireDialogActionAutorecodeClass *class)
379 {
380   PSPPIRE_DIALOG_ACTION_CLASS (class)->initial_activate = psppire_dialog_action_autorecode_activate;
381   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
382 }
383
384
385 static void
386 psppire_dialog_action_autorecode_init (PsppireDialogActionAutorecode *act)
387 {
388 }