7d09eb1a1171787ea12030f590538310f883015d
[pspp] / src / ui / gui / psppire-dictview.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2009, 2010, 2011, 2012, 2013, 2017,
3    2020  Free Software Foundation
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 #include <gtk/gtk.h>
21 #include "psppire-dictview.h"
22 #include "psppire-dict.h"
23 #include "dict-display.h"
24 #include "psppire-conf.h"
25 #include "options-dialog.h"
26 #include <data/format.h>
27 #include <libpspp/i18n.h>
28 #include "helper.h"
29
30 #include <gettext.h>
31 #define _(msgid) gettext (msgid)
32 #define N_(msgid) msgid
33
34 static void psppire_dict_view_class_init    (PsppireDictViewClass *class);
35 static void psppire_dict_view_init          (PsppireDictView      *dict_view);
36
37 G_DEFINE_TYPE (PsppireDictView, psppire_dict_view, GTK_TYPE_TREE_VIEW)
38
39 static void
40 psppire_dict_view_finalize (GObject *object)
41 {
42   PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
43
44   gtk_widget_destroy (dict_view->menu);
45 }
46
47 /* Properties */
48 enum
49 {
50   PROP_0,
51   PROP_DICTIONARY,
52   PROP_PREDICATE,
53   PROP_SELECTION_MODE
54 };
55
56
57
58 /* A GtkTreeModelFilterVisibleFunc to filter lines in the treeview */
59 static gboolean
60 filter_variables (GtkTreeModel *tmodel, GtkTreeIter *titer, gpointer data)
61 {
62   var_predicate_func *predicate = data;
63   struct variable *var;
64   GtkTreeModel *model = NULL;
65   GtkTreeIter iter ;
66   PsppireDict *dict ;
67   GtkTreePath *path ;
68   gint *idx;
69
70   get_base_model (tmodel, titer, &model, &iter);
71
72   dict = PSPPIRE_DICT (model);
73   path = gtk_tree_model_get_path (model, &iter);
74   idx = gtk_tree_path_get_indices (path);
75   var =  psppire_dict_get_variable (dict, *idx);
76
77   gtk_tree_path_free (path);
78
79   return predicate (var);
80 }
81
82 static gint
83 unsorted (GtkTreeModel *model,
84      GtkTreeIter *a,
85      GtkTreeIter *b,
86      gpointer user_data)
87 {
88   struct variable *var_a;
89   struct variable *var_b;
90
91   gtk_tree_model_get (model, a, DICT_TVM_COL_VAR,  &var_a, -1);
92   gtk_tree_model_get (model, b, DICT_TVM_COL_VAR,  &var_b, -1);
93
94   gint rval = compare_var_ptrs_by_dict_index (&var_a, &var_b, NULL);
95
96   var_unref (var_a);
97   var_unref (var_b);
98
99   return rval;
100 }
101
102 static gint
103 sort_by_name (GtkTreeModel *model,
104      GtkTreeIter *a,
105      GtkTreeIter *b,
106      gpointer user_data)
107 {
108   struct variable *var_a;
109   struct variable *var_b;
110
111   gtk_tree_model_get (model, a, DICT_TVM_COL_VAR,  &var_a, -1);
112   gtk_tree_model_get (model, b, DICT_TVM_COL_VAR,  &var_b, -1);
113
114   gint rval =  g_strcmp0 (var_get_name (var_a), var_get_name (var_b));
115
116   var_unref (var_a);
117   var_unref (var_b);
118
119   return rval;
120 }
121
122
123 static gint
124 sort_by_label (GtkTreeModel *model,
125      GtkTreeIter *a,
126      GtkTreeIter *b,
127      gpointer user_data)
128 {
129   struct variable *var_a;
130   struct variable *var_b;
131
132   gtk_tree_model_get (model, a, DICT_TVM_COL_VAR,  &var_a, -1);
133   gtk_tree_model_get (model, b, DICT_TVM_COL_VAR,  &var_b, -1);
134
135   gint rval = g_strcmp0 (var_get_label (var_a), var_get_label (var_b));
136
137   var_unref (var_a);
138   var_unref (var_b);
139
140   return rval;
141 }
142
143
144 static gint
145 default_sort (GtkTreeModel *model,
146      GtkTreeIter *a,
147      GtkTreeIter *b,
148      gpointer user_data)
149 {
150   int what = -1;
151   psppire_conf_get_enum (psppire_conf_new (), "VariableLists", "sort-order",
152                          PSPP_TYPE_OPTIONS_VAR_ORDER, &what);
153
154   switch (what)
155     {
156     default:
157       return unsorted (model, a, b, user_data);
158       break;
159     case PSPP_OPTIONS_VAR_ORDER_NAME:
160       return sort_by_name (model, a, b, user_data);
161       break;
162     case PSPP_OPTIONS_VAR_ORDER_LABEL:
163       return sort_by_label (model, a, b, user_data);
164       break;
165     }
166
167   g_assert_not_reached ();
168 }
169
170
171
172
173 static void
174 set_model (PsppireDictView *dict_view)
175 {
176   GtkTreeModel *model = NULL;
177
178   if (dict_view->dict == NULL)
179     return;
180
181   dict_view->sorted_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (dict_view->dict));
182   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (dict_view->sorted_model), default_sort, dict_view, 0);
183   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dict_view->sorted_model),
184                                         GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
185
186    if (dict_view->predicate)
187     {
188       model = gtk_tree_model_filter_new (dict_view->sorted_model,        NULL);
189
190       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
191                                               filter_variables,
192                                               dict_view->predicate,
193                                               NULL);
194     }
195   else
196     {
197       model = dict_view->sorted_model;
198       g_object_ref (model);
199     }
200
201   gtk_tree_view_set_model (GTK_TREE_VIEW (dict_view), model);
202   g_object_unref (model);
203 }
204
205 static void
206 psppire_dict_view_set_property (GObject         *object,
207                                guint            prop_id,
208                                const GValue    *value,
209                                GParamSpec      *pspec)
210 {
211   PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
212
213   switch (prop_id)
214     {
215     case PROP_DICTIONARY:
216       dict_view->dict = g_value_get_object (value);
217       break;
218     case PROP_PREDICATE:
219       dict_view->predicate = g_value_get_pointer (value);
220       break;
221     case PROP_SELECTION_MODE:
222       {
223         GtkTreeSelection *selection =
224           gtk_tree_view_get_selection (GTK_TREE_VIEW (dict_view));
225
226         GtkSelectionMode mode = g_value_get_enum (value);
227
228         gtk_tree_selection_set_mode (selection, mode);
229       }
230       break;
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233       break;
234     };
235
236
237   set_model (dict_view);
238 }
239
240
241 static void
242 psppire_dict_view_get_property (GObject         *object,
243                                guint            prop_id,
244                                GValue          *value,
245                                GParamSpec      *pspec)
246 {
247   PsppireDictView *dict_view = PSPPIRE_DICT_VIEW (object);
248
249   switch (prop_id)
250     {
251     case PROP_DICTIONARY:
252       g_value_set_object (value, dict_view->dict);
253       break;
254     case PROP_PREDICATE:
255       g_value_set_pointer (value, dict_view->predicate);
256       break;
257     case PROP_SELECTION_MODE:
258       {
259         GtkTreeSelection *selection =
260           gtk_tree_view_get_selection (GTK_TREE_VIEW (dict_view));
261
262         g_value_set_enum (value, gtk_tree_selection_get_mode (selection));
263       }
264       break;
265     default:
266       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267       break;
268     };
269 }
270
271
272
273 static void
274 psppire_dict_view_class_init (PsppireDictViewClass *class)
275 {
276   GObjectClass *object_class = G_OBJECT_CLASS (class);
277
278   object_class->finalize = psppire_dict_view_finalize;
279
280   GParamSpec *predicate_spec =
281     g_param_spec_pointer ("predicate",
282                           "Predicate",
283                           "A predicate function",
284                           G_PARAM_READABLE | G_PARAM_WRITABLE);
285
286
287   GParamSpec *selection_mode_spec =
288     g_param_spec_enum ("selection-mode",
289                        "Selection Mode",
290                        "How many things can be selected",
291                        GTK_TYPE_SELECTION_MODE,
292                        GTK_SELECTION_MULTIPLE,
293                        G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
294
295   object_class->set_property = psppire_dict_view_set_property;
296   object_class->get_property = psppire_dict_view_get_property;
297
298   g_object_class_override_property (object_class,
299                                     PROP_DICTIONARY,
300                                     "model");
301
302   g_object_class_install_property (object_class,
303                                    PROP_PREDICATE,
304                                    predicate_spec);
305
306   g_object_class_install_property (object_class,
307                                    PROP_SELECTION_MODE,
308                                    selection_mode_spec);
309 }
310
311 static gboolean
312 use_labels (PsppireDictView *dv)
313 {
314   gboolean disp_labels = TRUE;
315
316   if (gtk_check_menu_item_get_inconsistent (GTK_CHECK_MENU_ITEM
317                                             (dv->override_button)))
318     {
319       psppire_conf_get_boolean (psppire_conf_new (),
320                                 "VariableLists", "display-labels", &disp_labels);
321     }
322   else
323     {
324       disp_labels = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM
325                                                     (dv->override_button));
326     }
327   return disp_labels;
328 }
329
330
331 /* A GtkTreeCellDataFunc which renders the name and/or label of the
332    variable */
333 static void
334 var_description_cell_data_func (GtkTreeViewColumn *col,
335                                 GtkCellRenderer *cell,
336                                 GtkTreeModel *top_model,
337                                 GtkTreeIter *top_iter,
338                                 gpointer data)
339 {
340   PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
341   struct variable *var;
342   GtkTreeIter iter;
343   GtkTreeModel *model;
344
345   get_base_model (top_model, top_iter, &model, &iter);
346
347   gtk_tree_model_get (model,
348                       &iter, DICT_TVM_COL_VAR, &var, -1);
349
350   if (var_has_label (var) && use_labels (dv))
351     {
352       gchar *text = g_markup_printf_escaped (
353                                      "<span stretch=\"condensed\">%s</span>",
354                                      var_get_label (var));
355
356       g_object_set (cell, "markup", text, NULL);
357       g_free (text);
358     }
359   else
360     {
361       g_object_set (cell, "text", var_get_name (var), NULL);
362     }
363
364   var_unref (var);
365 }
366
367
368
369 /* A GtkTreeCellDataFunc which sets the icon appropriate to the type
370    of variable */
371 static void
372 var_icon_cell_data_func (GtkTreeViewColumn *col,
373                        GtkCellRenderer *cell,
374                        GtkTreeModel *model,
375                        GtkTreeIter *iter,
376                        gpointer data)
377 {
378   struct variable *var;
379
380   gtk_tree_model_get (model, iter, DICT_TVM_COL_VAR, &var, -1);
381
382   g_object_set (cell,
383                 "stock-size", GTK_ICON_SIZE_MENU,
384                 "icon-name", get_var_measurement_stock_id (var_get_print_format (var)->type,
385                                                            var_get_measure (var)),
386                 NULL);
387
388   var_unref (var);
389 }
390
391 const char *
392 get_var_measurement_stock_id (enum fmt_type type, enum measure measure)
393 {
394   switch (fmt_get_category (type))
395     {
396     case FMT_CAT_STRING:
397       switch (measure)
398         {
399         case MEASURE_NOMINAL: return "measure-string-nominal";
400         case MEASURE_ORDINAL: return "measure-string-ordinal";
401         case MEASURE_SCALE:   return "role-none";
402         case n_MEASURES: break;
403         }
404       break;
405
406     case FMT_CAT_DATE:
407     case FMT_CAT_TIME:
408       switch (measure)
409         {
410         case MEASURE_NOMINAL: return "measure-date-nominal";
411         case MEASURE_ORDINAL: return "measure-date-ordinal";
412         case MEASURE_SCALE:   return "measure-date-scale";
413         case n_MEASURES: break;
414         }
415       break;
416
417     default:
418       switch (measure)
419         {
420         case MEASURE_NOMINAL: return "measure-nominal";
421         case MEASURE_ORDINAL: return "measure-ordinal";
422         case MEASURE_SCALE:   return "measure-scale";
423         case n_MEASURES: break;
424         }
425       break;
426     }
427
428   g_return_val_if_reached ("");
429 }
430
431
432
433 /* Sets the tooltip to be the name of the variable under the cursor */
434 static gboolean
435 set_tooltip_for_variable (GtkTreeView  *treeview,
436                           gint        x,
437                           gint        y,
438                           gboolean    keyboard_mode,
439                           GtkTooltip *tooltip,
440                           gpointer    user_data)
441 {
442   gint bx, by;
443   GtkTreeIter iter;
444   GtkTreePath *path;
445   GtkTreeModel *tree_model;
446   struct variable *var = NULL;
447   gboolean ok;
448
449   gtk_tree_view_convert_widget_to_bin_window_coords (treeview,
450                                                      x, y, &bx, &by);
451
452   if (!gtk_tree_view_get_path_at_pos (treeview, bx, by,
453                                       &path, NULL, NULL, NULL))
454     return FALSE;
455
456   tree_model = gtk_tree_view_get_model (treeview);
457
458   gtk_tree_view_set_tooltip_row (treeview, tooltip, path);
459
460   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
461
462   gtk_tree_path_free (path);
463   if (!ok)
464     return FALSE;
465
466   gtk_tree_model_get (tree_model, &iter, DICT_TVM_COL_VAR,  &var, -1);
467
468   if (! var_has_label (var))
469     {
470       var_unref (var);
471       return FALSE;
472     }
473
474   {
475     const gchar *tip ;
476     GtkTreeModel *m;
477
478     get_base_model (tree_model, NULL, &m, NULL);
479
480     if (use_labels (PSPPIRE_DICT_VIEW (treeview)))
481       tip = var_get_name (var);
482     else
483       tip = var_get_label (var);
484
485     gtk_tooltip_set_text (tooltip, tip);
486   }
487
488   var_unref (var);
489   return TRUE;
490 }
491
492 static gboolean
493 show_menu (PsppireDictView *dv, GdkEvent *event, gpointer data)
494 {
495   if (((GdkEventButton *) event)->button != 3)
496     return FALSE;
497
498   gtk_menu_popup_at_pointer (GTK_MENU (dv->menu), event);
499
500   return TRUE;
501 }
502
503 static void
504 toggle_label_preference (GtkCheckMenuItem *checkbox, gpointer data)
505 {
506   PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
507
508   gboolean global_setting = TRUE;
509   psppire_conf_get_boolean (psppire_conf_new (),
510                             "VariableLists", "display-labels", &global_setting);
511
512   if (gtk_check_menu_item_get_inconsistent (checkbox))
513     gtk_check_menu_item_set_active (checkbox, !global_setting);
514
515   gtk_check_menu_item_set_inconsistent (checkbox, FALSE);
516
517   gtk_widget_queue_draw (GTK_WIDGET (dv));
518 }
519
520
521 static void
522 set_sort_criteria (GtkCheckMenuItem *checkbox, PsppireDictView *dv, GtkTreeIterCompareFunc func)
523 {
524   if (!gtk_check_menu_item_get_active (checkbox))
525     {
526       gtk_widget_queue_draw (GTK_WIDGET (dv));
527       return;
528     }
529
530
531   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (dv->sorted_model), func, 0, 0);
532
533
534   gtk_widget_queue_draw (GTK_WIDGET (dv));
535 }
536
537 static void
538 set_sort_criteria_default (GtkCheckMenuItem *checkbox, gpointer data)
539 {
540   PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
541   set_sort_criteria (checkbox, dv, default_sort);
542 }
543
544
545 static void
546 set_sort_criteria_name (GtkCheckMenuItem *checkbox, gpointer data)
547 {
548   PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
549   set_sort_criteria (checkbox, dv, sort_by_name);
550 }
551
552
553 static void
554 set_sort_criteria_label (GtkCheckMenuItem *checkbox, gpointer data)
555 {
556   PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
557   set_sort_criteria (checkbox, dv, sort_by_label);
558 }
559
560
561 static void
562 set_sort_criteria_unsorted (GtkCheckMenuItem *checkbox, gpointer data)
563 {
564   PsppireDictView *dv = PSPPIRE_DICT_VIEW (data);
565   set_sort_criteria (checkbox, dv, unsorted);
566 }
567
568
569
570 static void
571 psppire_dict_view_init (PsppireDictView *dict_view)
572 {
573   GtkTreeViewColumn *col = gtk_tree_view_column_new ();
574
575   GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new ();
576
577   dict_view->prefer_labels_override = FALSE;
578   dict_view->sorted_model = NULL;
579
580   gtk_tree_view_column_set_title (col, _("Variable"));
581
582   gtk_tree_view_column_pack_start (col, renderer, FALSE);
583
584   gtk_tree_view_column_set_cell_data_func (col, renderer,
585                                            var_icon_cell_data_func,
586                                            NULL, NULL);
587
588   renderer = gtk_cell_renderer_text_new ();
589   gtk_tree_view_column_pack_start (col, renderer, TRUE);
590   gtk_tree_view_column_set_cell_data_func (col, renderer,
591                                            var_description_cell_data_func,
592                                            dict_view, NULL);
593
594   g_object_set (renderer, "ellipsize-set", TRUE, NULL);
595   g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
596
597   gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
598
599   /* FIXME: make this a value in terms of character widths */
600   gtk_tree_view_column_set_min_width (col, 150);
601
602   gtk_tree_view_append_column (GTK_TREE_VIEW (dict_view), col);
603
604   g_object_set (dict_view,
605                 "has-tooltip", TRUE,
606                 "headers-visible", FALSE,
607                 NULL);
608
609   g_signal_connect (dict_view, "query-tooltip",
610                     G_CALLBACK (set_tooltip_for_variable), NULL);
611
612   dict_view->menu = gtk_menu_new ();
613
614   {
615     GSList *group = NULL;
616     GtkWidget *item =
617       gtk_check_menu_item_new_with_label  (_("Prefer variable labels"));
618
619     dict_view->override_button = item;
620     gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (item),
621                                           TRUE);
622
623     g_signal_connect (item, "toggled",
624                       G_CALLBACK (toggle_label_preference), dict_view);
625
626     gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), item);
627
628     item = gtk_separator_menu_item_new ();
629     gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), item);
630
631     item = gtk_radio_menu_item_new_with_label (group, _("Default sort order"));
632     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
633     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
634     gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), item);
635     g_signal_connect (item, "toggled", G_CALLBACK (set_sort_criteria_default), dict_view);
636
637     item = gtk_radio_menu_item_new_with_label (group, _("Unsorted (dictionary order)"));
638     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
639     gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), item);
640     g_signal_connect (item, "toggled", G_CALLBACK (set_sort_criteria_unsorted), dict_view);
641
642     item = gtk_radio_menu_item_new_with_label (group, _("Sort by name"));
643     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
644     gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), item);
645     g_signal_connect (item, "toggled", G_CALLBACK (set_sort_criteria_name), dict_view);
646
647     item = gtk_radio_menu_item_new_with_label (group, _("Sort by label"));
648     group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
649     gtk_menu_shell_append (GTK_MENU_SHELL (dict_view->menu), item);
650     g_signal_connect (item, "toggled", G_CALLBACK (set_sort_criteria_label), dict_view);
651   }
652
653   gtk_widget_show_all (dict_view->menu);
654
655   g_signal_connect (dict_view, "button-press-event",
656                     G_CALLBACK (show_menu), NULL);
657 }
658
659 static struct variable *
660 psppire_dict_view_iter_to_var (PsppireDictView *dict_view,
661                                GtkTreeIter *top_iter)
662 {
663   GtkTreeView *treeview = GTK_TREE_VIEW (dict_view);
664   GtkTreeModel *top_model = gtk_tree_view_get_model (treeview);
665
666   struct variable *var;
667   GtkTreeModel *model;
668   GtkTreeIter iter;
669
670   get_base_model (top_model, top_iter, &model, &iter);
671
672   g_assert (PSPPIRE_IS_DICT (model));
673
674   gtk_tree_model_get (model,
675                       &iter, DICT_TVM_COL_VAR, &var, -1);
676
677   return var;
678 }
679
680 struct get_vars_aux
681   {
682     PsppireDictView *dict_view;
683     struct variable **vars;
684     size_t idx;
685   };
686
687 static void
688 get_vars_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
689              gpointer data)
690 {
691   struct get_vars_aux *aux = data;
692   struct variable *var = psppire_dict_view_iter_to_var (aux->dict_view, iter);
693
694   g_return_if_fail (var != NULL);
695   aux->vars[aux->idx++] = var;
696 }
697
698 void
699 psppire_dict_view_get_selected_variables (PsppireDictView *dict_view,
700                                           struct variable ***vars,
701                                           size_t *n_varsp)
702 {
703   GtkTreeView *tree_view = GTK_TREE_VIEW (dict_view);
704   GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
705   gint n_vars = gtk_tree_selection_count_selected_rows (selection);
706   struct get_vars_aux aux;
707
708   *vars = g_malloc_n (n_vars, sizeof **vars);
709
710   aux.dict_view = dict_view;
711   aux.vars = *vars;
712   aux.idx = 0;
713   gtk_tree_selection_selected_foreach (selection, get_vars_cb, &aux);
714
715   *n_varsp = aux.idx;
716   g_return_if_fail (aux.idx >= n_vars);
717 }
718
719 struct variable *
720 psppire_dict_view_get_selected_variable (PsppireDictView *dict_view)
721 {
722   GtkTreeView *tree_view = GTK_TREE_VIEW (dict_view);
723   GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
724   GtkTreeIter iter;
725
726   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
727     return psppire_dict_view_iter_to_var (dict_view, &iter);
728   else
729     return NULL;
730 }