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