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