Merge commit 'origin/stable'
[pspp-builds.git] / src / ui / gui / widget-io.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009  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   g_free (a.arg);
104
105   output = g_string_sized_new (strlen (fmt));
106
107   for (i = 0 ; i < d.count ; ++i )
108     {
109       char_directive dir = d.dir[i];
110       GtkWidget *w ;
111       const gchar *entry_text;
112
113       if ( dir.conversion == '%')
114         {
115           s++;
116           continue;
117         }
118
119       w = widgets [dir.arg_index];
120       entry_text = gtk_entry_get_text (GTK_ENTRY (w));
121
122       if ( dir.dir_start > s )
123         g_string_append_len (output, s, dir.dir_start - s);
124
125       s = dir.dir_end;
126
127       g_string_append (output, entry_text);
128     }
129
130   free (widgets);
131   free (d.dir);
132
133   if (*s)
134     g_string_append_len (output, s, -1);
135
136   text = output->str;
137   g_string_free (output, FALSE);
138   return text;
139 }
140
141 /*
142    Returns a GtkHBox populated with an GtkLabel and GtkEntry widgets.
143    Each conversion in FMT will cause a GtkEntry (possibly a GtkSpinButton) to
144    be created.  Any text between conversions produces a GtkLabel.
145    There should be N arguments following FMT should be of type GtkEntry **,
146    where N is the number of conversions.
147    These arguments will be filled with a pointer to the corresponding widgets.
148    Their properties may be changed, but they should not be unrefed.
149  */
150 GtkWidget *
151 widget_scanf (const gchar *fmt, ...)
152 {
153   char_directives d;
154   arguments a;
155   int i;
156   va_list ap;
157   GtkWidget ***widgets = NULL;
158   GtkWidget *hbox = NULL;
159   GtkWidget **w;
160   const char *s = fmt;
161
162   if ( 0 !=  printf_parse (fmt, &d, &a) )
163     return NULL;
164
165   g_free (a.arg);
166
167   va_start (ap, fmt);
168
169   if ( d.count > 0 )
170     {
171       hbox = gtk_hbox_new (FALSE, 0);
172       widgets = calloc (sizeof (*widgets), d.count);
173     }
174
175   for (i = 0 ; i < d.count ; ++i )
176     {
177       if ( d.dir[i].conversion != '%')
178         widgets[i] = va_arg (ap, GtkWidget **);
179     }
180   va_end (ap);
181
182
183
184   for (i = 0 ; i < d.count ; ++i )
185     {
186       char_directive dir = d.dir[i];
187       int precision = 0;
188       int width = 0;
189
190
191       if ( dir.precision_start && dir.precision_end)
192         precision = strtol (dir.precision_start + 1,
193                             (char **) &dir.precision_end, 10);
194
195       if ( dir.width_start && dir.width_end )
196         width = strtol (dir.width_start, (char **) &dir.width_end, 10);
197
198       if ( dir.dir_start > s )
199         ship_label (GTK_BOX (hbox), &s, &d, i);
200
201       if ( dir.conversion == '%')
202         {
203           if (s) s++;
204           continue;
205         }
206
207       w = widgets [dir.arg_index];
208       switch (dir.conversion)
209         {
210         case 'd':
211         case 'i':
212         case 'f':
213           {
214             *w = gtk_spin_button_new_with_range (0, 100.0, 1.0);
215             g_object_set (*w, "digits", precision, NULL);
216           }
217           break;
218         case 's':
219           *w = gtk_entry_new ();
220           break;
221         };
222
223       g_object_set (*w, "width-chars", width, NULL);
224       gtk_box_pack_start (GTK_BOX (hbox), *w, FALSE, FALSE, 0);
225       gtk_widget_show (*w);
226     }
227
228   if ( s && *s )
229     ship_label (GTK_BOX (hbox), &s, NULL, 0);
230
231
232
233   g_free (widgets);
234
235   free (d.dir);
236
237   return hbox;
238 }