1 /* $XConsortium: makeform.c,v 1.6 95/01/04 16:28:51 gildea Exp $ */
2 /*
3
4 Copyright (c) 1988, 1991 X Consortium
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24
25 Except as contained in this notice, the name of the X Consortium shall
26 not be used in advertising or otherwise to promote the sale, use or
27 other dealings in this Software without prior written authorization
28 from the X Consortium.
29
30 */
31 /* $XFree86: xc/programs/xmessage/makeform.c,v 1.6 2002/11/22 03:56:39 paulo Exp $ */
32
33 #include <X11/Intrinsic.h>
34 #include <X11/StringDefs.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include <X11/Shell.h>
39 #include <X11/Xaw/Form.h>
40 #include <X11/Xaw/AsciiText.h>
41 #include <X11/Xaw/Command.h>
42 #include <X11/Xaw/Label.h>
43 #include <X11/Xaw/Scrollbar.h>
44
45 #include "xmessage.h"
46
47 typedef struct _ButtonRecord {
48 char *name;
49 int exitstatus;
50 Boolean print_value;
51 Widget widget;
52 } ButtonRecord;
53
54 static void
unquote_pairs(ButtonRecord * br,int n)55 unquote_pairs (ButtonRecord *br, int n)
56 {
57 int i;
58
59 for (i = 0; i < n; i++, br++) {
60 char *dst, *src;
61 int quoted = 0;
62
63 for (src = dst = br->name; *src; src++) {
64 if (quoted) {
65 *dst++ = *src;
66 quoted = 0;
67 } else if (src[0] == '\\') {
68 quoted = 1;
69 } else {
70 *dst++ = *src;
71 }
72 }
73 *dst = '\0';
74 }
75 return;
76 }
77
78 /*
79 * parses string of form "yes:11,no:12, ..."
80 * sets brptr to point to parsed table
81 * returns 0 if successful, -1 if not
82 */
83 static int
parse_name_and_exit_code_list(char * buttonlist,ButtonRecord ** brptr)84 parse_name_and_exit_code_list (char *buttonlist, ButtonRecord **brptr)
85 {
86 char *cp;
87 int shouldfind = 0, npairs = 0;
88 int default_exitcode = 100;
89 int quoted = 0;
90 ButtonRecord *br;
91 int len;
92 char *copy;
93
94 *brptr = NULL;
95 if (!buttonlist) return 0;
96
97 /*
98 * Figure out how many matches we will find so that we can preallocate
99 * space for button structures. If you add stripping of white space,
100 * make sure that you update this as well as the walking algorithm below.
101 */
102 if (buttonlist[0]) shouldfind++;
103 for (cp = buttonlist; *cp; cp++) {
104 if (quoted == 1) quoted = 0;
105 else if (*cp == '\\') quoted = 1;
106 else if (*cp == ',') shouldfind++;
107 }
108 len = (cp - buttonlist);
109
110 /*
111 * allocate space for button record
112 */
113 br = (ButtonRecord *) malloc (sizeof(ButtonRecord) * shouldfind);
114 if (!br) return -1;
115
116 cp = malloc (len + 1);
117 if (!cp) {
118 (void) free ((char *) br);
119 return -1;
120 }
121 copy = cp;
122 strcpy (copy, buttonlist);
123
124
125 /*
126 * walk down list separating into name:exitcode pairs
127 */
128 while (*cp) {
129 char *start, *colon, *comma;
130 int exitcode;
131
132 start = cp;
133 colon = comma = NULL;
134 exitcode = ++default_exitcode;
135 quoted = 0;
136
137 /* find the next name and exit code */
138 for (; *cp; cp++) {
139 if (quoted) quoted = 0;
140 else if (*cp == '\\') quoted = 1;
141 else if (*cp == ':') colon = cp;
142 else if (*cp == ',') {
143 comma = cp;
144 break;
145 }
146 }
147
148 /*
149 * If comma is NULL then we are at the end of the string. If colon
150 * is NULL, then there was no exit code given, so default to zero.
151 */
152
153 if (comma) *comma = '\0';
154
155 if (colon) {
156 exitcode = atoi (colon+1);
157 *colon = '\0';
158 }
159
160 /*
161 * make sure that we aren't about to stomp on memory
162 */
163 if (npairs >= shouldfind) {
164 fprintf (stderr,
165 "%s: internal error, found extra pairs (should be %d)\n",
166 ProgramName, shouldfind);
167 (void) free ((char *) br);
168 (void) free (copy);
169 return -1;
170 }
171
172 /*
173 * got it! start and exitcode contain the right values
174 */
175 br[npairs].name = start;
176 br[npairs].exitstatus = exitcode;
177 npairs++;
178
179 if (comma) cp++;
180 }
181
182
183 if (npairs != shouldfind) {
184 fprintf (stderr, "%s: internal error found %d instead of %d pairs\n",
185 ProgramName, npairs, shouldfind);
186 (void) free ((char *) br);
187 (void) free (copy);
188 return -1;
189 }
190
191 /*
192 * now, strip any quoted characters
193 */
194 unquote_pairs (br, npairs);
195 *brptr = br;
196 return npairs;
197 }
198
199 /* ARGSUSED */
200 static void
handle_button(Widget w,XtPointer closure,XtPointer client_data)201 handle_button (Widget w, XtPointer closure, XtPointer client_data)
202 {
203 ButtonRecord *br = (ButtonRecord *) closure;
204
205 if (br->print_value)
206 puts (br->name);
207 exit (br->exitstatus);
208 }
209
210 Widget
make_queryform(Widget parent,char * msgstr,int msglen,char * button_list,Boolean print_value,char * default_button,Dimension max_width,Dimension max_height)211 make_queryform(Widget parent, /* into whom widget should be placed */
212 char *msgstr, /* message string */
213 int msglen, /* characters in msgstr */
214 char *button_list, /* list of button title:status */
215 Boolean print_value, /* print button string on stdout? */
216 char *default_button, /* button activated by Return */
217 Dimension max_width,
218 Dimension max_height)
219 {
220 ButtonRecord *br;
221 int npairs, i;
222 Widget form, text, prev;
223 Arg args[10];
224 Cardinal n, thisn;
225 char *shell_geom;
226 int x, y, geom_flags;
227 unsigned int shell_w, shell_h;
228
229 npairs = parse_name_and_exit_code_list (button_list, &br);
230
231 form = XtCreateManagedWidget ("form", formWidgetClass, parent, NULL, 0);
232
233 text = XtVaCreateManagedWidget
234 ("message", asciiTextWidgetClass, form,
235 XtNleft, XtChainLeft,
236 XtNright, XtChainRight,
237 XtNtop, XtChainTop,
238 XtNbottom, XtChainBottom,
239 XtNdisplayCaret, False,
240 XtNlength, msglen,
241 XtNstring, msgstr,
242 NULL);
243 /*
244 * Did the user specify our geometry?
245 * If so, don't bother computing it ourselves, since we will be overridden.
246 */
247 XtVaGetValues(parent, XtNgeometry, &shell_geom, NULL);
248 geom_flags = XParseGeometry(shell_geom, &x, &y, &shell_w, &shell_h);
249 if (!(geom_flags & WidthValue && geom_flags & HeightValue)) {
250 Dimension width, height, height_addons = 0;
251 Dimension scroll_size, border_width;
252 Widget label, scroll;
253 Position left, right, top, bottom;
254 char *tmp;
255 /*
256 * A Text widget is used for the automatic scroll bars.
257 * But Text widget doesn't automatically compute its size.
258 * The Label widget does that nicely, so we create one and examine it.
259 * This widget is never visible.
260 */
261 XtVaGetValues(text, XtNtopMargin, &top, XtNbottomMargin, &bottom,
262 XtNleftMargin, &left, XtNrightMargin, &right, NULL);
263 label = XtVaCreateWidget("message", labelWidgetClass, form,
264 XtNlabel, msgstr,
265 XtNinternalWidth, (left+right+1)/2,
266 XtNinternalHeight, (top+bottom+1)/2,
267 NULL);
268 XtVaGetValues(label, XtNwidth, &width, XtNheight, &height, NULL);
269 XtDestroyWidget(label);
270 if (max_width == 0)
271 max_width = .7 * WidthOfScreen(XtScreen(text));
272 if (max_height == 0)
273 max_height = .7 * HeightOfScreen(XtScreen(text));
274 if (width > max_width) {
275 width = max_width;
276 /* add in the height of any horizontal scroll bar */
277 scroll = XtVaCreateWidget("hScrollbar", scrollbarWidgetClass, text,
278 XtNorientation, XtorientHorizontal,
279 NULL);
280 XtVaGetValues(scroll, XtNheight, &scroll_size,
281 XtNborderWidth, &border_width, NULL);
282 XtDestroyWidget(scroll);
283 height_addons = scroll_size + border_width;
284 }
285
286 /* This fixes the xmessage assumption that the label widget and the
287 * text widget have the same size. In Xaw 7, the text widget has
288 * one extra pixel between lines.
289 * Xmessage is not internationalized, so the code bellow is harmless.
290 */
291 tmp = msgstr;
292 while (tmp != NULL && *tmp) {
293 ++tmp;
294 ++height;
295 tmp = strchr(tmp, '\n');
296 }
297
298 if (height > max_height) {
299 height = max_height;
300 /* add in the width of any vertical scroll bar */
301 scroll = XtVaCreateWidget("vScrollbar", scrollbarWidgetClass, text,
302 XtNorientation, XtorientVertical, NULL);
303 XtVaGetValues(scroll, XtNwidth, &scroll_size,
304 XtNborderWidth, &border_width, NULL);
305 XtDestroyWidget(scroll);
306 width += scroll_size + border_width;
307 }
308 height += height_addons;
309 XtVaSetValues(text, XtNwidth, width, XtNheight, height, NULL);
310 }
311 /*
312 * Create the buttons
313 */
314 n = 0;
315 XtSetArg (args[n], XtNleft, XtChainLeft); n++;
316 XtSetArg (args[n], XtNright, XtChainLeft); n++;
317 XtSetArg (args[n], XtNtop, XtChainBottom); n++;
318 XtSetArg (args[n], XtNbottom, XtChainBottom); n++;
319 XtSetArg (args[n], XtNfromVert, text); n++;
320 XtSetArg (args[n], XtNvertDistance, 5); n++;
321
322 prev = NULL;
323 for (i = 0; i < npairs; i++) {
324 thisn = n;
325 XtSetArg (args[thisn], XtNfromHoriz, prev); thisn++;
326 prev = XtCreateManagedWidget (br[i].name, commandWidgetClass,
327 form, args, thisn);
328 br[i].widget = prev;
329 br[i].print_value = print_value;
330 XtAddCallback (prev, XtNcallback, handle_button, (XtPointer) &br[i]);
331 if (default_button && !strcmp(default_button, br[i].name)) {
332 Dimension border;
333
334 default_exitstatus = br[i].exitstatus;
335 XtVaGetValues(br[i].widget, XtNborderWidth, &border, NULL);
336 border *= 2;
337 XtVaSetValues(br[i].widget, XtNborderWidth, border, NULL);
338 }
339 }
340 return form;
341 }
342