1100ae103Smrg/* $XConsortium: makeform.c,v 1.6 95/01/04 16:28:51 gildea Exp $ */
2100ae103Smrg/*
3100ae103Smrg
4100ae103SmrgCopyright (c) 1988, 1991  X Consortium
5100ae103Smrg
6100ae103SmrgPermission is hereby granted, free of charge, to any person obtaining
7100ae103Smrga copy of this software and associated documentation files (the
8100ae103Smrg"Software"), to deal in the Software without restriction, including
9100ae103Smrgwithout limitation the rights to use, copy, modify, merge, publish,
10100ae103Smrgdistribute, sublicense, and/or sell copies of the Software, and to
11100ae103Smrgpermit persons to whom the Software is furnished to do so, subject to
12100ae103Smrgthe following conditions:
13100ae103Smrg
14100ae103SmrgThe above copyright notice and this permission notice shall be included
15100ae103Smrgin all copies or substantial portions of the Software.
16100ae103Smrg
17100ae103SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18100ae103SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19100ae103SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20100ae103SmrgIN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
21100ae103SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22100ae103SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23100ae103SmrgOTHER DEALINGS IN THE SOFTWARE.
24100ae103Smrg
25100ae103SmrgExcept as contained in this notice, the name of the X Consortium shall
26100ae103Smrgnot be used in advertising or otherwise to promote the sale, use or
27100ae103Smrgother dealings in this Software without prior written authorization
28100ae103Smrgfrom the X Consortium.
29100ae103Smrg
30100ae103Smrg*/
31100ae103Smrg/* $XFree86: xc/programs/xmessage/makeform.c,v 1.6 2002/11/22 03:56:39 paulo Exp $ */
32100ae103Smrg
33100ae103Smrg#include <X11/Intrinsic.h>
34100ae103Smrg#include <X11/StringDefs.h>
35100ae103Smrg#include <stdio.h>
36100ae103Smrg#include <stdlib.h>
37100ae103Smrg
38100ae103Smrg#include <X11/Shell.h>
39100ae103Smrg#include <X11/Xaw/Form.h>
40100ae103Smrg#include <X11/Xaw/AsciiText.h>
41100ae103Smrg#include <X11/Xaw/Command.h>
42100ae103Smrg#include <X11/Xaw/Label.h>
43100ae103Smrg#include <X11/Xaw/Scrollbar.h>
44100ae103Smrg
45100ae103Smrg#include "xmessage.h"
46100ae103Smrg
47100ae103Smrgtypedef struct _ButtonRecord {
48100ae103Smrg    char *name;
49100ae103Smrg    int exitstatus;
50100ae103Smrg    Boolean print_value;
51100ae103Smrg    Widget widget;
52100ae103Smrg} ButtonRecord;
53100ae103Smrg
54100ae103Smrgstatic void
55100ae103Smrgunquote_pairs (ButtonRecord *br, int n)
56100ae103Smrg{
57100ae103Smrg    int i;
58100ae103Smrg
59165cb819Smrg    for (i = 0; i < n; i++, br++) {
60100ae103Smrg	char *dst, *src;
61100ae103Smrg	int quoted = 0;
62100ae103Smrg
63100ae103Smrg	for (src = dst = br->name; *src; src++) {
64100ae103Smrg	    if (quoted) {
65100ae103Smrg		*dst++ = *src;
66100ae103Smrg		quoted = 0;
67100ae103Smrg	    } else if (src[0] == '\\') {
68100ae103Smrg		quoted = 1;
69100ae103Smrg	    } else {
70100ae103Smrg		*dst++ = *src;
71100ae103Smrg	    }
72100ae103Smrg	}
73100ae103Smrg	*dst = '\0';
74100ae103Smrg    }
75100ae103Smrg    return;
76100ae103Smrg}
77100ae103Smrg
78100ae103Smrg/*
79100ae103Smrg * parses string of form "yes:11,no:12, ..."
80100ae103Smrg * sets brptr to point to parsed table
81100ae103Smrg * returns 0 if successful, -1 if not
82100ae103Smrg */
83100ae103Smrgstatic int
84100ae103Smrgparse_name_and_exit_code_list (char *buttonlist, ButtonRecord **brptr)
85100ae103Smrg{
86100ae103Smrg    char *cp;
87100ae103Smrg    int shouldfind = 0, npairs = 0;
88100ae103Smrg    int default_exitcode = 100;
89100ae103Smrg    int quoted = 0;
90100ae103Smrg    ButtonRecord *br;
91100ae103Smrg    int len;
92100ae103Smrg    char *copy;
93100ae103Smrg
94165cb819Smrg    *brptr = NULL;
95100ae103Smrg    if (!buttonlist) return 0;
96100ae103Smrg
97100ae103Smrg    /*
98100ae103Smrg     * Figure out how many matches we will find so that we can preallocate
99100ae103Smrg     * space for button structures.  If you add stripping of white space,
100100ae103Smrg     * make sure that you update this as well as the walking algorithm below.
101100ae103Smrg     */
102100ae103Smrg    if (buttonlist[0]) shouldfind++;
103100ae103Smrg    for (cp = buttonlist; *cp; cp++) {
104100ae103Smrg	if (quoted == 1) quoted = 0;
105100ae103Smrg	else if (*cp == '\\') quoted = 1;
106100ae103Smrg	else if (*cp == ',') shouldfind++;
107100ae103Smrg    }
108100ae103Smrg    len = (cp - buttonlist);
109100ae103Smrg
110100ae103Smrg    /*
111100ae103Smrg     * allocate space for button record
112100ae103Smrg     */
113100ae103Smrg    br = (ButtonRecord *) malloc (sizeof(ButtonRecord) * shouldfind);
114100ae103Smrg    if (!br) return -1;
115100ae103Smrg
116100ae103Smrg    cp = malloc (len + 1);
117100ae103Smrg    if (!cp) {
1180103645bSmrg	free (br);
119100ae103Smrg	return -1;
120100ae103Smrg    }
121100ae103Smrg    copy = cp;
122100ae103Smrg    strcpy (copy, buttonlist);
123100ae103Smrg
124100ae103Smrg
125100ae103Smrg    /*
126100ae103Smrg     * walk down list separating into name:exitcode pairs
127100ae103Smrg     */
128100ae103Smrg    while (*cp) {
129100ae103Smrg	char *start, *colon, *comma;
130100ae103Smrg	int exitcode;
131100ae103Smrg
132100ae103Smrg	start = cp;
133100ae103Smrg	colon = comma = NULL;
134100ae103Smrg	exitcode = ++default_exitcode;
135100ae103Smrg	quoted = 0;
136100ae103Smrg
137100ae103Smrg	/* find the next name and exit code */
138100ae103Smrg	for (; *cp; cp++) {
139100ae103Smrg	    if (quoted) quoted = 0;
140100ae103Smrg	    else if (*cp == '\\') quoted = 1;
141100ae103Smrg	    else if (*cp == ':') colon = cp;
142100ae103Smrg	    else if (*cp == ',') {
143100ae103Smrg		comma = cp;
144100ae103Smrg		break;
145100ae103Smrg	    }
146100ae103Smrg	}
147100ae103Smrg
148100ae103Smrg	/*
149100ae103Smrg	 * If comma is NULL then we are at the end of the string.  If colon
150100ae103Smrg	 * is NULL, then there was no exit code given, so default to zero.
151100ae103Smrg	 */
152100ae103Smrg
153100ae103Smrg	if (comma) *comma = '\0';
154100ae103Smrg
155100ae103Smrg	if (colon) {
156100ae103Smrg	    exitcode = atoi (colon+1);
157100ae103Smrg	    *colon = '\0';
158100ae103Smrg	}
159100ae103Smrg
160100ae103Smrg	/*
161100ae103Smrg	 * make sure that we aren't about to stomp on memory
162100ae103Smrg	 */
163100ae103Smrg	if (npairs >= shouldfind) {
164100ae103Smrg	    fprintf (stderr,
165100ae103Smrg		     "%s:  internal error, found extra pairs (should be %d)\n",
166100ae103Smrg		     ProgramName, shouldfind);
1670103645bSmrg	    free (br);
1680103645bSmrg	    free (copy);
169100ae103Smrg	    return -1;
170100ae103Smrg	}
171100ae103Smrg
172100ae103Smrg	/*
173100ae103Smrg	 * got it!  start and exitcode contain the right values
174100ae103Smrg	 */
175100ae103Smrg	br[npairs].name = start;
176100ae103Smrg	br[npairs].exitstatus = exitcode;
177100ae103Smrg	npairs++;
178100ae103Smrg
179100ae103Smrg	if (comma) cp++;
180100ae103Smrg    }
181100ae103Smrg
182100ae103Smrg
183100ae103Smrg    if (npairs != shouldfind) {
184100ae103Smrg	fprintf (stderr, "%s:  internal error found %d instead of %d pairs\n",
185100ae103Smrg		 ProgramName, npairs, shouldfind);
1860103645bSmrg	free (br);
1870103645bSmrg	free (copy);
188100ae103Smrg	return -1;
189100ae103Smrg    }
190100ae103Smrg
191100ae103Smrg    /*
192100ae103Smrg     * now, strip any quoted characters
193100ae103Smrg     */
194100ae103Smrg    unquote_pairs (br, npairs);
195100ae103Smrg    *brptr = br;
196100ae103Smrg    return npairs;
197100ae103Smrg}
198100ae103Smrg
199100ae103Smrg/* ARGSUSED */
200100ae103Smrgstatic void
201100ae103Smrghandle_button (Widget w, XtPointer closure, XtPointer client_data)
202100ae103Smrg{
203100ae103Smrg    ButtonRecord *br = (ButtonRecord *) closure;
204100ae103Smrg
205100ae103Smrg    if (br->print_value)
206100ae103Smrg	puts (br->name);
207100ae103Smrg    exit (br->exitstatus);
208100ae103Smrg}
209100ae103Smrg
210100ae103SmrgWidget
211100ae103Smrgmake_queryform(Widget parent,	/* into whom widget should be placed */
212100ae103Smrg    char *msgstr,		/* message string */
213100ae103Smrg    int msglen,			/* characters in msgstr */
214100ae103Smrg    char *button_list,		/* list of button title:status */
215100ae103Smrg    Boolean print_value,	/* print button string on stdout? */
216100ae103Smrg    char *default_button,	/* button activated by Return */
217100ae103Smrg    Dimension max_width,
218100ae103Smrg    Dimension max_height)
219100ae103Smrg{
220100ae103Smrg    ButtonRecord *br;
221100ae103Smrg    int npairs, i;
222100ae103Smrg    Widget form, text, prev;
223100ae103Smrg    Arg args[10];
224100ae103Smrg    Cardinal n, thisn;
225100ae103Smrg    char *shell_geom;
226100ae103Smrg    int x, y, geom_flags;
227100ae103Smrg    unsigned int shell_w, shell_h;
228100ae103Smrg
229100ae103Smrg    npairs = parse_name_and_exit_code_list (button_list, &br);
230100ae103Smrg
231100ae103Smrg    form = XtCreateManagedWidget ("form", formWidgetClass, parent, NULL, 0);
232100ae103Smrg
233100ae103Smrg    text = XtVaCreateManagedWidget
234100ae103Smrg	("message", asciiTextWidgetClass, form,
235100ae103Smrg	 XtNleft, XtChainLeft,
236100ae103Smrg	 XtNright, XtChainRight,
237100ae103Smrg	 XtNtop, XtChainTop,
238100ae103Smrg	 XtNbottom, XtChainBottom,
239100ae103Smrg	 XtNdisplayCaret, False,
240100ae103Smrg	 XtNlength, msglen,
241100ae103Smrg	 XtNstring, msgstr,
242100ae103Smrg	 NULL);
243100ae103Smrg    /*
244100ae103Smrg     * Did the user specify our geometry?
245100ae103Smrg     * If so, don't bother computing it ourselves, since we will be overridden.
246100ae103Smrg     */
247100ae103Smrg    XtVaGetValues(parent, XtNgeometry, &shell_geom, NULL);
248100ae103Smrg    geom_flags = XParseGeometry(shell_geom, &x, &y, &shell_w, &shell_h);
249100ae103Smrg    if (!(geom_flags & WidthValue && geom_flags & HeightValue)) {
250100ae103Smrg	Dimension width, height, height_addons = 0;
251100ae103Smrg	Dimension scroll_size, border_width;
252100ae103Smrg	Widget label, scroll;
253100ae103Smrg	Position left, right, top, bottom;
254100ae103Smrg	char *tmp;
255100ae103Smrg	/*
256100ae103Smrg	 * A Text widget is used for the automatic scroll bars.
257100ae103Smrg	 * But Text widget doesn't automatically compute its size.
258100ae103Smrg	 * The Label widget does that nicely, so we create one and examine it.
259100ae103Smrg	 * This widget is never visible.
260100ae103Smrg	 */
261100ae103Smrg	XtVaGetValues(text, XtNtopMargin, &top, XtNbottomMargin, &bottom,
262100ae103Smrg		      XtNleftMargin, &left, XtNrightMargin, &right, NULL);
263100ae103Smrg	label = XtVaCreateWidget("message", labelWidgetClass, form,
264100ae103Smrg				 XtNlabel, msgstr,
265100ae103Smrg				 XtNinternalWidth, (left+right+1)/2,
266100ae103Smrg				 XtNinternalHeight, (top+bottom+1)/2,
267100ae103Smrg				 NULL);
268100ae103Smrg	XtVaGetValues(label, XtNwidth, &width, XtNheight, &height, NULL);
269100ae103Smrg	XtDestroyWidget(label);
270100ae103Smrg	if (max_width == 0)
271100ae103Smrg	    max_width = .7 * WidthOfScreen(XtScreen(text));
272100ae103Smrg	if (max_height == 0)
273100ae103Smrg	    max_height = .7 * HeightOfScreen(XtScreen(text));
274100ae103Smrg	if (width > max_width) {
275100ae103Smrg	    width = max_width;
276100ae103Smrg	    /* add in the height of any horizontal scroll bar */
277100ae103Smrg	    scroll = XtVaCreateWidget("hScrollbar", scrollbarWidgetClass, text,
278100ae103Smrg				      XtNorientation, XtorientHorizontal,
279100ae103Smrg				      NULL);
280100ae103Smrg	    XtVaGetValues(scroll, XtNheight, &scroll_size,
281100ae103Smrg			  XtNborderWidth, &border_width, NULL);
282100ae103Smrg	    XtDestroyWidget(scroll);
283100ae103Smrg	    height_addons = scroll_size + border_width;
284100ae103Smrg	}
285100ae103Smrg
286100ae103Smrg	/* This fixes the xmessage assumption that the label widget and the
287100ae103Smrg	 * text widget have the same size. In Xaw 7, the text widget has
288100ae103Smrg	 * one extra pixel between lines.
289100ae103Smrg	 * Xmessage is not internationalized, so the code bellow is harmless.
290100ae103Smrg	 */
291100ae103Smrg	tmp = msgstr;
292100ae103Smrg	while (tmp != NULL && *tmp) {
293100ae103Smrg	    ++tmp;
294100ae103Smrg	    ++height;
295100ae103Smrg	    tmp = strchr(tmp, '\n');
296100ae103Smrg	}
297100ae103Smrg
298100ae103Smrg	if (height > max_height) {
299100ae103Smrg	    height = max_height;
300100ae103Smrg	    /* add in the width of any vertical scroll bar */
301100ae103Smrg	    scroll = XtVaCreateWidget("vScrollbar", scrollbarWidgetClass, text,
302100ae103Smrg				      XtNorientation, XtorientVertical, NULL);
303100ae103Smrg	    XtVaGetValues(scroll, XtNwidth, &scroll_size,
304100ae103Smrg			  XtNborderWidth, &border_width, NULL);
305100ae103Smrg	    XtDestroyWidget(scroll);
306100ae103Smrg	    width += scroll_size + border_width;
307100ae103Smrg	}
308100ae103Smrg	height += height_addons;
309100ae103Smrg	XtVaSetValues(text, XtNwidth, width, XtNheight, height, NULL);
310100ae103Smrg    }
311100ae103Smrg    /*
312100ae103Smrg     * Create the buttons
313100ae103Smrg     */
314100ae103Smrg    n = 0;
315100ae103Smrg    XtSetArg (args[n], XtNleft, XtChainLeft); n++;
316100ae103Smrg    XtSetArg (args[n], XtNright, XtChainLeft); n++;
317100ae103Smrg    XtSetArg (args[n], XtNtop, XtChainBottom); n++;
318100ae103Smrg    XtSetArg (args[n], XtNbottom, XtChainBottom); n++;
319100ae103Smrg    XtSetArg (args[n], XtNfromVert, text); n++;
320100ae103Smrg    XtSetArg (args[n], XtNvertDistance, 5); n++;
321100ae103Smrg
322100ae103Smrg    prev = NULL;
323100ae103Smrg    for (i = 0; i < npairs; i++) {
324100ae103Smrg	thisn = n;
325100ae103Smrg	XtSetArg (args[thisn], XtNfromHoriz, prev); thisn++;
326100ae103Smrg	prev = XtCreateManagedWidget (br[i].name, commandWidgetClass,
327100ae103Smrg				      form, args, thisn);
328100ae103Smrg	br[i].widget = prev;
329100ae103Smrg	br[i].print_value = print_value;
330100ae103Smrg	XtAddCallback (prev, XtNcallback, handle_button, (XtPointer) &br[i]);
331100ae103Smrg	if (default_button && !strcmp(default_button, br[i].name)) {
332100ae103Smrg	    Dimension border;
333100ae103Smrg
334100ae103Smrg	    default_exitstatus = br[i].exitstatus;
335100ae103Smrg	    XtVaGetValues(br[i].widget, XtNborderWidth, &border, NULL);
336100ae103Smrg	    border *= 2;
337100ae103Smrg	    XtVaSetValues(br[i].widget, XtNborderWidth, border, NULL);
338100ae103Smrg	}
339100ae103Smrg    }
340100ae103Smrg    return form;
341100ae103Smrg}
342