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