Revert "Fixed a use after free error when manipulating datasets."
[pspp] / src / ui / gui / psppire-var-view.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2009, 2010, 2011  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 <gtk/gtk.h>
20 #include "psppire-var-view.h"
21 #include "psppire-var-ptr.h"
22 #include "psppire-select-dest.h"
23
24 #include <libpspp/cast.h>
25 #include <libpspp/str.h>
26 #include <data/variable.h>
27
28 #include <gettext.h>
29 #define _(msgid) gettext (msgid)
30 #define N_(msgid) msgid
31
32 static void psppire_var_view_base_finalize (PsppireVarViewClass *, gpointer);
33 static void psppire_var_view_base_init     (PsppireVarViewClass *class);
34 static void psppire_var_view_class_init    (PsppireVarViewClass *class);
35 static void psppire_var_view_init          (PsppireVarView      *var_view);
36
37 /* Returns TRUE iff VV contains the item V.
38    V must be an initialised value containing a
39    PSPPIRE_VAR_PTR_TYPE.
40 */
41 static gboolean
42 var_view_contains_var (PsppireSelectDestWidget *sdm, const GValue *v)
43 {
44   gboolean ok;
45   GtkTreeIter iter;
46   PsppireVarView *vv = PSPPIRE_VAR_VIEW (sdm);
47   g_return_val_if_fail (G_VALUE_HOLDS (v, PSPPIRE_VAR_PTR_TYPE), FALSE);
48
49   for (ok = psppire_var_view_get_iter_first (vv, &iter);
50        ok;
51        ok = psppire_var_view_get_iter_next (vv, &iter))
52     {
53       const struct variable *var = psppire_var_view_get_variable (vv, 0, &iter);
54       if (var == g_value_get_boxed (v))
55         return TRUE;
56     }
57
58   return FALSE;
59 }
60
61 static void
62 model_init (PsppireSelectDestWidgetIface *iface)
63 {
64   iface->contains_var = var_view_contains_var;
65 }
66
67 GType
68 psppire_var_view_get_type (void)
69 {
70   static GType psppire_var_view_type = 0;
71
72   if (!psppire_var_view_type)
73     {
74       static const GTypeInfo psppire_var_view_info =
75       {
76         sizeof (PsppireVarViewClass),
77         (GBaseInitFunc) psppire_var_view_base_init,
78         (GBaseFinalizeFunc) psppire_var_view_base_finalize,
79         (GClassInitFunc)psppire_var_view_class_init,
80         (GClassFinalizeFunc) NULL,
81         NULL,
82         sizeof (PsppireVarView),
83         0,
84         (GInstanceInitFunc) psppire_var_view_init,
85       };
86
87       static const GInterfaceInfo var_view_model_info = {
88         (GInterfaceInitFunc) model_init, /* Fill this in */
89         NULL,
90         NULL
91       };
92
93       psppire_var_view_type =
94         g_type_register_static (GTK_TYPE_TREE_VIEW, "PsppireVarView",
95                                 &psppire_var_view_info, 0);
96
97       g_type_add_interface_static (psppire_var_view_type,
98                                    PSPPIRE_TYPE_SELECT_DEST_WIDGET,
99                                    &var_view_model_info);
100     }
101
102   return psppire_var_view_type;
103 }
104
105 void
106 psppire_var_view_clear (PsppireVarView *vv)
107 {
108   GtkListStore *l = gtk_list_store_newv  (vv->n_cols, vv->cols);
109
110   gtk_tree_view_set_model (GTK_TREE_VIEW (vv), GTK_TREE_MODEL (l));
111 }
112
113
114 static void
115 psppire_var_view_finalize (GObject *object)
116 {
117   PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
118   g_free (var_view->nums);
119   g_free (var_view->cols);
120 }
121
122
123
124 /* Properties */
125 enum
126 {
127   PROP_0,
128   PROP_N_COLS
129 };
130
131 /* A (*GtkTreeCellDataFunc) function.
132    This function expects TREEMODEL to hold PSPPIRE_VAR_PTR_TYPE.
133    It renders the name of the variable into CELL.
134 */
135 static void
136 display_cell_var_name (GtkTreeViewColumn *tree_column,
137                        GtkCellRenderer *cell,
138                        GtkTreeModel *treemodel,
139                        GtkTreeIter *iter,
140                        gpointer data)
141 {
142   struct variable *var;
143   GValue value = {0};
144   gint *col = data;
145
146   GtkTreePath *path = gtk_tree_model_get_path (treemodel, iter);
147
148   gtk_tree_model_get_value (treemodel, iter, *col, &value);
149
150   gtk_tree_path_free (path);
151
152   var = g_value_get_boxed (&value);
153
154   g_value_unset (&value);
155
156   g_object_set (cell, "text", var ? var_get_name (var) : "", NULL);
157 }
158
159
160 static void
161 psppire_var_view_get_property (GObject         *object,
162                                guint            prop_id,
163                                GValue          *value,
164                                GParamSpec      *pspec)
165 {
166   PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
167
168   switch (prop_id)
169     {
170     case PROP_N_COLS:
171       g_value_set_int (value,  var_view->n_cols);
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175       break;
176     };
177 }
178
179 static void
180 set_renderers (PsppireVarView *var_view)
181 {
182   gint c;
183   var_view->nums = g_malloc (sizeof *var_view->nums * var_view->n_cols);
184
185   for (c = 0 ; c < var_view->n_cols; ++c)
186     {
187       GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
188       GtkTreeViewColumn *col = gtk_tree_view_column_new ();
189
190       gchar *label = g_strdup_printf (_("Var%d"), c + 1);
191
192       gtk_tree_view_column_set_min_width (col, 100);
193       gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
194       gtk_tree_view_column_set_resizable (col, TRUE);
195       gtk_tree_view_column_set_title (col, label);
196
197       g_free (label);
198
199       var_view->nums[c] = c;
200
201       gtk_tree_view_column_pack_start (col, renderer, TRUE);
202       gtk_tree_view_column_set_cell_data_func (col, renderer,
203                                                display_cell_var_name,
204                                                &var_view->nums[c], 0);
205
206       gtk_tree_view_append_column (GTK_TREE_VIEW (var_view), col);
207     }
208 }
209
210
211 GtkTreeModel *
212 psppire_var_view_get_current_model (PsppireVarView *vv)
213 {
214   return gtk_tree_view_get_model (GTK_TREE_VIEW (vv));
215 }
216
217 static void
218 psppire_var_view_set_property (GObject         *object,
219                                guint            prop_id,
220                                const GValue    *value,
221                                GParamSpec      *pspec)
222 {
223   PsppireVarView *var_view = PSPPIRE_VAR_VIEW (object);
224
225   switch (prop_id)
226     {
227     case PROP_N_COLS:
228       {
229         gint c;
230         var_view->n_cols = g_value_get_int (value);
231
232         var_view->cols = xrealloc (var_view->cols, sizeof (GType) *  var_view->n_cols);
233
234         for (c = 0 ; c < var_view->n_cols; ++c)
235           var_view->cols[c] = PSPPIRE_VAR_PTR_TYPE;
236
237         set_renderers (var_view);
238
239         psppire_var_view_clear (var_view);
240       }
241       break;
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245     };
246 }
247
248 static void
249 psppire_var_view_class_init (PsppireVarViewClass *class)
250 {
251   GObjectClass *object_class = G_OBJECT_CLASS (class);
252
253   GParamSpec *n_cols_spec =
254     g_param_spec_int ("n-cols",
255                       "Number of columns",
256                       "The Number of Columns in the Variable View",
257                       1, 20,
258                       1,
259                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_WRITABLE);
260
261
262   object_class->set_property = psppire_var_view_set_property;
263   object_class->get_property = psppire_var_view_get_property;
264
265   g_object_class_install_property (object_class,
266                                    PROP_N_COLS,
267                                    n_cols_spec);
268 }
269
270
271 static void
272 psppire_var_view_base_init (PsppireVarViewClass *class)
273 {
274
275   GObjectClass *object_class = G_OBJECT_CLASS (class);
276
277   object_class->finalize = psppire_var_view_finalize;
278 }
279
280
281
282 static void
283 psppire_var_view_base_finalize (PsppireVarViewClass *class,
284                                  gpointer class_data)
285 {
286 }
287
288
289
290 static void
291 psppire_var_view_init (PsppireVarView *vv)
292 {
293   GtkTreeSelection* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (vv));
294   vv->cols = 0;
295
296   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
297 }
298
299
300 GtkWidget*
301 psppire_var_view_new (void)
302 {
303   return GTK_WIDGET (g_object_new (psppire_var_view_get_type (), NULL));
304 }
305
306
307 gboolean
308 psppire_var_view_get_iter_first (PsppireVarView *vv, GtkTreeIter *iter)
309 {
310   GtkTreeIter dummy;
311   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (vv));
312   return gtk_tree_model_get_iter_first (model, iter ? iter : &dummy);
313 }
314
315 gboolean
316 psppire_var_view_get_iter_next (PsppireVarView *vv, GtkTreeIter *iter)
317 {
318   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (vv));
319   return gtk_tree_model_iter_next (model, iter);
320 }
321
322 const struct variable *
323 psppire_var_view_get_var_from_model (GtkTreeModel *model, gint column, GtkTreeIter *iter)
324 {
325   const struct variable *var = NULL;
326   GValue value = {0};
327   gtk_tree_model_get_value (model, iter, column, &value);
328
329   if (G_VALUE_TYPE (&value) == PSPPIRE_VAR_PTR_TYPE)
330     var = g_value_get_boxed (&value);
331   else
332     g_critical ("Unsupported type `%s', in variable name treeview.",
333                 G_VALUE_TYPE_NAME (&value));
334
335   g_value_unset (&value);
336
337   return var;
338 }
339
340 const struct variable *
341 psppire_var_view_get_variable (PsppireVarView *vv, gint column, GtkTreeIter *iter)
342 {
343   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (vv));
344   return psppire_var_view_get_var_from_model (model, column, iter);
345 }
346
347
348
349 /*
350   Append the names of selected variables to STRING.
351   Returns the number of variables appended.
352 */
353 gint
354 psppire_var_view_append_names (PsppireVarView *vv, gint column, GString *string)
355 {
356   gint n_vars = 0;
357   GtkTreeIter iter;
358
359   if (psppire_var_view_get_iter_first (vv, &iter))
360     {
361       do
362         {
363           const struct variable *var = psppire_var_view_get_variable (vv, column, &iter);
364           g_string_append (string, " ");
365           g_string_append (string, var_get_name (var));
366
367           n_vars++;
368         }
369       while (psppire_var_view_get_iter_next (vv, &iter));
370     }
371
372   return n_vars;
373 }
374
375 /* Return a linked list of struct variables which are
376    contained in VV.
377    The caller is responsible for freeing the returned list.
378    The variables however are owned by their dictionary
379    and should not be freed.
380   */
381 GSList *
382 psppire_var_view_list_names (PsppireVarView *vv, gint column)
383 {
384   GtkTreeIter iter;
385   GSList *list = NULL;
386
387   if (psppire_var_view_get_iter_first (vv, &iter))
388     {
389       do
390         {
391           const struct variable *var = psppire_var_view_get_variable (vv, column, &iter);
392           list = g_slist_prepend (list, CONST_CAST (struct variable *, var));
393         }
394       while (psppire_var_view_get_iter_next (vv, &iter));
395     }
396
397   return list;
398 }
399
400
401 /*
402   Append the names of selected variables to STR
403   Returns the number of variables appended.
404 */
405 gint
406 psppire_var_view_append_names_str (PsppireVarView *vv, gint column, struct string *str)
407 {
408   gint n_vars = 0;
409   GtkTreeIter iter;
410
411   if (psppire_var_view_get_iter_first (vv, &iter))
412     {
413       do
414         {
415           const struct variable *var = psppire_var_view_get_variable (vv, column, &iter);
416           ds_put_cstr (str, " ");
417           ds_put_cstr (str, var_get_name (var));
418
419           n_vars++;
420         }
421       while (psppire_var_view_get_iter_next (vv, &iter));
422     }
423
424   return n_vars;
425 }
426
427
428