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