#ifndef sun

#else
/*	@(#)guiSubr.c	1.10 11/12/92
 *	Author:	Roger A. Cole
 *	Date:	02-08-91
 *
 *	Experimental Physics and Industrial Control System (EPICS)
 *
 *	Copyright 1991-92, the Regents of the University of California,
 *	and the University of Chicago Board of Governors.
 *
 *	This software was produced under  U.S. Government contracts:
 *	(W-7405-ENG-36) at the Los Alamos National Laboratory,
 *	and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *	Initial development by:
 *		The Controls and Automation Group (AT-8)
 *		Ground Test Accelerator
 *		Accelerator Technology Division
 *		Los Alamos National Laboratory
 *
 *	Co-developed with
 *		The Controls and Computing Group
 *		Accelerator Systems Division
 *		Advanced Photon Source
 *		Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 *  .00 02-08-91 rac	initial version
 *  .01 07-30-91 rac	installed in SCCS
 *  .02 08-14-91 rac	add guiNoticeName; add more documentation
 *  .03 09-06-91 rac	add GuiCheckbox..., guiGetNameFromSeln
 *  .04 10-23-91 rac	allow window manager to position command
 *			frames; add file selector routine; add
 *			guiInit, guiFileSelect, guiShellCmd; add
 *			guiTextswXxx; don't specify position for
 *			frames
 *  .05 02-25-92 rac	add a text editor command frame; guiInit
 *			now gets host name and user name; add a
 *			printer command frame and print routine;
 *			add "puts" for text subwindows
 *  .06 03-15-92 rac	add guiLock as a locking mechanism
 *  .07 03-23-92 rac	add guiTimer; fix guiEditorCreate for sane behavior;
 *			add guiXxxPrintf routines; assume make for XVIEW;
 *			add guiCanvas; rearrange guiEditor panel and add
 *			a status message; add guiCFshow and guiCFshowPin;
 *			change guiCFShow so that it will expose hidden
 *			command frames (this requires OpenWindows Version 3);
 *			for guiGetNameFromSeln, require that selection be
 *			in caller's window;
 *  .08 08-26-92 rac	fix a bug in guiEditorUpdateSet;
 *  .09 09-30-92 rac	allow a bigger buffer for browser; change extension
 *			on edit lock file
 *
 */
#if 0	/* allow embedding comments in the header below */
/*+/mod***********************************************************************
* TITLE	guiSubr.c - general gui routines for xview
*
* DESCRIPTION
*	These routines comprise an initial attempt to provide gui capabilities
*	in a generic way.  At present, the routines are heavily xview
*	oriented.  The routines will probably evolve, especially with
*	regard to xview item typedef usage (e.g., Panel, Xv_opaque, etc.).
*
*	For more information, see USAGE, below.
*
* QUICK REFERENCE
* #include <guiSubr.h>
*
*     Textsw guiBrowser(parent, panel, nCol, nLines, fileName)
* Panel_item guiButton(label, panel, <>pX, pY, <>pHt, proc, menu,
*                                      key1, val1, key2, val2)
*       void guiButtonCenter(button1, button2, panel)
*     Canvas guiCanvas(parentFrame, x, y, proc, >pBgColor, >pFgColor,
*                                      key1, val1, key2, val2)
*       void guiCFdismiss_xvo(item)
*       void guiCFshow(frame)
*       void guiCFshowPin(frame)
*       void guiCFshow_mi(menu, menuItem)
*       void guiCFshow_xvo(item)
*       void guiCFshowPin_mi(menu, menuItem)
*       void guiCFshowPin_xvo(item)
* Panel_item guiCheckbox(label, panel, nItems, <>pX, pY, <>pHt, proc,
*                                      key1, val1, key2, val2)
*       void guiCheckboxItem(label,checkbox,itemNum,dfltFlag,<>pX,pY,<>pHt)
* Panel_item guiChoice(label, item1, item2, ..., NULL, panel, excl, nRow, nCol,
*                      <>pX, pY, <>pHt, proc, key1, val1, key2, val2) 
*  GUI_EDIT *guiEditor(pGuiCtx, title, dirName, fileName, pPrtCtx,
*                                        addExtrasFn, addExtrasArg)
*            void addExtrasFn(pGuiCtx, pEdit, panel, pX,pY,pHt, addExtrasArg)
*      char *guiEditorGets(pEditor, pBuf, dim, <>pCharPos)
*       void guiEditorLoadFile(pEditor, dirName, fileName)
*       void guiEditorNewEntry_pb(item)
*       void guiEditorPrintf(pEditor, fmt, ...)
*        int guiEditorPuts(pEditor, pBuf, <>pCharPos)
*       void guiEditorReset(pEditor)
*       void guiEditorShowCF(pEditor)
*       void guiEditorShowCF_mi(menu, menuItem)
*       void guiEditorShowCF_xvo(item)
*       void guiEditorStatusPrintf(pEditor, fmt, ...)
*      char *guiFileSelect(pGuiCtx, title, dir, file, dim, callbackFn, pArg)
*               void callbackFn(pArg, newPath, newDir, newFileName);
*      Frame guiFrame(label, x, y, width, height, >ppDisp, >pServer)
*      Frame guiFrameCmd(label, parentFrame, pPanel, resizeProc)
*      char *guiGetNameFromSeln(pGuiCtx, textSw, headFlag, tailFlag)
*      char *guiGetSelnInTextsw(pGuiCtx, textSw)
*       Icon guiIconCreate(frame, iconBits)
*      Panel guiInit(pGuiCtx, pArgc, argv, label, x, y, width, height)
*        int guiLock(pGuiCtx, text, lockPath, flag)
*                      flag is 0,1 to reset/set the lock
*       Menu guiMenu(proc, key1, val1, key2, val2)
*  Menu_item guiMenuItem(label, menu, proc, inact, dflt,
*                                      key1, val1, key2, val2)
* Panel_item guiMessage(label, panel, <>pX, pY, <>pHt)
*       void guiMessagePrintf(Panel_item, fmt, ...)
*       void guiNotice(pGuiCtx, msg)
*       void guiNoticeFile(pGuiCtx, msg, fName)
*       void guiNoticeName(pGuiCtx, msg, name)
*       void guiNoticePrintf(pGuiCtx, fmt, ...)
*   GUI_PRT *guiPrinter(pGuiCtx, parentFrame, title, useEnscript)
*       void guiPrinterShowCF_mi(menu, menuItem)
*       void guiPrinterShowCF_xvo(Xv_opaque)
*       void guiPrintFile(pPrtCtx, path)
*       void guiPrintGetCmd(pPrtCtx, path, commandBuf)
*       void guiShellCmd(pGuiCtx, cmdBuf, clientNum, callbackFn, callbackArg)
*               void callbackFn(callbackArg, resultBuf)
* Panel_item guiTextField(label, panel, <>pX, pY, <>pHt, proc, nStr, nDsp,
*                                      key1, val1, key2, val2)
*       void guiTextFieldPrintf(Panel_item, fmt, ...)
*      char *guiTextswGets(textsw, pBuf, dim, <>pCharPos)
*       void guiTextswPrintf(textsw, fmt, ...)
*        int guiTextswPuts(textsw, pBuf, <>pCharPos)
*       void guiTextswReset(textsw)
*       void guiTimer(seconds, callbackFn, callbackArg)
*
* NOTES
* 1.	Many guiXxx routines have <>pX,pY,<>pHt as arguments.  pX,pY specifies
*	the desired position, in pixels, of the item within the panel.  pX
*	is modified to be a suitable x position for an item following on
*	the same line.  pHt is modified to be the height, in pixels, of the
*	item, if its height exceeds the present value of pHt.
*
* BUGS
* o	although they provide some isolation, these routines are still heavily
*	XView specific
* o	there is no "signal handler" routine to allow trapping a window
*	manager forced exit
*
* USAGE
*	In the synopsis for each routine, the `pGuiCtx' is intended to be
*	&guiContext, (or whatever name is chosen for the GUI_CTX in the
*	program).
*
* EXAMPLE PROGRAM
*	This example shows some of the features of these routines.  It
*	produces a program which can be used as a computerized logbook.
*	In addition to an editor and a "directory lister", the program
*	has a button which starts a new logbook entry with time, date,
*	and user name.
*
*	This example assumes that the icon editor has been used to make
*	an icon for the program.
*
* #include <stdio.h>
* #include <xview/xview.h>
* #include <xview/frame.h>
* #include <xview/panel.h>
* #include <xview/icon.h>
* #include <guiSubr.h>
*
* /*--------------------------------------------------------------------------
* *     This is a "context" structure for the program.  It is used to
* *     organize information about the gui operations.
* *-------------------------------------------------------------------------*/
* typedef struct {
*     GUI_CTX   guiCtx;     /* context block for the gui routines */
*     GUI_EDIT  *pEditor;   /* context block for the guiEditor */
*     GUI_PRT   *pPrt;      /* context block for the guiPrinter */
* } TEST_CTX;
* 
* /*--------------------------------------------------------------------------
* *     Some identifiers for XV_KEY_DATA arguments are usually needed.
* *     The "user" identifiers can start at values of 100 and above.
* *-------------------------------------------------------------------------*/
* typedef enum {
*     PCTX=100,             /* TEST context */
*     PCMD,                 /* TEST shell command string */
* } TEST_XV_KEY_DATA;
* 
* void edit_pb();
* void quit_pb();
* void extra();
* 
* /*--------------------------------------------------------------------------
* *   The main program
* *-------------------------------------------------------------------------*/
* main(argc, argv)
* int   argc;
* char  *argv[];
* {
*     TEST_CTX  testCtx;
*     static short icon_bits[]={
* #include "guiTest.icon"   /* this file was created with the icon editor */
*     };
*     Panel     panel;
*     int       y=5, x=5, ht=0;
* 
*     panel = guiInit(&testCtx.guiCtx,
*                    &argc, argv, "gui test program", 100, 100, 0,0);
*     guiIconCreate(testCtx.guiCtx.baseFrame, icon_bits);
* 
*     testCtx.pEditor = NULL;
*     testCtx.pPrt = guiPrinter(&testCtx.guiCtx,
*                    testCtx.guiCtx.baseFrame, "test: printer setup", 1);
* 
*     guiButton("Edit", panel, &x,&y,&ht, edit_pb, NULL, PCTX,&testCtx,0,NULL);
*     guiButton("Quit", panel, &x,&y,&ht, quit_pb, NULL, PCTX,&testCtx,0,NULL);
* 
*     window_fit(panel);
*     window_fit(testCtx.guiCtx.baseFrame);
*     xv_main_loop(testCtx.guiCtx.baseFrame);
*     printf("exiting\n");
*     return 0;
* }
*
* /*--------------------------------------------------------------------------
* *   subroutine to handle the "Edit" button.  This routine asks guiEditor
* *   to call the extra() function below to add a "New entry" button to
* *   the editor panel.
* *
* *   Common practice is to use suffixes on subroutine names when they
* *   are called as a result of activating various gui items.  This allows
* *   signifying what sort of arguments are needed.
* *       _pb indicates a routine called from a panel button
* *           void subr_pb(item)
* *           Panel_item item;
* *           {
* *               program statements
* *           }
* *       _mi indicates a routine called from a menu item
* *           void subr_mi(menu, item)
* *           Menu      menu;
* *           Menu_item item;
* *           {
* *               program statements
* *           }
* *       _xvo indicates a generic "one argument" callback routine
* *           void subr_xvo(item)
* *           Xv_opaque item;
* *           {
* *               program statements
* *           }
* *-------------------------------------------------------------------------*/
* static void edit_pb(item)
* Panel_item item;
* {   TEST_CTX  *pCtx=(TEST_CTX *)xv_get(item, XV_KEY_DATA, PCTX);
*     if (pCtx->pEditor == NULL) {
*         pCtx->pEditor = guiEditor(&pCtx->guiCtx, "test",
*                        pCtx->guiCtx.pwd, NULL, pCtx->pPrt, extra, pCtx);
*     }
* 
*     guiEditorShowCF(pCtx->pEditor);
* }
*
* void extra(pGuiCtx, pEditor, panel, pX,pY,pHt, pCtx)
* GUI_CTX  *pGuiCtx;
* GUI_EDIT *pEditor;
* Panel    panel;
* int      *pX,*pY,*pHt;
* TEST_CTX *pCtx;
* {
*     *pX = 5; *pY += *pHt + 10; *pHt = 0;
*     guiButton("New entry", panel, pX,pY,pHt, guiEditorNewEntry_pb, NULL,
*                    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEditor);
* }
*
* /*--------------------------------------------------------------------------
* *   subroutine to handle the "Quit" button.  This is where various
* *   cleanup activities would normally occur.
* *-------------------------------------------------------------------------*/
* static void quit_pb(item)
* Panel_item item;
* {   TEST_CTX  *pCtx=(TEST_CTX *)xv_get(item, XV_KEY_DATA, PCTX);
* 
*     xv_destroy_safe(pCtx->guiCtx.baseFrame);
* }
*
*-***************************************************************************/
#endif

#include <stdio.h>
#include <varargs.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <sys/ioctl.h>	/* for use with notifier */
#include <sys/fcntl.h>
#include <signal.h>	/* for use with notifier */
#include <sys/wait.h>	/* for use with notifier */

#define XVIEW
#if defined XVIEW
#   include <xview/xview.h>
#   include <xview/frame.h>
#   include <xview/icon.h>
#   include <xview/notice.h>
#   include <xview/notify.h>
#   include <xview/openmenu.h>
#   include <xview/panel.h>
#   include <xview/textsw.h>
#   include <xview/seln.h>
#   include <xview/svrimage.h>
#   include <X11/Xos.h>	/* for <sys/time.h> */
#endif

#include <guiSubr.h>
#include <tsDefs.h>

void guiShellCmd_work();
Notify_value	guiCmdRead(), guiCmdSigchld();

static int	callbackNum=-1;
struct callbackStruct {
    Notify_client clientNum;
    void	(*callbackFn)();
    void	*callbackArg;
    char	result[GUI_TDIM];
    int		readDone;
    int		sigChldDone;
    int		pipe_io;
};
static struct callbackStruct callback[100];

#if GUI_TEST
typedef struct {
    GUI_CTX	guiCtx;
    GUI_EDIT	*pEditor;
    GUI_PRT	*pPrt;
} TEST_CTX;

typedef enum {
    PCTX=100,		/* TEST context */
    PCMD,		/* TEST shell command string */
} TEST_XV_KEY_DATA;

void edit_pb();
void quit_pb();
void extra();
void testTime();

/*+/subr**********************************************************************
* NAME	main - test program
*
*-*/
main(argc, argv)
int	argc;
char	*argv[];
{
    TEST_CTX	testCtx;
    static short icon_bits[]={
#include "guiTest.icon"
    };
    Panel	panel;
    Panel_item	choice;
    int		y=5, x=5, ht=0;
    int		x1;

    panel = guiInit(&testCtx.guiCtx,
			&argc, argv, "gui test program", 100, 100, 0,0);
    guiIconCreate(testCtx.guiCtx.baseFrame, icon_bits);

    testCtx.pEditor = NULL;
    testCtx.pPrt = guiPrinter(&testCtx.guiCtx,
		testCtx.guiCtx.baseFrame, "test: printer setup", 1);

    guiButton("Edit", panel, &x,&y,&ht, edit_pb, NULL, PCTX,&testCtx,0,NULL);
    guiButton("Quit", panel, &x,&y,&ht, quit_pb, NULL, PCTX,&testCtx,0,NULL);
    y += 5 + ht; x = 5; ht = 0;
    choice = guiChoice("exclusive    ",
		"abc", "def", "ghi", "jkl", "mno", NULL,
		panel, 1, 0, 2, &x,&y,&ht, NULL, 0,NULL,0,NULL);
    y += 5 + ht; x = 5; ht = 0;
    choice = guiChoice("non-exclusive",
		"abc", "def", "ghi", "jkl", "mno", NULL,
		panel, 0, 2, 0, &x,&y,&ht, NULL, 0,NULL,0,NULL);
    y += 5 + ht; x = 5; ht = 0;
    guiMessage("exclusive", panel, &x,&y,&ht);
    x1 = 150;
    choice = guiChoice("",
		"abc", "def", "ghi", "jkl", "mno", NULL,
		panel, 1, 0, 2, &x1,&y,&ht, NULL, 0,NULL,0,NULL);
    y += 5 + ht; x = 5; ht = 0;
    guiMessage("non-exclusive", panel, &x,&y,&ht);
    x1 = 150;
    choice = guiChoice("",
		"abc", "def", "ghi", "jkl", "mno", NULL,
		panel, 0, 2, 0, &x1,&y,&ht, NULL, 0,NULL,0,NULL);

    window_fit(panel);
    window_fit(testCtx.guiCtx.baseFrame);
    guiTimer(10., testTime, &testCtx);
    xv_main_loop(testCtx.guiCtx.baseFrame);
    printf("exiting\n");
    return 0;
}
static void edit_pb(item)
Panel_item item;
{   TEST_CTX	*pCtx=(TEST_CTX *)xv_get(item, XV_KEY_DATA, PCTX);
    if (pCtx->pEditor == NULL) {
	pCtx->pEditor = guiEditor(&pCtx->guiCtx,
		"test", pCtx->guiCtx.pwd, "test", pCtx->pPrt, extra, pCtx);
    }

    guiEditorShowCF(pCtx->pEditor);
}

void extra(pGuiCtx, pEditor, panel, pX,pY,pHt, pCtx)
GUI_CTX	*pGuiCtx;
GUI_EDIT *pEditor;
Panel	panel;
int	*pX,*pY,*pHt;
TEST_CTX *pCtx;
{
    *pX = 5; *pY += *pHt + 10; *pHt = 0;
    guiButton("New entry", panel, pX,pY,pHt, guiEditorNewEntry_pb, NULL,
    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEditor);
}
static void quit_pb(item)
Panel_item item;
{   TEST_CTX	*pCtx=(TEST_CTX *)xv_get(item, XV_KEY_DATA, PCTX);

    xv_destroy_safe(pCtx->guiCtx.baseFrame);
}

void testTime(pCtx, dummy)
TEST_CTX *pCtx;
int	dummy;
{
    printf("timer %x\n", pCtx);
}
#endif

/*+/internal******************************************************************
* NAME	guiAbove - move a frame "above" in the stacking order
*
* BUGS
* o	this doesn't work under OpenWindows version 2; under version 3,
*	the function can actually be invoked.
*
*-*/
guiAbove(frame)
Frame	frame;
{
#if 0
    XWindowChanges chg;
    Display	*pDisp=(Display *)xv_get(frame, XV_DISPLAY);
    Window	win=(Window)xv_get(frame, XV_XID);

    chg.stack_mode = TopIf;
    XConfigureWindow(pDisp, win, CWStackMode, &chg);
#if 0
    XRaiseWindow(pDisp, win);
#endif
#endif
    if ((int)xv_get(frame, XV_SHOW) == 0)
	xv_set(frame, XV_SHOW, TRUE, NULL);
}



/*+/subr**********************************************************************
* NAME	guiBrowser - create a browsing text subwindow
*
* DESCRIPTION
*
* RETURNS
*
*
*-*/
Textsw
guiBrowser(parentFrame, panel, nCol, nLines, fileName)
Frame	parentFrame;	/* I parent of text subwindow */
Panel	panel;		/* I panel preceding textsw */
int	nCol;		/* I number of columns wide for text */
int	nLines;		/* I number of lines long for text */
char	*fileName;	/* I file to load, or NULL */
{
    Textsw	textsw;

    textsw = (Textsw)xv_create(parentFrame, TEXTSW,
	XV_X, 0, WIN_BELOW, panel,
	WIN_COLUMNS, nCol, WIN_ROWS, nLines,
	TEXTSW_BROWSING,	TRUE,
	TEXTSW_DISABLE_CD,	TRUE,
	TEXTSW_DISABLE_LOAD,	TRUE,
	TEXTSW_MEMORY_MAXIMUM,	200000,
	TEXTSW_FILE,		fileName,
	NULL);
    if (textsw == NULL) {
	(void)printf("error creating browser text subwindow\n");
	exit(1);
    }
    xv_set(textsw, WIN_WIDTH, WIN_EXTEND_TO_EDGE, NULL);

    return textsw;
}

/*+/subr**********************************************************************
* NAME	guiButton - create a button on a panel
*
* DESCRIPTION
*
* RETURNS
*	Panel_item handle
*
*-*/
Panel_item
guiButton(label, panel, pX, pY, pHt, proc, menu, key1, val1, key2, val2)
char	*label;		/* I label for button */
Panel	panel;		/* I handle of panel containing button */
int	*pX;		/* IO pointer to x position in panel, in pixels */
int	*pY;		/* I pointer to y position in panel, in pixels */
int	*pHt;		/* IO ptr to height used, in pixels, or NULL */
void	(*proc)();	/* I pointer to procedure to handle button push */
Menu	menu;		/* I handle of button menu, or NULL */
enum	key1;		/* I key for context information for object */
void	*val1;		/* I value associated with key */
enum	key2;		/* I key for context information for object */
void	*val2;		/* I value associated with key */
{
    Panel_item	button;
    int		height;

    button = (Panel_item)xv_create(panel, PANEL_BUTTON,
		PANEL_LABEL_STRING, label, XV_X, *pX, XV_Y, *pY, NULL);
    if (button == NULL) {
	(void)printf("error creating \"%s\" button\n", label);
	exit(1);
    }
    if (proc != NULL) {
	if (xv_set(button, PANEL_NOTIFY_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error adding proc to \"%s\" button\n", label);
	    exit(1);
	}
    }
    if (menu != NULL) {
	if (xv_set(button, PANEL_ITEM_MENU, menu, NULL) != XV_OK) {
	    (void)printf("error adding menu to \"%s\" button\n", label);
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(button, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error adding key1 to \"%s\" button\n", label);
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(button, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error adding key2 to \"%s\" button\n", label);
	    exit(1);
	}
    }
    *pX += (int)xv_get(button, XV_WIDTH) + 10;
    if (pHt != NULL) {
	height = xv_get(button, XV_HEIGHT);
	if (height > *pHt)
	    *pHt = height;
    }

    return button;
}

/*+/subr**********************************************************************
* NAME	guiButtonCenter - center one or two buttons within a panel
*
* DESCRIPTION
*
* RETURNS
*	void
*
* EXAMPLE
*	Panel_item button1, button2;
*
*	button1 = guiButton(...
*	button2 = guiButton(...
*
*	window_fit(panel)
*	guiButtonCenter(button1, button2, panel);
*
*-*/
void
guiButtonCenter(button1, button2, panel)
Panel_item button1;	/* I first button */
Panel_item button2;	/* I second button, or NULL */
Panel	panel;		/* I panel containing button(s) */
{
    int		width1, width2, x, panelWidth, y;
    panelWidth = (int)xv_get(panel, XV_WIDTH);
    width1 = (int)xv_get(button1, XV_WIDTH);
    y = (int)xv_get(button1, XV_Y);
    if (button2 != NULL) {
	width2 = (int)xv_get(button2, XV_WIDTH);
	if ((x = (panelWidth - width1 - width2 - 20) / 2) > 0) {
	    xv_set(button1, XV_X, x, XV_Y, y, NULL);
	    x += width1 + 20;
	    xv_set(button2, XV_X, x, XV_Y, y, NULL);
	}
    }
    else if ((x = (panelWidth - width1) / 2) > 0)
	xv_set(button1, XV_X, x, XV_Y, y, NULL);
}

/*+/subr**********************************************************************
* NAME	guiCanvas - create a canvas within a frame
*
* DESCRIPTION
*
* RETURNS
*	Canvas handle
*
* EXAMPLE
*	Canvas	canvas;
*
*	canvas = guiCanvas(baseFrame, 0, 100, canvasEvent, NULL, NULL,
*			0,NULL,0,NULL);
*
* void
* canvasEvent(canvas, event)
* Canvas	canvas;
* Event		*event;
* {
*	if (event_id(event) == WIN_REPAINT) {
*		repaint activity
*	}
*	else if (event_id(event) == MS_LEFT && event_is_up(event)) {
*	    left mouse release activity
*	}
* {
*-*/
Canvas
guiCanvas(parentFrame, x, y, proc, pBgColor, pFgColor, key1, val1, key2, val2)
Frame	parentFrame;	/* I parent frame to contain canvas */
int	x;		/* I x position in frame, in pixels */
int	y;		/* I y position in frame, in pixels */
void	(*proc)();	/* I pointer to procedure to handle resize & events */
unsigned long *pBgColor;/* O pointer to background pixel value, or NULL */
unsigned long *pFgColor;/* O pointer to foreground pixel value, or NULL */
enum	key1;		/* I key for context information for canvas */
void	*val1;		/* I value associated with key */
enum	key2;		/* I key for context information for canvas */
void	*val2;		/* I value associated with key */
{
    Canvas	canvas;
    Xv_window	paintWin;
    unsigned long *colors;

    canvas = (Canvas)xv_create(parentFrame, CANVAS,
	XV_X, x, XV_Y, y,
	CANVAS_X_PAINT_WINDOW,	TRUE,
	CANVAS_FIXED_IMAGE,	FALSE,
	WIN_RETAINED,		FALSE,
	CANVAS_AUTO_CLEAR,	TRUE,
	CANVAS_AUTO_SHRINK,	TRUE,
	CANVAS_AUTO_EXPAND,	TRUE,
	NULL);
    if (canvas == NULL) {
	(void)printf("error creating canvas\n");
	exit(1);
    }
    paintWin = canvas_paint_window(canvas);
    if (xv_set(paintWin, WIN_COLLAPSE_EXPOSURES, TRUE,
		WIN_CONSUME_EVENTS, WIN_MOUSE_BUTTONS, WIN_REPAINT, NULL,
		NULL) != XV_OK) {
	(void)printf("error setting events for canvas\n");
	exit(1);
    }
    if (proc != NULL) {
	if (xv_set(paintWin, WIN_EVENT_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error adding event procedure for canvas\n");
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(paintWin, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error adding key1 to canvas\n");
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(paintWin, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error adding key2 to canvas\n");
	    exit(1);
	}
    }

    colors = (unsigned long *)xv_get(paintWin, WIN_X_COLOR_INDICES);
    if (pBgColor != NULL)
	*pBgColor=colors[(unsigned long)xv_get(paintWin,WIN_BACKGROUND_COLOR)];
    if (pFgColor != NULL)
	*pFgColor=colors[(unsigned long)xv_get(paintWin,WIN_FOREGROUND_COLOR)];
    return canvas;
}

/*+/subr**********************************************************************
* NAME	guiCF - command frame dismiss and show routines
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o	text
*
* SEE ALSO
*	guiFrameCmd
*
* EXAMPLES
*	guiButton("Dismiss", panel, ...
*			guiCFdismiss_xvo, NULL, GUI_CF, cmdFrame, 0, NULL);
*
*	menu = guiMenu(..., GUI_PCTX, pGuiCtx...);
*	menuItem = guiMenuItem("label", menu, guiCFshow_mi, ...,
*							GUI_CF, frame...);
*
*	guiButton("Show...", panel, ..., guiCFshow_xvo, ...,
*					 GUI_PCTX, pGuiCtx, GUI_CF, frame);
*
*	menu = guiMenu(..., GUI_PCTX, pGuiCtx...);
*	menuItem = guiMenuItem("label", menu, guiCFshowPin_mi, ...,
*							GUI_CF, frame...);
*
*	guiButton("Show...", panel, ..., guiCFshowPin_xvo, ...,
*					 GUI_PCTX, pGuiCtx, GUI_CF, frame);
*
*-*/
void
guiCFdismiss_xvo(item)
Xv_opaque item;
{   Frame	frame=(Frame)xv_get(item, XV_KEY_DATA, GUI_CF);
    xv_set(frame, XV_SHOW, FALSE, FRAME_CMD_PUSHPIN_IN, FALSE, NULL);
}
void
guiCFshow(frame)
Frame	frame;
{
    if ((int)xv_get(frame, XV_SHOW) == TRUE)
	guiAbove(frame);
    else
	xv_set(frame, XV_SHOW, TRUE, NULL);
}
void
guiCFshowPin(frame)
Frame	frame;
{
    if ((int)xv_get(frame, XV_SHOW) == TRUE)
	guiAbove(frame);
    else
	xv_set(frame, XV_SHOW, TRUE, FRAME_CMD_PUSHPIN_IN, TRUE, NULL);
}

void guiCFshow_mi(menu, menuItem) Menu menu; Menu_item menuItem;
{   guiCFshow_xvo(menuItem); }

void guiCFshow_xvo(item)
Xv_opaque item;
{   GUI_CTX	*pGuiCtx=(GUI_CTX *)xv_get(item, XV_KEY_DATA, GUI_PCTX);
    Frame	frame=(Frame)xv_get(item, XV_KEY_DATA, GUI_CF);
    guiCFshow(frame);
}

void guiCFshowPin_mi(menu, menuItem) Menu menu; Menu_item menuItem;
{   guiCFshowPin_xvo(menuItem); }

void guiCFshowPin_xvo(item)
Xv_opaque item;
{   GUI_CTX	*pGuiCtx=(GUI_CTX *)xv_get(item, XV_KEY_DATA, GUI_PCTX);
    Frame	frame=(Frame)xv_get(item, XV_KEY_DATA, GUI_CF);
    guiCFshowPin(frame);
}

/*+/subr**********************************************************************
* NAME	guiCheckbox - create an empty checkbox list
*
* DESCRIPTION
*	Creates the framework for an empty checkbox selection list.  The
*	actual checkbox items must be filled in by calls to
*	guiCheckboxItem.
*
*	Checkbox lists have a fixed number of items--the number of items
*	can't be increased or decreased after the empty list is created.
*	Individual items on a list can be changed at any time, however.
*
* RETURNS
*	Panel_item for the checkbox
*
* BUGS
* o	list can't dynamically grow and shrink
* o	the maximum size for a checkbox list is 10 items
*
* SEE ALSO
*	guiCheckboxItem
*
* EXAMPLE
*	Set up a 3-item checkbox.  Each of the first two items is to have
*	an associated status message.  The appearance of the setup will
*	resemble the following:
*
* Retrieve from:
* [] text file       ________status message________
* [] binary file     ________status message________
* [] Channel Access
*
*	The third item will be the default.  When any of the items are
*	selected, choose_xvo() is to be called; that routine uses a pointer
*	to a `myStruct' structure for its operations.
*
*    struct myStruct {
*	Panel_item	textStatus;	status for text file
*	Panel_item	binStatus;	status for binary file
*    } context;
*#define PCTX 101                 (a unique integer for identifying myStruct)
*    Panel_item	ckbx;
*    int	x=5, y=5, ht=0;
*    int	x1, x2, y1, y2;   (for aligning buttons and status messages)
*
*    ckbx = guiCheckbox("Retrieve from:", panel, 3, &x, &y, &ht,
*					choose_xvo, PCTX, &context, 0, NULL);
*    y += ht + 5; x = 5; ht = 0;
*    guiCheckboxItem("text file", ckbx, 0, 0, &x, &y, &ht);
*    x1 = x; y1 =y;
*    y += ht + 5; x = 5; ht = 0;
*    guiCheckboxItem("binary file", ckbx, 1, 0, &x, &y, &ht);
*    x2 = x; y2 =y;
*    y += ht + 5; x = 5; ht = 0;
*    guiCheckboxItem("Channel Access", ckbx, 2, 1, &x, &y, &ht);
*    y += ht + 5; x = 5; ht = 0;
*    if (x1 < x2)	x1 = x2;	(which extends further right?)
*    else		x2 = x1;
*    context.textStatus = guiMessage("", panel, &x1, &y1, &ht);
*    context.binStatus = guiMessage("", panel, &x2, &y2, &ht);
*
*-*/
Panel_item
guiCheckbox(label, panel, nItems, pX, pY, pHt, proc, key1, val1, key2, val2)
char	*label;		/* I label for checkbox list */
Panel	panel;		/* I handle of panel containing checkbox list */
int	nItems;		/* I the number of items to be held by the list */
int	*pX;		/* IO pointer to x position in panel, in pixels */
int	*pY;		/* I pointer to y position in panel, in pixels */
int	*pHt;		/* IO ptr to height used by label, in pixels, or NULL */
void	(*proc)();	/* I pointer to procedure to handle making a choice */
enum	key1;		/* I key for context information for object */
void	*val1;		/* I value associated with key */
enum	key2;		/* I key for context information for object */
void	*val2;		/* I value associated with key */
{
    Panel_item	checkbox;
    int		height;

/*-----------------------------------------------------------------------------
* NOTE:  the kludgey way this is done is because XView in SunOS 4.1 can't
*	seem to handle defining the number of items except at compile time
*----------------------------------------------------------------------------*/
#define GUI_CHK checkbox=xv_create(panel,PANEL_CHECK_BOX,PANEL_CHOICE_STRINGS,
    if (nItems == 1)	   GUI_CHK "", NULL,NULL);
    else if (nItems == 2)  GUI_CHK "","", NULL,NULL);
    else if (nItems == 3)  GUI_CHK "","","", NULL,NULL);
    else if (nItems == 4)  GUI_CHK "","","","", NULL,NULL);
    else if (nItems == 5)  GUI_CHK "","","","","", NULL,NULL);
    else if (nItems == 6)  GUI_CHK "","","","","","", NULL,NULL);
    else if (nItems == 7)  GUI_CHK "","","","","","","", NULL,NULL);
    else if (nItems == 8)  GUI_CHK "","","","","","","","", NULL,NULL);
    else if (nItems == 9)  GUI_CHK "","","","","","","","","", NULL,NULL);
    else if (nItems == 10) GUI_CHK "","","","","","","","","","", NULL,NULL);
    else {
	(void)printf("guiCheckbox can only handle up to 10 items\n");
	exit(1);
    }
    if (checkbox == NULL) {
	(void)printf("error creating \"%s\"s checkbox list\n", label);
	exit(1);
    }
    xv_set(checkbox,
	PANEL_LAYOUT,		PANEL_VERTICAL,
	PANEL_CHOOSE_ONE,	TRUE,
	PANEL_CHOOSE_NONE,	TRUE,
	NULL);
    xv_set(checkbox, PANEL_VALUE, -1, NULL);
    if (proc != NULL) {
	if (xv_set(checkbox, PANEL_NOTIFY_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error adding proc to \"%s\"s checkbox list\n", label);
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(checkbox, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error adding key1 to \"%s\" checkbox list\n", label);
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(checkbox, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error adding key2 to \"%s\" checkbox list\n", label);
	    exit(1);
	}
    }
    guiMessage(label, panel, pX, pY, pHt);
    return checkbox;
}

/*+/subr**********************************************************************
* NAME	guiCheckboxItem - create an item in a checkbox list
*
* DESCRIPTION
*	Change one of the items in a checkbox list.  This may be either
*	setting the item following the creation of the list with guiCheckbox,
*	or a subsequent change to an item.
*
* RETURNS
*	void
*
* SEE ALSO
*	guiCheckbox
*
*-*/
void
guiCheckboxItem(label, checkbox, itemNum, dfltFlag, pX, pY, pHt)
char	*label;		/* I label for item */
Panel_item checkbox;	/* I handle of checkbox list */
int	itemNum;	/* I number (starting with 0) within checkbox list */
int	dfltFlag;	/* I if 1, then this item will be pre-selected */
int	*pX;		/* IO pointer to x position in panel, in pixels */
int	*pY;		/* I pointer to y position in panel, in pixels */
int	*pHt;		/* IO ptr to height used by label, in pixels, or NULL */
{
    Panel	panel=(Panel)xv_get(checkbox, PANEL_PARENT_PANEL);

    xv_set(checkbox,
	PANEL_CHOICE_X,		itemNum, *pX,
	PANEL_CHOICE_Y,		itemNum, *pY-5,
	NULL);
    *pX += 40;
    *pHt = 20;
    guiMessage(label, panel, pX, pY, pHt);
    if (dfltFlag)
	xv_set(checkbox, PANEL_VALUE, itemNum, NULL);
}

#if 0
/*+/macros********************************************************************
* NAME	guiChoice - set up a choice list
*
* SYNOPSIS
*   Panel_item 
*   guiChoice(label, item1, item2, ..., NULL, panel, excl, nRow, nCol,
*			pX,pY,pHt, proc, key1, val1, key2, val2) 
*   char	*label;	/* I label for choice list */
*   char	*item1;	/* I label for first choice item */
*   char	*item2;	/* I label for second choice item */
*   ...
*   Panel	panel;	/* I handle of panel containing choice list */
*   int		excl;	/* I 1 for exclusive choice list, else 0 */
*   int		nRow;	/* I number of rows for organizing buttons */
*   int		nCol;	/* I number of columns for organizing buttons */
*   int		*pX;	/* IO pointer to x position in panel, in pixels */
*   int		*pY;	/* I pointer to y position in panel, in pixels */
*   int		*pHt;	/* IO ptr to height used by label, in pixels, or NULL */
*   void	(*proc)();/* I pointer to procedure to handle making a choice */
*   enum	key1;	/* I key for context information for object */
*   void	*val1;	/* I value associated with key */
*   enum	key2;	/* I key for context information for object */
*   void	*val2;	/* I value associated with key */
*
*-*/
#endif
Panel_item
guiChoice(va_alist)
va_dcl
{
    va_list	pArg;

    Panel_item  item; 
    int		height, lastItem;
    char	*label, *I[100];
    Panel	panel;
    int		excl, nRow, nCol;
    int		*pX;
    int		*pY;
    int		*pHt;
    void	(*proc)();
    int		key1;
    void	*val1;
    int		key2;
    void	*val2;
    typedef	void FN();
    int		type, df;

    va_start(pArg);
    label = va_arg(pArg, char *);
    for (lastItem=0; lastItem<100; lastItem++) {
	I[lastItem] = va_arg(pArg, char *);
	if (I[lastItem] == NULL)
	    break;
    }
    panel = va_arg(pArg, Panel);
    excl = va_arg(pArg, int);
    nRow = va_arg(pArg, int);
    nCol = va_arg(pArg, int);
    pX = va_arg(pArg, int *);
    pY = va_arg(pArg, int *);
    pHt = va_arg(pArg, int *);
    proc = va_arg(pArg, FN *);
    key1 = va_arg(pArg, int);
    val1 = va_arg(pArg, void *);
    key2 = va_arg(pArg, int);
    val2 = va_arg(pArg, void *);

    va_end(pArg);

    if (excl == 1)	type = TRUE, df = 0;
    else		type = FALSE, df = 1;
#define Ps xv_create(panel,PANEL_CHOICE,PANEL_CHOOSE_ONE,type,\
	PANEL_VALUE,df,PANEL_LABEL_STRING,label,PANEL_CHOICE_STRINGS,
#define Pe , NULL, XV_X, *pX, XV_Y, *pY, NULL); break;
    switch (lastItem) {
	case 1: item = Ps I[0] Pe
	case 2: item = Ps I[0],I[1] Pe
	case 3: item = Ps I[0],I[1],I[2] Pe
	case 4: item = Ps I[0],I[1],I[2],I[3] Pe
	case 5: item = Ps I[0],I[1],I[2],I[3],I[4] Pe
	case 6: item = Ps I[0],I[1],I[2],I[3],I[4],I[5] Pe
	case 7: item = Ps I[0],I[1],I[2],I[3],I[4],I[5],I[6] Pe
	case 8: item = Ps I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7] Pe
	case 9: item = Ps I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8] Pe
	case 10: item = Ps I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8],I[9] Pe
	dflt:
		printf("can't handle more than 10 choices right now\n");
		exit(1);
    }
    if (item == NULL) {
	(void)printf("error creating choice\n");
	exit(1);
    }
    if (nRow > 0)
	xv_set(item, PANEL_CHOICE_NROWS, nRow, NULL);
    else if (nCol > 0)
	xv_set(item, PANEL_CHOICE_NCOLS, nCol, NULL);

    if (proc != NULL) {
	if (xv_set(item, PANEL_NOTIFY_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error adding proc to choice\n");
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(item, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error adding key1 to choice\n");
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(item, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error adding key2 to choice\n");
	    exit(1);
	}
    }

    *pX += (int)xv_get(item, XV_WIDTH);
    if (pHt != NULL) {
	height = xv_get(item, XV_HEIGHT);
	if (height > *pHt)
	    *pHt = height;
    }
    return item;
}

void guiEditorCancelFile_mi();
void guiEditorCreateFile_mi();
char *guiEditorGetPath();
void guiEditorLoadFile_1();
void guiEditorLoadFile_mi();
void guiEditorPickFile_mi();
void guiEditorPick_callback();
void guiEditorPrintFile_mi();
void guiEditorSaveFile_mi();
void guiEditorUpdateSet();
void guiEditorUpdateRst();
void guiEditorUpdate_mi();

/*+/subr**********************************************************************
* NAME	guiEditor - create a text editor command frame
*
* DESCRIPTION
*	Creates a text editor command frame with amenities to support good
*	editing practice:
*	o   allow specifying directory and file
*	o   support easy access to a list of directory contents
*	o   easy choice of file name from directory list
*	o   "locking" of file under edit, with corresponding notification
*	    to users who try to edit a locked file
*	o   support for caller to add items to the text editor panel
*	o   check for write permission before allowing editing
*
*	If the caller specifies a file name, then that file will be loaded
*	automatically into the text subwindow.  (If the file doesn't
*	exist, a notice will pop up.)
*
*	If the caller specifies a function to be called to add items to
*	the editor panel, the function will be called as follows:
*
*          void addExtrasFn(pGuiCtx, pEdit, panel, pX,pY,pHt, addExtrasArg)
*
* RETURNS
*	GUI_EDIT *, or
*	NULL
*
* BUGS
* o	doesn't allow printing "memory" copy of file, and insists that user
*	save the file if it has been modified
* o	doesn't provide a way to save updates if the program aborts
*
*-*/
GUI_EDIT *
guiEditor(pGuiCtx, title, dir, file, pPrtCtx, addExtrasFn, arg)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*title;		/* I text for command frame title bar */
char	*dir;		/* I directory of file to load, or NULL */
char	*file;		/* I file to load, or NULL */
GUI_PRT	*pPrtCtx;	/* I pointer to printer context, or NULL */
void	(*addExtrasFn)();/* I pointer to function to add extra stuff to
				editor panel, or NULL*/
void	*arg;		/* I argument to pass to addExtrasFn */
{
    GUI_EDIT	*pEdit;
    Panel	panel;
    Menu	menu;
    Textsw	textsw;
    Panel_item	ck;
    int		y=5, x=5, ht=0, ht1;

/*-----------------------------------------------------------------------------
* Dir: [<]_____________________[>]  File: [<]_______________[>]
* File v       Edit v    status......
*   load         edit
*   save         read-only
*   list dir...  discard edits
*   create
*   print
*   printer setup...
*----------------------------------------------------------------------------*/

    if ((pEdit=(GUI_EDIT *)malloc(sizeof(GUI_EDIT))) == NULL)
	return NULL;
    bzero((char *)pEdit, sizeof(*pEdit));
    pEdit->pGuiCtx = pGuiCtx;
    pEdit->pPrtCtx = pPrtCtx;
    pEdit->edit_CF = guiFrameCmd(title, pGuiCtx->baseFrame, &panel, NULL);
    pEdit->loaded = 0;
    pEdit->updFlag = 0;
    strcpy(pEdit->title, title);

    pEdit->dir_PT = guiTextField("Dir:", panel, &x,&y,&ht,
		NULL, GUI_TDIM-1, 40, 0, NULL, 0, NULL);
    pEdit->file_PT = guiTextField("File:", panel, &x,&y,&ht,
		NULL, GUI_TDIM-1, 20, 0, NULL, 0, NULL);
    if (dir == NULL)
	dir = pGuiCtx->pwd;
    xv_set(pEdit->dir_PT, PANEL_VALUE, dir, NULL);
    xv_set(pEdit->file_PT, PANEL_VALUE, file, NULL);
    y += ht + 5; x = 5; ht = 0;

    menu = guiMenu(NULL, 0, NULL, 0, NULL);
    guiMenuItem("Load", menu, guiEditorLoadFile_mi, 0, 1,
		    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEdit);
    guiMenuItem("Save", menu, guiEditorSaveFile_mi, 0, 0,
		    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEdit);
    guiMenuItem("List dir...", menu, guiEditorPickFile_mi, 0, 0,
		    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEdit);
    guiMenuItem("Create", menu, guiEditorCreateFile_mi, 0, 0,
		    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEdit);
    if (pPrtCtx != NULL) {
	guiMenuItem("Print file", menu, guiEditorPrintFile_mi, 0, 0,
		    GUI_PEDIT, pEdit, GUI_PPRT, pEdit->pPrtCtx);
	guiMenuItem("Printer setup...", menu, guiPrinterShowCF_mi, 0, 0,
		    GUI_PPRT, pEdit->pPrtCtx, 0, NULL);
    }
    guiButton("File", panel, &x, &y, &ht, NULL, menu, 0,NULL,0,NULL);

    menu = guiMenu(NULL, 0, NULL, 0, NULL);
    guiMenuItem("set edit mode", menu, guiEditorUpdate_mi, 0, 1,
		    GUI_PEDIT, pEdit, GUI_INT, 1);
    guiMenuItem("set read-only mode", menu, guiEditorUpdate_mi, 0, 0,
		    GUI_PEDIT, pEdit, GUI_INT, 0);
    guiMenuItem("discard edits", menu, guiEditorCancelFile_mi, 0, 0,
		    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEdit);
    guiButton("Edit", panel, &x, &y, &ht, NULL, menu, 0,NULL,0,NULL);
    pEdit->status_PM = guiMessage("", panel, &x, &y, &ht);
    y += ht + 5; x = 5; ht = 0;

    if (addExtrasFn != NULL)
	addExtrasFn(pGuiCtx, pEdit, panel, &x,&y,&ht, arg);

    y += ht + 5; x = 5; ht = 0;

    window_fit(panel);
    xv_set(panel, WIN_WIDTH, WIN_EXTEND_TO_EDGE, NULL);
    pEdit->text_TSW = (Textsw)xv_create(pEdit->edit_CF, TEXTSW,
	XV_X, 0, WIN_BELOW, panel,
	WIN_COLUMNS, 80, WIN_ROWS, 40,
	TEXTSW_BROWSING,	TRUE,
	TEXTSW_DISABLE_CD,	TRUE,
	TEXTSW_DISABLE_LOAD,	TRUE,
	NULL);
    if (pEdit->text_TSW == NULL) {
	(void)printf("error creating editor text subwindow\n");
	exit(1);
    }

    guiEditorGetPath(pEdit, 0);
    xv_set(pEdit->text_TSW, WIN_WIDTH, WIN_EXTEND_TO_EDGE, NULL);
    window_fit(pEdit->edit_CF);
    if (file != NULL && file[0] != '\0')
	guiEditorLoadFile_1(pEdit);
    return pEdit;
}

/*+/internal******************************************************************
* NAME	guiEditorCancelFile_mi
*
*-*/
static void
guiEditorCancelFile_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);

    if (pEdit->loaded == 0)
	return;
    textsw_reset(pEdit->text_TSW, 0, 0);
    guiEditorUpdateRst(pEdit);
    guiEditorLoadFile_1(pEdit);
    guiMessagePrintf(pEdit->status_PM, "updates discarded; read-only mode");
}

/*+/internal******************************************************************
* NAME	guiEditorCreateFile_mi
*
*-*/
static void
guiEditorCreateFile_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    FILE	*pFile;
    int		fd;
    char	path[GUI_TDIM*3];
    int		upd=pEdit->updFlag;

    path[0] = '\0';
    textsw_append_file_name(pEdit->text_TSW, path);

#if 0
    if (pEdit->loaded)
	guiEditorUpdateRst(pEdit);
    if (pEdit->updFlag)
	goto createFail;
#endif
    if (guiEditorGetPath(pEdit, 1) == NULL)
	goto createFail;
    if ((pFile = fopen(pEdit->path, "r")) != NULL) {
	fclose(pFile);
	guiNoticeFile(pEdit->pGuiCtx, "file already exists:", pEdit->path);
	goto createFail;
    }
    if ((fd = open(pEdit->path, O_WRONLY|O_CREAT, 0664)) < 0) {
	guiNoticeFile(pEdit->pGuiCtx, "couldn't create file:", pEdit->path);
	goto createFail;
    }
    close(fd);
    if (pEdit->updFlag == 1) {
	pEdit->updFlag = 0;
	guiLock(pEdit->pGuiCtx, pEdit->file, pEdit->lockFile, 0);
    }
    if (pEdit->loaded)
	textsw_store_file(pEdit->text_TSW, pEdit->path, 0, 0);
    guiEditorLoadFile_1(pEdit);
    guiEditorUpdateSet(pEdit);
    if (upd) {
	guiMessagePrintf(pEdit->status_PM, "%s created from %s; edit mode",
			pEdit->file, path);
    }
    else
	guiMessagePrintf(pEdit->status_PM,"%s created; edit mode",pEdit->file);
    return;
createFail:
    guiMessagePrintf(pEdit->status_PM, "create failed");
}

/*+/internal******************************************************************
* NAME	guiEditorGetPath
*
*-*/
static char *
guiEditorGetPath(pEdit, notice)
GUI_EDIT *pEdit;
int	notice;		/* 1 if want you want a notice issued on problems */
{
    char	*name, *dir;

    dir = (char *)xv_get(pEdit->dir_PT, PANEL_VALUE);
    name = (char *)xv_get(pEdit->file_PT, PANEL_VALUE);
    strcpy(pEdit->file, name);
    strcpy(pEdit->dir, dir);
    if (name[0] == '\0') {
	if (notice)
	    guiNotice(pEdit->pGuiCtx, "no file name specified");
	return NULL;
    }
    strcpy(pEdit->path, dir);
    if (strlen(pEdit->path) > 0)
	strcat(pEdit->path, "/");
    strcat(pEdit->path, name);
    return pEdit->path;
}


/*+/subr**********************************************************************
* NAME	guiEditorGets - get the next '\n' terminated string from editor
*
* DESCRIPTION
*	Reads the next string from the editor text subwindow into the caller's
*	buffer.  Reading stops when a newline is encountered or when the
*	caller's buffer is full; the newline IS stored in the buffer.
*	The string in the buffer is always null terminated.
*
*	The caller must supply the character index (starting with 0) of
*	the position in the textsw where reading is to start.  On return,
*	the caller's index is set to the next available character for
*	reading.
*
* RETURNS
*	pointer to string, or
*	NULL if there are no more characters in text subwindow
*
*-*/
char *
guiEditorGets(pEdit, pBuf, dim, pCharPos)
GUI_EDIT *pEdit;	/* I pointer to guiEditor */
char	*pBuf;		/* I pointer to buffer to receive string */
int	dim;		/* I dimension of buffer */
int	*pCharPos;	/* IO index of character to start reading */
{
    Textsw	textsw=pEdit->text_TSW;
    Textsw_index nextPos;
    int		i;
    char	*pStr;

    if (pEdit->loaded == 0) {
	guiNotice(pEdit->pGuiCtx, "no file loaded");
	return NULL;
    }
    nextPos = (Textsw_index)xv_get(textsw, TEXTSW_CONTENTS, *pCharPos,
			pBuf, dim-1);
    if (nextPos == *pCharPos)
	return NULL;
    for (i=0; i<dim-1; i++) {
	if (*pCharPos >= nextPos) {
	    pBuf[i] = '\0';
	    return pBuf;
	}
	(*pCharPos)++;
	if (pBuf[i] == '\n') {
	    pBuf[i+1] = '\0';
	    return pBuf;
	}
    }
    pBuf[i] = '\0';
    return pBuf;
}

/*+/internal******************************************************************
* NAME	guiEditorLoadFile_mi
*
*-*/
static void
guiEditorLoadFile_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    guiEditorLoadFile_1(pEdit);
}
/*+/subr**********************************************************************
* NAME	guiEditorLoadFile - load a file for editing
*
* DESCRIPTION
*	Loads the specified file into the editor window.  If the editor
*	window already holds a file which has been edited, a notice pops
*	up and the load of the new file is not performed.
*
*-*/
void
guiEditorLoadFile(pEdit, dir, file)
GUI_EDIT *pEdit;	/* I pointer to guiEditor */
char	*dir;		/* I directory */
char	*file;		/* I file name */
{
    guiEditorShowCF(pEdit);
    guiEditorUpdateRst(pEdit);
    if (pEdit->updFlag == 1) {
	guiMessagePrintf(pEdit->status_PM, "%s not loaded", file);
	return;
    }
    xv_set(pEdit->dir_PT, PANEL_VALUE, dir, NULL);
    xv_set(pEdit->file_PT, PANEL_VALUE, file, NULL);
    guiEditorLoadFile_1(pEdit);
}
/*+/internal******************************************************************
* NAME	guiEditorLoadFile_1
*
*-*/
static void
guiEditorLoadFile_1(pEdit)
GUI_EDIT *pEdit;
{
    Textsw_status status;

    if ((int)xv_get(pEdit->text_TSW, TEXTSW_MODIFIED)) {
	guiNotice(pEdit->pGuiCtx,
		"text has been modified; use \"Save\" or \"discard edits\"");
	goto loadFailed;
    }
    if (guiEditorGetPath(pEdit, 1) == NULL)
	goto loadFailed;
    if (pEdit->updFlag) {
	guiEditorUpdateRst(pEdit);
	pEdit->updFlag = 0;
    }
    xv_set(pEdit->text_TSW, TEXTSW_STATUS, &status,
				TEXTSW_FILE, pEdit->path, NULL);
    if (status == TEXTSW_STATUS_OKAY) {
	guiEditorUpdateRst(pEdit);
	pEdit->loaded = 1;
    }
    else {
	guiNoticeFile(pEdit->pGuiCtx, "couldn't open file", pEdit->path);
	pEdit->loaded = 0;
	goto loadFailed;
    }
    guiMessagePrintf(pEdit->status_PM,
			"%s loaded; read-only mode set", pEdit->file);
    return;
loadFailed:
    guiMessagePrintf(pEdit->status_PM, "load failed");
}

/*+/subr**********************************************************************
* NAME	guiEditorNewEntry_pb
*
* DESCRIPTION
*
* RETURNS
*	void
*
*               void addExtrasFn(pGuiCtx, panel, pX,pY,pHt, addExtrasArg)
*
* EXAMPLE
*	Add an extra button to a text editor to allow adding a new entry
*	(as for a "log book") to a file.  The new entry will have the form:
*
*		== Mar 06, 1992 07:27:31  userName ==
*
*
*	TEST_CTX	*pCtx;
*	GUI_CTX		*pGui;
*	GUI_EDIT	*pEdit;
*	void		extra();
*
*	pEdit = guiEditor(pGui, ".", NULL, NULL, extra, pCtx);
*
*
*
* void extra(pGuiCtx, pEditor, panel, pX,pY,pHt, pCtx)
* GUI_CTX  *pGuiCtx;
* GUI_EDIT *pEditor;
* Panel    panel;
* int      *pX,*pY,*pHt;
* TEST_CTX *pCtx;
* {
*     *pX = 5; *pY += *pHt + 10; *pHt = 0;
*     guiButton("New entry", panel, pX,pY,pHt, guiEditorNewEntry_pb, NULL,
*                    GUI_PCTX, pGuiCtx, GUI_PEDIT, pEditor);
* }
*-*/
void
guiEditorNewEntry_pb(item)
Panel_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    Textsw	tsw=pEdit->text_TSW;
    TS_STAMP	nowTs;
    char	now[32];
    int		i;

    if (!pEdit->loaded) {
	guiNotice(pEdit->pGuiCtx, "no file has been loaded");
	return;
    }
    if (pEdit->updFlag == 0) {
	guiNotice(pEdit->pGuiCtx, "edit mode must be set");
	return;
    }
    xv_set(tsw, TEXTSW_INSERTION_POINT, TEXTSW_INFINITY, NULL);
    textsw_possibly_normalize(tsw,
		(Textsw_index)xv_get(tsw, TEXTSW_INSERTION_POINT));
    tsLocalTime(&nowTs);
    tsStampToText(&nowTs, TS_TEXT_MONDDYYYY, now);
    now[21] = '\0';	/* don't print fractions of sec */
    guiEditorPrintf(pEdit, "\n== %s  %s ==\n", now, pEdit->pGuiCtx->user);
}

/*+/internal******************************************************************
* NAME	guiEditorPickFile_mi
*
*-*/
static void
guiEditorPickFile_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_CTX	*pGuiCtx=(GUI_CTX *)xv_get(item, XV_KEY_DATA, GUI_PCTX);
    GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    char	title[GUI_TDIM*2];

    sprintf(title, "%s: %s", pEdit->title, "List dir");
    guiEditorGetPath(pEdit, 0);
    guiFileSelect(pGuiCtx, title, pEdit->dir, pEdit->file,
		GUI_TDIM, guiEditorPick_callback, pEdit);
}

/*+/internal******************************************************************
* NAME	guiEditorPick_callback
*
*-*/
static void
guiEditorPick_callback(pEdit, newPath, newDir, newFileName)
GUI_EDIT *pEdit;
char	*newPath;
char	*newDir;
char	*newFileName;
{
    xv_set(pEdit->dir_PT, PANEL_VALUE, newDir, NULL);
    xv_set(pEdit->file_PT, PANEL_VALUE, newFileName, NULL);
    guiEditorLoadFile_1(pEdit);
    guiMessagePrintf(pEdit->status_PM,
		"%s loaded; read-only mode set", newFileName);
}

/*+/internal******************************************************************
* NAME	guiEditorPrintFile_mi
*
*-*/
static void
guiEditorPrintFile_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);

    if (!pEdit->loaded) {
	guiNotice(pEdit->pGuiCtx, "no file has been loaded");
	goto printFailed;
    }
    if ((int)xv_get(pEdit->text_TSW, TEXTSW_MODIFIED)) {
	guiNotice(pEdit->pGuiCtx, "file is modified; save before printing");
	goto printFailed;
    }
    guiPrintFile(pEdit->pPrtCtx, pEdit->path);
    guiMessagePrintf(pEdit->status_PM, "printout sent to %s",
		(char *)xv_get(pEdit->pPrtCtx->printer_PT, PANEL_VALUE));
    return;
printFailed:
    guiMessagePrintf(pEdit->status_PM, "print failed");
}

#if 0
/*+/macros********************************************************************
* NAME	guiEditorPrintf - do a printf to a guiEditor
*
* SYNOPSIS
*	void
*	guiEditorPrintf(pEdit, fmt, ...)
*	GUI_EDIT *pEdit;/* I pointer to guiEditor */
*	char	*fmt;	/* I printf format string */
*	...		/* I other arguments, for printing */
*
*-*/
#endif
void guiEditorPrintf(va_alist)
va_dcl
{
    va_list	pArgs;
    GUI_EDIT	*pEdit;
    Textsw	textsw;
    char	*fmt;
    char	message[500];
    int		n, L;

    va_start(pArgs);
    pEdit = va_arg(pArgs, GUI_EDIT *);
    if (pEdit->loaded == 0) {
	guiNotice(pEdit->pGuiCtx, "no file loaded");
	return;
    }
    textsw = pEdit->text_TSW;
    fmt = va_arg(pArgs, char *);
    vsprintf(message, fmt, pArgs);
    va_end(pArgs);
    L = strlen(message);
    n = textsw_insert(textsw, message, L);
    if (n != L)
        return;
    n = (int)xv_get(textsw, TEXTSW_INSERTION_POINT);
    textsw_possibly_normalize(textsw, n);
}

/*+/subr**********************************************************************
* NAME	guiEditorPuts - write string to guiEditor
*
* DESCRIPTION
*	Writes the string to the text subwindow of a guiEditor.
*
*	The caller must supply the character index (starting with 0) of
*	the position in the textsw where writing is to start.  On return,
*	the caller's index is set to the next available character for
*	writing.
*
* RETURNS
*	0, or
*	EOF	if an error occurs during the write
*
*-*/
int
guiEditorPuts(pEdit, pBuf, pCharPos)
GUI_EDIT *pEdit;	/* I pointer to guiEditor */
char	*pBuf;		/* I pointer to buffer to write to text subwindow */
int	*pCharPos;	/* IO index of character position to start writing */
{
    Textsw	textsw=pEdit->text_TSW;
    Textsw_index	n;
    int		L=strlen(pBuf);

    if (pEdit->loaded == 0) {
	guiNotice(pEdit->pGuiCtx, "no file loaded");
	return EOF;
    }
    xv_set(textsw, TEXTSW_INSERTION_POINT, *pCharPos, NULL);
    n = textsw_insert(textsw, pBuf, L);
    if (n != L)
	return EOF;
    *pCharPos = (int)xv_get(textsw, TEXTSW_INSERTION_POINT);
    textsw_possibly_normalize(textsw, *pCharPos);
    return 0;
}

/*+/subr**********************************************************************
* NAME	guiEditorReset - reset an editor text subwindow, discarding contents
*
* DESCRIPTION
*
* RETURNS
*	void
*
*-*/
void
guiEditorReset(pEdit)
GUI_EDIT *pEdit;	/* I pointer to guiEditor */
{
    Textsw	textsw=pEdit->text_TSW;

    if (pEdit->loaded == 0)
	return;
    textsw_reset(textsw, 0, 0);
    guiEditorUpdateRst(pEdit);
}

/*+/internal******************************************************************
* NAME	guiEditorSaveFile_mi
*
*-*/
static void
guiEditorSaveFile_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    unsigned	stat;

    if (!pEdit->loaded) {
	guiNotice(pEdit->pGuiCtx, "no file has been loaded");
	return;
    }
    if ((int)xv_get(pEdit->text_TSW, TEXTSW_MODIFIED)) {
	if (guiEditorGetPath(pEdit, 1) != NULL) {
	    stat = textsw_store_file(pEdit->text_TSW, pEdit->path, 0, 0);
	    if (stat != 0) {
		guiNoticeFile(pEdit->pGuiCtx,"unable to save in:",pEdit->path);
		guiMessagePrintf(pEdit->status_PM, "save failed");
		return;
	    }
	}
    }
    guiEditorUpdateRst(pEdit);
    guiMessagePrintf(pEdit->status_PM, "saved in %s", pEdit->file);
}

#if 0
/*+/macros********************************************************************
* NAME	guiEditorShowCF - show the command frame containing the editor
*
* DESCRIPTION
*	Make an editor command frame visible.  There are several versions
*	of this routine available, to accomodate the different methods of
*	calling it:
*
*	void guiEditorShowCF(pEdit)		call directly
*		GUI_EDIT *pEdit;	/* I pointer to editor context */
*
*	void guiEditorShowCF_mi(menu, item)	call from a menu item
*		Menu	menu;		/* I menu handle */
*		Menu_item item;		/* I menu item handle--must have
*					XV_KEY_DATA of GUI_PEDIT, pEdit */
*
*	void guiEditorShowCF_xvo(item)	call from a button item
*		Xv_opaque item;		/* I item handle--must have
*					XV_KEY_DATA of GUI_PEDIT, pEdit */
*-*/
#endif
void
guiEditorShowCF(pEdit)
GUI_EDIT *pEdit;	/* I pointer to editor context */
{
    if ((int)xv_get(pEdit->edit_CF, XV_SHOW) == TRUE)
	guiAbove(pEdit->edit_CF);
    else
	xv_set(pEdit->edit_CF, XV_SHOW, TRUE, FRAME_CMD_PUSHPIN_IN, TRUE, NULL);
}
void
guiEditorShowCF_mi(menu, menuItem)
Menu	menu;
Menu_item menuItem;
{   GUI_EDIT *pEdit=(GUI_EDIT *)xv_get(menuItem, XV_KEY_DATA, GUI_PEDIT);
    guiEditorShowCF(pEdit);
}
void
guiEditorShowCF_xvo(item)
Xv_opaque item;
{   GUI_EDIT *pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    guiEditorShowCF(pEdit);
}

#if 0
/*+/macro*********************************************************************
* NAME	guiEditorStatusPrintf - do a printf to the guiEditor status line
*
* SYNOPSIS
*	void
*	guiEditorStatusPrintf(pEdit, fmt, ...)
*	GUI_EDIT *pEdit;/* I pointer to guiEditor */
*	char	*fmt;	/* I printf format string */
*	...		/* I other arguments, for printing */
*
*-*/
#endif
void guiEditorStatusPrintf(va_alist)
va_dcl
{
    GUI_EDIT	*pEdit;
    va_list	pArgs;
    Xv_opaque	item;
    char	*fmt;
    char	message[500];

    va_start(pArgs);
    pEdit = va_arg(pArgs, GUI_EDIT *);
    fmt = va_arg(pArgs, char *);
    vsprintf(message, fmt, pArgs);
    va_end(pArgs);
    xv_set(pEdit->status_PM, PANEL_LABEL_STRING, message, NULL);
}

/*+/internal******************************************************************
* NAME	guiEditorUpdate_mi
*
*-*/
void guiEditorUpdate_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_EDIT	*pEdit=(GUI_EDIT *)xv_get(item, XV_KEY_DATA, GUI_PEDIT);
    int		value=(int)xv_get(item, XV_KEY_DATA, GUI_INT);
    if (value == 0)
	guiEditorUpdateRst(pEdit);
    else
	guiEditorUpdateSet(pEdit);
}

/*+/internal******************************************************************
* NAME	guiEditorUpdateRst
*
*-*/
void guiEditorUpdateRst(pEdit)
GUI_EDIT *pEdit;
{
    if ((int)xv_get(pEdit->text_TSW, TEXTSW_MODIFIED))
	guiNotice(pEdit->pGuiCtx,
		"text has been modified; use \"Save\" or \"discard edits\"");
    else {
	xv_set(pEdit->text_TSW, TEXTSW_BROWSING, TRUE, NULL);
	if (pEdit->updFlag == 1) {
	    pEdit->updFlag = 0;
	    guiLock(pEdit->pGuiCtx, pEdit->file, pEdit->lockFile, 0);
	}
    }
    if (pEdit->updFlag == 1)
	guiMessagePrintf(pEdit->status_PM, "edit mode");
    else
	guiMessagePrintf(pEdit->status_PM, "read-only mode");
}

/*+/internal******************************************************************
* NAME	guiEditorUpdateSet
*
*-*/
void static
guiEditorUpdateSet(pEdit)
GUI_EDIT *pEdit;
{
    Textsw	tsw=pEdit->text_TSW;
    int		top;
    char	lockFile[2*GUI_TDIM + 5];
    FILE	*pFile;

    if (pEdit->updFlag == 1) {
	guiEditorUpdateRst(pEdit);
	goto updateDone;
    }
    if (!pEdit->loaded) {
	guiNotice(pEdit->pGuiCtx, "no file has been loaded");
	guiEditorUpdateRst(pEdit);
	return;
    }

    if ((pFile = fopen(pEdit->path, "r+")) == NULL) {
	guiNoticeFile(pEdit->pGuiCtx, "file isn't writeable:", pEdit->file);
	guiEditorUpdateRst(pEdit);
	goto updateDone;
    }
    fclose(pFile);

    top = (int)xv_get(tsw, TEXTSW_FIRST_LINE);
    top += 2;
    strcpy(lockFile, pEdit->path);
    strcat(lockFile, ".editLock");
    if (guiLock(pEdit->pGuiCtx, pEdit->file, lockFile, 1) != 1)  {
	guiEditorUpdateRst(pEdit);	/* lock attempt didn't succeed */
	goto updateDone;
    }
    xv_set(tsw, TEXTSW_FIRST_LINE, top, NULL);
    xv_set(tsw, TEXTSW_BROWSING, FALSE, NULL);
    pEdit->updFlag = 1;
    strcpy(pEdit->lockFile, lockFile);
updateDone:
    if (pEdit->updFlag == 1)
	guiMessagePrintf(pEdit->status_PM, "edit mode");
    else
	guiMessagePrintf(pEdit->status_PM, "read-only mode");
}


void guiFileSelCom_mi();
void guiFileSelLs_mi();
void guiFileSelLsDone();

/*+/subr**********************************************************************
* NAME	guiFileSelect - select a file from a group
*
* DESCRIPTION
*	Present a list of files in a directory and allow the user to
*	choose one of them.  Support is given for changing directory.
*
*	Only one file select command frame can be active at a given time--
*	each call to this routine will result in wrapping up any
*	previous call to it.
*
*	The callback routine will be called if the user requests a change
*	in file path.  The callback routine is called with 4 arguments:
*
*		callback(pArg, newPath, newDir, newFileName);
*
* BUGS
* o	doesn't support wildcard lists (e.g., ls *.c)
*
*-*/
void
guiFileSelect(pGuiCtx, title, dir, file, dim, callback, pArg)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*title;		/* I title for command frame title bar */
char	*dir;		/* I directory */
char	*file;		/* I file name */
int	dim;		/* I dimension of the dir & file arrays */
void	(*callback)();	/* I routine to call if user changes file/dir */
void	*pArg;		/* I argument to pass to callback routine */
{
    Frame	frame;
    Panel	panel;
    Menu	menu;
    int		x=5, y=5, ht=0;
    char	message[GUI_TDIM*3];

    pGuiCtx->fsCallFn = callback;
    pGuiCtx->fsCallArg = pArg;

    if (pGuiCtx->fsFrame != NULL) {
	frame = pGuiCtx->fsFrame;
	xv_set(frame, FRAME_LABEL, title, NULL);
    }
    else {
	frame = guiFrameCmd(title, pGuiCtx->baseFrame, &panel, NULL);
	xv_set(frame, XV_KEY_DATA, GUI_PCTX, pGuiCtx, NULL);
	pGuiCtx->fsFrame = frame;
	pGuiCtx->fsDir_PT = guiTextField("Dir:", panel, &x,&y,&ht,
		NULL, dim-1, 40, 0, NULL, 0, NULL);
	pGuiCtx->fsFile_PT = guiTextField("File:", panel, &x,&y,&ht,
		NULL, dim-1, 20, 0, NULL, 0, NULL);
	y += ht + 10; x = 5; ht = 0;

	menu = guiMenu(NULL, 0, NULL, 0, NULL);
	guiMenuItem("Load", menu, guiFileSelCom_mi, 0, 1,
		    GUI_PCTX, pGuiCtx, 0, NULL);
	guiMenuItem("List dir...", menu, guiFileSelLs_mi, 0, 0,
		    GUI_PCTX, pGuiCtx, 0, NULL);
	guiButton("File", panel, &x, &y, &ht, NULL, menu, 0,NULL,0,NULL);

	pGuiCtx->fsStatus_PM = guiMessage(" ", panel, &x, &y, &ht);
	y += ht + 5; x = 5; ht = 0;

	window_fit(panel);
	pGuiCtx->fsTextsw = guiBrowser(frame, panel, 60, 30, NULL);
    }
    xv_set(pGuiCtx->fsDir_PT, PANEL_VALUE, dir, NULL);
    xv_set(pGuiCtx->fsFile_PT, PANEL_VALUE, file, NULL);
    window_fit(frame);
    xv_set(frame, XV_SHOW, TRUE, NULL);
    if (dir[0] == '\0') {
	guiNotice(pGuiCtx, "directory must be specified");
	return;
    }
    sprintf(pGuiCtx->fsLsFile, "/tmp/guiFileSel%d.ls.tmp", getpid());
    unlink(pGuiCtx->fsLsFile);
    sprintf(message, "cd %s; ls -l >%s", dir, pGuiCtx->fsLsFile);
    guiShellCmd_work(pGuiCtx, message, GUI_FILE_SEL_CLIENT,
					guiFileSelLsDone, pGuiCtx, 0);
    sprintf(message, "processing names in %s", dir);
    xv_set(pGuiCtx->fsStatus_PM, PANEL_LABEL_STRING, message, NULL);
}

/*+/internal******************************************************************
* NAME	guiFileSelCom_mi
*
*-*/
static void
guiFileSelCom_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_CTX	*pGuiCtx=(GUI_CTX *)xv_get(item, XV_KEY_DATA, GUI_PCTX);
    char	*pSel, *dir, *file, path[240];

/*-----------------------------------------------------------------------------
*	if there's selected text, use it for file name.  If it's in the
*	window listing the files, get the file name from the end of the
*	line containing the selected text.
*----------------------------------------------------------------------------*/
    pSel = guiGetNameFromSeln(pGuiCtx, pGuiCtx->fsTextsw, 0, 1);
    if (pSel != NULL)
	xv_set(pGuiCtx->fsFile_PT, PANEL_VALUE, pSel, NULL);

    dir = (char *)xv_get(pGuiCtx->fsDir_PT, PANEL_VALUE);
    file = (char *)xv_get(pGuiCtx->fsFile_PT, PANEL_VALUE);
    if (strlen(file) > 0) {
	sprintf(path, "%s/%s", dir, file);
	pGuiCtx->fsCallFn(pGuiCtx->fsCallArg, path, dir, file);
#if 0
	xv_set(pGuiCtx->fsFrame,
		XV_SHOW, FALSE, FRAME_CMD_PUSHPIN_IN, FALSE, NULL);
#endif
    }
    else
	guiNotice(pGuiCtx, "file name is blank; select file or use Cancel");
}

/*+/internal******************************************************************
* NAME	guiFileSelLs_mi
*
*-*/
static void
guiFileSelLs_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_CTX	*pGuiCtx=(GUI_CTX *)xv_get(item, XV_KEY_DATA, GUI_PCTX);
    char	message[GUI_TDIM];
    char	*dir=(char *)xv_get(pGuiCtx->fsDir_PT, PANEL_VALUE);
    unlink(pGuiCtx->fsLsFile);
    sprintf(message, "cd %s; ls -l >%s", dir, pGuiCtx->fsLsFile);
    guiShellCmd_work(pGuiCtx, message, GUI_FILE_SEL_CLIENT,
					guiFileSelLsDone, pGuiCtx, 0);
    sprintf(message, "processing names in %s", dir);
    xv_set(pGuiCtx->fsStatus_PM, PANEL_LABEL_STRING, message, NULL);
}

/*+/internal******************************************************************
* NAME	guiFileSelLsDone
*
*-*/
static void
guiFileSelLsDone(pGuiCtx)
GUI_CTX	*pGuiCtx;
{
    char	*dir=(char *)xv_get(pGuiCtx->fsDir_PT, PANEL_VALUE);
    char	message[GUI_TDIM];
    xv_set(pGuiCtx->fsTextsw, TEXTSW_FILE, pGuiCtx->fsLsFile, NULL);
    unlink(pGuiCtx->fsLsFile);
    if ((int)xv_get(pGuiCtx->fsTextsw, TEXTSW_LENGTH) <= 0)
	sprintf(message, "there are no files in %s", dir);
    else
	sprintf(message, "size and modification date are shown");
    xv_set(pGuiCtx->fsStatus_PM, PANEL_LABEL_STRING, message, NULL);
}

/*+/subr**********************************************************************
* NAME	guiFrame - create a base frame
*
* DESCRIPTION
*	Create a base frame.  The frame will have have the specified label
*	in its title bar, as well as for its icon.  The frame will have
*	resize corners.  If height and width are specified, they will
*	determine the size of the frame; otherwise, it will have a
*	default size.
*
*	If pointers are specified in the call, then the X11 display and
*	the XView server are passed back to the caller.
*
* RETURNS
*	Frame handle
*
* SEE ALSO
*	guiFrameCmd, guiIconCreate
*-*/
Frame
guiFrame(label, x, y, width, height, ppDisp, pServer)
char	*label;		/* I label for frame and icon */
int	x;		/* I x coordinate for frame, in pixels */
int	y;		/* I y coordinate for frame, in pixels */
int	width;		/* I width of frame, in pixels, or 0 */
int	height;		/* I height of frame, in pixels, or 0 */
Display **ppDisp;	/* O pointer to X display pointer, or NULL */
Xv_server *pServer;	/* O pointer to xview server handle, or NULL */
{
    Icon	icon;
    Frame	frame;

    icon = (Icon)xv_create(NULL, ICON, ICON_LABEL, label, NULL);
    if (icon == NULL) {
	(void)printf("error creating \"%s\" icon\n", label);
	exit(1);
    }
    frame = (Frame)xv_create(NULL, FRAME, FRAME_LABEL, label, FRAME_ICON, icon,
	WIN_X, x, WIN_Y, y, NULL);
    if (frame == NULL) {
	(void)printf("error creating \"%s\" frame\n", label);
	exit(1);
    }
    if (width > 0)	xv_set(frame, XV_WIDTH, width, NULL);
    if (height > 0)	xv_set(frame, XV_HEIGHT, height, NULL);
    if (ppDisp != NULL)
	*ppDisp = (Display *)xv_get(frame, XV_DISPLAY);
    if (pServer != NULL)
	*pServer = (Xv_server)xv_get(xv_get(frame, XV_SCREEN), SCREEN_SERVER);
    
    return frame;
}

/*+/subr**********************************************************************
* NAME	guiFrameCmd - create a command frame
*
* DESCRIPTION
*	Create a command frame and an associated panel.
*
*	If a procedure is specified for handling resize, then the frame
*	is set to receive resize events.  (The frame will have resize
*	corners regardless of whether a procedure is specified.)
*
* RETURNS
*	Frame handle
*
* NOTES
* 1.	For command frames with text subwindows, the resize procedure can
*	be omitted, and the text subwindow will be automatically resized
*	when the command frame is resized.
*
*-*/
Frame
guiFrameCmd(label, parentFrame, pPanel, resizeProc)
char	*label;		/* I label for command frame */
Frame	parentFrame;	/* I parent frame for command frame */
Panel	*pPanel;	/* O ptr to default panel of command frame, or NULL */
void	(*resizeProc)();/* I function to handle resize, or NULL */
{
    Frame	frame;

    if (resizeProc == NULL) {
	frame = (Frame)xv_create(parentFrame, FRAME_CMD, FRAME_LABEL, label,
				FRAME_SHOW_RESIZE_CORNER, TRUE,
				XV_SHOW, FALSE, NULL);
    }
    else {
	frame = (Frame)xv_create(parentFrame, FRAME_CMD, FRAME_LABEL, label,
				FRAME_SHOW_RESIZE_CORNER, TRUE,
				WIN_EVENT_PROC,		resizeProc,
				WIN_CONSUME_EVENTS,	WIN_RESIZE, NULL,
				XV_SHOW, FALSE, NULL);
    }
    if (frame == NULL) {
	(void)printf("error creating \"%s\" command frame\n", label);
	exit(1);
    }
    if (pPanel != NULL) {
	*pPanel = (Panel)xv_get(frame, FRAME_CMD_PANEL);
	if (*pPanel == NULL) {
	    (void)printf("error getting default panel for \"%s\"\n", label);
	    exit(1);
	}
	xv_set(*pPanel, WIN_WIDTH, WIN_EXTEND_TO_EDGE, NULL);
    }
    return frame;
}

/*+/subr**********************************************************************
* NAME	guiGetNameFromSeln - get a name from a line of selected text
*
* DESCRIPTION
*	Examines the selected text to extract a name to return to the caller.
*	A `name' is considered to be a string of characters delimited by
*	white space.  The entire line containing the selection is treated
*	as being selected.
*
*	The selected text must be within the caller's text subwindow.
*
*	The actual action taken depends on the arguments:
*
*	head  tail            pointer is returned to:
*	----  ----  -----------------------------------------------
*	  0     0    entire line containing selection
*	  1     0    first word of line containing selection
*	  0     1    last word of line containing selection
*
* RETURNS
*	char * to name
*
* BUGS
* o	this routine isn't reentrant
* o	if the caller wants a permanent copy of the name, it must be copied
*	prior to the next call to guiGetNameFromSeln
*
*-*/
char *
guiGetNameFromSeln(pGuiCtx, textSw, headFlag, tailFlag)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
Textsw	textSw;		/* I handle to text subwindow to treat special */
int	headFlag;	/* I 1 to return only the head of the line */
int	tailFlag;	/* I 1 to return only the tail of the line */
{
    int		i, bufLen;
    char	*pSel, *buf;

    buf = pSel = guiGetSelnInTextsw(pGuiCtx, textSw);
    if (pSel == NULL)
	return NULL;
    bufLen = strlen(pSel);
    if (headFlag) {
/*-----------------------------------------------------------------------------
*	skip leading blanks, then look for the first white space; return
*	what's in between to the caller
*----------------------------------------------------------------------------*/
	pSel = buf;
	while (*pSel == ' ') {
	    pSel++;		/* skip leading blanks */
	    bufLen--;
	}
	i = 0;
	while (i < bufLen-1) {
	    if (isspace(pSel[i]))
		break;
	    i++;
	}
	pSel[i] = '\0';
    }
    else if (tailFlag) {
/*-----------------------------------------------------------------------------
*	looking backward from end, find the first white space; return the
*	"tail" of the line to the caller
*----------------------------------------------------------------------------*/
	i = bufLen - 1;
	while (i > 0) {
	    if (buf[i] == '\n')
		buf[i] = '\0';
	    else if (isspace(buf[i])) {
		i++;
		break;
	    }
	    i--;
	}
	pSel = &buf[i];
    }

    return pSel;
}

/*+/subr**********************************************************************
* NAME	guiGetSelnInTextsw - get selected text if it's in callers textsw
*
* DESCRIPTION
*	Checks to see if there's a text selection in the caller's text
*	subwindow.  If so, a pointer to it is returned.
*
* RETURNS
*	char * to selected text
*
* BUGS
* o	this routine isn't reentrant
* o	if the caller wants a permanent copy of the name, it must be copied
*	prior to the next call to guiGetSelnInTextsw
*
*-*/
char *
guiGetSelnInTextsw(pGuiCtx, textSw)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
Textsw	textSw;		/* I handle to text subwindow to treat special */
{
    Seln_holder	holder;
    Seln_request *pResponse;
    static char	buf[GUI_TDIM*2];
    int		bufLen;
    char	*pSel;

    holder = selection_inquire(pGuiCtx->server, SELN_PRIMARY);
    pResponse = selection_ask(pGuiCtx->server, &holder,
	SELN_REQ_CONTENTS_ASCII, NULL,
	NULL);
    pSel = pResponse->data + sizeof(SELN_REQ_CONTENTS_ASCII);
    bufLen = strlen(pSel);
    if (bufLen == 0)
	pSel = NULL;	/* no selection */
    else if (seln_holder_same_client(&holder, textSw)) {
	pResponse = selection_ask(pGuiCtx->server, &holder,
		SELN_REQ_FAKE_LEVEL,	SELN_LEVEL_LINE,
		SELN_REQ_CONTENTS_ASCII, NULL,
		NULL);
	pSel = pResponse->data;
	pSel += sizeof(SELN_REQ_FAKE_LEVEL);
	pSel += sizeof(SELN_LEVEL_LINE);
	pSel += sizeof(SELN_REQ_CONTENTS_ASCII);
	bufLen = strlen(pSel);
	if (bufLen > sizeof(buf)-1) {
	    guiNotice(pGuiCtx, "too much text selected");
	    return (char *)NULL;
	}
	strcpy(buf, pSel);
    }
    else
	pSel = NULL;
    return pSel;
}

/*+/subr**********************************************************************
* NAME	guiIconCreate - create an icon to attach to a frame
*
* DESCRIPTION
*	Using the bit image for an icon, creates the internal structure
*	needed to draw the icon.  The icon is then associated with the
*	specified frame.
*
* RETURNS
*	Icon handle
*
* BUGS
* o	assumes an icon size of 64x64
*
* EXAMPLE
*	Associate the icon in "../src/myIcon.icon" with the base frame.
*	(myIcon.icon was produced with the OpenLook "iconedit" program.)
*
* #include <xview/icon.h>
*
*	myIcon_bits[]={
* #include "../src/myIcon.icon"
*	};
*
*	Icon	myIcon;
*
*	myIcon = (Icon)guiIconCreate(pGuiCtx->baseFrame, myIcon_bits);
*
*-*/
Icon
guiIconCreate(frame, iconBits)
Frame	frame;		/* I handle for frame to receive the icon */
short	*iconBits;	/* I array of bits for the icon (64 by 64 assuemd) */
{
    Server_image icon_image;
    Icon	icon;

    icon_image = (Server_image)xv_create(NULL, SERVER_IMAGE,
		XV_WIDTH,		64,
		XV_HEIGHT,		64,
		SERVER_IMAGE_BITS, iconBits,
		NULL);
    icon = (Icon)xv_create(frame, ICON, ICON_IMAGE, icon_image,
		ICON_TRANSPARENT, TRUE, NULL);
    xv_set(frame, FRAME_ICON, icon, NULL);

    return icon;
}

/*+/subr**********************************************************************
* NAME	guiInit - initialize the gui routines
*
* DESCRIPTION
*	Sets up for gui operations, including:
*	o   connecting to X11
*	o   assimilating X11 options from the command line
*	o   creating a base frame
*	o   creating a panel within the base frame
*	o   initializing the gui context block with:
*		.user		user name
*		.host		host name (or X server name if different)
*		.pwd		working directory
*		.baseFrame	Xview Frame of main program window
*		.pDisp		the X Display pointer
*		.server		the Xview server handle
*
* RETURNS
*	Panel handle
*
*-*/
Panel
guiInit(pGuiCtx, pArgc, argv, label, x, y, width, height)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
int     *pArgc;		/* I pointer to number of command line args */
char	*argv[];	/* I command line args */
char	*label;		/* I label for frame and icon */
int	x;		/* I x coordinate for frame, in pixels */
int	y;		/* I y coordinate for frame, in pixels */
int	width;		/* I width of frame, in pixels, or 0 */
int	height;		/* I height of frame, in pixels, or 0 */
{
    char	*user, *pwd;

    xv_init(XV_INIT_ARGC_PTR_ARGV, pArgc, argv, 0);
    pGuiCtx->baseFrame = guiFrame(label, x, y, width, height,
		&pGuiCtx->pDisp, &pGuiCtx->server);
    pGuiCtx->fsFrame = NULL;
    strcpy(pGuiCtx->host, pGuiCtx->pDisp->display_name);
    pwd = getenv("PWD");
    strncpy(pGuiCtx->pwd, pwd, GUI_TDIM-1);
    user = getenv("USER");
    strncpy(pGuiCtx->user, user, GUI_TDIM-1);
    if (pGuiCtx->host[0] == '\0' || pGuiCtx->host[0] == ':' ||
			strncmp(pGuiCtx->host, "unix", 4) == 0) {
	gethostname(pGuiCtx->host, GUI_TDIM);
    }

    return (Panel)xv_create(pGuiCtx->baseFrame, PANEL,
	    XV_X, 0, XV_Y, 0, PANEL_LAYOUT, PANEL_HORIZONTAL, NULL);
}

/*+/subr**********************************************************************
* NAME	guiLock - provide a "global" lock and unlock mechanism
*
* DESCRIPTION
*
* RETURNS
*	1	if lock succeeds
*	0	if unlock succeeds
*	-1	if either operation fails
*
*-*/
int
guiLock(pGuiCtx, text, lockPath, lock)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context block */
char	*text;		/* I name or description for error messages */
char	*lockPath;	/* I pointer to path for lock file */
int	lock;		/* I 0,1 to reset,set the lock */
{
    FILE	*pFile;
    char	msgLock[2*GUI_TDIM];
    char	msgWho[2*GUI_TDIM];
    TS_STAMP	ts;
    char	tsText[32];
    int		noticeVal;

    if (lock) {
	if ((pFile = fopen(lockPath, "r+")) != NULL) {
	    fread(msgWho, GUI_TDIM, 1, pFile);
	    fclose(pFile);
	    if (strncmp(msgWho, "unlocked", 8) != 0) {
		sprintf(msgLock, "%s is locked:", text);
		noticeVal = notice_prompt(pGuiCtx->baseFrame, NULL,
		    NOTICE_MESSAGE_STRINGS,	msgLock, msgWho,
			"you can cancel the operation or force an unlock", NULL,
		    NOTICE_BUTTON_YES,	"Cancel",
		    NOTICE_BUTTON_NO,	"Unlock",
		    NULL);
		if (noticeVal == NOTICE_YES)
		    return -1;
	    }
	}
    }
    if ((pFile = fopen(lockPath, "w+")) == NULL) {
	guiNoticeFile(pGuiCtx, "couldn't open lock file", lockPath);
	return -1;
    }
    if (lock) {
	tsLocalTime(&ts);
	tsStampToText(&ts, TS_TEXT_MONDDYYYY, tsText);
	sprintf(msgWho, "by:%s  on:%s  at:%.21s",
			pGuiCtx->user, pGuiCtx->host, tsText);
	fwrite(msgWho, strlen(msgWho)+1, 1, pFile);
	fclose(pFile);
	return 1;
    }
    else {
	fwrite("unlocked", 9, 1, pFile);
	fclose(pFile);
	unlink(lockPath);
	return 0;
    }
}

/*+/subr**********************************************************************
* NAME	guiMenu - create a menu
*
* DESCRIPTION
*
* RETURNS
*	Menu handle
*
*-*/
Menu
guiMenu(proc, key1, val1, key2, val2)
void	(*proc)();	/* I pointer to procedure to handle menu selection */
enum	key1;		/* I key for context information for object */
void	*val1;		/* I value associated with key */
enum	key2;		/* I key for context information for object */
void	*val2;		/* I value associated with key */
{
    Menu	menu;

    menu = (Menu)xv_create(NULL, MENU, NULL);
    if (menu == NULL) {
	(void)printf("error creating menu\n");
	exit(1);
    }
    if (proc != NULL) {
	if (xv_set(menu, MENU_NOTIFY_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error adding proc to menu\n");
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(menu, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error adding key1 to menu\n");
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(menu, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error adding key2 to menu\n");
	    exit(1);
	}
    }

    return menu;
}

/*+/subr**********************************************************************
* NAME	guiMenuItem - add a menu item to a menu
*
* DESCRIPTION
*
* RETURNS
*	Menu_item handle
*
*-*/
Menu_item
guiMenuItem(label, menu, proc, inact, dflt, key1, val1, key2, val2)
char	*label;		/* I label for menu item */
Menu	menu;		/* I menu on which to add item */
void	(*proc)();	/* I pointer to procedure to handle menu selection */
int	inact;		/* I 0,1 for item active,inactive */
int	dflt;		/* I 0,1 for item isn't,is the default */
enum	key1;		/* I key for context information for object */
void	*val1;		/* I value associated with key */
enum	key2;		/* I key for context information for object */
void	*val2;		/* I value associated with key */
{
    Menu_item	menuItem;

    menuItem = (Menu_item)xv_create(NULL, MENUITEM,
				    MENU_STRING, label, MENU_RELEASE, NULL);
    if (menuItem == NULL) {
	(void)printf("error creating \"%s\" menu item\n", label);
	exit(1);
    }
    if (xv_set(menu, MENU_APPEND_ITEM, menuItem, NULL) != XV_OK) {
	(void)printf("error adding \"%s\" menu item to menu\n", label);
	exit(1);
    }
    if (proc != NULL) {
	if (xv_set(menuItem, MENU_NOTIFY_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error adding proc to \"%s\" menu item\n", label);
	    exit(1);
	}
    }
    if (inact) {
	if (xv_set(menuItem, MENU_INACTIVE, TRUE, NULL) != XV_OK) {
	    (void)printf("error setting \"%s\" menu item inactive\n", label);
	    exit(1);
	}
    }
    if (dflt) {
	if (xv_set(menu, MENU_DEFAULT_ITEM, menuItem, NULL) != XV_OK) {
	    (void)printf("error setting \"%s\" menu item default\n", label);
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(menuItem, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error adding key1 to \"%s\" menu item\n", label);
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(menuItem, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error adding key2 to \"%s\" menu item\n", label);
	    exit(1);
	}
    }

    return menuItem;
}

/*+/subr**********************************************************************
* NAME	guiMessage - create a text message object
*
* DESCRIPTION
*
* RETURNS
*	Panel_item handle to message
*
* BUGS
* o	text
*
* SEE ALSO
*	guiMessagePrintf
*
* EXAMPLE
*
*-*/
Panel_item
guiMessage(label, panel, pX, pY, pHt)
char	*label;		/* I label for button */
Panel	panel;		/* I handle of panel containing message */
int	*pX;		/* IO pointer to x position in panel, in pixels */
int	*pY;		/* I pointer to y position in panel, in pixels */
int	*pHt;		/* IO ptr to height of message, in pixels, or NULL */
{
    Panel_item	item;
    int		height;

    item = (Panel_item)xv_create(panel, PANEL_MESSAGE,
		PANEL_LABEL_STRING, label, XV_X, *pX, XV_Y, *pY, NULL);
    if (item == NULL) {
	(void)printf("error creating message object\n");
	exit(1);
    }
    *pX += (int)xv_get(item, XV_WIDTH) + 10;
    if (pHt != NULL) {
	height = xv_get(item, XV_HEIGHT);
	if (height > *pHt)
	    *pHt = height;
    }

    return item;
}

#if 0
/*+/macro*********************************************************************
* NAME	guiMessagePrintf - do a printf to a guiMessage object
*
* SYNOPSIS
*	void
*	guiMessagePrintf(panelMessage, fmt, ...)
*	Panel_item panelMessage;/* I handle from guiMessage */
*	char	*fmt;	/* I printf format string */
*	...		/* I other arguments, for printing */
*
*-*/
#endif
void guiMessagePrintf(va_alist)
va_dcl
{
    va_list	pArgs;
    Xv_opaque	item;
    char	*fmt;
    char	message[500];

    va_start(pArgs);
    item = va_arg(pArgs, Xv_opaque);
    fmt = va_arg(pArgs, char *);
    vsprintf(message, fmt, pArgs);
    va_end(pArgs);
    xv_set(item, PANEL_LABEL_STRING, message, NULL);
}

/*+/macro*********************************************************************
* NAME	guiNotice - display a pop-up notice
*
* DESCRIPTION
*	Display a pop-up notice with a "continue" button.  Several different
*	routines are available:
*
*	guiNotice(pGuiCtx, message)   simply displays the message
*
*	guiNoticeFile(pGuiCtx, message, filePath)   calls "perror()" to print
*			the Unix error message and file name to stdout, then
*			displays the message and file path in the pop-up
*
*	guiNoticeName(pGuiCtx, message, name)   displays the message and the
*			name (usually for a channel or file) in the pop-up
*
*	In each case, the first argument is a GUI_CTX * pointing to a
*	gui context block.  The other arguments are char * pointing to
*	text strings.
*
* RETURNS
*	void
*
*-*/
void
guiNotice(pGuiCtx, msg)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*msg;
{
    notice_prompt(pGuiCtx->baseFrame, NULL, NOTICE_MESSAGE_STRINGS, msg, NULL,
	    NOTICE_BUTTON_YES,	"Continue", NULL);
    return;
}
void
guiNoticeFile(pGuiCtx, msg, fName)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*msg;
char	*fName;
{
    perror(fName);
    notice_prompt(pGuiCtx->baseFrame, NULL,
	    NOTICE_MESSAGE_STRINGS, msg, fName, NULL,
	    NOTICE_BUTTON_YES,	"Continue", NULL);
    return;
}
void
guiNoticeName(pGuiCtx, msg, name)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*msg;
char	*name;
{
    char	myName[GUI_TDIM], *pName;
    if (strlen(name) < GUI_TDIM-3){
	sprintf(myName, "\"%s\"", name);
	pName = myName;
    }
    else
	pName = name;
    notice_prompt(pGuiCtx->baseFrame, NULL,
	    NOTICE_MESSAGE_STRINGS, msg, pName, NULL,
	    NOTICE_BUTTON_YES,	"Continue", NULL);
    return;
}

#if 0
/*+/macro*********************************************************************
* NAME	guiNoticePrintf - do a printf to a guiNotice
*
* SYNOPSIS
*	void
*	guiNoticePrintf(pGuiCtx, fmt, ...)
*	GUI_CTX	*pGuiCTx;/* I pointer to gui context */
*	char	*fmt;	/* I printf format string */
*	...		/* I other arguments, for printing */
*
*-*/
#endif
void guiNoticePrintf(va_alist)
va_dcl
{
    va_list	pArgs;
    GUI_CTX	*pCtx;
    char	*fmt;
    char	message[500];

    va_start(pArgs);
    pCtx = va_arg(pArgs, GUI_CTX *);
    fmt = va_arg(pArgs, char *);
    vsprintf(message, fmt, pArgs);
    va_end(pArgs);
    guiNotice(pCtx, message);
}

/*+/subr**********************************************************************
* NAME	guiPrinter - create a printer context and command frame
*
* DESCRIPTION
*	Create a printer context block and command frame for handling
*	printer operations.  The amenities provided include:
*
*	o   grabbing the PRINTER environment variable to set the default
*	    printer to use
*	o   allowing user to choose point size of font
*	o   allowing user to choose page orientation
*	o   allowing user to choose whether to use enscript or lpr for
*	    the printing operation
*
*	The printer context block can be used in the guiEditor call to
*	enable the "Print" button for the editor.
*
* RETURNS
*	GUI_PRT *, or
*	NULL
*
* SEE ALSO
*	guiPrintFile, guiPrinterShowCF_mi, guiPrinterShowCF_xvo
*	guiEditor
*
*-*/
GUI_PRT *
guiPrinter(pGuiCtx, parentFrame, title, useEnscript)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
Frame	parentFrame;	/* I parent of editor command frame */
char	*title;		/* I text for command frame title bar */
int	useEnscript;	/* I 0,1 to use lpr,enscript for printing */
{
    GUI_PRT	*pPrt;
    Panel	panel;
    Panel_item	item;
    char	*printer;
    Panel_item	ck;
    int		y=10, x=5, ht=0;

/*-----------------------------------------------------------------------------
* Printer: ______ point size: __  v portrait   v enscript
*                                   landscape    lpr
* ..................status message..............................
*----------------------------------------------------------------------------*/

    if ((pPrt=(GUI_PRT *)malloc(sizeof(GUI_PRT))) == NULL) {
	return NULL;
    }
    bzero((char *)pPrt, sizeof(GUI_PRT));
    pPrt->pGuiCtx = pGuiCtx;
    pPrt->print_CF = guiFrameCmd(title, parentFrame, &panel, NULL);

    pPrt->printer_PT = guiTextField("Printer:", panel, &x,&y,&ht,
		NULL, 19, 8, 0, NULL, 0, NULL);
    xv_set(pPrt->printer_PT, PANEL_VALUE, getenv("PRINTER"), NULL);
    pPrt->point_PT = guiTextField("point size:", panel, &x,&y,&ht,
		NULL, 2, 2, 0, NULL, 0, NULL);
    xv_set(pPrt->point_PT, PANEL_VALUE, "8", NULL);
    pPrt->landscape_PCS = item = xv_create(panel, PANEL_CHOICE_STACK,
	PANEL_LAYOUT,		PANEL_VERTICAL,
	PANEL_CHOICE_STRINGS,	"portrait","landscape",NULL,
	PANEL_VALUE,	0,
	XV_X, x, XV_Y, y-5, NULL);
    x += (int)xv_get(item, XV_WIDTH) + 5;
    pPrt->prog_PCS = item = xv_create(panel, PANEL_CHOICE_STACK,
	PANEL_LAYOUT,		PANEL_VERTICAL,
	PANEL_CHOICE_STRINGS,	"enscript","lpr",NULL,
	PANEL_VALUE,	0,
	XV_X, x, XV_Y, y-5, NULL);
    x += (int)xv_get(item, XV_WIDTH) + 5;
    y += ht + 5; x = 5; ht = 0;

    window_fit(panel);
    window_fit(pPrt->print_CF);
    return pPrt;
}

/*+/subr**********************************************************************
* NAME	guiPrintFile
*
* DESCRIPTION
*	Print a file using the settings in the printer context block.
*
* RETURNS
*	void
*
* SEE ALSO
*	guiPrinter
*
*-*/
void
guiPrintFile(pPrtCtx, path)
GUI_PRT	*pPrtCtx;	/* I pointer to printer context */
char	*path;		/* I pathname of file to be printed */
{
    char	command[GUI_TDIM*3];

    guiPrintGetCmd(pPrtCtx, path, command);
    guiShellCmd(pPrtCtx->pGuiCtx, command, GUI_PRINT_CLIENT, NULL, NULL);
}

/*+/subr**********************************************************************
* NAME	guiPrintGetCmd
*
* DESCRIPTION
*	Get a print command, using the settings in the printer context block.
*
* RETURNS
*	void
*
* SEE ALSO
*	guiPrinter
*
*-*/
void
guiPrintGetCmd(pPrtCtx, path, command)
GUI_PRT	*pPrtCtx;	/* I pointer to printer context */
char	*path;		/* I pathname of file to be printed, or NULL */
{
    char	mode[8];

    
    if ((int)xv_get(pPrtCtx->landscape_PCS, PANEL_VALUE) == 0)
	strcpy(mode, "");	/* portrait mode */
    else
	strcpy(mode, "-r");	/* rotate to landscape mode */
    if ((int)xv_get(pPrtCtx->prog_PCS, PANEL_VALUE) == 0) {
	sprintf(command, "enscript -fCourier%s -q -P%s %s %s",
		(char *)xv_get(pPrtCtx->point_PT, PANEL_VALUE),
		(char *)xv_get(pPrtCtx->printer_PT, PANEL_VALUE),
		mode, path);
    }
    else {
	if (mode[0] != '\0')
	    guiNotice(pPrtCtx->pGuiCtx, "landscape is ignored when using lpr");
	sprintf(command, "lpr -P%s %s",
		(char *)xv_get(pPrtCtx->printer_PT, PANEL_VALUE), path);
    }
}

/*+/macros********************************************************************
* NAME	guiPrinterShowCF_... - make printer command frame visible
*
* DESCRIPTION
*	Make the printer setup command frame visible, so that the user
*	can modify print parameters.
*
*	There are two forms of this routine.  The first is to be called
*	from a menu; the second is to be called from other objects, such
*	as a button.
*		guiPrinterShowCF_mi(menu, item);
*		guiPrinterShowCF_xvo(item);
*
* RETURNS
*	void
*
* SEE ALSO
*	guiPrinter, guiPrintFile
*
* EXAMPLE
*	Set up a button with a two-item menu--the first item will print
*	a file (whose name is in the program's context block), and the
*	second item will call this routine.  Make the first item the
*	default.
*
*    Menu    menu;
*    void    print_mi();
*
*    menu = guiMenu(NULL, 0, NULL, 0, NULL);
*    guiMenuItem("Print file", menu, print_mi, 0,1,
*                   PCTX, pCtx, GUI_PPRT, pEdit->pPrtCtx);
*    guiMenuItem("Printer setup...", menu, guiPrinterShowCF_mi, 0,0,
*                   GUI_PPRT, pEdit->pPrtCtx, 0, NULL);
*    guiButton("Print", panel, &x, &y, &ht, NULL, menu, 0, NULL, 0, NULL);
*
*
* void print_mi(menu, item)
* Menu        menu;
* Menu_item   item;
* {  GUI_PRT    *pPrt=(GUI_PRT *)xv_get(item, XV_KEY_DATA, GUI_PPRT);
*    TEST_CTX   *pCtx=(TEST_CTX *)xv_get(item, XV_KEY_DATA, PCTX);
*
*    guiPrintFile(pPrt, pCtx->file);
* }
*
*-*/
void
guiPrinterShowCF_mi(menu, item)
Menu	menu;
Menu_item item;
{   GUI_PRT	*pPrtCtx=(GUI_PRT *)xv_get(item, XV_KEY_DATA, GUI_PPRT);
    if ((int)xv_get(pPrtCtx->print_CF, XV_SHOW) == TRUE)
	guiAbove(pPrtCtx->print_CF);
    else
	xv_set(pPrtCtx->print_CF, XV_SHOW, TRUE, NULL);
}

/*+/internal******************************************************************
* NAME	guiPrinterShowCF_xvo
*
*-*/
void
guiPrinterShowCF_xvo(item)
Xv_opaque item;
{   GUI_PRT	*pPrtCtx=(GUI_PRT *)xv_get(item, XV_KEY_DATA, GUI_PPRT);
    if ((int)xv_get(pPrtCtx->print_CF, XV_SHOW) == TRUE)
	guiAbove(pPrtCtx->print_CF);
    else
	xv_set(pPrtCtx->print_CF, XV_SHOW, TRUE, NULL);
}

/*+/subr**********************************************************************
* NAME	guiShellCmd - issue a shell command, cooperating with the notifier
*
* DESCRIPTION
*	This routine issues a shell command under the auspices of the
*	xview notifier.  The command is issued following a fork, with a
*	popen call.  The critical issue is that the fork results in a
*	SIGCHLD signal which the notifier must handle.
*
*	The callback routine has the following form:
*		void callback(callbackArg, resultBuf)

*	resultBuf is a pointer to the text produced by the command (limited to
*	a single line)
*
* RETURNS
*	void
*
* BUGS
* o	when running under dbx, the child process always results in a
*	core dump
*
*-*/
void
guiShellCmd(pGuiCtx, cmdBuf, clientNum, callbackFn, callbackArg)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*cmdBuf;	/* I the command string to give the shell */
Notify_client clientNum;/* I a unique value in the range 100 to 200 to
				be used by guiCmdSigchld when the child
				is done processing the command */
void	(*callbackFn)();/* I pointer to function for guiCmdSigchld to call */
void	*callbackArg;	/* I arg to pass to callbackFn */
{
    guiShellCmd_work(pGuiCtx, cmdBuf, clientNum, callbackFn, callbackArg, 0);
}
void
guiShellCmd_execv_argv(pGuiCtx, argv, clientNum, callbackFn, callbackArg)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*argv;		/* I arguments to give the shell */
Notify_client clientNum;/* I a unique value in the range 100 to 200 to
				be used by guiCmdSigchld when the child
				is done processing the command */
void	(*callbackFn)();/* I pointer to function for guiCmdSigchld to call */
void	*callbackArg;	/* I arg to pass to callbackFn */
{
    guiShellCmd_work(pGuiCtx, argv, clientNum, callbackFn, callbackArg, 2);
}
void
guiShellCmd_work(pGuiCtx, cmdBuf, clientNum, callbackFn, callbackArg, mode)
GUI_CTX	*pGuiCtx;	/* I pointer to gui context */
char	*cmdBuf;	/* I the command string to give the shell (or
				pointer to argv list if mode==2)*/
Notify_client clientNum;/* I a unique value in the range 100 to 200 to
				be used by guiCmdSigchld when the child
				is done processing the command */
void	(*callbackFn)();/* I pointer to function for guiCmdSigchld to call */
void	*callbackArg;	/* I arg to pass to callbackFn */
int	mode;		/* I 0,1,2 for popen,execvp,execvp_with_argv */
{
    FILE	*fp;
    int		i, pid, cbNum;
    int		pipe_io[2][2];
    char	resultBuf[GUI_TDIM];
    char	*argv[20], *buf, *pBuf, dlm;

    cbNum = callbackNum;
    while (cbNum >= 0 && callback[cbNum].clientNum != clientNum)
	cbNum--;
    if (cbNum < 0) {
	if (++cbNum < 100) {
	    callbackNum = cbNum;
	    callback[cbNum].clientNum = clientNum;
	}
	else {
	    guiNotice(pGuiCtx, "guiShellCmd: overflow of client number table");
	    return;
	}
    }
    callback[cbNum].callbackFn = callbackFn;
    callback[cbNum].callbackArg = callbackArg;
    callback[cbNum].result[0] = '\0';
    callback[cbNum].readDone = 0;
    callback[cbNum].sigChldDone = 0;

/*-----------------------------------------------------------------------------
* blatantly stolen from "XView Programming Manual", Dan Heller, O'Reilly &
*	Associates, Inc., July 1990, pp.386-389; section 19.8.2, Reading and
*	Writing on Pipes; Example 19-4.  The ntfy_pipe.c program
*
*    child reads:  |<=========== pipe_io[0] <===========|  parent writes:
*  pipe_io[0][0]                                           pipe_io[0][1]
*
*   parent reads:  |<=========== pipe_io[1] <===========|  child writes:
*  pipe_io[1][0]                                           pipe_io[1][1]
*----------------------------------------------------------------------------*/

    if (mode == 1) {
	pBuf = buf = (char *)malloc(strlen(cmdBuf)+1);
	strcpy(pBuf, cmdBuf);
	for (i=0; i<19; i++) {
	    if (nextNonSpaceField(&pBuf, &argv[i], &dlm) <= 1)
		break;
	}
	argv[i] = NULL;
    }

    pipe(pipe_io[0]);
    pipe(pipe_io[1]);
    if ((pid = fork()) == -1) {
	perror("error executing shell command");
	close(pipe_io[0][0]);
	close(pipe_io[0][1]);
	close(pipe_io[1][0]);
	close(pipe_io[1][1]);
	if (mode == 1)
	    free(buf);
	return;
    }
    else if (pid == 0) {				/* child process */
	dup2(pipe_io[0][0], 0);		/* stdin */
	dup2(pipe_io[1][1], 1);		/* stdout */
	dup2(pipe_io[1][1], 2);		/* stderr */
	for (i=getdtablesize(); i>2; i--)
	    close(i);			/* close the pipe fd's */
	for (i=0; i<NSIG; i++)
	    signal(i, SIG_DFL);		/* don't need parent's sig masks */
	if (mode == 1) {
	    if (strncmp(*argv, "/bin/sh", 7) == 0)
		execvp("/bin/sh", argv);
	    else
		execvp(*argv, argv);
	    if (errno == ENOENT)
		printf("%s: command not found.\n", *argv);
	    else
		perror(*argv);
	    perror("execvp");
	    _exit(-1);
	}
	if (mode == 2) {
	    if (strncmp(*cmdBuf, "/bin/sh", 7) == 0)
		execvp("/bin/sh", cmdBuf);
	    else
		execvp(*cmdBuf, cmdBuf);
	    if (errno == ENOENT)
		printf("%s: command not found.\n", *cmdBuf);
	    else
		perror(*cmdBuf);
	    perror("execvp");
	    _exit(-1);
	}
	else {
	    if ((fp = popen(cmdBuf, "r")) == NULL) {
		perror("couldn't do popen to shell");
		return;
	    }
	    while ((i = fread(resultBuf, 1, sizeof(resultBuf), fp)) > 0)
		fwrite(resultBuf, 1, i, stdout);
	    _exit(0);
	}
    }
    if (mode == 1)
	free(buf);
    close(pipe_io[0][0]);
    close(pipe_io[0][1]);
    close(pipe_io[1][1]);
    callback[cbNum].pipe_io = pipe_io[1][0];
    notify_set_input_func(clientNum, guiCmdRead, pipe_io[1][0]);
    notify_set_wait3_func(clientNum, guiCmdSigchld, pid);
}
Notify_value
guiCmdRead(client, fd)
Notify_client client;
int		fd;
{
    char	buf[GUI_TDIM];
    int		nBytes, i;
    int		j, thisNum=-1;


    for (j=0; j<=callbackNum; j++) {
	if (client == callback[j].clientNum) {
	    thisNum = j;
	    break;
	}
    }

    if (ioctl(fd, FIONREAD, &nBytes) == 0) {
	while (nBytes > 0) {
	    if ((i = read(fd, buf, sizeof(buf)-1)) > 0) {
		buf[i] = '\0';
		if (thisNum >= 0)
		    strcpy(callback[thisNum].result, buf);
		nBytes -= i;
	    }
	}
    }
    if (thisNum >= 0) {
	callback[thisNum].readDone = 1;
	notify_set_input_func(client, NOTIFY_FUNC_NULL,
			callback[thisNum].pipe_io);
	close(fd);
	if (callback[thisNum].sigChldDone) {
	    if (callback[thisNum].callbackFn != NULL) {
		callback[thisNum].callbackFn(callback[thisNum].callbackArg,
					callback[thisNum].result);
	    }
	}
    }
    return NOTIFY_DONE;
}

/*/subhead guiCmdSigchld-------------------------------------------------------
*	When the child process gets done creating a file, it will die,
*	producing SIGCHLD.  At that time, call the callback function.
*----------------------------------------------------------------------------*/
Notify_value
guiCmdSigchld(client, pid, status)
Notify_client client;
int	pid;
union wait *status;
{
    Notify_value i=NOTIFY_IGNORED;
    int		j;


    for (j=0; j<=callbackNum; j++) {
	if (client == callback[j].clientNum) {
	    callback[j].sigChldDone = 1;
	    if (WIFEXITED(*status)) {
		if (callback[j].readDone) {
		    if (callback[j].callbackFn != NULL) {
			callback[j].callbackFn(callback[j].callbackArg,
						callback[j].result);
		    }
		}
		i = NOTIFY_DONE;
	    }

	    return i;
	}
    }
    return i;
}

/*+/subr**********************************************************************
* NAME	guiTextField - create a text entry field
*
* DESCRIPTION
*
* RETURNS
*	Panel_item handle for text field
*
*-*/
Panel_item
guiTextField(label, panel, pX, pY, pHt, proc, nStored, nShown,
						key1, val1, key2, val2)
char	*label;		/* I label for text field */
Panel	panel;		/* I handle of panel containing text field */
int	*pX;		/* IO pointer to x position in panel, in pixels */
int	*pY;		/* I pointer to y position in panel, in pixels */
int	*pHt;		/* IO ptr to height used by field, in pixels, or NULL */
void	(*proc)();	/* I pointer to procedure to handle carriage return */
int	nStored;	/* I number of characters to store in field */
int	nShown;		/* I number of characters to show on screen */
enum	key1;		/* I key for context information for object */
void	*val1;		/* I value associated with key */
enum	key2;		/* I key for context information for object */
void	*val2;		/* I value associated with key */
{
    Panel_item	item;
    int		height;

    item = (Panel_item)xv_create(panel, PANEL_TEXT,
	PANEL_LABEL_STRING, label,
	PANEL_VALUE_STORED_LENGTH, nStored,
	PANEL_VALUE_DISPLAY_LENGTH, nShown,
	XV_X, *pX, XV_Y, *pY, NULL);
    if (item == NULL) {
	(void)printf("error creating \"%s\" text field\n", label);
	exit(1);
    }
    if (proc != NULL) {
	if (xv_set(item, PANEL_NOTIFY_LEVEL, PANEL_SPECIFIED,
				    PANEL_NOTIFY_PROC, proc, NULL) != XV_OK) {
	    (void)printf("error setting PANEL_SPECIFIED for \"%s\"\n",label);
	    exit(1);
	}
    }
    if (key1 != 0) {
	if (xv_set(item, XV_KEY_DATA, key1, val1, NULL) != XV_OK) {
	    (void)printf("error setting XV_KEY_DATA for \"%s\"\n",label);
	    exit(1);
	}
    }
    if (key2 != 0) {
	if (xv_set(item, XV_KEY_DATA, key2, val2, NULL) != XV_OK) {
	    (void)printf("error setting XV_KEY_DATA for \"%s\"\n",label);
	    exit(1);
	}
    }
    *pX += (int)xv_get(item, XV_WIDTH) + 10;
    if (pHt != NULL) {
	height = (int)xv_get(item, XV_HEIGHT);
	if (height > *pHt)
	    *pHt = height;
    }

    return item;
}

#if 0
/*+/macro*********************************************************************
* NAME	guiTextFieldPrintf - do a printf to a text entry field
*
* SYNOPSIS
*	void
*	guiTextFieldPrintf(panelItem, fmt, ...)
*	Panel_item panelItem;/* I handle from guiTextField */
*	char	*fmt;	/* I printf format string */
*	...		/* I other arguments, for printing */
*
*-*/
#endif
void guiTextFieldPrintf(va_alist)
va_dcl
{
    va_list	pArgs;
    Xv_opaque	item;
    char	*fmt;
    char	message[500];

    va_start(pArgs);
    item = va_arg(pArgs, Xv_opaque);
    fmt = va_arg(pArgs, char *);
    vsprintf(message, fmt, pArgs);
    va_end(pArgs);
    xv_set(item, PANEL_VALUE, message, NULL);
}

/*+/subr**********************************************************************
* NAME	guiTextswGets - get the next '\n' terminated string from textsw
*
* DESCRIPTION
*	Reads the next string from the text subwindow into the caller's
*	buffer.  Reading stops when a newline is encountered or when the
*	caller's buffer is full; the newline IS stored in the buffer.
*	The string in the buffer is always null terminated.
*
*	The caller must supply the character index (starting with 0) of
*	the position in the textsw where reading is to start.  On return,
*	the caller's index is set to the next available character for
*	reading.
*
* RETURNS
*	pointer to string, or
*	NULL if there are no more characters in text subwindow
*
*-*/
char *
guiTextswGets(textsw, pBuf, dim, pCharPos)
Textsw	textsw;		/* I the text subwindow */
char	*pBuf;		/* I pointer to buffer to receive string */
int	dim;		/* I dimension of buffer */
int	*pCharPos;	/* IO index of character to start reading */
{
    Textsw_index nextPos;
    int		i;
    char	*pStr;

    nextPos = (Textsw_index)xv_get(textsw, TEXTSW_CONTENTS, *pCharPos,
			pBuf, dim-1);
    if (nextPos == *pCharPos)
	return NULL;
    for (i=0; i<dim-1; i++) {
	if (*pCharPos >= nextPos) {
	    pBuf[i] = '\0';
	    return pBuf;
	}
	(*pCharPos)++;
	if (pBuf[i] == '\n') {
	    pBuf[i+1] = '\0';
	    return pBuf;
	}
    }
    pBuf[i] = '\0';
    return pBuf;
}

#if 0
/*+/macro*********************************************************************
* NAME	guiTextswPrintf - do a printf to a guiTextsw
*
* SYNOPSIS
*	void
*	guiTextswPrintf(textsw, fmt, ...)
*	Textsw	textsw;	/* I handle from guiTextsw */
*	char	*fmt;	/* I printf format string */
*	...		/* I other arguments, for printing */
*
*-*/
#endif
void guiTextswPrintf(va_alist)
va_dcl
{
    va_list	pArgs;
    Textsw	textsw;
    char	*fmt;
    char	message[500];
    int		n, L;

    va_start(pArgs);
    textsw = va_arg(pArgs, Textsw);
    fmt = va_arg(pArgs, char *);
    vsprintf(message, fmt, pArgs);
    va_end(pArgs);
    L = strlen(message);
    n = textsw_insert(textsw, message, L);
    if (n != L)
        return;
    n = (int)xv_get(textsw, TEXTSW_INSERTION_POINT);
    textsw_possibly_normalize(textsw, n);
}

/*+/subr**********************************************************************
* NAME	guiTextswPuts - write string into textsw
*
* DESCRIPTION
*	Writes the string to the text subwindow.
*
*	The caller must supply the character index (starting with 0) of
*	the position in the textsw where writing is to start.  On return,
*	the caller's index is set to the next available character for
*	writing.
*
* RETURNS
*	0, or
*	EOF	if an error occurs during the write
*
*-*/
int
guiTextswPuts(textsw, pBuf, pCharPos)
Textsw	textsw;		/* I the text subwindow */
char	*pBuf;		/* I pointer to buffer to write to text subwindow */
int	*pCharPos;	/* IO index of character position to start writing */
{
    Textsw_index	n;
    int		L=strlen(pBuf);

    xv_set(textsw, TEXTSW_INSERTION_POINT, *pCharPos, NULL);
    n = textsw_insert(textsw, pBuf, L);
    if (n != L)
	return EOF;
    *pCharPos = (int)xv_get(textsw, TEXTSW_INSERTION_POINT);
    textsw_possibly_normalize(textsw, *pCharPos);
    return 0;
}

/*+/subr**********************************************************************
* NAME	guiTextswReset - reset a text subwindow, discarding its contents
*
* DESCRIPTION
*
* RETURNS
*	void
*
*-*/
void
guiTextswReset(textsw)
Textsw	textsw;		/* I the text subwindow */
{
    textsw_reset(textsw, 0, 0);
}

/*+/subr**********************************************************************
* NAME	guiTimer - create a timer
*
* DESCRIPTION
*	Creates and starts an interval timer.  The interval may include
*	fractions of seconds.
*
*	The timer callback function has the form of the usual Unix itimer
*	function:
*
*	void callbackFn(callbackArg, dummy)
*	void	*callbackArg
*	int     dummy;
*	{
*	}
*
* BUGS
* o	a mechanism for cancelling the timer or changing its interval isn't
*	yet part of guiSubr
*
*-*/
void
guiTimer(seconds, callbackFn, callbackArg)
double	seconds;	/* I number of seconds for timer interval */
void	(*callbackFn)();/* I name of callback function */
void	*callbackArg;	/* I argument to pass to callback function */
{
    struct itimerval t;

    t.it_interval.tv_sec = t.it_value.tv_sec = (int)aint(seconds);
    t.it_interval.tv_usec = t.it_value.tv_usec =
					(int)(fmod(seconds, 1.)*1000000.);
    notify_set_itimer_func(callbackArg, callbackFn, ITIMER_REAL, &t, NULL);
}

#endif
