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