/*
 * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Software Research Associates not be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Software Research Associates
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Erik M. van der Poel
 *         Software Research Associates, Inc., Tokyo, Japan
 *         erik@sra.co.jp
 */

#ifndef NOSELFILE /* for xdvik */

/*
 * Author's address:
 *
 *     erik@sra.co.jp
 *                                            OR
 *     erik%sra.co.jp@uunet.uu.net
 *                                            OR
 *     erik%sra.co.jp@mcvax.uucp
 *                                            OR
 *     try junet instead of co.jp
 *                                            OR
 *     Erik M. van der Poel
 *     Software Research Associates, Inc.
 *     1-1-1 Hirakawa-cho, Chiyoda-ku
 *     Tokyo 102 Japan. TEL +81-3-234-2692
 */

#include <stdio.h>
#include <errno.h>
/* BSD 4.3 errno.h does not declare errno */
extern int errno;
extern int sys_nerr;
#include <sys/param.h>
#ifndef HAVE_EXTERN_SYS_ERRLIST
extern char *sys_errlist[];
#endif

#include <X11/cursorfont.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Composite.h>
#include <X11/Shell.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Cardinals.h>

/* Not worth config.h just to get this.  */
#include <sys/stat.h>
#if !defined(S_ISDIR) && defined(S_IFDIR)
#define	S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

#include "sfinternal.h"

#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif /* ndef MAXPATHLEN */

extern int SFchdir ();

int SFstatus = SEL_FILE_NULL;

char
	SFstartDir[MAXPATHLEN],
	SFcurrentPath[MAXPATHLEN],
	SFcurrentDir[MAXPATHLEN];

Widget
	selFile,
	selFileCancel,
	selFileField,
	selFileForm,
	selFileHScroll,
	selFileHScrolls[3],
	selFileLists[3],
	selFileOK,
	selFilePrompt,
	selFileVScrolls[3];

/* For file filter. */
Widget	selFileLabel, selFileMask, selFileHide;
#define MASKWIDTH 10
char 	fileMask[MASKWIDTH+2] = "*.dvi";

Display *SFdisplay;

Pixel SFfore, SFback;

Atom	SFwmDeleteWindow;

XSegment SFsegs[2], SFcompletionSegs[2];

XawTextPosition SFtextPos;

int SFupperX, SFlowerY, SFupperY;

int SFtextX, SFtextYoffset;

int SFentryWidth, SFentryHeight;

int SFlineToTextH = 3;

int SFlineToTextV = 3;

int SFbesideText = 3;

int SFaboveAndBelowText = 2;

int SFcharsPerEntry = 15;

int SFlistSize = 10;

int SFworkProcAdded = 0;

XtAppContext SFapp;

int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;

char SFtextBuffer[MAXPATHLEN];

XtIntervalId SFdirModTimerId;

int (*SFfunc)();

static char *oneLineTextEditTranslations = "\
	<Key>Return:	redraw-display()\n\
	Ctrl<Key>M:	redraw-display()\n\
";

/* ARGSUSED */
static void
SFexposeList(w, n, event, cont)
	Widget		w;
	XtPointer	n;
        XEvent   	*event;
        Boolean         *cont;
{
	extern void SFdrawList ();
	
	if ((event->type == NoExpose) || event->xexpose.count) {
		return;
	}

	SFdrawList(n, SF_DO_NOT_SCROLL);
}

/* ARGSUSED */
static void
SFmodVerifyCallback(w, client_data, event, cont)
	Widget			w;
	XtPointer		client_data;
        XEvent	                *event;
        Boolean                 *cont;
{
	char	buf[2];

	if (
		(XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
		((*buf) == '\r')
	) {
		SFstatus = SEL_FILE_OK;
	} else {
		SFstatus = SEL_FILE_TEXT;
	}
}

/* ARGSUSED */
static void
SFokCallback(w, cl, cd)
	Widget	w;
        XtPointer cl, cd;
{
	SFstatus = SEL_FILE_OK;
}

static XtCallbackRec SFokSelect[] = {
	{ SFokCallback, (XtPointer) NULL },
	{ NULL, (XtPointer) NULL },
};

/* ARGSUSED */
static void
SFcancelCallback(w, cl, cd)
	Widget	w;
        XtPointer cl, cd;
{
	SFstatus = SEL_FILE_CANCEL;
}

static XtCallbackRec SFcancelSelect[] = {
	{ SFcancelCallback, (XtPointer) NULL },
	{ NULL, (XtPointer) NULL },
};

/* ARGSUSED */
static void
SFdismissAction(w, event, params, num_params)
	Widget	w;
	XEvent *event;
	String *params;
	Cardinal *num_params;
{
	if (event->type == ClientMessage &&
	    event->xclient.data.l[0] != SFwmDeleteWindow) return;

	SFstatus = SEL_FILE_CANCEL;
}

static char *wmDeleteWindowTranslation = "\
	<Message>WM_PROTOCOLS:	SelFileDismiss()\n\
";

static XtActionsRec actions[] = {
	{"SelFileDismiss",	SFdismissAction},
};

/* Don't show files that don't get through the filter.  */

int
maskFile(mask, filename)
   char	*mask, *filename;
/* return 1 if file is masked (mask does not match filename), 0 otherwise */
{
int 	c, c1;

	while  ((c = *mask++)) {
	  if (c == '*') {
	    while ((c1 = *mask++)) {
	      if (c1 != '*') {
		if (!(*filename)) return 1;
		if (c1 != '?') {
		  while ((filename = strchr(filename, c1))) {
		    if (!maskFile(mask, ++filename)) return 0;
		  }
		  return 1;
		}
		else filename++;
	      }
	    }
	    return 0;
	  }
	  if (c == '?') { if (!*filename) return 1; }
	  else if (c != *filename) return 1;
	  filename++;
	}
	return (*filename)? 1 : 0;
}

int	hideFlag = 1;
int
showEntry(entryReal, entryShown, statBuf)
    char	*entryReal, **entryShown;
    struct	stat	*statBuf;
{
	if (!(S_ISDIR(statBuf->st_mode))) {
	  if (hideFlag)
	    if (entryReal[0] == '.') return 0;
	  if (maskFile(fileMask, entryReal)) return 0;
	}
	entryReal[strlen(entryReal)] = SFstatChar(statBuf);
        return 1;
}

/* ARGSUSED */
void
maskChanged(w, client_data, event)
    Widget		w;
    XtPointer	client_data;
    XKeyPressedEvent	*event;
{
    char	buf[2];
    register SFDir		*dir;
    extern void SFupdatePath ();

	if ((XLookupString(event, buf, 2, NULL, NULL) == 1) &&
	    ((*buf) == '\r')) {
	  for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--)
	    *(dir->dir) = 0; /* force a re-read */
	  SFupdatePath();
	}
}

/* ARGSUSED */
void
hideFiles(w, client_data, event)
    Widget	w;
    XtPointer	client_data;
    XEvent	*event;
{
    register SFDir		*dir;
    register SFEntry	*entry;
    extern void SFupdatePath (), SFdrawLists ();

	hideFlag = 1 - hideFlag;
	if (hideFlag) {
	  XtVaSetValues(w, XtNlabel, "hidden", NULL);
	  for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) {
	    if (!(dir->nEntries)) continue;
	    dir->vOrigin = 0;
	    for (entry = &(dir->entries[dir->nEntries - 1]);
		 entry >= dir->entries; entry--)
	      entry->statDone = 0;
	    SFdrawLists(SF_DO_SCROLL);
	  }
	} else {
	  XtVaSetValues(w, XtNlabel, "shown", NULL);
	  for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--)
	    *(dir->dir) = 0;	/* force a re-read */
	  SFupdatePath();
	}
}


static void
SFcreateWidgets(toplevel, prompt, ok, cancel)
	Widget	toplevel;
	char	*prompt;
	char	*ok;
	char	*cancel;
{
	Cardinal	i;
	long		n;
	int		listWidth, listHeight;
	int		listSpacing = 10;
	int		scrollThickness = 15;
	int		hScrollX, hScrollY;
	int		vScrollX, vScrollY;
	Cursor
			xtermCursor,
			sbRightArrowCursor,
			dotCursor;
	Arg		arglist[20];
	extern void SFinitFont (), SFcreateGC ();

	i = 0;
	XtSetArg(arglist[i], XtNtransientFor, toplevel);		i++;

	selFile = XtAppCreateShell("selFile", "SelFile",
		transientShellWidgetClass, SFdisplay, arglist, i);

	/* Add WM_DELETE_WINDOW protocol */
	XtAppAddActions(XtWidgetToApplicationContext(selFile),
		actions, XtNumber(actions));
	XtOverrideTranslations(selFile,
		XtParseTranslationTable(wmDeleteWindowTranslation));

	i = 0;
	XtSetArg(arglist[i], XtNdefaultDistance, 30);			i++;
	selFileForm = XtCreateManagedWidget("selFileForm",
		formWidgetClass, selFile, arglist, i);

	i = 0;
	XtSetArg(arglist[i], XtNlabel, prompt);				i++;
	XtSetArg(arglist[i], XtNresizable, True);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNborderWidth, 0);			i++;
	selFilePrompt = XtCreateManagedWidget("selFilePrompt",
		labelWidgetClass, selFileForm, arglist, i);

	i = 0;
	XtSetArg(arglist[i], XtNforeground, &SFfore);			i++;
	XtSetArg(arglist[i], XtNbackground, &SFback);			i++;
	XtGetValues(selFilePrompt, arglist, i);

	SFinitFont();

	SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth +
			SFbesideText;
	SFentryHeight = SFaboveAndBelowText + SFcharHeight +
			SFaboveAndBelowText;

	listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 +
			scrollThickness;
	listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
			SFlineToTextV + SFlistSize * SFentryHeight +
			SFlineToTextV + 1 + scrollThickness;

	SFpathScrollWidth = 3 * listWidth + 2 * listSpacing + 4;

	hScrollX = -1;
	hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
			SFlineToTextV + SFlistSize * SFentryHeight +
			SFlineToTextV;
	SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH;

	vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH;
	vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV;
	SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight +
			SFlineToTextV;

	SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1;
	SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
			SFlineToTextV;
	SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
			SFlineToTextV + SFlistSize * SFentryHeight - 1;

	SFtextX = SFlineToTextH + SFbesideText;
	SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent;

	SFsegs[0].x1 = 0;
	SFsegs[0].y1 = vScrollY;
	SFsegs[0].x2 = vScrollX - 1;
	SFsegs[0].y2 = vScrollY;
	SFsegs[1].x1 = vScrollX;
	SFsegs[1].y1 = 0;
	SFsegs[1].x2 = vScrollX;
	SFsegs[1].y2 = vScrollY - 1;

	SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH;
	SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 =
		SFlineToTextH + SFentryWidth - 1;

	i = 0;
	XtSetArg(arglist[i], XtNwidth, 3 * listWidth + 2 * listSpacing + 4);
									i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;

	XtSetArg(arglist[i], XtNfromVert, selFilePrompt);		i++;
	XtSetArg(arglist[i], XtNvertDistance, 10);			i++;
	XtSetArg(arglist[i], XtNresizable, True);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNstring, SFtextBuffer);			i++;
	XtSetArg(arglist[i], XtNlength, MAXPATHLEN);			i++;
	XtSetArg(arglist[i], XtNeditType, XawtextEdit);			i++;
	XtSetArg(arglist[i], XtNwrap, XawtextWrapWord);			i++;
	XtSetArg(arglist[i], XtNresize, XawtextResizeHeight);		i++;
	XtSetArg(arglist[i], XtNuseStringInPlace, True);		i++;
	selFileField = XtCreateManagedWidget("selFileField",
		asciiTextWidgetClass, selFileForm, arglist, i);

	XtOverrideTranslations(selFileField,
		XtParseTranslationTable(oneLineTextEditTranslations));
/*	XtSetKeyboardFocus(selFileForm, selFileField);
	need focus for selFileMask wigget to set the filter */

	i = 0;
	XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);	i++;
	XtSetArg(arglist[i], XtNwidth, SFpathScrollWidth);		i++;
	XtSetArg(arglist[i], XtNheight, scrollThickness);		i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;
	XtSetArg(arglist[i], XtNfromVert, selFileField);		i++;
	XtSetArg(arglist[i], XtNvertDistance, 30);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	selFileHScroll = XtCreateManagedWidget("selFileHScroll",
		scrollbarWidgetClass, selFileForm, arglist, i);

	XtAddCallback(selFileHScroll, XtNjumpProc,
		SFpathSliderMovedCallback, (XtPointer) NULL);
	XtAddCallback(selFileHScroll, XtNscrollProc,
		SFpathAreaSelectedCallback, (XtPointer) NULL);

	i = 0;
	XtSetArg(arglist[i], XtNwidth, listWidth);			i++;
	XtSetArg(arglist[i], XtNheight, listHeight);			i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;
	XtSetArg(arglist[i], XtNfromVert, selFileHScroll);		i++;
	XtSetArg(arglist[i], XtNvertDistance, 10);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	selFileLists[0] = XtCreateManagedWidget("selFileList1",
		compositeWidgetClass, selFileForm, arglist, i);

	i = 0;
	XtSetArg(arglist[i], XtNwidth, listWidth);			i++;
	XtSetArg(arglist[i], XtNheight, listHeight);			i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;
	XtSetArg(arglist[i], XtNfromHoriz, selFileLists[0]);		i++;
	XtSetArg(arglist[i], XtNfromVert, selFileHScroll);		i++;
	XtSetArg(arglist[i], XtNhorizDistance, listSpacing);		i++;
	XtSetArg(arglist[i], XtNvertDistance, 10);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	selFileLists[1] = XtCreateManagedWidget("selFileList2",
		compositeWidgetClass, selFileForm, arglist, i);

	i = 0;
	XtSetArg(arglist[i], XtNwidth, listWidth);			i++;
	XtSetArg(arglist[i], XtNheight, listHeight);			i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;
	XtSetArg(arglist[i], XtNfromHoriz, selFileLists[1]);		i++;
	XtSetArg(arglist[i], XtNfromVert, selFileHScroll);		i++;
	XtSetArg(arglist[i], XtNhorizDistance, listSpacing);		i++;
	XtSetArg(arglist[i], XtNvertDistance, 10);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	selFileLists[2] = XtCreateManagedWidget("selFileList3",
		compositeWidgetClass, selFileForm, arglist, i);

	for (n = 0; n < 3; n++) {

		i = 0;
		XtSetArg(arglist[i], XtNx, vScrollX);			i++;
		XtSetArg(arglist[i], XtNy, vScrollY);			i++;
		XtSetArg(arglist[i], XtNwidth, scrollThickness);	i++;
		XtSetArg(arglist[i], XtNheight, SFvScrollHeight);	i++;
		XtSetArg(arglist[i], XtNborderColor, SFfore);		i++;
		selFileVScrolls[n] = XtCreateManagedWidget("selFileVScroll",
			scrollbarWidgetClass, selFileLists[n], arglist, i);

		XtAddCallback(selFileVScrolls[n], XtNjumpProc,
			SFvFloatSliderMovedCallback, (XtPointer) n);
		XtAddCallback(selFileVScrolls[n], XtNscrollProc,
			SFvAreaSelectedCallback, (XtPointer) n);

		i = 0;

		XtSetArg(arglist[i], XtNorientation, XtorientHorizontal);
									i++;
		XtSetArg(arglist[i], XtNx, hScrollX);			i++;
		XtSetArg(arglist[i], XtNy, hScrollY);			i++;
		XtSetArg(arglist[i], XtNwidth, SFhScrollWidth);		i++;
		XtSetArg(arglist[i], XtNheight, scrollThickness);	i++;
		XtSetArg(arglist[i], XtNborderColor, SFfore);		i++;
		selFileHScrolls[n] = XtCreateManagedWidget("selFileHScroll",
			scrollbarWidgetClass, selFileLists[n], arglist, i);

		XtAddCallback(selFileHScrolls[n], XtNjumpProc,
			SFhSliderMovedCallback, (XtPointer) n);
		XtAddCallback(selFileHScrolls[n], XtNscrollProc,
			SFhAreaSelectedCallback, (XtPointer) n);
	}

	i = 0;
	XtSetArg(arglist[i], XtNlabel, ok);				i++;
	XtSetArg(arglist[i], XtNresizable, True);			i++;
	XtSetArg(arglist[i], XtNcallback, SFokSelect);			i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;
	XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);		i++;
	XtSetArg(arglist[i], XtNvertDistance, 30);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	selFileOK = XtCreateManagedWidget("selFileOK", commandWidgetClass,
		selFileForm, arglist, i);

	i = 0;
	XtSetArg(arglist[i], XtNlabel, cancel);				i++;
	XtSetArg(arglist[i], XtNresizable, True);			i++;
	XtSetArg(arglist[i], XtNcallback, SFcancelSelect);		i++;
	XtSetArg(arglist[i], XtNborderColor, SFfore);			i++;
	XtSetArg(arglist[i], XtNfromHoriz, selFileOK);			i++;
	XtSetArg(arglist[i], XtNfromVert, selFileLists[0]);		i++;
	XtSetArg(arglist[i], XtNhorizDistance, 30);			i++;
	XtSetArg(arglist[i], XtNvertDistance, 30);			i++;
	XtSetArg(arglist[i], XtNtop, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNbottom, XtChainTop);			i++;
	XtSetArg(arglist[i], XtNleft, XtChainLeft);			i++;
	XtSetArg(arglist[i], XtNright, XtChainLeft);			i++;
	selFileCancel = XtCreateManagedWidget("selFileCancel",
		commandWidgetClass, selFileForm, arglist, i);

        /* Do the file filter stuff.  */
	selFileLabel = XtVaCreateManagedWidget("selFileLabel",
		labelWidgetClass, selFileForm,
		XtNfromVert, selFileLists[0],
		XtNvertDistance, 30,
		XtNfromHoriz, selFileCancel,
		XtNhorizDistance, 60,
		XtNlabel,  "File Mask:",
		XtNborderWidth, 0,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		NULL);
	
	selFileMask = XtVaCreateManagedWidget("selFileMask",
		asciiTextWidgetClass, selFileForm,
		XtNwidth, MASKWIDTH*SFcharWidth,
		XtNfromVert, selFileLists[0],
		XtNvertDistance, 30,
		XtNfromHoriz, selFileLabel,
		XtNhorizDistance, 0,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNstring, fileMask,
		XtNlength, sizeof(fileMask),
		XtNeditType, XawtextEdit,
		XtNwrap, XawtextWrapNever,
		XtNuseStringInPlace, True,
		NULL);

	for (i = 0; i < 3; i++)
	  XtSetKeyboardFocus(selFileLists[i], selFileField);

	XtOverrideTranslations(selFileMask,
		XtParseTranslationTable(oneLineTextEditTranslations));

	XtAddEventHandler(selFileMask, KeyPressMask, False,
		(XtEventHandler)maskChanged, (XtPointer) NULL);

	selFileLabel = XtVaCreateManagedWidget("selFileLabel",
		labelWidgetClass, selFileForm,
		XtNfromVert, selFileLists[0],
		XtNvertDistance, 30,
		XtNfromHoriz, selFileMask,
		XtNhorizDistance, 40,
		XtNlabel,  "dot files",
		XtNborderWidth, 0,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		NULL);

	selFileHide = XtVaCreateManagedWidget("selFileHide",
		commandWidgetClass, selFileForm,
		XtNfromVert, selFileLists[0],
		XtNvertDistance, 30,
		XtNfromHoriz, selFileLabel,
		XtNhorizDistance, 2,
		XtNlabel, "hidden",
		XtNborderWidth, 0,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
#if 0 /* missing in R4, says 
         pete@lovelace.thi.informatik.uni-frankfurt.de (Peter Dyballa) */
		XtNcursorName, "dot",
#endif
	       	NULL);
	XtAddCallback(selFileHide, XtNcallback, hideFiles, (XtCallbackProc)NULL);

	XtSetMappedWhenManaged(selFile, False);
	XtRealizeWidget(selFile);

	/* Add WM_DELETE_WINDOW protocol */
	SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False);
	XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1);

	SFcreateGC();

	xtermCursor = XCreateFontCursor(SFdisplay, XC_xterm);

	sbRightArrowCursor = XCreateFontCursor(SFdisplay, XC_sb_right_arrow);
	dotCursor = XCreateFontCursor(SFdisplay, XC_dot);

	XDefineCursor(SFdisplay, XtWindow(selFileForm), xtermCursor);
	XDefineCursor(SFdisplay, XtWindow(selFileField), xtermCursor);

	for (n = 0; n < 3; n++) {
		XDefineCursor(SFdisplay, XtWindow(selFileLists[n]),
			sbRightArrowCursor);
	}
	XDefineCursor(SFdisplay, XtWindow(selFileOK), dotCursor);
	XDefineCursor(SFdisplay, XtWindow(selFileCancel), dotCursor);

	for (n = 0; n < 3; n++) {
		XtAddEventHandler(selFileLists[n], ExposureMask, True,
			SFexposeList, (XtPointer) n);
		XtAddEventHandler(selFileLists[n], EnterWindowMask, False,
			SFenterList, (XtPointer) n);
		XtAddEventHandler(selFileLists[n], LeaveWindowMask, False,
			SFleaveList, (XtPointer) n);
		XtAddEventHandler(selFileLists[n], PointerMotionMask, False,
			SFmotionList, (XtPointer) n);
		XtAddEventHandler(selFileLists[n], ButtonPressMask, False,
			SFbuttonPressList, (XtPointer) n);
		XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False,
			SFbuttonReleaseList, (XtPointer) n);
	}

	XtAddEventHandler(selFileField, KeyPressMask, False,
		SFmodVerifyCallback, (XtPointer) NULL);

	SFapp = XtWidgetToApplicationContext(selFile);

}

/* position widget under the cursor */
void
SFpositionWidget(w)
    Widget w;
{
    Arg args[3];
    Cardinal num_args;
    Dimension width, height, b_width;
    int x, y, max_x, max_y;
    Window root, child;
    int dummyx, dummyy;
    unsigned int dummymask;
    
    XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y,
		  &dummyx, &dummyy, &dummymask);
    num_args = 0;
    XtSetArg(args[num_args], XtNwidth, &width); num_args++;
    XtSetArg(args[num_args], XtNheight, &height); num_args++;
    XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
    XtGetValues(w, args, num_args);

    width += 2 * b_width;
    height += 2 * b_width;

    x -= ( (Position) width/2 );
    if (x < 0) x = 0;
    if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x;

    y -= ( (Position) height/2 );
    if (y < 0) y = 0;
    if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y;
    
    num_args = 0;
    XtSetArg(args[num_args], XtNx, x); num_args++;
    XtSetArg(args[num_args], XtNy, y); num_args++;
    XtSetValues(w, args, num_args);
}

FILE *
SFopenFile(name, mode, prompt, failed)
    char *name;
    char *mode;
    char *prompt;
    char *failed;
{
    Arg args[1];
    FILE *fp;

    SFchdir(SFstartDir);
    if (!name || *name == 0 || (fp = fopen(name, mode)) == NULL) {
	char *buf;
	if (!name || *name == 0)
	  errno = sys_nerr + 1; /* no errno for an empty filename */
	if (errno <= sys_nerr) {
	    buf = XtMalloc(strlen(failed) + strlen(sys_errlist[errno]) + 
			   strlen(prompt) + 2);
	    strcpy(buf, failed);
	    strcat(buf, sys_errlist[errno]);
/* 	    strcat(buf, "\n"); */
	    strcat(buf, " ");
	    strcat(buf, prompt);
	} else {
	    buf = XtMalloc(strlen(failed) + strlen(prompt) + 2);
	    strcpy(buf, failed);
/*	    strcat(buf, "\n"); */
	    strcat(buf, " ");
	    strcat(buf, prompt);
	}
	XtSetArg(args[0], XtNlabel, buf);
	XtSetValues(selFilePrompt, args, ONE);
	XtFree(buf);
	return NULL;
    }
    return fp;
}

void
SFtextChanged()
{
	extern void SFupdatePath ();
	
	if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~')) {
		(void) strcpy(SFcurrentPath, SFtextBuffer);

		SFtextPos = XawTextGetInsertionPoint(selFileField);
	} else {
		(void) strcat(strcpy(SFcurrentPath, SFstartDir), SFtextBuffer);

		SFtextPos = XawTextGetInsertionPoint(selFileField) +
			strlen(SFstartDir);
	}

	if (!SFworkProcAdded) {
		(void) XtAppAddWorkProc(SFapp, SFworkProc, NULL);
		SFworkProcAdded = 1;
	}

	SFupdatePath();
}

static char *
SFgetText()
{
	return strcpy(XtMalloc((unsigned) (strlen(SFtextBuffer) + 1)),
		SFtextBuffer);
}

static void
SFprepareToReturn()
{
	SFstatus = SEL_FILE_NULL;
	XtRemoveGrab(selFile);
	XtUnmapWidget(selFile);
	XtRemoveTimeOut(SFdirModTimerId);
	if (SFchdir(SFstartDir)) {
		XtAppError(
			SFapp,
			"XsraSelFile: can't return to current directory"
		);
	}
}

FILE *
XsraSelFile(toplevel, prompt, ok, cancel, failed,
	    init_path, mode, show_entry, name_return)
	Widget		toplevel;
	char		*prompt;
	char		*ok;
	char		*cancel;
	char		*failed;
	char		*init_path;
	char		*mode;
	int		(*show_entry)();
	char		**name_return;
{
	extern void SFsetText ();
	static int	firstTime = 1;
	Cardinal	i;
	Arg		arglist[20];
	XEvent		event;
	FILE		*fp;

	if (!prompt) {
		prompt = "Pathname:";
	}

	if (!ok) {
		ok = "OK";
	}

	if (!cancel) {
		cancel = "Cancel";
	}

	if (firstTime) {
		firstTime = 0;
		SFdisplay = XtDisplay(toplevel);
		SFcreateWidgets(toplevel, prompt, ok, cancel);
	} else {
		i = 0;

		XtSetArg(arglist[i], XtNlabel, prompt);			i++;
		XtSetValues(selFilePrompt, arglist, i);

		i = 0;
		XtSetArg(arglist[i], XtNlabel, ok);			i++;
		XtSetValues(selFileOK, arglist, i);

		i = 0;
		XtSetArg(arglist[i], XtNlabel, cancel);			i++;
		XtSetValues(selFileCancel, arglist, i);
	}

	SFpositionWidget(selFile);
	XtMapWidget(selFile);

        {
          extern char *xgetcwd (); /* In xgetcwd.c. */
          char *cwd = xgetcwd ();
          strcpy (SFstartDir, cwd);
          free (cwd);
        }
	if (SFstartDir[0] == 0) {
		XtAppError(SFapp, "XsraSelFile: can't get current directory");
	}
	(void) strcat(SFstartDir, "/");
	(void) strcpy(SFcurrentDir, SFstartDir);

	if (init_path) {
		if (init_path[0] == '/') {
			(void) strcpy(SFcurrentPath, init_path);
			if (strncmp(
				SFcurrentPath,
				SFstartDir,
				strlen(SFstartDir)
			)) {
				SFsetText(SFcurrentPath);
			} else {
				SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
			}
		} else {
			(void) strcat(strcpy(SFcurrentPath, SFstartDir),
				init_path);
			SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
		}
	} else {
		(void) strcpy(SFcurrentPath, SFstartDir);
	}

/*	SFfunc = show_entry;
	disabled in order to implement file filter */
	SFfunc = showEntry;

	SFtextChanged();

	XtAddGrab(selFile, True, True);

	SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
		SFdirModTimer, (XtPointer) NULL);

	while (1) {
		XtAppNextEvent(SFapp, &event);
		XtDispatchEvent(&event);
		switch (SFstatus) {
		case SEL_FILE_TEXT:
			SFstatus = SEL_FILE_NULL;
			SFtextChanged();
			break;
		case SEL_FILE_OK:
			*name_return = SFgetText();
			if ((fp = SFopenFile(*name_return, mode,
					    prompt, failed))) {
				SFprepareToReturn();
				return fp;
			}
			SFstatus = SEL_FILE_NULL;
			break;
		case SEL_FILE_CANCEL:
			SFprepareToReturn();
			return NULL;
		case SEL_FILE_NULL:
			break;
		}
	}
}

#endif /* not no SELFILE */
