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