gui: widget-io: Fix cleanup code in widget_printf(), widget_scanf().
[pspp-builds.git] / src / ui / gui / widget-io.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009, 2011  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 #include <config.h>
18
19 #include "widget-io.h"
20
21 #include <string.h>
22 #include <stdlib.h>
23 #include <gl/printf-parse.h>
24 #include <stdarg.h>
25 #include <gtk/gtk.h>
26
27 #include <gl/gettext.h>
28
29 #include "xalloc.h"
30
31 /* Create a GtkLabel and pack it into BOX.
32    The label is created using part of the string at S, and the directives
33    at DIRS[DIR_IDX] and subsequent.
34
35    After this function returns, *S points to the first unused character.
36 */
37 static void
38 ship_label (GtkBox *box, const char **s,
39             const char_directives *dirs, size_t dir_idx)
40 {
41   GtkWidget *label ;
42   GString *str = g_string_new (*s);
43
44   if ( dirs)
45     {
46       char_directive dir = dirs->dir[dir_idx];
47       int n = 0;
48
49       while (dir_idx < dirs->count && dir.conversion == '%' )
50         {
51           g_string_erase (str, dir.dir_start - *s, 1);
52           dir = dirs->dir[++dir_idx];
53           n++;
54         }
55
56       g_string_truncate (str, dir.dir_start - *s - n);
57
58       if ( dir_idx >= dirs->count)
59         *s = NULL;
60       else
61         *s = dir.dir_end;
62     }
63
64   label = gtk_label_new (str->str);
65
66   g_string_free (str, TRUE);
67
68   gtk_box_pack_start (box, label, FALSE, FALSE, 0);
69   gtk_widget_show (label);
70 }
71
72 /* Returns a string generated from FMT and a list of GtkEntry widgets.
73    Each conversion in FMT will be replaced with the text from the
74    corresponding GtkEntry.  The normal printf semantics will be ignored.
75    Note that the GtkEntry widgets may be GtkSpinbuttons or any other widget
76    derived from GtkEntry.
77    The returned string should be freed when no longer required.
78  */
79 gchar *
80 widget_printf (const gchar *fmt, ...)
81 {
82   gint i;
83   char_directives d;
84   arguments a;
85   GString *output;
86   GtkWidget **widgets;
87   gchar *text;
88   va_list ap;
89   const char *s = fmt;
90
91   if ( 0 !=  printf_parse (fmt, &d, &a) )
92     return NULL;
93
94   widgets = xcalloc (sizeof (*widgets), d.count);
95   va_start (ap, fmt);
96   for (i = 0 ; i < d.count ; ++i )
97     {
98       if ( d.dir[i].conversion != '%')
99         widgets[i] = va_arg (ap, GtkWidget *);
100     }
101   va_end (ap);
102
103   if (a.arg != a.direct_alloc_arg)
104     free (a.arg);
105
106   output = g_string_sized_new (strlen (fmt));
107
108   for (i = 0 ; i < d.count ; ++i )
109     {
110       char_directive dir = d.dir[i];
111       GtkWidget *w ;
112       const gchar *entry_text;
113
114       if ( dir.conversion == '%')
115         {
116           s++;
117           continue;
118         }
119
120       w = widgets [dir.arg_index];
121       entry_text = gtk_entry_get_text (GTK_ENTRY (w));
122
123       if ( dir.dir_start > s )
124         g_string_append_len (output, s, dir.dir_start - s);
125
126       s = dir.dir_end;
127
128       g_string_append (output, entry_text);
129     }
130
131   free (widgets);
132   if (d.dir != d.direct_alloc_dir)
133     free (d.dir);
134
135   if (*s)
136     g_string_append_len (output, s, -1);
137
138   text = output->str;
139   g_string_free (output, FALSE);
140   return text;
141 }
142
143 /*
144    Returns a GtkHBox populated with an GtkLabel and GtkEntry widgets.
145    Each conversion in FMT will cause a GtkEntry (possibly a GtkSpinButton) to
146    be created.  Any text between conversions produces a GtkLabel.
147    There should be N arguments following FMT should be of type GtkEntry **,
148    where N is the number of conversions.
149    These arguments will be filled with a pointer to the corresponding widgets.
150    Their properties may be changed, but they should not be unrefed.
151  */
152 GtkWidget *
153 widget_scanf (const gchar *fmt, ...)
154 {
155   char_directives d;
156   arguments a;
157   int i;
158   va_list ap;
159   GtkWidget ***widgets = NULL;
160   GtkWidget *hbox = NULL;
161   GtkWidget **w;
162   const char *s = fmt;
163
164   if ( 0 !=  printf_parse (fmt, &d, &a) )
165     return NULL;
166
167   if (a.arg != a.direct_alloc_arg)
168     free (a.arg);
169
170   va_start (ap, fmt);
171
172   if ( d.count > 0 )
173     {
174       hbox = gtk_hbox_new (FALSE, 0);
175       widgets = calloc (sizeof (*widgets), d.count);
176     }
177
178   for (i = 0 ; i < d.count ; ++i )
179     {
180       if ( d.dir[i].conversion != '%')
181         widgets[i] = va_arg (ap, GtkWidget **);
182     }
183   va_end (ap);
184
185
186
187   for (i = 0 ; i < d.count ; ++i )
188     {
189       char_directive dir = d.dir[i];
190       int precision = 0;
191       int width = 0;
192
193
194       if ( dir.precision_start && dir.precision_end)
195         precision = strtol (dir.precision_start + 1,
196                             (char **) &dir.precision_end, 10);
197
198       if ( dir.width_start && dir.width_end )
199         width = strtol (dir.width_start, (char **) &dir.width_end, 10);
200
201       if ( dir.dir_start > s )
202         ship_label (GTK_BOX (hbox), &s, &d, i);
203
204       if ( dir.conversion == '%')
205         {
206           if (s) s++;
207           continue;
208         }
209
210       w = widgets [dir.arg_index];
211       switch (dir.conversion)
212         {
213         case 'd':
214         case 'i':
215         case 'f':
216           {
217             *w = gtk_spin_button_new_with_range (0, 100.0, 1.0);
218             g_object_set (*w, "digits", precision, NULL);
219           }
220           break;
221         case 's':
222           *w = gtk_entry_new ();
223           break;
224         };
225
226       g_object_set (*w, "width-chars", width, NULL);
227       gtk_box_pack_start (GTK_BOX (hbox), *w, FALSE, FALSE, 0);
228       gtk_widget_show (*w);
229     }
230
231   if ( s && *s )
232     ship_label (GTK_BOX (hbox), &s, NULL, 0);
233
234
235
236   g_free (widgets);
237
238   if (d.dir != d.direct_alloc_dir)
239     free (d.dir);
240
241   return hbox;
242 }