1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "psppire-scanf.h"
21 #include <gl/printf-parse.h>
30 static void psppire_scanf_class_init (PsppireScanfClass *class);
31 static void psppire_scanf_init (PsppireScanf *w);
33 G_DEFINE_TYPE (PsppireScanf, psppire_scanf, GTK_TYPE_HBOX)
43 /* Create a GtkLabel and pack it into BOX.
44 The label is created using part of the string at S, and the directives
45 at DIRS[DIR_IDX] and subsequent.
47 After this function returns, *S points to the first unused character.
50 ship_label (GtkBox *box, const char **s,
51 const char_directives *dirs, size_t dir_idx)
54 GString *str = g_string_new (*s);
58 char_directive dir = dirs->dir[dir_idx];
61 while (dir_idx < dirs->count && dir.conversion == '%' )
63 g_string_erase (str, dir.dir_start - *s, 1);
64 dir = dirs->dir[++dir_idx];
68 g_string_truncate (str, dir.dir_start - *s - n);
70 if ( dir_idx >= dirs->count)
76 label = gtk_label_new (str->str);
78 g_string_free (str, TRUE);
80 gtk_box_pack_start (box, label, FALSE, FALSE, 0);
81 gtk_widget_show (label);
85 guts (PsppireScanf *scanf)
89 const char *s = scanf->format;
91 /* Get the number of args into D */
92 g_return_if_fail (0 == printf_parse (scanf->format, &scanf->d, &a));
94 if ( scanf->d.count > 0)
95 scanf->widgets = xcalloc (sizeof (*scanf->widgets), scanf->d.count);
97 /* A is not used, so get rid of it */
98 if (a.arg != a.direct_alloc_arg)
101 for (i = 0 ; i < scanf->d.count ; ++i )
104 char_directive dir = scanf->d.dir[i];
108 if ( dir.precision_start && dir.precision_end)
109 precision = strtol (dir.precision_start + 1,
110 (char **) &dir.precision_end, 10);
112 if ( dir.width_start && dir.width_end )
113 width = strtol (dir.width_start, (char **) &dir.width_end, 10);
115 if ( dir.dir_start > s )
116 ship_label (GTK_BOX (scanf), &s, &scanf->d, i);
118 if ( dir.conversion == '%')
124 w = &scanf->widgets [dir.arg_index];
125 switch (dir.conversion)
131 *w = gtk_spin_button_new_with_range (0, 100.0, 1.0);
132 g_object_set (*w, "digits", precision, NULL);
136 *w = gtk_entry_new ();
139 g_object_set (*w, "width-chars", width, NULL);
140 gtk_box_pack_start (GTK_BOX (scanf), *w, FALSE, FALSE, 0);
141 gtk_widget_show (*w);
145 ship_label (GTK_BOX (scanf), &s, NULL, 0);
151 psppire_scanf_set_property (GObject *object,
156 PsppireScanf *scanf = PSPPIRE_SCANF (object);
161 scanf->format = g_value_get_string (value);
165 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172 psppire_scanf_get_property (GObject *object,
177 PsppireScanf *scanf = PSPPIRE_SCANF (object);
182 g_value_set_string (value, scanf->format);
185 g_value_set_int (value, scanf->d.count);
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194 static GObjectClass *parent_class = NULL;
197 psppire_scanf_dispose (GObject *obj)
199 PsppireScanf *w = (PsppireScanf *)obj;
201 if (w->dispose_has_run)
204 /* Make sure dispose does not run twice. */
205 w->dispose_has_run = TRUE;
208 /* Chain up to the parent class */
209 G_OBJECT_CLASS (parent_class)->dispose (obj);
213 psppire_scanf_finalize (GObject *obj)
215 PsppireScanf *w = PSPPIRE_SCANF (obj);
219 if (w->d.dir != w->d.direct_alloc_dir)
222 /* Chain up to the parent class */
223 G_OBJECT_CLASS (parent_class)->finalize (obj);
227 psppire_scanf_class_init (PsppireScanfClass *class)
229 GObjectClass *object_class = G_OBJECT_CLASS (class);
231 GParamSpec *format_spec =
232 g_param_spec_string ("format",
234 "A Scanf style format string",
236 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
238 GParamSpec *nconv_spec =
239 g_param_spec_int ("n-conv",
241 "The number of conversions in the format string",
246 parent_class = g_type_class_peek_parent (class);
248 object_class->dispose = psppire_scanf_dispose;
249 object_class->finalize = psppire_scanf_finalize;
251 object_class->set_property = psppire_scanf_set_property;
252 object_class->get_property = psppire_scanf_get_property;
254 g_object_class_install_property (object_class,
258 g_object_class_install_property (object_class,
267 psppire_scanf_init (PsppireScanf *w)
272 psppire_get_conversion_char (PsppireScanf *w, gint n)
274 g_return_val_if_fail ( n < w->d.count, '\0');
275 return w->d.dir[n].conversion;
279 psppire_scanf_get_child (PsppireScanf *w, gint n)
281 g_return_val_if_fail ( n < w->d.count, NULL);
282 return w->widgets[n];
287 This widget is a GtkHBox populated with GtkLabel and GtkEntry widgets.
288 Each conversion in FMT will cause a GtkEntry (possibly a GtkSpinButton) to
289 be created. Any text between conversions produces a GtkLabel.
290 There should be N arguments following FMT should be of type GtkEntry **,
291 where N is the number of conversions.
292 These arguments will be filled with a pointer to the corresponding widgets.
293 Their properties may be changed, but they should not be unrefed.
296 psppire_scanf_new (const gchar *fmt, ...)
301 GtkWidget *w = GTK_WIDGET (g_object_new (psppire_scanf_get_type (),
302 "format", fmt, NULL));
304 g_object_get (w, "n-conv", &n, NULL);
308 for (i = 0 ; i < n ; ++i )
312 if ( psppire_get_conversion_char (PSPPIRE_SCANF (w), i) == '%')
315 field = va_arg (ap, GtkWidget **);
317 *field = psppire_scanf_get_child (PSPPIRE_SCANF (w), i);