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
var_view_contains_var(PsppireSelectDestWidget * sdm,const GValue * v)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
model_init(PsppireSelectDestWidgetIface * iface)60 model_init (PsppireSelectDestWidgetIface *iface)
61 {
62   iface->contains_var = var_view_contains_var;
63 }
64 
G_DEFINE_TYPE_WITH_CODE(PsppireVarView,psppire_var_view,GTK_TYPE_TREE_VIEW,G_IMPLEMENT_INTERFACE (PSPPIRE_TYPE_SELECT_DEST_WIDGET,model_init))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
psppire_var_view_finalize(GObject * object)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
display_cell_var_name(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * treemodel,GtkTreeIter * iter,gpointer data)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
psppire_var_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
set_renderers(PsppireVarView * var_view)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 *
psppire_var_view_get_current_model(PsppireVarView * vv)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
psppire_var_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
psppire_var_view_class_init(PsppireVarViewClass * class)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
psppire_var_view_init(PsppireVarView * vv)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*
psppire_var_view_new(void)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
psppire_var_view_get_iter_first(PsppireVarView * vv,GtkTreeIter * iter)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
psppire_var_view_get_iter_next(PsppireVarView * vv,GtkTreeIter * iter)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 *
psppire_var_view_get_var_from_model(GtkTreeModel * model,gint column,GtkTreeIter * iter)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 *
psppire_var_view_get_variable(PsppireVarView * vv,gint column,GtkTreeIter * iter)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
psppire_var_view_append_names(PsppireVarView * vv,gint column,GString * string)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 *
psppire_var_view_list_names(PsppireVarView * vv,gint column)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
psppire_var_view_append_names_str(PsppireVarView * vv,gint column,struct string * str)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 
374