Properly escape marked up text in dictionary views
[pspp-builds.git] / src / ui / gui / dict-display.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007  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
18 #include <config.h>
19 #include <gettext.h>
20 #include <gtk/gtk.h>
21
22 #include "dict-display.h"
23
24 #include "psppire-dict.h"
25 #include "helper.h"
26 #include <data/variable.h>
27 #include <data/format.h>
28
29 #define _(msgid) gettext (msgid)
30 #define N_(msgid) msgid
31
32
33 /* A GtkTreeModelFilterVisibleFunc to filter lines in the treeview */
34 static gboolean
35 filter_variables (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
36 {
37   var_predicate_func *predicate = data;
38   struct variable *var;
39   PsppireDict *dict = PSPPIRE_DICT (model);
40
41   GtkTreePath *path = gtk_tree_model_get_path (model, iter);
42
43   gint *idx = gtk_tree_path_get_indices (path);
44
45   var =  psppire_dict_get_variable (dict, *idx);
46
47   gtk_tree_path_free (path);
48
49   return predicate (var);
50 }
51
52 /* A GtkTreeCellDataFunc which sets the icon appropriate to the type
53    of variable */
54 static void
55 var_icon_cell_data_func (GtkTreeViewColumn *col,
56                        GtkCellRenderer *cell,
57                        GtkTreeModel *model,
58                        GtkTreeIter *iter,
59                        gpointer data)
60 {
61   struct variable *var;
62   gtk_tree_model_get (model, iter, DICT_TVM_COL_VAR, &var, -1);
63
64   if ( var_is_alpha (var))
65     {
66       g_object_set (cell, "stock-id", "var-string", NULL);
67     }
68   else
69     {
70       const struct fmt_spec *fs = var_get_write_format (var);
71       int cat = fmt_get_category (fs->type);
72       switch ( var_get_measure (var))
73         {
74         case MEASURE_NOMINAL:
75           g_object_set (cell, "stock-id", "var-nominal", NULL);
76           break;
77         case MEASURE_ORDINAL:
78           g_object_set (cell, "stock-id", "var-ordinal", NULL);
79           break;
80         case MEASURE_SCALE:
81           if ( ( FMT_CAT_DATE | FMT_CAT_TIME ) & cat )
82             g_object_set (cell, "stock-id", "var-date-scale", NULL);
83           else
84             g_object_set (cell, "stock-id", "var-scale", NULL);
85           break;
86         default:
87           g_assert_not_reached ();
88         };
89     }
90 }
91
92
93 void
94 get_base_model (GtkTreeModel *top_model, GtkTreeIter *top_iter,
95                 GtkTreeModel **model, GtkTreeIter *iter
96                 )
97 {
98   *model = top_model;
99   *iter = *top_iter;
100   while (GTK_IS_TREE_MODEL_FILTER (*model))
101     {
102       GtkTreeIter parent_iter = *iter;
103       GtkTreeModelFilter *parent_model = GTK_TREE_MODEL_FILTER (*model);
104
105       *model = gtk_tree_model_filter_get_model (parent_model);
106
107       gtk_tree_model_filter_convert_iter_to_child_iter (parent_model,
108                                                         iter,
109                                                         &parent_iter);
110     }
111
112   g_assert (PSPPIRE_IS_DICT (*model));
113 }
114
115 /* A GtkTreeCellDataFunc which renders the name and/or label of the
116    variable */
117 static void
118 var_description_cell_data_func (GtkTreeViewColumn *col,
119                                 GtkCellRenderer *cell,
120                                 GtkTreeModel *top_model,
121                                 GtkTreeIter *top_iter,
122                                 gpointer data)
123 {
124   struct variable *var;
125   GtkTreeIter iter;
126   GtkTreeModel *model;
127
128
129   get_base_model (top_model, top_iter, &model, &iter);
130
131   g_assert (PSPPIRE_IS_DICT (model));
132
133
134   gtk_tree_model_get (model,
135                       &iter, DICT_TVM_COL_VAR, &var, -1);
136
137   if ( var_has_label (var))
138     {
139       gchar *text = g_markup_printf_escaped (
140                                      "<span stretch=\"condensed\">%s</span>",
141                                      var_get_label (var));
142
143
144       char *utf8 = pspp_locale_to_utf8 (text, -1, NULL);
145
146       g_free (text);
147       g_object_set (cell, "markup", utf8, NULL);
148       g_free (utf8);
149     }
150   else
151     {
152       g_object_set (cell, "text", var_get_name (var), NULL);
153     }
154 }
155
156
157 #if GTK_CHECK_VERSION (2, 12, 0)
158 /* Sets the tooltip to be the name of the variable under the cursor */
159 static gboolean
160 set_tooltip_for_variable (GtkTreeView  *treeview,
161                           gint        x,
162                           gint        y,
163                           gboolean    keyboard_mode,
164                           GtkTooltip *tooltip,
165                           gpointer    user_data)
166
167 {
168   gint bx, by;
169   GtkTreeIter iter;
170   GtkTreePath *path;
171   GtkTreeModel *tree_model;
172   struct variable *var = NULL;
173   gboolean ok;
174
175
176   gtk_tree_view_convert_widget_to_bin_window_coords (treeview,
177                                                      x, y, &bx, &by);
178
179   if (!gtk_tree_view_get_path_at_pos (treeview, bx, by,
180                                       &path, NULL, NULL, NULL))
181     return FALSE;
182
183   tree_model = gtk_tree_view_get_model (treeview);
184
185
186   gtk_tree_view_set_tooltip_row (treeview, tooltip, path);
187
188   ok = gtk_tree_model_get_iter (tree_model, &iter, path);
189
190   gtk_tree_path_free (path);
191   if (!ok)
192     return FALSE;
193
194
195   gtk_tree_model_get (tree_model, &iter, DICT_TVM_COL_VAR,  &var, -1);
196
197   if ( ! var_has_label (var))
198     return FALSE;
199
200   gtk_tooltip_set_text (tooltip, var_get_name (var));
201
202   return TRUE;
203 }
204 #endif
205
206    /* Sets up TREEVIEW to display the variables of DICT.
207    MODE is the selection mode for TREEVIEW.
208    PREDICATE determines which variables should be visible, or NULL if
209    all are to be visible.
210  */
211 void
212 attach_dictionary_to_treeview (GtkTreeView *treeview, PsppireDict *dict,
213                                GtkSelectionMode mode,
214                                var_predicate_func *predicate
215                                )
216 {
217   GtkTreeViewColumn *col;
218
219   GtkTreeSelection *selection =
220     gtk_tree_view_get_selection (treeview);
221
222   GtkCellRenderer *renderer;
223
224   GtkTreeModel *model ;
225
226   if ( predicate )
227     {
228       model = gtk_tree_model_filter_new (GTK_TREE_MODEL (dict),
229                                           NULL);
230
231       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
232                                               filter_variables,
233                                               predicate,
234                                               NULL);
235     }
236   else
237     {
238       model = GTK_TREE_MODEL (dict);
239     }
240
241   gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), model);
242
243
244   col = gtk_tree_view_column_new ();
245   gtk_tree_view_column_set_title (col, _("Variable"));
246
247   renderer = gtk_cell_renderer_pixbuf_new ();
248   gtk_tree_view_column_pack_start (col, renderer, FALSE);
249
250   gtk_tree_view_column_set_cell_data_func (col, renderer,
251                                            var_icon_cell_data_func,
252                                            NULL, NULL);
253
254
255   renderer = gtk_cell_renderer_text_new ();
256   gtk_tree_view_column_pack_start (col, renderer, TRUE);
257   gtk_tree_view_column_set_cell_data_func (col, renderer,
258                                            var_description_cell_data_func,
259                                            NULL, NULL);
260
261   g_object_set (renderer, "ellipsize-set", TRUE, NULL);
262   g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
263
264   gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
265
266   /* FIXME: make this a value in terms of character widths */
267   gtk_tree_view_column_set_min_width (col, 150);
268
269   gtk_tree_view_append_column (treeview, col);
270
271   gtk_tree_selection_set_mode (selection, mode);
272
273   g_object_set (treeview, "has-tooltip", TRUE, NULL);
274
275 #if GTK_CHECK_VERSION (2, 12, 0)
276   g_signal_connect (treeview, "query-tooltip", G_CALLBACK (set_tooltip_for_variable), NULL);
277 #endif
278 }
279
280
281 void
282 insert_source_row_into_entry (GtkTreeIter iter,
283                               GtkWidget *dest,
284                               GtkTreeModel *model,
285                               gpointer data
286                               )
287 {
288   GtkTreePath *path;
289   GtkTreeModel *dict;
290   gint *idx;
291   struct variable *var;
292   GtkTreeIter dict_iter;
293   gchar *name;
294
295   g_return_if_fail (GTK_IS_ENTRY(dest));
296
297   get_base_model (model, &iter, &dict, &dict_iter);
298
299   path = gtk_tree_model_get_path (GTK_TREE_MODEL (dict), &dict_iter);
300
301   idx = gtk_tree_path_get_indices (path);
302
303   var =  psppire_dict_get_variable (PSPPIRE_DICT (dict), *idx);
304
305   gtk_tree_path_free (path);
306
307   name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
308   gtk_entry_set_text (GTK_ENTRY (dest),  name);
309   g_free (name);
310 }
311
312
313
314 void
315 insert_source_row_into_tree_view (GtkTreeIter iter,
316                                   GtkWidget *dest,
317                                   GtkTreeModel *model,
318                                   gpointer data
319                                   )
320 {
321   GtkTreePath *path;
322   GtkTreeIter dest_iter;
323   GtkTreeIter dict_iter;
324   gint *row ;
325   GtkTreeModel *destmodel = gtk_tree_view_get_model ( GTK_TREE_VIEW (dest));
326
327   GtkTreeModel *dict;
328
329
330   get_base_model (model, &iter, &dict, &dict_iter);
331
332   path = gtk_tree_model_get_path (dict, &dict_iter);
333
334   row = gtk_tree_path_get_indices (path);
335
336   gtk_list_store_append (GTK_LIST_STORE (destmodel),  &dest_iter);
337   gtk_list_store_set (GTK_LIST_STORE (destmodel), &dest_iter, 0, *row, -1);
338
339   gtk_tree_path_free (path);
340 }
341
342
343 gboolean
344 is_currently_in_entry (GtkTreeModel *model, GtkTreeIter *iter,
345                        PsppireSelector *selector)
346 {
347   gboolean result;
348   gchar *name;
349   GtkTreeIter dict_iter;
350   GtkTreeModel *dict;
351   struct variable *var;
352   gint dict_index;
353   gint *indeces;
354   GtkTreePath *path;
355   const gchar *text =   gtk_entry_get_text (GTK_ENTRY (selector->dest));
356
357   get_base_model (model, iter, &dict, &dict_iter);
358
359   path = gtk_tree_model_get_path (dict, &dict_iter);
360
361   indeces = gtk_tree_path_get_indices (path);
362
363   dict_index = indeces [0];
364
365   var = psppire_dict_get_variable (PSPPIRE_DICT (dict), dict_index);
366
367   gtk_tree_path_free (path);
368
369   name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
370   result = ( 0 == strcmp (text, name));
371   g_free (name);
372
373   return result;
374 }
375
376