1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2012 Free Software Foundation, Inc.
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 #include "psppire-value-entry.h"
19 #include "data/data-in.h"
20 #include "data/value-labels.h"
21 #include "data/variable.h"
22 #include "libpspp/cast.h"
23 #include "libpspp/i18n.h"
24 #include "ui/gui/helper.h"
25 #include "ui/gui/psppire-format.h"
26
27 static void psppire_value_entry_finalize (GObject *);
28
29 G_DEFINE_TYPE (PsppireValueEntry,
30 psppire_value_entry,
31 GTK_TYPE_COMBO_BOX);
32
33 enum
34 {
35 COL_LABEL, /* Value label string. */
36 COL_VALUE, /* union value *. */
37 N_COLUMNS
38 };
39
40 enum
41 {
42 PROP_0,
43 PROP_SHOW_VALUE_LABEL,
44 PROP_VARIABLE,
45 PROP_VALUE_LABELS,
46 PROP_FORMAT,
47 PROP_ENCODING,
48 PROP_WIDTH
49 };
50
51 enum {EDIT_DONE, /* Emitted when the entry has changed and is ready to be fetched */
52 n_SIGNALS};
53
54 static guint signals [n_SIGNALS];
55
56
57 static void
psppire_value_entry_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)58 psppire_value_entry_set_property (GObject *object,
59 guint prop_id,
60 const GValue *value,
61 GParamSpec *pspec)
62 {
63 PsppireValueEntry *obj = PSPPIRE_VALUE_ENTRY (object);
64
65 switch (prop_id)
66 {
67 case PROP_SHOW_VALUE_LABEL:
68 psppire_value_entry_set_show_value_label (obj,
69 g_value_get_boolean (value));
70 break;
71
72 case PROP_VARIABLE:
73 psppire_value_entry_set_variable (obj, g_value_get_pointer (value));
74 break;
75
76 case PROP_VALUE_LABELS:
77 psppire_value_entry_set_value_labels (obj, g_value_get_pointer (value));
78 break;
79
80 case PROP_FORMAT:
81 psppire_value_entry_set_format (obj, g_value_get_boxed (value));
82 break;
83
84 case PROP_ENCODING:
85 psppire_value_entry_set_encoding (obj, g_value_get_string (value));
86 break;
87
88 case PROP_WIDTH:
89 psppire_value_entry_set_width (obj, g_value_get_int (value));
90 break;
91
92 default:
93 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
94 break;
95 }
96 }
97
98 static void
psppire_value_entry_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)99 psppire_value_entry_get_property (GObject *object,
100 guint prop_id,
101 GValue *value,
102 GParamSpec *pspec)
103 {
104 PsppireValueEntry *obj = PSPPIRE_VALUE_ENTRY (object);
105
106 switch (prop_id)
107 {
108 case PROP_SHOW_VALUE_LABEL:
109 g_value_set_boolean (value,
110 psppire_value_entry_get_show_value_label (obj));
111 break;
112
113 case PROP_VARIABLE:
114 g_return_if_reached ();
115
116 case PROP_VALUE_LABELS:
117 g_value_set_pointer (value, obj->val_labs);
118 break;
119
120 case PROP_FORMAT:
121 g_value_set_boxed (value, &obj->format);
122 break;
123
124 case PROP_ENCODING:
125 g_value_set_string (value, psppire_value_entry_get_encoding (obj));
126 break;
127
128 case PROP_WIDTH:
129 g_value_set_int (value, psppire_value_entry_get_width (obj));
130 break;
131
132 default:
133 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
134 break;
135 }
136 }
137
138 static void
psppire_value_entry_text_changed(GtkEntryBuffer * buffer,GParamSpec * pspec,PsppireValueEntry * obj)139 psppire_value_entry_text_changed (GtkEntryBuffer *buffer,
140 GParamSpec *pspec,
141 PsppireValueEntry *obj)
142 {
143 obj->cur_value = NULL;
144 }
145
146 static void
on_entry_activate(GtkWidget * w)147 on_entry_activate (GtkWidget *w)
148 {
149 g_signal_emit (w, signals [EDIT_DONE], 0);
150 }
151
152 static void
on_realize(GtkWidget * w)153 on_realize (GtkWidget *w)
154 {
155 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (w)));
156 GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry);
157
158 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (w), COL_LABEL);
159
160 g_signal_connect (buffer, "notify::text",
161 G_CALLBACK (psppire_value_entry_text_changed), w);
162
163 g_signal_connect_swapped (entry, "activate",
164 G_CALLBACK (on_entry_activate), w);
165
166 gtk_widget_set_can_focus (GTK_WIDGET (entry), TRUE);
167
168 GTK_WIDGET_CLASS (psppire_value_entry_parent_class)->realize (w);
169 }
170
171
172 /*
173 The "has-entry" property for the parent class (GTK_COMBO_BOX) is
174 a) Construct-only ; and b) defaults to FALSE.
175 We want it to default to TRUE. So we override it here.
176 */
177 static GObject*
my_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)178 my_constructor (GType type,
179 guint n_construct_properties,
180 GObjectConstructParam *construct_properties)
181 {
182 GObject *o =
183 G_OBJECT_CLASS (psppire_value_entry_parent_class)->constructor
184 (type, n_construct_properties, construct_properties);
185
186 g_object_set (o, "has-entry", TRUE, NULL);
187
188 return o;
189 }
190
191 static void
psppire_value_entry_class_init(PsppireValueEntryClass * class)192 psppire_value_entry_class_init (PsppireValueEntryClass *class)
193 {
194 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
195 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
196
197
198 gobject_class->finalize = psppire_value_entry_finalize;
199 gobject_class->set_property = psppire_value_entry_set_property;
200 gobject_class->get_property = psppire_value_entry_get_property;
201 gobject_class->constructor = my_constructor;
202 widget_class->realize = on_realize;
203
204 g_object_class_install_property (
205 gobject_class, PROP_SHOW_VALUE_LABEL,
206 g_param_spec_boolean ("show-value-label",
207 "Show Value Label",
208 "If true, a value that has a value label is shown "
209 "as the label. If false, all values are shown "
210 "literally.",
211 TRUE,
212 G_PARAM_WRITABLE | G_PARAM_READABLE));
213
214 g_object_class_install_property (
215 gobject_class, PROP_VARIABLE,
216 g_param_spec_pointer ("variable",
217 "Variable",
218 "Set to configure the PsppireValueEntry according "
219 "to the specified variable's value labels, format, "
220 "width, and encoding.",
221 G_PARAM_WRITABLE));
222
223 g_object_class_install_property (
224 gobject_class, PROP_VALUE_LABELS,
225 g_param_spec_pointer ("value-labels",
226 "Value Labels",
227 "The set of value labels from which the user may "
228 "choose and which is used to display the value (if "
229 "value labels are to be displayed)",
230 G_PARAM_READABLE | G_PARAM_WRITABLE));
231
232 g_object_class_install_property (
233 gobject_class, PROP_FORMAT,
234 g_param_spec_boxed ("format",
235 "Format",
236 "The format used to display values (that are not "
237 "displayed as value labels) and to interpret values "
238 "entered.",
239 PSPPIRE_TYPE_FORMAT,
240 G_PARAM_READABLE | G_PARAM_WRITABLE));
241
242 g_object_class_install_property (
243 gobject_class, PROP_ENCODING,
244 g_param_spec_string ("encoding",
245 "Encoding",
246 "The encoding (e.g. \"UTF-8\") for string values. "
247 "For numeric values this setting has no effect.",
248 "UTF-8",
249 G_PARAM_READABLE | G_PARAM_WRITABLE));
250
251 g_object_class_install_property (
252 gobject_class, PROP_WIDTH,
253 g_param_spec_int ("width",
254 "Width",
255 "Width of the value, either 0 for a numeric value or "
256 "a positive integer count of bytes for string values.",
257 0, MAX_STRING,
258 0,
259 G_PARAM_READABLE | G_PARAM_WRITABLE));
260
261 signals [EDIT_DONE] =
262 g_signal_new ("edit-done",
263 G_TYPE_FROM_CLASS (class),
264 G_SIGNAL_RUN_FIRST,
265 0,
266 NULL, NULL,
267 g_cclosure_marshal_VOID__VOID,
268 G_TYPE_NONE,
269 0);
270 }
271
272 static void
psppire_value_entry_init(PsppireValueEntry * obj)273 psppire_value_entry_init (PsppireValueEntry *obj)
274 {
275 obj->show_value_label = true;
276 obj->val_labs = NULL;
277 obj->format = F_8_0;
278 obj->encoding = NULL;
279 obj->cur_value = NULL;
280 }
281
282 static void
psppire_value_entry_finalize(GObject * gobject)283 psppire_value_entry_finalize (GObject *gobject)
284 {
285 PsppireValueEntry *obj = PSPPIRE_VALUE_ENTRY (gobject);
286
287 val_labs_destroy (obj->val_labs);
288 g_free (obj->encoding);
289
290 G_OBJECT_CLASS (psppire_value_entry_parent_class)->finalize (gobject);
291 }
292
293 GtkWidget *
psppire_value_entry_new(void)294 psppire_value_entry_new (void)
295 {
296 return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_VALUE_ENTRY, NULL));
297 }
298
299 static void
psppire_value_entry_refresh_model(PsppireValueEntry * obj)300 psppire_value_entry_refresh_model (PsppireValueEntry *obj)
301 {
302 GtkTreeModel *model;
303 GtkTreeModel *old_model;
304
305 if (val_labs_count (obj->val_labs) > 0)
306 {
307 const struct val_lab **vls = val_labs_sorted (obj->val_labs);
308 size_t n_vls = val_labs_count (obj->val_labs);
309
310 GtkListStore *list_store;
311 size_t i;
312
313 list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
314 model = GTK_TREE_MODEL (list_store);
315 for (i = 0; i < n_vls; i++)
316 {
317 const struct val_lab *vl = vls[i];
318 GtkTreeIter iter;
319
320 gtk_list_store_append (list_store, &iter);
321 gtk_list_store_set (list_store, &iter,
322 COL_LABEL, val_lab_get_label (vl),
323 COL_VALUE, val_lab_get_value (vl),
324 -1);
325 }
326 free (vls);
327 }
328 else
329 model = NULL;
330
331 old_model = gtk_combo_box_get_model (GTK_COMBO_BOX (obj));
332
333 if (old_model != model)
334 {
335 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (obj)));
336 gtk_entry_set_text (entry, "");
337 }
338
339 gtk_combo_box_set_model (GTK_COMBO_BOX (obj), model);
340 if (model != NULL)
341 g_object_unref (model);
342 }
343
344 void
psppire_value_entry_set_show_value_label(PsppireValueEntry * obj,gboolean show_value_label)345 psppire_value_entry_set_show_value_label (PsppireValueEntry *obj,
346 gboolean show_value_label)
347 {
348 if (obj->show_value_label != show_value_label)
349 {
350 obj->show_value_label = show_value_label;
351 g_object_notify (G_OBJECT (obj), "show-value-label");
352 }
353 }
354
355 gboolean
psppire_value_entry_get_show_value_label(const PsppireValueEntry * obj)356 psppire_value_entry_get_show_value_label (const PsppireValueEntry *obj)
357 {
358 return obj->show_value_label;
359 }
360
361 void
psppire_value_entry_set_variable(PsppireValueEntry * obj,const struct variable * var)362 psppire_value_entry_set_variable (PsppireValueEntry *obj,
363 const struct variable *var)
364 {
365 if (var != NULL)
366 {
367 psppire_value_entry_set_value_labels (obj, var_get_value_labels (var));
368 obj->format = *var_get_print_format (var);
369 psppire_value_entry_set_encoding (obj, var_get_encoding (var));
370 }
371 else
372 psppire_value_entry_set_value_labels (obj, NULL);
373 }
374
375 void
psppire_value_entry_set_value_labels(PsppireValueEntry * obj,const struct val_labs * val_labs)376 psppire_value_entry_set_value_labels (PsppireValueEntry *obj,
377 const struct val_labs *val_labs)
378 {
379 if (!val_labs_equal (obj->val_labs, val_labs))
380 {
381 obj->cur_value = NULL;
382
383 val_labs_destroy (obj->val_labs);
384 obj->val_labs = val_labs_clone (val_labs);
385
386 if (val_labs != NULL)
387 {
388 int width = val_labs_get_width (val_labs);
389 if (width != fmt_var_width (&obj->format))
390 obj->format = fmt_default_for_width (width);
391 }
392
393 psppire_value_entry_refresh_model (obj);
394
395 g_object_notify (G_OBJECT (obj), "value-labels");
396 }
397 }
398
399 const struct val_labs *
psppire_value_entry_get_value_labels(const PsppireValueEntry * obj)400 psppire_value_entry_get_value_labels (const PsppireValueEntry *obj)
401 {
402 return obj->val_labs;
403 }
404
405 void
psppire_value_entry_set_format(PsppireValueEntry * obj,const struct fmt_spec * format)406 psppire_value_entry_set_format (PsppireValueEntry *obj,
407 const struct fmt_spec *format)
408 {
409 if (!fmt_equal (format, &obj->format))
410 {
411 obj->cur_value = NULL;
412 obj->format = *format;
413
414 if (obj->val_labs
415 && val_labs_get_width (obj->val_labs) != fmt_var_width (format))
416 psppire_value_entry_set_value_labels (obj, NULL);
417
418 g_object_notify (G_OBJECT (obj), "format");
419 }
420 }
421
422 const struct fmt_spec *
psppire_value_entry_get_format(const PsppireValueEntry * obj)423 psppire_value_entry_get_format (const PsppireValueEntry *obj)
424 {
425 return &obj->format;
426 }
427
428 void
psppire_value_entry_set_encoding(PsppireValueEntry * obj,const gchar * encoding)429 psppire_value_entry_set_encoding (PsppireValueEntry *obj,
430 const gchar *encoding)
431 {
432 g_free (obj->encoding);
433 obj->encoding = encoding != NULL ? g_strdup (encoding) : NULL;
434
435 g_object_notify (G_OBJECT (obj), "encoding");
436 }
437
438 const gchar *
psppire_value_entry_get_encoding(const PsppireValueEntry * obj)439 psppire_value_entry_get_encoding (const PsppireValueEntry *obj)
440 {
441 return obj->encoding ? obj->encoding : UTF8;
442 }
443
444 void
psppire_value_entry_set_width(PsppireValueEntry * obj,int width)445 psppire_value_entry_set_width (PsppireValueEntry *obj, int width)
446 {
447 if (width != fmt_var_width (&obj->format))
448 {
449 struct fmt_spec format = fmt_default_for_width (width);
450 psppire_value_entry_set_format (obj, &format);
451 }
452 }
453
454 int
psppire_value_entry_get_width(const PsppireValueEntry * obj)455 psppire_value_entry_get_width (const PsppireValueEntry *obj)
456 {
457 return fmt_var_width (&obj->format);
458 }
459
460 void
psppire_value_entry_set_value(PsppireValueEntry * obj,const union value * value,int width)461 psppire_value_entry_set_value (PsppireValueEntry *obj,
462 const union value *value,
463 int width)
464 {
465 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (obj)));
466 gchar *string;
467
468 obj->cur_value = NULL;
469
470 if (value == NULL)
471 return;
472
473 if (obj->show_value_label)
474 {
475 struct val_lab *vl = val_labs_lookup (obj->val_labs, value);
476 if (vl != NULL)
477 {
478 gtk_entry_set_text (entry, val_lab_get_label (vl));
479 obj->cur_value = val_lab_get_value (vl);
480 return;
481 }
482 }
483
484 string = value_to_text__ (*value, &obj->format, obj->encoding);
485 gtk_entry_set_text (entry, string);
486 g_free (string);
487 }
488
489 gboolean
psppire_value_entry_get_value(PsppireValueEntry * obj,union value * value,int width)490 psppire_value_entry_get_value (PsppireValueEntry *obj,
491 union value *value,
492 int width)
493 {
494 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (obj)));
495 GtkTreeIter iter;
496
497 g_return_val_if_fail (fmt_var_width (&obj->format) == width, FALSE);
498
499 if (obj->cur_value)
500 {
501 value_copy (value, obj->cur_value, width);
502 return TRUE;
503 }
504 else if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (obj), &iter))
505 {
506 union value *v;
507
508 gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (obj)), &iter,
509 COL_VALUE, &v,
510 -1);
511 value_copy (value, v, width);
512 return TRUE;
513 }
514 else
515 {
516 const gchar *new_text;
517
518 new_text = gtk_entry_get_text (entry);
519 return data_in_msg (ss_cstr (new_text), UTF8,
520 obj->format.type,
521 value, width, obj->encoding);
522 }
523 }
524