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