Namespace police duty.
[pspp-builds.git] / src / ui / gui / psppire-dictview.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2009  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/gtktreeview.h>
20 #include "psppire-dictview.h"
21 #include "psppire-dict.h"
22 #include "psppire-conf.h"
23 #include <data/format.h>
24 #include "helper.h"
25
26 #include <gettext.h>
27 #define _(msgid) gettext (msgid)
28 #define N_(msgid) msgid
29
30 static void psppire_dict_view_base_finalize (PsppireDictViewClass *, gpointer);
31 static void psppire_dict_view_base_init     (PsppireDictViewClass *class);
32 static void psppire_dict_view_class_init    (PsppireDictViewClass *class);
33 static void psppire_dict_view_init          (PsppireDictView      *dict_view);
34
35
36 GType
37 psppire_dict_view_get_type (void)
38 {
39   static GType psppire_dict_view_type = 0;
40
41   if (!psppire_dict_view_type)
42     {
43       static const GTypeInfo psppire_dict_view_info =
44       {
45         sizeof (PsppireDictViewClass),
46         (GBaseInitFunc) psppire_dict_view_base_init,
47         (GBaseFinalizeFunc) psppire_dict_view_base_finalize,
48         (GClassInitFunc)psppire_dict_view_class_init,
49         (GClassFinalizeFunc) NULL,
50         NULL,
51         sizeof (PsppireDictView),
52         0,
53         (GInstanceInitFunc) psppire_dict_view_init,
54       };
55
56       psppire_dict_view_type =
57         g_type_register_static (GTK_TYPE_TREE_VIEW, "PsppireDictView",
58                                 &psppire_dict_view_info, 0);
59     }
60
61   return psppire_dict_view_type;
62 }
63
64
65 static void
66 psppire_dict_view_finalize (GObject *object)
67 {
68 }
69
70 /* Properties */
71 enum
72 {
73   PROP_0,
74   PROP_MODEL,
75   PROP_PREDICATE,
76   PROP_SELECTION_MODE
77 };
78
79
80 /* A GtkTreeModelFilterVisibleFunc to filter lines in the treeview */
81 static gboolean
82 filter_variables (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
83 {
84   var_predicate_func *predicate = data;
85   struct variable *var;
86   PsppireDict *dict = PSPPIRE_DICT (model);
87
88   GtkTreePath *path = gtk_tree_model_get_path (model, iter);
89
90   gint *idx = gtk_tree_path_get_indices (path);
91
92   var =  psppire_dict_get_variable (dict, *idx);
93
94   gtk_tree_path_free (path);
95
96   return predicate (var);
97 }
98
99 static void
100 set_model (PsppireDictView *dict_view)
101 {
102   GtkTreeModel *model ;
103
104   if ( dict_view->predicate )
105     {
106       model = gtk_tree_model_filter_new (GTK_TREE_MODEL (dict_view->dict),
107                                          NULL);
108
109       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
110                                               filter_variables,
111                                               dict_view->predicate,
112                                               NULL);
113     }
114   else
115     {
116       model = GTK_TREE_MODEL (dict_view->dict);
117     }
118
119   gtk_tree_view_set_model (GTK_TREE_VIEW (dict_view), model);
120 }
121
122 static void
123 psppire_dict_view_set_property (GObject         *object,
124                                guint            prop_id,
125                                const GValue    *value,
126                                GParamSpec      *pspec)
127 {
128   PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
129
130   switch (prop_id)
131     {
132     case PROP_MODEL:
133       dict_view->dict = g_value_get_object (value);
134       break;
135     case PROP_PREDICATE:
136       dict_view->predicate = g_value_get_pointer (value);
137       break;
138     case PROP_SELECTION_MODE:
139       {
140         GtkTreeSelection *selection =
141           gtk_tree_view_get_selection (GTK_TREE_VIEW (dict_view));
142
143         GtkSelectionMode mode = g_value_get_enum (value);
144
145         gtk_tree_selection_set_mode (selection, mode);
146       }
147       break;
148     default:
149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150       break;
151     };
152
153
154   set_model (dict_view);
155 }
156
157
158 static void
159 psppire_dict_view_get_property (GObject         *object,
160                                guint            prop_id,
161                                GValue          *value,
162                                GParamSpec      *pspec)
163 {
164   PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
165
166   switch (prop_id)
167     {
168     case PROP_MODEL:
169       g_value_set_object (value, dict_view->dict);
170       break;
171     case PROP_PREDICATE:
172       g_value_set_pointer (value, dict_view->predicate);
173       break;
174     case PROP_SELECTION_MODE:
175       {
176         GtkTreeSelection *selection =
177           gtk_tree_view_get_selection (GTK_TREE_VIEW (dict_view));
178
179         g_value_set_enum (value, gtk_tree_selection_get_mode (selection));
180       }
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183       break;
184     };
185 }
186
187
188
189 static void
190 psppire_dict_view_class_init (PsppireDictViewClass *class)
191 {
192   GObjectClass *object_class = G_OBJECT_CLASS (class);
193
194   GParamSpec *model_spec =
195     g_param_spec_object ("model",
196                          "Model",
197                          _("The dictionary to be displayed by this widget"),
198                          PSPPIRE_TYPE_DICT,
199                          G_PARAM_READABLE | G_PARAM_WRITABLE);
200
201   GParamSpec *predicate_spec =
202     g_param_spec_pointer ("predicate",
203                           "Predicate",
204                           _("A predicate function"),
205                           G_PARAM_READABLE | G_PARAM_WRITABLE);
206
207
208   GParamSpec *selection_mode_spec =
209     g_param_spec_enum ("selection-mode",
210                        "Selection Mode",
211                        _("How many things can be selected"),
212                        GTK_TYPE_SELECTION_MODE,
213                        GTK_SELECTION_MULTIPLE,
214                        G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
215
216
217   object_class->set_property = psppire_dict_view_set_property;
218   object_class->get_property = psppire_dict_view_get_property;
219
220   g_object_class_install_property (object_class,
221                                    PROP_MODEL,
222                                    model_spec);
223
224   g_object_class_install_property (object_class,
225                                    PROP_PREDICATE,
226                                    predicate_spec);
227
228   g_object_class_install_property (object_class,
229                                    PROP_SELECTION_MODE,
230                                    selection_mode_spec);
231 }
232
233
234 static void
235 psppire_dict_view_base_init (PsppireDictViewClass *class)
236 {
237   GObjectClass *object_class = G_OBJECT_CLASS (class);
238
239   object_class->finalize = psppire_dict_view_finalize;
240 }
241
242
243
244 static void
245 psppire_dict_view_base_finalize(PsppireDictViewClass *class,
246                                 gpointer class_data)
247 {
248
249 }
250
251
252 static void
253 dv_get_base_model (GtkTreeModel *top_model, GtkTreeIter *top_iter,
254                 GtkTreeModel **model, GtkTreeIter *iter
255                 )
256 {
257   *model = top_model;
258   *iter = *top_iter;
259   while (GTK_IS_TREE_MODEL_FILTER (*model))
260     {
261       GtkTreeIter parent_iter = *iter;
262       GtkTreeModelFilter *parent_model = GTK_TREE_MODEL_FILTER (*model);
263
264       *model = gtk_tree_model_filter_get_model (parent_model);
265
266       gtk_tree_model_filter_convert_iter_to_child_iter (parent_model,
267                                                         iter,
268                                                         &parent_iter);
269     }
270
271   g_assert (PSPPIRE_IS_DICT (*model));
272 }
273
274
275
276 /* A GtkTreeCellDataFunc which renders the name and/or label of the
277    variable */
278 static void
279 var_description_cell_data_func (GtkTreeViewColumn *col,
280                                 GtkCellRenderer *cell,
281                                 GtkTreeModel *top_model,
282                                 GtkTreeIter *top_iter,
283                                 gpointer data)
284 {
285   struct variable *var;
286   GtkTreeIter iter;
287   GtkTreeModel *model;
288   gboolean prefer_labels = FALSE;
289
290   PsppireConf *conf = psppire_conf_new ();
291
292   psppire_conf_get_boolean (conf, "dialog-boxes", "prefer-labels",
293                             &prefer_labels);
294
295   dv_get_base_model (top_model, top_iter, &model, &iter);
296
297   g_assert (PSPPIRE_IS_DICT (model));
298
299
300   gtk_tree_model_get (model,
301                       &iter, DICT_TVM_COL_VAR, &var, -1);
302
303   if ( var_has_label (var) && prefer_labels)
304     {
305       gchar *text = g_strdup_printf (
306                                      "<span stretch=\"condensed\">%s</span>",
307                                      var_get_label (var));
308
309       char *utf8 = pspp_locale_to_utf8 (text, -1, NULL);
310
311       g_free (text);
312       g_object_set (cell, "markup", utf8, NULL);
313       g_free (utf8);
314     }
315   else
316     {
317       char *name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
318       g_object_set (cell, "text", name, NULL);
319       g_free (name);
320     }
321 }
322
323
324
325 /* A GtkTreeCellDataFunc which sets the icon appropriate to the type
326    of variable */
327 static void
328 var_icon_cell_data_func (GtkTreeViewColumn *col,
329                        GtkCellRenderer *cell,
330                        GtkTreeModel *model,
331                        GtkTreeIter *iter,
332                        gpointer data)
333 {
334   struct variable *var;
335   gtk_tree_model_get (model, iter, DICT_TVM_COL_VAR, &var, -1);
336
337   if ( var_is_alpha (var))
338     {
339       g_object_set (cell, "stock-id", "var-string", NULL);
340     }
341   else
342     {
343       const struct fmt_spec *fs = var_get_write_format (var);
344       int cat = fmt_get_category (fs->type);
345       switch ( var_get_measure (var))
346         {
347         case MEASURE_NOMINAL:
348           g_object_set (cell, "stock-id", "var-nominal", NULL);
349           break;
350         case MEASURE_ORDINAL:
351           g_object_set (cell, "stock-id", "var-ordinal", NULL);
352           break;
353         case MEASURE_SCALE:
354           if ( ( FMT_CAT_DATE | FMT_CAT_TIME ) & cat )
355             g_object_set (cell, "stock-id", "var-date-scale", NULL);
356           else
357             g_object_set (cell, "stock-id", "var-scale", NULL);
358           break;
359         default:
360           g_assert_not_reached ();
361         };
362     }
363 }
364
365
366 /* Sets the tooltip to be the name of the variable under the cursor */
367 static gboolean
368 set_tooltip_for_variable (GtkTreeView  *treeview,
369                           gint        x,
370                           gint        y,
371                           gboolean    keyboard_mode,
372                           GtkTooltip *tooltip,
373                           gpointer    user_data)
374
375 {
376   gint bx, by;
377   GtkTreeIter iter;
378   GtkTreePath *path;
379   GtkTreeModel *tree_model;
380   struct variable *var = NULL;
381   gboolean ok;
382
383
384   gtk_tree_view_convert_widget_to_bin_window_coords (treeview,
385                                                      x, y, &bx, &by);
386
387   if (!gtk_tree_view_get_path_at_pos (treeview, bx, by,
388                                       &path, NULL, NULL, NULL))
389     return FALSE;
390
391   tree_model = gtk_tree_view_get_model (treeview);
392
393
394   gtk_tree_view_set_tooltip_row (treeview, tooltip, path);
395
396   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
397
398   gtk_tree_path_free (path);
399   if (!ok)
400     return FALSE;
401
402
403   gtk_tree_model_get (tree_model, &iter, DICT_TVM_COL_VAR,  &var, -1);
404
405   if ( ! var_has_label (var))
406     return FALSE;
407
408   {
409     gchar *tip ;
410     gboolean prefer_labels = FALSE;
411
412     PsppireConf *conf = psppire_conf_new ();
413
414     psppire_conf_get_boolean (conf, "dialog-boxes", "prefer-labels",
415                               &prefer_labels);
416
417     if ( prefer_labels )
418       tip = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
419     else
420       tip = pspp_locale_to_utf8 (var_get_label (var), -1, NULL);
421
422     gtk_tooltip_set_text (tooltip, tip);
423
424     g_free (tip);
425   }
426
427   return TRUE;
428 }
429
430
431 static void
432 psppire_dict_view_init (PsppireDictView *dict_view)
433 {
434   GtkTreeViewColumn *col;
435
436   GtkCellRenderer *renderer;
437
438   col = gtk_tree_view_column_new ();
439   gtk_tree_view_column_set_title (col, _("Variable"));
440
441   renderer = gtk_cell_renderer_pixbuf_new ();
442   gtk_tree_view_column_pack_start (col, renderer, FALSE);
443
444   gtk_tree_view_column_set_cell_data_func (col, renderer,
445                                            var_icon_cell_data_func,
446                                            NULL, NULL);
447
448
449   renderer = gtk_cell_renderer_text_new ();
450   gtk_tree_view_column_pack_start (col, renderer, TRUE);
451   gtk_tree_view_column_set_cell_data_func (col, renderer,
452                                            var_description_cell_data_func,
453                                            NULL, NULL);
454
455   g_object_set (renderer, "ellipsize-set", TRUE, NULL);
456   g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
457
458   gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
459
460   /* FIXME: make this a value in terms of character widths */
461   gtk_tree_view_column_set_min_width (col, 150);
462
463   gtk_tree_view_append_column (GTK_TREE_VIEW (dict_view), col);
464
465   g_object_set (dict_view, "has-tooltip", TRUE, NULL);
466
467   g_signal_connect (dict_view, "query-tooltip",
468                     G_CALLBACK (set_tooltip_for_variable), NULL);
469 }
470
471
472 GtkWidget*
473 psppire_dict_view_new (void)
474 {
475   return GTK_WIDGET (g_object_new (psppire_dict_view_get_type (), NULL));
476 }
477
478
479
480 struct variable *
481 psppire_dict_view_get_selected_variable (PsppireDictView *treeview)
482 {
483   struct variable *var;
484   GtkTreeModel *top_model;
485   GtkTreeIter top_iter;
486
487   GtkTreeModel *model;
488   GtkTreeIter iter;
489
490   GtkTreeSelection *selection =
491     gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
492
493   if (! gtk_tree_selection_get_selected (selection,
494                                          &top_model, &top_iter))
495     {
496       return NULL;
497     }
498
499   dv_get_base_model (top_model, &top_iter, &model, &iter);
500
501   g_assert (PSPPIRE_IS_DICT (model));
502
503   gtk_tree_model_get (model,
504                       &iter, DICT_TVM_COL_VAR, &var, -1);
505
506   return var;
507 }
508
509