[comp.windows.x] xprompt - prompt user for input

brachman@grads.cs.ubc.ca (Barry Brachman) (01/25/89)

Enclosed is an X11R3 program that pops up a window and prompts the
user for one or more items.
Please consult the README for details.

----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE
#! /bin/sh
# This is a shar archive.  Extract with sh, not csh.
# This archive ends with exit, so do not worry about trailing junk.
# Created by brachman@grads.cs.ubc.ca on Tue Jan 24 14:01:18 PST 1989
# Contents:
#     README Makefile Imakefile xprompt.c xprompt.man
echo 'Extracting README'
sed 's/^X//' > README << '+ END-OF-FILE README'
X
XXprompt provides a means by which programs can ask the user
Xfor one or more responses.  I have found it especially useful for
Xreducing the size of my twm menus.
XInstead of hardwiring alternatives into a menu, a single script is used
Xto call xprompt and then invoke the appropriate thing.
X
XFor example, I have a twm menu item:
X
X	"RLOGIN"	!"xprompt.rlogin &"
X
Xthat invokes a shell script called xprompt.rlogin:
X
X#! /bin/sh
X
Xdefaulthost=${HOME}/.xprompt.rlogin
Xreply=
X
Xif [ -r $defaulthost ]
Xthen
X	reply=`cat $defaulthost`
Xfi
X
Xreply=`xprompt -p "Hostname" -r "$reply"`
Xif [ $? = 1 ]
Xthen
X	exit 0
Xfi
Xecho "$reply" > $defaulthost
X
Xxhost "$reply" > /dev/null 2>&1
Xxterm -T "rlogin $reply" -e rlogin "$reply" &
X
Xexit 0
X
XXprompt relies on the user to set its geometry to something reasonable.
XI set the following resources in my .xresources:
X	XPrompt*Geometry: 800x30+300+2
X	XPrompt*Rlen: 70
X
XThis is my first X program and there's a good chance I've made a few major
XX faux pas.  Suggestions on how to clean it up will be gratefully received.
XThere are a few minor problems, perhaps my fault, perhaps the fault
Xof the text widget.  Horizontal scrolling, for example, doesn't seem
Xto work correctly.  Still, I've found the program useful and perhaps others
Xwill as well.
X
XThe program has been tested on monochrome Sun 3/50's running SunOS 4.0 and
Xhas been compiled using gcc 1.32.  It has been written for X11R3.
XI can't vouch for the Imakefile.
X
XOne possible enhancement is to provide optional word and file name completion.
X
XPlease report bugs, enhancements, suggestions, etc. to me rather than
Xposting to the net.
X
X-----
XBarry Brachman           | UUCP:    {alberta,uw-beaver,uunet}!
XDept. of Computer Science|           ubc-vision!ubc-csgrads!brachman
XUniv. of British Columbia| Internet: brachman@cs.ubc.ca
XVancouver, B.C. V6T 1W5  |           brachman%ubc.csnet@csnet-relay.arpa
X(604) 228-4327           | brachman@ubc.csnet
X
+ END-OF-FILE README
chmod 'u=rw,g=r,o=r' 'README'
echo '	-rw-r--r--  1 brachman     1925 Jan 24 14:00 README        (as sent)'
echo -n '	'
/bin/ls -l README
echo 'Extracting Makefile'
sed 's/^X//' > Makefile << '+ END-OF-FILE Makefile'
X
XXLIB=/active/X/usr/lib
XXINC=/usr/include/X11
X
XCC=gcc
XCFLAGS=-I$(XINC) -O
X
Xxprompt: xprompt.o
X	$(CC) -o xprompt xprompt.o -L$(XLIB) -lXaw -lXt -lXmu -lX11 -lm
X
X.c.o:
X	$(CC) $(CFLAGS) -c $*.c
X
+ END-OF-FILE Makefile
chmod 'u=rw,g=r,o=r' 'Makefile'
echo '	-rw-r--r--  1 brachman      192 Jan 24 13:31 Makefile        (as sent)'
echo -n '	'
/bin/ls -l Makefile
echo 'Extracting Imakefile'
sed 's/^X//' > Imakefile << '+ END-OF-FILE Imakefile'
X
X       INCLUDES = -I$(TOP)
XLOCAL_LIBRARIES = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB)
X
X           SRCS = xprompt.c
X           OBJS = xprompt.o
X
XComplexProgramTarget(xprompt)
+ END-OF-FILE Imakefile
chmod 'u=rw,g=r,o=r' 'Imakefile'
echo '	-rw-r--r--  1 brachman      174 Jan 16 18:45 Imakefile        (as sent)'
echo -n '	'
/bin/ls -l Imakefile
echo 'Extracting xprompt.c'
sed 's/^X//' > xprompt.c << '+ END-OF-FILE xprompt.c'
X/* vi: set tabstop=4 : */
X
X/*
X * xprompt - prompt the user for one or more replies
X *
X * Written for X11R3
X * 24-Jan-89 bjb
X *
X * Copyright (C) 1989 Barry Brachman and The University of British Columbia
X *
X * Permission is given to freely copy and distribute this software provided:
X *
X *	1) You do not sell it,
X *	2) You do not use it for commercial advantage, and
X *	3) This notice accompanies the distribution
X *
X * Barry Brachman           | UUCP:    {alberta,uw-beaver,uunet}!
X * Dept. of Computer Science|           ubc-vision!ubc-csgrads!brachman
X * Univ. of British Columbia| Internet: brachman@cs.ubc.ca
X * Vancouver, B.C. V6T 1W5  |           brachman%ubc.csnet@csnet-relay.arpa
X * (604) 228-4327           | brachman@ubc.csnet
X */
X
X#include <stdio.h>
X#include <X11/Xatom.h>
X#include <X11/IntrinsicP.h>
X#include <X11/StringDefs.h>
X#include <X11/Box.h>
X#include <X11/Shell.h>
X#include <X11/AsciiText.h>
X#include <X11/TextP.h>
X#include <X11/Cardinals.h>
X#include <X11/Xutil.h>
X
Xstatic XrmOptionDescRec table[] = {
X    {"-rlen",   "Rlen",              XrmoptionSepArg, NULL},
X    {"-ibw",    "insideborderWidth", XrmoptionSepArg, NULL},
X    {"-grab",   "Grab",              XrmoptionNoArg,  (caddr_t) "on"},
X    {"-nograb", "Grab",              XrmoptionNoArg,  (caddr_t) "off"},
X    {"-rfn",    "replyFont",         XrmoptionSepArg, NULL},
X    {"-pfn",    "promptFont",        XrmoptionSepArg, NULL},
X	{"-p",      "",                  XrmoptionSkipLine, NULL},
X};
X
Xtypedef struct {
X    int rlen;				/* maximum reply length */
X    Boolean grab;			/* If TRUE, grab the keyboard */
X    char *geometry;
X    int borderwidth;
X    int insideborderwidth;
X    XFontStruct *font;
X    XFontStruct *pfont;
X    XFontStruct *rfont;
X} app_resourceRec, *app_res;
X
Xstatic app_resourceRec app_resources;
X
Xstatic XtResource resources[] = {
X{"rlen",  "Rlen", XtRInt, sizeof(int),
X   XtOffset(app_res, rlen), XtRImmediate, (caddr_t) 80},
X{"grab",  "Grab", XtRBoolean, sizeof(Boolean),
X   XtOffset(app_res, grab), XtRImmediate, (caddr_t) TRUE },
X{"insideborderWidth", "insideborderWidth", XtRInt, sizeof(int),
X   XtOffset(app_res, insideborderwidth), XtRImmediate, (caddr_t) 1},
X{"replyfont", "replyFont", XtRFontStruct, sizeof(XFontStruct *),
X   XtOffset(app_res, rfont), XtRString, NULL},
X{"promptfont", "promptFont", XtRFontStruct, sizeof(XFontStruct *),
X   XtOffset(app_res, pfont), XtRString, NULL},
X
X{XtNgeometry,  "Geometry", XtRString, sizeof(caddr_t),
X   XtOffset(app_res, geometry), XtRString, (caddr_t) "500x150+500+400"},
X{XtNborderWidth, "borderWidth", XtRInt, sizeof(int),
X   XtOffset(app_res, borderwidth), XtRImmediate, (caddr_t) 1},
X{XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
X   XtOffset(app_res, font), XtRString, (caddr_t) XtDefaultFont},
X};
X
Xstatic char xprompt_TextTranslations[] =
X"\
XCtrl<Key>C:		abort() \n\
XCtrl<Key>D:		finish-prompt() \n\
XCtrl<Key>J:     next-prompt() \n\
XCtrl<Key>M:     next-prompt() \n\
X<Key>Down:		next-prompt() \n\
X<Key>Up:		previous-prompt() \n\
X<Key>Linefeed:	next-prompt() \n\
X<Key>Return:	next-prompt() \n\
XCtrl<Key>U:     erase-line() \n\
X<Btn1Down>:     finish-prompt() \n\
X<Btn2Down>:     finish-prompt() \n\
X<Btn3Down>:     finish-prompt() \n\
X";
X
Xstatic char *promptbuf, *replybuf;
Xstatic struct promptargs {
X	char *prompt;
X	char *reply;
X	struct promptargs *next;
X	struct promptargs *prev;
X} *promptargs, *npa, *npa_prev;
Xstatic int cpromptarg, npromptargs;
Xstatic Widget toplevel, box, popup, reply_w, prompt_w;
X
Xstatic void changeprompt(), unparsegeometry();
X
Xstatic void
XEraseLine(ctx, event, args, nargs)
XTextWidget ctx;
XXEvent event;
XString *args;
XCardinal *nargs;
X{
X
X/*	XStoreBuffer(XtDisplay(ctx), replybuf, strlen(replybuf), 1); */
X	replybuf[0] = '\0';
X	XtTextSetLastPos(reply_w, 0);
X	XtTextSetInsertionPoint(reply_w, 0);
X	XtTextDisplay(reply_w);
X}
X
Xstatic void
XFinishPrompt(w, event, args, nargs)
XWidget w;
XXEvent event;
XString *args;
XCardinal *nargs;
X{
X	int i;
X
X	strcpy(npa->reply, replybuf);
X	for (i = 0, npa = promptargs; i < npromptargs; npa = npa->next, i++)
X		printf("%s\n", npa->reply);
X	XtDestroyWidget(toplevel);
X	exit(0);
X}
X
Xstatic void
XPreviousPrompt(w, event, args, nargs)
XWidget w;
XXEvent event;
XString *args;
XCardinal *nargs;
X{
X
X	npa_prev = npa;
X	npa = npa->prev;
X	if (--cpromptarg == 0)
X		cpromptarg = npromptargs;
X	changeprompt();
X}
X
Xstatic void
XNextPrompt(w, event, args, nargs)
XWidget w;
XXEvent event;
XString *args;
XCardinal *nargs;
X{
X
X	npa_prev = npa;
X	npa = npa->next;
X	if (++cpromptarg > npromptargs)
X		cpromptarg = 1;
X	changeprompt();
X}
X
Xstatic void
Xchangeprompt()
X{
X	int replylen, promptlen;
X
X	if (npromptargs > 1)
X		sprintf(promptbuf, "%s[%d/%d]:", npa->prompt, cpromptarg, npromptargs);
X	else
X		sprintf(promptbuf, "%s:", npa->prompt);
X	strcpy(npa_prev->reply, replybuf);
X	strcpy(replybuf, npa->reply);
X
X	replylen = strlen(replybuf);
X	promptlen = strlen(promptbuf);
X	XtTextSetLastPos(reply_w, replylen);
X	XtTextSetLastPos(prompt_w, promptlen);
X
X	XtTextSetInsertionPoint(reply_w, replylen);
X	XtTextSetInsertionPoint(prompt_w, promptlen);
X
X	XtTextDisplay(reply_w);
X	XtTextDisplay(prompt_w);
X}
X
Xstatic void
XAbort(w, event, args, nargs)
XWidget w;
XXEvent event;
XString *args;
XCardinal *nargs;
X{
X
X    XtDestroyWidget(toplevel);
X    exit(1);
X}
X
Xstatic XtActionsRec xprompt_actions[] = {
X  {"erase-line",		EraseLine },
X  {"next-prompt",		NextPrompt },
X  {"previous-prompt",	PreviousPrompt },
X  {"finish-prompt",		FinishPrompt },
X  {"abort",				Abort },
X};
X
Xstatic
XSyntax(call)
Xchar *call;
X{
X
X    fprintf(stderr, "Usage: %s [flags] [xtoolkitargs] -p prompt [-r reply] \
X[-p prompt [-r reply]] ...\n", call);
X    fprintf(stderr, "where <flags> is one or more of:\n");
Xfprintf(stderr, "-rlen #     (Maximum length of user's reply: default 80)\n");
Xfprintf(stderr, "-ibw #      (Border width of inside window: default 1)\n");
Xfprintf(stderr, "-grab       (Grab keyboard)\n");
Xfprintf(stderr, "-nograb     (Don't grab keyboard)\n");
Xfprintf(stderr, "-pfn <font> (Prompt font)\n");
Xfprintf(stderr, "-rfn <font> (Reply font)\n");
X    exit(1);
X}
X
Xfoo()
X{}
X
Xvoid unparsegeometry();
X
Xvoid
Xmain(argc, argv)
Xunsigned int argc;
Xchar **argv;
X{
X    register int i, j;
X	int len, maxpromptlen;
X	TextWidget ctx;
X    Arg arg[10];
X    int geom_mask, geom_x, geom_y, geom_width, geom_height;
X    char geom_str[100];
X    XFontStruct *font;
X    int bb_width, bb_height;
X
X    toplevel = XtInitialize( argv[0], "XPrompt", table, XtNumber(table),
X		&argc, argv);
X
X    XtGetApplicationResources(toplevel, &app_resources, resources,
X                  XtNumber(resources), NULL, 0);
X
X    j = 0;
X	promptargs = NULL;
X	maxpromptlen = 0;
X    for (i = 1; i < argc; i++) {
X		if (i == argc - 1 || strcmp(argv[i], "-p"))
X			Syntax(argv[0]);
X		if (promptargs) {
X			npa->next =
X				(struct promptargs *) malloc(sizeof(struct promptargs));
X			npa->next->prev = npa;
X			npa = npa->next;
X			npa->next = NULL;
X		}
X		else {
X			promptargs = npa =
X				(struct promptargs *) malloc(sizeof(struct promptargs));
X			npa->prev = npa->next = NULL;
X		}
X		npa->prompt = argv[++i];
X		if ((len = strlen(npa->prompt)) > maxpromptlen)
X			maxpromptlen = len;
X		if ((npa->reply = (char *) malloc(app_resources.rlen + 1)) == NULL) {
X			fprintf(stderr, "Can't alloc reply buffer\n");
X			exit(1);
X		}
X		npa->reply[0] = '\0';
X		if (argv[i+1] && !strcmp(argv[i+1], "-r")) {
X			if (++i == argc - 1)
X				Syntax(argv[0]);
X			if (strlen(argv[++i]) > app_resources.rlen) {
X				fprintf(stderr, "xprompt: default reply is too long\n");
X				exit(1);
X			}
X			strcpy(npa->reply, argv[i]);
X		}
X		j++;
X	}
X	if ((npromptargs = j) == 0)
X		Syntax(argv[0]);
X	promptargs->prev = npa;
X	npa->next = promptargs;
X	npa = promptargs;
X	cpromptarg = 1;
X	replybuf = (char *) malloc(app_resources.rlen + 1);
X	if (npromptargs > 1) {
X		maxpromptlen += 5;
X		if (npromptargs < 10)
X			maxpromptlen += 2;
X		else if (npromptargs < 100)
X			maxpromptlen += 4;
X		else
X			maxpromptlen += 10;		/* yuk */
X	}
X	else
X		maxpromptlen += 2;
X	promptbuf = (char *) malloc(maxpromptlen);
X
X    if (app_resources.pfont == NULL)
X		app_resources.pfont = app_resources.font;
X    if (app_resources.rfont == NULL)
X		app_resources.rfont = app_resources.font;
X
X    XtAddActions( xprompt_actions, XtNumber( xprompt_actions ));
X
X    geom_x = 500;
X    geom_y = 400;
X    geom_width = 500;
X    geom_height = 100;
X    geom_mask =
X		XParseGeometry( app_resources.geometry, &geom_x, &geom_y, &geom_width,
X			&geom_height );
X    unparsegeometry( geom_str, geom_mask, geom_width, geom_height, geom_x, geom_y);
X    XtSetArg( arg[0], XtNgeometry, geom_str );
X    XtSetArg( arg[1], XtNborderWidth, app_resources.borderwidth );
X    popup = XtCreatePopupShell( "popup", overrideShellWidgetClass,
X			toplevel, arg, 2 );
X
Xbox =
XXtCreateManagedWidget( "box", boxWidgetClass, popup, NULL, ZERO );
X
X    font = app_resources.pfont;
X    bb_width = font->max_bounds.rbearing - font->min_bounds.lbearing;
X    bb_height = font->max_bounds.ascent + font->max_bounds.descent;
X	if (npromptargs > 1)
X		sprintf(promptbuf, "%s[1/%d]:", promptargs->prompt, npromptargs);
X	else
X		sprintf(promptbuf, "%s:", promptargs->prompt);
X    XtSetArg( arg[0], XtNstring, promptbuf );
X	XtSetArg( arg[1], XtNlength, maxpromptlen );
X    XtSetArg( arg[2], XtNborderWidth, 0);
X    XtSetArg( arg[3], XtNfont, app_resources.pfont );
X	XtSetArg( arg[4], XtNeditType, XttextRead );
X	XtSetArg( arg[5], XtNwidth, bb_width * maxpromptlen );
X	XtSetArg( arg[6], XtNsensitive, False );
Xprompt_w =
XXtCreateManagedWidget( "text", asciiStringWidgetClass, box, arg, 7 );
X	/*
X	 * The following kludge is used because I couldn't find any obvious
X	 * way to turn off the cursor in the prompt window
X	 * I suppose one could define a null cursor and use that...
X	 */
X	ctx= (TextWidget)prompt_w;
X	ctx->text.sink->InsertCursor = foo;
X
X    font = app_resources.rfont;
X    bb_width = font->max_bounds.rbearing - font->min_bounds.lbearing;
X    bb_height = font->max_bounds.ascent + font->max_bounds.descent;
X	strcpy(replybuf, promptargs->reply);
X    XtSetArg( arg[0], XtNtextOptions, editable | scrollOnOverflow );
X    XtSetArg( arg[1], XtNheight, bb_height + 6 );
X    XtSetArg( arg[2], XtNstring, replybuf );
X    XtSetArg( arg[3], XtNlength, app_resources.rlen );
X    XtSetArg( arg[4], XtNeditType, XttextEdit );
X    XtSetArg( arg[5], XtNborderWidth, app_resources.insideborderwidth );
X    XtSetArg( arg[6], XtNwidth, app_resources.rlen * bb_width );
X    XtSetArg( arg[7], XtNinsertPosition, strlen(promptargs->reply) );
X    XtSetArg( arg[8], XtNfont, app_resources.rfont );
Xreply_w =
XXtCreateManagedWidget( "text", asciiStringWidgetClass, box, arg, 9 );
X
XXtOverrideTranslations( reply_w, XtParseTranslationTable( xprompt_TextTranslations ) );
X
X    XtPopup( popup, XtGrabExclusive );
X    XtRealizeWidget( popup );
X    if ( app_resources.grab ) {
X	    XGrabKeyboard( XtDisplay (reply_w), XtWindow (reply_w), False,
X			GrabModeAsync, GrabModeAsync, CurrentTime );
X	    XSetInputFocus( XtDisplay (reply_w), XtWindow (reply_w),
X		        RevertToPointerRoot, CurrentTime );
X    }
X    XtMainLoop();
X}
X
Xstatic void
Xunparsegeometry(buf, mask, w, h, x, y)
Xchar *buf;
Xint mask, w, h, x, y;
X{
X
X	sprintf( buf, "%dx%d%c%d%c%d",
X			w, h,
X			mask & XNegative ? '-' : '+', x,
X			mask & YNegative ? '-' : '+', y);
X}
+ END-OF-FILE xprompt.c
chmod 'u=rw,g=r,o=r' 'xprompt.c'
echo '	-rw-r--r--  1 brachman    11184 Jan 24 13:58 xprompt.c        (as sent)'
echo -n '	'
/bin/ls -l xprompt.c
echo 'Extracting xprompt.man'
sed 's/^X//' > xprompt.man << '+ END-OF-FILE xprompt.man'
X.TH XPROMPT 1 "24 January 1989"
X.SH NAME
Xxprompt \- prompt a user for input
X.SH SYNOPSIS
X.B xprompt
X[ X toolkit options ] [-rlen number] [-ibw number]
X.ti +8
X[-grab] [-nograb] [-r reply] [-pfn fontname]
X.ti +8
X[-rfn fontname] -p prompt [-r reply]
X.ti +8
X[-p prompt [-r reply]]...
X.SH DESCRIPTION
X.I Xprompt
Xpops up a window containing a prompt and an optional default reply.
XIf more than one prompt argument is given, the cursor-down key and the return
Xkey select the next prompt and the cursor-up key selects the previous
Xprompt.
XThe program exits normally by typing <ctrl> D or
Xpressing any mouse button inside the reply area of the window.
XThe replies are printed to stdout, one per line.
XMost standard editing characters
X(e.g., Delete, Backspace, <ctrl> U, cursor movement)
Xare available.
X.PP
X.I Xprompt
Xcan be aborted by typing <ctrl> C.
XIn this case, no output is generated and an exit code of
X1 is returned by the program.
X.PP
XThe default is to position the window in the middle of the screen
Xand to grab keyboard input so that the cursor need not be positioned
Xinside the text area.
X.SH OPTIONS
XThe following command line flags are recognized:
X.TP 8
X.BI \-rlen " number"
XSpecifies the maximum length of the reply.
XThe default is 80 characters.
X.TP 8
X.BI \-ibw " number"
XSpecifies the border width for the box bounding the text area.
XThe default is 1.
X.TP 8
X.B \-nograb
XThe cursor must be in the text portion of the window for
Xinput to be accepted.
X.TP 8
X.B \-grab
XThe cursor may be anywhere but
Xinput is directed to the text area.
X.TP 8
X.BI \-r " reply"
XInsert
X.I reply
Xinto the text area as the default reply.
X.TP 8
X.BI \-pfn " fontname"
XSpecifies the font to be used for the prompt string.
X.TP 8
X.BI \-rfn " fontname"
XSpecifies the font to be used for the reply string.
X.SH X DEFAULTS
XThe standard X toolkit options and resources are accepted.
XFor example, the default font can be changed by the standard
X.I -fn fontname
Xflag or by the resource
X.IR XPrompt*Font.
XIn addition, the following resources are understood:
X.TP 8
X.B "Rlen (\fPclass\fB Integer)"
XSpecifies the maximum length of the reply.
X.TP 8
X.B "insideborderWidth (\fPclass\fB BorderWidth)"
XSpecifies the border width for the box bounding the text area.
X.TP 8
X.B "Grab (\fPclass\fB Boolean)"
XSpecifies whether keyboard input should be focussed on the text area
Xregardless of where the cursor is.
X.TP 8
X.B "replyFont (\fPclass\fB Font)"
XThe font to use for the reply string, overriding
XXPrompt*Font.
X.TP 8
X.B "promptFont (\fPclass\fB Font)"
XThe font to use for the prompt string, overriding
XXPrompt*Font.
X.SH NOTE
XIf
X.I xprompt
Xis invoked from a Bourne shell script,
Xthe following hack can be used to quickly grab multi-reply output:
X.sp 2
X.in +4
Xreply=`xprompt -p "Prompt1" -p "Prompt2" -p "Prompt3"`
X.br
XIFS="
X.br
X"
X.br
Xset $reply
X.br
Xecho "First reply is: $1"
X.br
Xecho "Second reply is: $2"
X.br
Xecho "Third reply is: $3"
X.in -4
X.SH EXIT STATUS
XOn normal completion 0 is returned.
XIf the program is aborted, 1 is returned.
X.SH AUTHOR
X.nf
XBarry Brachman
Xbrachman@cs.ubc.ca
XValuable suggestions by Rick Morrison.
X.SH BUGS
XThe reply is limited to a single line.
XThe user is responsible for ensuring that a proper window size
Xis chosen.
X
+ END-OF-FILE xprompt.man
chmod 'u=rw,g=r,o=r' 'xprompt.man'
echo '	-rw-r--r--  1 brachman     3206 Jan 24 13:50 xprompt.man        (as sent)'
echo -n '	'
/bin/ls -l xprompt.man
exit 0