1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011 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
18 #include <config.h>
19 #include <gtk/gtk.h>
20 #include "psppire-scanf.h"
21
22 #include <gl/printf-parse.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include "xalloc.h"
27
28
29 static void psppire_scanf_class_init (PsppireScanfClass *class);
30 static void psppire_scanf_init (PsppireScanf *w);
31
32 G_DEFINE_TYPE (PsppireScanf, psppire_scanf, GTK_TYPE_BOX)
33
34 /* Properties */
35 enum
36 {
37 PROP_0,
38 PROP_FORMAT,
39 PROP_NCONV,
40 PROP_USE_UNDERLINE,
41 PROP_MNEMONIC_WIDGET
42 };
43
44 /* Create a GtkLabel and pack it into BOX.
45 The label is created using part of the string at S, and the directives
46 at DIRS[DIR_IDX] and subsequent.
47
48 After this function returns, *S points to the first unused character.
49 */
50 static void
51 ship_label (PsppireScanf *box, const char **s,
52 const char_directives *dirs, size_t dir_idx)
53 {
54 GtkWidget *label ;
55 GString *str = g_string_new (*s);
56
57 if (dirs)
58 {
59 char_directive dir = dirs->dir[dir_idx];
60 int n = 0;
61
62 while (dir_idx < dirs->count && dir.conversion == '%')
63 {
64 g_string_erase (str, dir.dir_start - *s, 1);
65 dir = dirs->dir[++dir_idx];
66 n++;
67 }
68
69 g_string_truncate (str, dir.dir_start - *s - n);
70
71 if (dir_idx >= dirs->count)
72 *s = NULL;
73 else
74 *s = dir.dir_end;
EVP_bf_cfb(void)75 }
76
77 label = gtk_label_new (str->str);
78
79 g_string_free (str, TRUE);
80
81 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
82 gtk_widget_show (label);
83 }
84
EVP_des_cfb(void)85 static void
86 guts (PsppireScanf *scanf)
87 {
88 gint i;
89 arguments a;
90 const char *s = scanf->format;
91
92 /* Get the number of args into D */
93 g_return_if_fail (0 == printf_parse (scanf->format, &scanf->d, &a));
94
95 if (scanf->d.count > 0)
96 scanf->widgets = xcalloc (scanf->d.count, sizeof (*scanf->widgets));
97
98 /* A is not used, so get rid of it */
99 if (a.arg != a.direct_alloc_arg)
100 free (a.arg);
101
102 for (i = 0 ; i < scanf->d.count ; ++i)
103 {
104 GtkWidget **w;
105 char_directive dir = scanf->d.dir[i];
106 int precision = 0;
107 int width = 0;
108
109 if (dir.precision_start && dir.precision_end)
110 precision = g_ascii_strtoll (dir.precision_start + 1,
111 (char **) &dir.precision_end, 10);
112
113 if (dir.width_start && dir.width_end)
114 width = g_ascii_strtoll (dir.width_start, (char **) &dir.width_end, 10);
115
116 if (dir.dir_start > s)
117 ship_label (scanf, &s, &scanf->d, i);
118
119 if (dir.conversion == '%')
120 {
121 if (s) s++;
122 continue;
123 }
124
125 w = &scanf->widgets [dir.arg_index];
126 switch (dir.conversion)
127 {
128 case 'd':
129 case 'i':
130 case 'f':
131 {
132 *w = gtk_spin_button_new_with_range (0, 100.0, 1.0);
133 g_object_set (*w, "digits", precision, NULL);
134 }
135 break;
136 case 's':
137 *w = gtk_entry_new ();
138 break;
139 };
140 g_object_set (*w, "width-chars", width, NULL);
141 gtk_box_pack_start (GTK_BOX (scanf), *w, FALSE, FALSE, 0);
142 gtk_widget_show (*w);
143 }
144
145 if (s && *s)
146 ship_label (scanf, &s, NULL, 0);
147
148 }
149
150
151 static void
152 set_mnemonic (PsppireScanf *scanf)
EVP_aes_256_cfb(void)153 {
154 if (scanf->use_underline || scanf->mnemonic_widget)
155 {
156 GList *l = gtk_container_get_children (GTK_CONTAINER (scanf));
157 while (l)
158 {
159 if (GTK_IS_LABEL (l->data))
160 {
161 const gchar *t = gtk_label_get_label (l->data);
162 if (g_strstr_len (t, -1, "_"))
163 {
164 g_object_set (l->data,
165 "use-underline", TRUE,
166 "mnemonic-widget", scanf->mnemonic_widget,
167 NULL);
168
169 break;
170 }
171 }
172 l = l->next;
173 }
174 g_list_free (l);
175 }
176 }
177
178 static void
179 psppire_scanf_set_property (GObject *object,
180 guint prop_id,
181 const GValue *value,
182 GParamSpec *pspec)
183 {
184 PsppireScanf *scanf = PSPPIRE_SCANF (object);
185
186 switch (prop_id)
187 {
188 case PROP_FORMAT:
189 scanf->format = g_value_get_string (value);
190 guts (scanf);
191 break;
192 case PROP_MNEMONIC_WIDGET:
193 scanf->mnemonic_widget = g_value_get_object (value);
194 set_mnemonic (scanf);
195 break;
196 case PROP_USE_UNDERLINE:
197 scanf->use_underline = g_value_get_boolean (value);
198 set_mnemonic (scanf);
199 break;
200 default:
201 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202 break;
203 };
204 }
205
206
207 static void
208 psppire_scanf_get_property (GObject *object,
209 guint prop_id,
210 GValue *value,
211 GParamSpec *pspec)
212 {
213 PsppireScanf *scanf = PSPPIRE_SCANF (object);
214
215 switch (prop_id)
216 {
217 case PROP_FORMAT:
218 g_value_set_string (value, scanf->format);
219 break;
220 case PROP_NCONV:
221 g_value_set_int (value, scanf->d.count);
222 break;
223 case PROP_USE_UNDERLINE:
224 g_value_set_boolean (value, scanf->use_underline);
225 break;
226 case PROP_MNEMONIC_WIDGET:
227 g_value_set_object (value, scanf->mnemonic_widget);
228 break;
229 default:
230 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231 break;
232 };
233 }
234
235
236 static GObjectClass *parent_class = NULL;
237
238 static void
239 psppire_scanf_dispose (GObject *obj)
240 {
241 PsppireScanf *w = PSPPIRE_SCANF (obj);
242
243 if (w->dispose_has_run)
244 return;
245
246 /* Make sure dispose does not run twice. */
247 w->dispose_has_run = TRUE;
248
249
250 /* Chain up to the parent class */
251 G_OBJECT_CLASS (parent_class)->dispose (obj);
252 }
253
254 static void
255 psppire_scanf_finalize (GObject *obj)
256 {
257 PsppireScanf *w = PSPPIRE_SCANF (obj);
258
259 free (w->widgets);
260
261 if (w->d.dir != w->d.direct_alloc_dir)
262 free (w->d.dir);
263
264 /* Chain up to the parent class */
265 G_OBJECT_CLASS (parent_class)->finalize (obj);
266 }
267
268 static void
269 psppire_scanf_class_init (PsppireScanfClass *class)
270 {
271 GObjectClass *object_class = G_OBJECT_CLASS (class);
272
273 GParamSpec *format_spec =
274 g_param_spec_string ("format",
275 "Format",
276 "A Scanf style format string",
277 NULL,
278 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
279
280 GParamSpec *nconv_spec =
281 g_param_spec_int ("n-conv",
282 "Conversions",
283 "The number of conversions in the format string",
284 0, G_MAXINT, 0,
285 G_PARAM_READABLE);
286
287
288 GParamSpec *use_underline_spec =
289 g_param_spec_boolean ("use-underline",
290 "Use Underline",
291 "If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key",
292 FALSE,
293 G_PARAM_READWRITE);
294
295
296 GParamSpec *mnemonic_widget_spec =
297 g_param_spec_object ("mnemonic-widget",
298 "Mnemonic widget",
299 "The widget which is to be activated when the Scanf's mnemonic key is pressed. Has no effect if use-underline is false.",
300 GTK_TYPE_WIDGET,
301 G_PARAM_READWRITE);
302
303
304 parent_class = g_type_class_peek_parent (class);
305
306 object_class->dispose = psppire_scanf_dispose;
307 object_class->finalize = psppire_scanf_finalize;
308
309 object_class->set_property = psppire_scanf_set_property;
310 object_class->get_property = psppire_scanf_get_property;
311
312 g_object_class_install_property (object_class,
313 PROP_NCONV,
314 nconv_spec);
315
316 g_object_class_install_property (object_class,
317 PROP_FORMAT,
318 format_spec);
319
320 g_object_class_install_property (object_class,
321 PROP_USE_UNDERLINE,
322 use_underline_spec);
323
324 g_object_class_install_property (object_class,
325 PROP_MNEMONIC_WIDGET,
326 mnemonic_widget_spec);
327 }
328
329
330
331 static void
332 psppire_scanf_init (PsppireScanf *w)
333 {
334 w->dispose_has_run = FALSE;
335
336 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), GTK_ORIENTATION_HORIZONTAL);
337 }
338
339 static gchar
340 psppire_get_conversion_char (PsppireScanf *w, gint n)
341 {
342 g_return_val_if_fail (n < w->d.count, '\0');
343 return w->d.dir[n].conversion;
344 }
345
346 GtkWidget *
347 psppire_scanf_get_child (PsppireScanf *w, gint n)
348 {
349 g_return_val_if_fail (n < w->d.count, NULL);
350 return w->widgets[n];
351 }
352
353
354 /*
355 This widget is a horizontal GtkBox populated with GtkLabel and GtkEntry widgets.
356 Each conversion in FMT will cause a GtkEntry (possibly a GtkSpinButton) to
357 be created. Any text between conversions produces a GtkLabel.
358 There should be N arguments following FMT should be of type GtkEntry **,
359 where N is the number of conversions.
360 These arguments will be filled with a pointer to the corresponding widgets.
361 Their properties may be changed, but they should not be unrefed.
362 */
363 GtkWidget *
364 psppire_scanf_new (const gchar *fmt, ...)
365 {
366 gint n, i;
367 va_list ap;
368
369 GtkWidget *w = GTK_WIDGET (g_object_new (psppire_scanf_get_type (),
370 "format", fmt, NULL));
371
372 g_object_get (w, "n-conv", &n, NULL);
373
374 va_start (ap, fmt);
375
376 for (i = 0 ; i < n ; ++i)
377 {
378 GtkWidget **field;
379
380 if (psppire_get_conversion_char (PSPPIRE_SCANF (w), i) == '%')
381 continue;
382
383 field = va_arg (ap, GtkWidget **);
384
385 *field = psppire_scanf_get_child (PSPPIRE_SCANF (w), i);
386 }
387 va_end (ap);
388
389 return w;
390 }
391