view src/gui_at_fs.c @ 11757:74abb6c84984 v8.0.0761

patch 8.0.0761: options not set properly for a terminal buffer commit https://github.com/vim/vim/commit/1f2903c43109b16594d141a730659317b15f388d Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jul 23 19:51:01 2017 +0200 patch 8.0.0761: options not set properly for a terminal buffer Problem: Options of a buffer for a terminal window are not set properly. Solution: Add "terminal" value for 'buftype'. Make 'buftype' and 'bufhidden' not depend on the quickfix feature. Also set the buffer name and show "running" or "finished" in the window title.
author Christian Brabandt <cb@256bit.org>
date Sun, 23 Jul 2017 20:00:05 +0200
parents 4aead6a9b7a9
children 27b9a84395b5
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4 noet: */

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

/*
 * Heavely modified for Vim by Bram Moolenaar
 */

#include "vim.h"

/* Only include this when using the file browser */

#ifdef FEAT_BROWSE

/* Weird complication: for "make lint" Text.h doesn't combine with Xm.h */
#if defined(FEAT_GUI_MOTIF) && defined(FMT8BIT)
# undef FMT8BIT
#endif

#ifndef FEAT_GUI_NEXTAW
# include "gui_at_sb.h"
#endif

/***************** SFinternal.h */

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xos.h>
#ifdef FEAT_GUI_NEXTAW
# include <X11/neXtaw/Text.h>
# include <X11/neXtaw/AsciiText.h>
# include <X11/neXtaw/Scrollbar.h>
#else
# include <X11/Xaw/Text.h>
# include <X11/Xaw/AsciiText.h>
#endif

#define SEL_FILE_CANCEL		-1
#define SEL_FILE_OK		0
#define SEL_FILE_NULL		1
#define SEL_FILE_TEXT		2

#define SF_DO_SCROLL		1
#define SF_DO_NOT_SCROLL	0

typedef struct
{
    int		statDone;
    char	*real;
    char	*shown;
} SFEntry;

typedef struct
{
    char	*dir;
    char	*path;
    SFEntry	*entries;
    int		nEntries;
    int		vOrigin;
    int		nChars;
    int		hOrigin;
    int		changed;
    int		beginSelection;
    int		endSelection;
    time_t	mtime;
} SFDir;

static char	SFstartDir[MAXPATHL],
		SFcurrentPath[MAXPATHL],
		SFcurrentDir[MAXPATHL];

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

static Display	*SFdisplay;

static int	SFcharWidth, SFcharAscent, SFcharHeight;

static SFDir	*SFdirs = NULL;

static int	SFdirEnd;
static int	SFdirPtr;

static Pixel	SFfore, SFback;

static Atom	SFwmDeleteWindow;

static XSegment SFsegs[2], SFcompletionSegs[2];

static XawTextPosition SFtextPos;

static int	SFupperX, SFlowerY, SFupperY;

static int	SFtextX, SFtextYoffset;

static int	SFentryWidth, SFentryHeight;

static int	SFlineToTextH = 3;
static int	SFlineToTextV = 3;

static int	SFbesideText = 3;
static int	SFaboveAndBelowText = 2;

static int	SFcharsPerEntry = 15;

static int	SFlistSize = 10;

static int	SFcurrentInvert[3] = { -1, -1, -1 };

static int	SFworkProcAdded = 0;

static XtAppContext SFapp;

static int	SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;

#ifdef FEAT_XFONTSET
static char	SFtextBuffer[MAXPATHL*sizeof(wchar_t)];
#else
static char	SFtextBuffer[MAXPATHL];
#endif

static int	SFbuttonPressed = 0;

static XtIntervalId SFdirModTimerId;

static int	(*SFfunc)();

static int	SFstatus = SEL_FILE_NULL;

/***************** static functions */

static void SFsetText(char *path);
static void SFtextChanged(void);
static char *SFgetText(void);
static void SFupdatePath(void);
static int SFgetDir(SFDir *dir);
static void SFdrawLists(int doScroll);
static void SFdrawList(int n, int doScroll);
static void SFclearList(int n, int doScroll);
static void SFbuttonPressList(Widget w, int n, XButtonPressedEvent *event);
static void SFbuttonReleaseList(Widget w, int n, XButtonReleasedEvent *event);
static void SFdirModTimer(XtPointer cl, XtIntervalId *id);
static char SFstatChar(stat_T *statBuf);
static void SFdrawStrings(Window w, SFDir *dir, int from, int to);
static int SFnewInvertEntry(int n, XMotionEvent *event);
static void SFinvertEntry(int n);
static void SFenterList(Widget w, int n, XEnterWindowEvent *event);
static void SFleaveList(Widget w, int n, XEvent *event);
static void SFmotionList(Widget w, int n, XMotionEvent *event);
static void SFvFloatSliderMovedCallback(Widget w, XtPointer n, XtPointer fnew);
static void SFvSliderMovedCallback(Widget w, int n, int nw);
static void SFvAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew);
static void SFhSliderMovedCallback(Widget w, XtPointer n, XtPointer nw);
static void SFhAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew);
static void SFpathSliderMovedCallback(Widget w, XtPointer client_data, XtPointer nw);
static void SFpathAreaSelectedCallback(Widget w, XtPointer client_data, XtPointer pnew);
static Boolean SFworkProc(void);
static int SFcompareEntries(const void *p, const void *q);
static void SFprepareToReturn(void);
static void SFcreateWidgets(Widget toplevel, char *prompt, char *ok, char *cancel);
static void SFsetColors(guicolor_T bg, guicolor_T fg, guicolor_T scroll_bg, guicolor_T scrollfg);

/***************** xstat.h */

#ifndef S_IXUSR
# define S_IXUSR 0100
#endif
#ifndef S_IXGRP
# define S_IXGRP 0010
#endif
#ifndef S_IXOTH
# define S_IXOTH 0001
#endif

#define S_ISXXX(m) ((m) & (S_IXUSR | S_IXGRP | S_IXOTH))

/***************** Path.c */

#include <pwd.h>

typedef struct
{
    char	*name;
    char	*dir;
} SFLogin;

static int	SFdoNotTouchDirPtr = 0;

static int	SFdoNotTouchVorigin = 0;

static SFDir	SFrootDir, SFhomeDir;

static SFLogin	*SFlogins;

static int	SFtwiddle = 0;

static int SFchdir(char *path);

    static int
SFchdir(char *path)
{
    int		result;

    result = 0;

    if (strcmp(path, SFcurrentDir))
    {
	result = mch_chdir(path);
	if (!result)
	    (void) strcpy(SFcurrentDir, path);
    }

    return result;
}

static void SFfree(int i);

    static void
SFfree(int i)
{
    SFDir	*dir;
    int		j;

    dir = &(SFdirs[i]);

    for (j = dir->nEntries - 1; j >= 0; j--)
    {
	if (dir->entries[j].shown != dir->entries[j].real)
	    XtFree(dir->entries[j].shown);
	XtFree(dir->entries[j].real);
    }

    XtFree((char *)dir->entries);
    XtFree(dir->dir);

    dir->dir = NULL;
}

static void SFstrdup(char **s1, char *s2);

    static void
SFstrdup(char **s1, char *s2)
{
    *s1 = strcpy(XtMalloc((unsigned)(strlen(s2) + 1)), s2);
}

static void SFunreadableDir(SFDir *dir);

    static void
SFunreadableDir(SFDir *dir)
{
    char	*cannotOpen = _("<cannot open> ");

    dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
    dir->entries[0].statDone = 1;
    SFstrdup(&dir->entries[0].real, cannotOpen);
    dir->entries[0].shown = dir->entries[0].real;
    dir->nEntries = 1;
    dir->nChars = strlen(cannotOpen);
}

static void SFreplaceText(SFDir *dir, char *str);

    static void
SFreplaceText(SFDir *dir, char *str)
{
    int	len;

    *(dir->path) = 0;
    len = strlen(str);
    if (str[len - 1] == '/')
	(void) strcat(SFcurrentPath, str);
    else
	(void) strncat(SFcurrentPath, str, len - 1);
    if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir)))
	SFsetText(SFcurrentPath);
    else
	SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));

    SFtextChanged();
}

static void SFexpand(char *str);

    static void
SFexpand(char *str)
{
    int		len;
    int		cmp;
    char	*name, *growing;
    SFDir	*dir;
    SFEntry	*entry, *max;

    len = strlen(str);

    dir = &(SFdirs[SFdirEnd - 1]);

    if (dir->beginSelection == -1)
    {
	SFstrdup(&str, str);
	SFreplaceText(dir, str);
	XtFree(str);
	return;
    }
    else if (dir->beginSelection == dir->endSelection)
    {
	SFreplaceText(dir, dir->entries[dir->beginSelection].shown);
	return;
    }

    max = &(dir->entries[dir->endSelection + 1]);

    name = dir->entries[dir->beginSelection].shown;
    SFstrdup(&growing, name);

    cmp = 0;
    while (!cmp)
    {
	entry = &(dir->entries[dir->beginSelection]);
	while (entry < max)
	{
	    if ((cmp = strncmp(growing, entry->shown, len)))
		break;
	    entry++;
	}
	len++;
    }

    /*
     * SFreplaceText() expects filename
     */
    growing[len - 2] = ' ';

    growing[len - 1] = 0;
    SFreplaceText(dir, growing);
    XtFree(growing);
}

static int SFfindFile(SFDir *dir, char *str);

    static int
SFfindFile(SFDir *dir, char *str)
{
    int		i, last, max;
    char	*name, save;
    SFEntry	*entries;
    int		len;
    int		begin, end;
    int		result;

    len = strlen(str);

    if (str[len - 1] == ' ')
    {
	SFexpand(str);
	return 1;
    }
    else if (str[len - 1] == '/')
	len--;

    max = dir->nEntries;

    entries = dir->entries;

    i = 0;
    while (i < max)
    {
	name = entries[i].shown;
	last = strlen(name) - 1;
	save = name[last];
	name[last] = 0;

	result = strncmp(str, name, len);

	name[last] = save;
	if (result <= 0)
	    break;
	i++;
    }
    begin = i;
    while (i < max)
    {
	name = entries[i].shown;
	last = strlen(name) - 1;
	save = name[last];
	name[last] = 0;

	result = strncmp(str, name, len);

	name[last] = save;
	if (result)
	    break;
	i++;
    }
    end = i;

    if (begin != end)
    {
	if ((dir->beginSelection != begin) || (dir->endSelection != end - 1))
	{
	    dir->changed = 1;
	    dir->beginSelection = begin;
	    if (str[strlen(str) - 1] == '/')
		dir->endSelection = begin;
	    else
		dir->endSelection = end - 1;
	}
    }
    else if (dir->beginSelection != -1)
    {
	dir->changed = 1;
	dir->beginSelection = -1;
	dir->endSelection = -1;
    }

    if (SFdoNotTouchVorigin
	    || ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize)))
    {
	SFdoNotTouchVorigin = 0;
	return 0;
    }

    i = begin - 1;
    if (i > max - SFlistSize)
	i = max - SFlistSize;
    if (i < 0)
	i = 0;

    if (dir->vOrigin != i)
    {
	dir->vOrigin = i;
	dir->changed = 1;
    }

    return 0;
}

static void SFunselect(void);

    static void
SFunselect(void)
{
    SFDir	*dir;

    dir = &(SFdirs[SFdirEnd - 1]);
    if (dir->beginSelection != -1)
	dir->changed = 1;
    dir->beginSelection = -1;
    dir->endSelection = -1;
}

static int SFcompareLogins(const void *p, const void *q);

    static int
SFcompareLogins(const void *p, const void *q)
{
    return strcmp(((SFLogin *)p)->name, ((SFLogin *)q)->name);
}

static void SFgetHomeDirs(void);

    static void
SFgetHomeDirs(void)
{
    struct	passwd	*pw;
    int		Alloc;
    int		i;
    SFEntry	*entries = NULL;
    int		len;
    int		maxChars;

    Alloc = 1;
    i = 1;
    entries = (SFEntry *)XtMalloc(sizeof(SFEntry));
    SFlogins = (SFLogin *)XtMalloc(sizeof(SFLogin));
    entries[0].real = XtMalloc(3);
    (void) strcpy(entries[0].real, "~");
    entries[0].shown = entries[0].real;
    entries[0].statDone = 1;
    SFlogins[0].name = "";
    pw = getpwuid((int) getuid());
    SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/");
    maxChars = 0;

    (void) setpwent();

    while ((pw = getpwent()) && (*(pw->pw_name)))
    {
	if (i >= Alloc)
	{
	    Alloc *= 2;
	    entries = (SFEntry *) XtRealloc((char *)entries,
					 (unsigned)(Alloc * sizeof(SFEntry)));
	    SFlogins = (SFLogin *) XtRealloc((char *)SFlogins,
					 (unsigned)(Alloc * sizeof(SFLogin)));
	}
	len = strlen(pw->pw_name);
	entries[i].real = XtMalloc((unsigned) (len + 3));
	(void) strcat(strcpy(entries[i].real, "~"), pw->pw_name);
	entries[i].shown = entries[i].real;
	entries[i].statDone = 1;
	if (len > maxChars)
	    maxChars = len;
	SFstrdup(&SFlogins[i].name, pw->pw_name);
	SFstrdup(&SFlogins[i].dir, pw->pw_dir);
	i++;
    }

    SFhomeDir.dir		= XtMalloc(1);
    SFhomeDir.dir[0]		= 0;
    SFhomeDir.path		= SFcurrentPath;
    SFhomeDir.entries		= entries;
    SFhomeDir.nEntries		= i;
    SFhomeDir.vOrigin		= 0;	/* :-) */
    SFhomeDir.nChars		= maxChars + 2;
    SFhomeDir.hOrigin		= 0;
    SFhomeDir.changed		= 1;
    SFhomeDir.beginSelection	= -1;
    SFhomeDir.endSelection	= -1;

    qsort((char *)entries, (size_t)i, sizeof(SFEntry), SFcompareEntries);
    qsort((char *)SFlogins, (size_t)i, sizeof(SFLogin), SFcompareLogins);

    for (i--; i >= 0; i--)
	(void)strcat(entries[i].real, "/");
}

static int SFfindHomeDir(char *begin, char *end);

    static int
SFfindHomeDir(char *begin, char *end)
{
    char	save;
    char	*theRest;
    int	i;

    save = *end;
    *end = 0;

    for (i = SFhomeDir.nEntries - 1; i >= 0; i--)
    {
	if (!strcmp(SFhomeDir.entries[i].real, begin))
	{
	    *end = save;
	    SFstrdup(&theRest, end);
	    (void) strcat(strcat(strcpy(SFcurrentPath,
					SFlogins[i].dir), "/"), theRest);
	    XtFree(theRest);
	    SFsetText(SFcurrentPath);
	    SFtextChanged();
	    return 1;
	}
    }

    *end = save;

    return 0;
}

    static void
SFupdatePath(void)
{
    static int	Alloc;
    static int	wasTwiddle = 0;
    char	*begin, *end;
    int		i, j;
    int		prevChange;
    int		SFdirPtrSave, SFdirEndSave;
    SFDir	*dir;

    if (!SFdirs)
    {
	SFdirs = (SFDir *) XtMalloc((Alloc = 10) * sizeof(SFDir));
	dir = &(SFdirs[0]);
	SFstrdup(&dir->dir, "/");
	(void) SFchdir("/");
	(void) SFgetDir(dir);
	for (j = 1; j < Alloc; j++)
	    SFdirs[j].dir = NULL;
	dir->path = SFcurrentPath + 1;
	dir->vOrigin = 0;
	dir->hOrigin = 0;
	dir->changed = 1;
	dir->beginSelection = -1;
	dir->endSelection = -1;
	SFhomeDir.dir = NULL;
    }

    SFdirEndSave = SFdirEnd;
    SFdirEnd = 1;

    SFdirPtrSave = SFdirPtr;
    SFdirPtr = 0;

    begin = NULL;

    if (SFcurrentPath[0] == '~')
    {
	if (!SFtwiddle)
	{
	    SFtwiddle = 1;
	    dir = &(SFdirs[0]);
	    SFrootDir = *dir;
	    if (!SFhomeDir.dir)
		SFgetHomeDirs();
	    *dir = SFhomeDir;
	    dir->changed = 1;
	}
	end = SFcurrentPath;
	SFdoNotTouchDirPtr = 1;
	wasTwiddle = 1;
    }
    else
    {
	if (SFtwiddle)
	{
	    SFtwiddle = 0;
	    dir = &(SFdirs[0]);
	    *dir = SFrootDir;
	    dir->changed = 1;
	}
	end = SFcurrentPath + 1;
    }

    i = 0;

    prevChange = 0;

    while (*end)
    {
	while (*end++ == '/')
	    ;
	end--;
	begin = end;
	while ((*end) && (*end++ != '/'))
	    ;
	if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/'))
	{
	    SFdirPtr = i - 1;
	    if (SFdirPtr < 0)
		SFdirPtr = 0;
	}
	if (*begin)
	{
	    if (*(end - 1) == '/')
	    {
		char save = *end;

		if (SFtwiddle)
		{
		    if (SFfindHomeDir(begin, end))
			return;
		}
		*end = 0;
		i++;
		SFdirEnd++;
		if (i >= Alloc)
		{
		    SFdirs = (SFDir *) XtRealloc((char *) SFdirs,
				    (unsigned)((Alloc *= 2) * sizeof(SFDir)));
		    for (j = Alloc / 2; j < Alloc; j++)
			SFdirs[j].dir = NULL;
		}
		dir = &(SFdirs[i]);
		if ((!(dir->dir)) || prevChange || strcmp(dir->dir, begin))
		{
		    if (dir->dir)
			SFfree(i);
		    prevChange = 1;
		    SFstrdup(&dir->dir, begin);
		    dir->path = end;
		    dir->vOrigin = 0;
		    dir->hOrigin = 0;
		    dir->changed = 1;
		    dir->beginSelection = -1;
		    dir->endSelection = -1;
		    (void)SFfindFile(dir - 1, begin);
		    if (SFchdir(SFcurrentPath) || SFgetDir(dir))
		    {
			SFunreadableDir(dir);
			break;
		    }
		}
		*end = save;
		if (!save)
		    SFunselect();
	    }
	    else
	    {
		if (SFfindFile(&(SFdirs[SFdirEnd-1]), begin))
		    return;
	    }
	}
	else
	    SFunselect();
    }

    if ((end == SFcurrentPath + 1) && (!SFtwiddle))
	SFunselect();

    for (i = SFdirEnd; i < Alloc; i++)
	if (SFdirs[i].dir)
	    SFfree(i);

    if (SFdoNotTouchDirPtr)
    {
	if (wasTwiddle)
	{
	    wasTwiddle = 0;
	    SFdirPtr = SFdirEnd - 2;
	    if (SFdirPtr < 0)
		SFdirPtr = 0;
	}
	else
	    SFdirPtr = SFdirPtrSave;
	SFdoNotTouchDirPtr = 0;
    }

    if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave))
    {
#ifdef FEAT_GUI_NEXTAW
	XawScrollbarSetThumb( selFileHScroll,
		(float) (((double) SFdirPtr) / SFdirEnd),
		(float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
			 SFdirEnd));
#else
	vim_XawScrollbarSetThumb( selFileHScroll,
		(float) (((double) SFdirPtr) / SFdirEnd),
		(float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
			 SFdirEnd),
		(double)SFdirEnd);
#endif
    }

    if (SFdirPtr != SFdirPtrSave)
	SFdrawLists(SF_DO_SCROLL);
    else
	for (i = 0; i < 3; i++)
	{
	    if (SFdirPtr + i < SFdirEnd)
	    {
		if (SFdirs[SFdirPtr + i].changed)
		{
		    SFdirs[SFdirPtr + i].changed = 0;
		    SFdrawList(i, SF_DO_SCROLL);
		}
	    }
	    else
		SFclearList(i, SF_DO_SCROLL);
	}
}

#ifdef XtNinternational
    static int
WcsLen(wchar_t *p)
{
    int i = 0;
    while (*p++ != 0)
	i++;
    return i;
}
#endif

    static void
SFsetText(char *path)
{
    XawTextBlock	text;

    text.firstPos = 0;
    text.length = strlen(path);
    text.ptr = path;
    text.format = FMT8BIT;

#ifdef XtNinternational
    if ((unsigned long)_XawTextFormat((TextWidget)selFileField) == XawFmtWide)
    {
	XawTextReplace(selFileField, (XawTextPosition)0,
				    (XawTextPosition)WcsLen((wchar_t *)&SFtextBuffer[0]), &text);
	XawTextSetInsertionPoint(selFileField,
					   (XawTextPosition)WcsLen((wchar_t *)&SFtextBuffer[0]));
    }
    else
    {
	XawTextReplace(selFileField, (XawTextPosition)0,
				    (XawTextPosition)strlen(SFtextBuffer), &text);
	XawTextSetInsertionPoint(selFileField,
					   (XawTextPosition)strlen(SFtextBuffer));
    }
#else
    XawTextReplace(selFileField, (XawTextPosition)0,
				(XawTextPosition)strlen(SFtextBuffer), &text);
    XawTextSetInsertionPoint(selFileField,
				       (XawTextPosition)strlen(SFtextBuffer));
#endif
}

    static void
SFbuttonPressList(
    Widget		w UNUSED,
    int			n UNUSED,
    XButtonPressedEvent	*event UNUSED)
{
    SFbuttonPressed = 1;
}

    static void
SFbuttonReleaseList(
    Widget		 w,
    int			 n,
    XButtonReleasedEvent *event)
{
    SFDir	*dir;

    SFbuttonPressed = 0;

    if (SFcurrentInvert[n] != -1)
    {
	if (n < 2)
	    SFdoNotTouchDirPtr = 1;
	SFdoNotTouchVorigin = 1;
	dir = &(SFdirs[SFdirPtr + n]);
	SFreplaceText(dir,
		       dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown);
	SFmotionList(w, n, (XMotionEvent *) event);
    }
}

static int SFcheckDir(int n, SFDir *dir);

    static int
SFcheckDir(int n, SFDir *dir)
{
    stat_T	statBuf;
    int		i;

    if ((!mch_stat(".", &statBuf)) && (statBuf.st_mtime != dir->mtime))
    {
	/*
	 * If the pointer is currently in the window that we are about
	 * to update, we must warp it to prevent the user from
	 * accidentally selecting the wrong file.
	 */
	if (SFcurrentInvert[n] != -1)
	{
	    XWarpPointer(
		    SFdisplay,
		    None,
		    XtWindow(selFileLists[n]),
		    0,
		    0,
		    0,
		    0,
		    0,
		    0);
	}

	for (i = dir->nEntries - 1; i >= 0; i--)
	{
	    if (dir->entries[i].shown != dir->entries[i].real)
		XtFree(dir->entries[i].shown);
	    XtFree(dir->entries[i].real);
	}
	XtFree((char *) dir->entries);
	if (SFgetDir(dir))
	    SFunreadableDir(dir);
	if (dir->vOrigin > dir->nEntries - SFlistSize)
	    dir->vOrigin = dir->nEntries - SFlistSize;
	if (dir->vOrigin < 0)
	    dir->vOrigin = 0;
	if (dir->hOrigin > dir->nChars - SFcharsPerEntry)
	    dir->hOrigin = dir->nChars - SFcharsPerEntry;
	if (dir->hOrigin < 0)
	    dir->hOrigin = 0;
	dir->beginSelection = -1;
	dir->endSelection = -1;
	SFdoNotTouchVorigin = 1;
	if ((dir + 1)->dir)
	    (void) SFfindFile(dir, (dir + 1)->dir);
	else
	    (void) SFfindFile(dir, dir->path);

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

static int SFcheckFiles(SFDir *dir);

    static int
SFcheckFiles(SFDir *dir)
{
    int		from, to;
    int		result;
    char	oldc, newc;
    int		i;
    char	*str;
    int		last;
    stat_T	statBuf;

    result = 0;

    from = dir->vOrigin;
    to = dir->vOrigin + SFlistSize;
    if (to > dir->nEntries)
	to = dir->nEntries;

    for (i = from; i < to; i++)
    {
	str = dir->entries[i].real;
	last = strlen(str) - 1;
	oldc = str[last];
	str[last] = 0;
	if (mch_stat(str, &statBuf))
	    newc = ' ';
	else
	    newc = SFstatChar(&statBuf);
	str[last] = newc;
	if (newc != oldc)
	    result = 1;
    }

    return result;
}

    static void
SFdirModTimer(XtPointer cl UNUSED, XtIntervalId *id UNUSED)
{
    static int		n = -1;
    static int		f = 0;
    char		save;
    SFDir		*dir;

    if ((!SFtwiddle) && (SFdirPtr < SFdirEnd))
    {
	n++;
	if ((n > 2) || (SFdirPtr + n >= SFdirEnd))
	{
	    n = 0;
	    f++;
	    if ((f > 2) || (SFdirPtr + f >= SFdirEnd))
		f = 0;
	}
	dir = &(SFdirs[SFdirPtr + n]);
	save = *(dir->path);
	*(dir->path) = 0;
	if (SFchdir(SFcurrentPath))
	{
	    *(dir->path) = save;

	    /*
	     * force a re-read
	     */
	    *(dir->dir) = 0;

	    SFupdatePath();
	}
	else
	{
	    *(dir->path) = save;
	    if (SFcheckDir(n, dir) || ((f == n) && SFcheckFiles(dir)))
		SFdrawList(n, SF_DO_SCROLL);
	}
    }

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

/* Return a single character describing what kind of file STATBUF is.  */

    static char
SFstatChar(stat_T *statBuf)
{
    if (S_ISDIR (statBuf->st_mode))
	return '/';
    if (S_ISREG (statBuf->st_mode))
	return S_ISXXX (statBuf->st_mode) ? '*' : ' ';
#ifdef S_ISSOCK
    if (S_ISSOCK (statBuf->st_mode))
	return '=';
#endif /* S_ISSOCK */
    return ' ';
}

/***************** Draw.c */

#ifdef FEAT_GUI_NEXTAW
# include <X11/neXtaw/Cardinals.h>
#else
# include <X11/Xaw/Cardinals.h>
#endif

#ifdef FEAT_XFONTSET
# define SF_DEFAULT_FONT "-misc-fixed-medium-r-normal--14-*"
#else
# define SF_DEFAULT_FONT "9x15"
#endif

#ifdef ABS
# undef ABS
#endif
#define ABS(x) (((x) < 0) ? (-(x)) : (x))

typedef struct
{
    char *fontname;
} TextData;

static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;

static XtResource textResources[] =
{
#ifdef FEAT_XFONTSET
	{XtNfontSet, XtCFontSet, XtRString, sizeof (char *),
		XtOffsetOf(TextData, fontname), XtRString, SF_DEFAULT_FONT},
#else
	{XtNfont, XtCFont, XtRString, sizeof (char *),
		XtOffsetOf(TextData, fontname), XtRString, SF_DEFAULT_FONT},
#endif
};

#ifdef FEAT_XFONTSET
static XFontSet SFfont;
#else
static XFontStruct *SFfont;
#endif

static int SFcurrentListY;

static XtIntervalId SFscrollTimerId;

static void SFinitFont(void);

    static void
SFinitFont(void)
{
    TextData	*data;
#ifdef FEAT_XFONTSET
    XFontSetExtents *extents;
    char **missing, *def_str;
    int  num_missing;
#endif

    data = XtNew(TextData);

    XtGetApplicationResources(selFileForm, (XtPointer) data, textResources,
	    XtNumber(textResources), (Arg *) NULL, ZERO);

#ifdef FEAT_XFONTSET
    SFfont = XCreateFontSet(SFdisplay, data->fontname,
			    &missing, &num_missing, &def_str);
#else
    SFfont = XLoadQueryFont(SFdisplay, data->fontname);
#endif
    if (!SFfont)
    {
#ifdef FEAT_XFONTSET
	SFfont = XCreateFontSet(SFdisplay, SF_DEFAULT_FONT,
					    &missing, &num_missing, &def_str);
#else
	SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT);
#endif
	if (!SFfont)
	{
	    EMSG2(_("E616: vim_SelFile: can't get font %s"), SF_DEFAULT_FONT);
	    SFstatus = SEL_FILE_CANCEL;
	    return;
	}
    }

#ifdef FEAT_XFONTSET
    extents = XExtentsOfFontSet(SFfont);
    SFcharWidth = extents->max_logical_extent.width;
    SFcharAscent = -extents->max_logical_extent.y;
    SFcharHeight = extents->max_logical_extent.height;
#else
    SFcharWidth = (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2;
    SFcharAscent = SFfont->max_bounds.ascent;
    SFcharHeight = SFcharAscent + SFfont->max_bounds.descent;
#endif
}

static void SFcreateGC(void);

    static void
SFcreateGC(void)
{
    XGCValues	gcValues;
    XRectangle	rectangles[1];

    gcValues.foreground = SFfore;

    SFlineGC = XtGetGC(
	    selFileLists[0],
	    (XtGCMask)GCForeground,
	    &gcValues);

    SFscrollGC = XtGetGC(
	    selFileLists[0],
	    (XtGCMask)0,
	    &gcValues);

    gcValues.function = GXxor;
    gcValues.foreground = SFfore ^ SFback;
    gcValues.background = SFfore ^ SFback;

    SFinvertGC = XtGetGC(
	    selFileLists[0],
	    (XtGCMask)GCFunction | GCForeground | GCBackground,
	    &gcValues);

    gcValues.foreground = SFfore;
    gcValues.background = SFback;
#ifndef FEAT_XFONTSET
    gcValues.font = SFfont->fid;
#endif

    SFtextGC = XCreateGC(
	    SFdisplay,
	    XtWindow(selFileLists[0]),
#ifdef FEAT_XFONTSET
	    (unsigned long)GCForeground | GCBackground,
#else
	    (unsigned long)GCForeground | GCBackground | GCFont,
#endif
	    &gcValues);

    rectangles[0].x = SFlineToTextH + SFbesideText;
    rectangles[0].y = 0;
    rectangles[0].width = SFcharsPerEntry * SFcharWidth;
    rectangles[0].height = SFupperY + 1;

    XSetClipRectangles(
	    SFdisplay,
	    SFtextGC,
	    0,
	    0,
	    rectangles,
	    1,
	    Unsorted);
}

    static void
SFclearList(int n, int doScroll)
{
    SFDir	*dir;

    SFcurrentInvert[n] = -1;

    XClearWindow(SFdisplay, XtWindow(selFileLists[n]));

    XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs, 2);

    if (doScroll)
    {
	dir = &(SFdirs[SFdirPtr + n]);

	if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars)
	{
#ifdef FEAT_GUI_NEXTAW
	    XawScrollbarSetThumb(
		    selFileVScrolls[n],
		    (float) (((double) dir->vOrigin) /
			     dir->nEntries),
		    (float) (((double) ((dir->nEntries < SFlistSize)
					? dir->nEntries : SFlistSize)) /
			     dir->nEntries));
#else
	    vim_XawScrollbarSetThumb(
		    selFileVScrolls[n],
		    (float) (((double) dir->vOrigin) /
			     dir->nEntries),
		    (float) (((double) ((dir->nEntries < SFlistSize)
					? dir->nEntries : SFlistSize)) /
			     dir->nEntries),
		    (double)dir->nEntries);
#endif

#ifdef FEAT_GUI_NEXTAW
	    XawScrollbarSetThumb(
		    selFileHScrolls[n],
		    (float) (((double) dir->hOrigin) / dir->nChars),
		    (float) (((double) ((dir->nChars <
					 SFcharsPerEntry) ? dir->nChars :
					SFcharsPerEntry)) / dir->nChars));
#else
	    vim_XawScrollbarSetThumb(
		    selFileHScrolls[n],
		    (float) (((double) dir->hOrigin) / dir->nChars),
		    (float) (((double) ((dir->nChars <
					 SFcharsPerEntry) ? dir->nChars :
					SFcharsPerEntry)) / dir->nChars),
		    (double)dir->nChars);
#endif
	}
	else
	{
#ifdef FEAT_GUI_NEXTAW
	    XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
		    (float) 1.0);
#else
	    vim_XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
		    (float) 1.0, 1.0);
#endif
#ifdef FEAT_GUI_NEXTAW
	    XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
		    (float) 1.0);
#else
	    vim_XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
		    (float) 1.0, 1.0);
#endif
	}
    }
}

static void SFdeleteEntry(SFDir *dir, SFEntry *entry);

    static void
SFdeleteEntry(SFDir *dir, SFEntry *entry)
{
    SFEntry	*e;
    SFEntry	*end;
    int		n;
    int		idx;

    idx = entry - dir->entries;

    if (idx < dir->beginSelection)
	dir->beginSelection--;
    if (idx <= dir->endSelection)
	dir->endSelection--;
    if (dir->beginSelection > dir->endSelection)
	dir->beginSelection = dir->endSelection = -1;

    if (idx < dir->vOrigin)
	dir->vOrigin--;

    XtFree(entry->real);

    end = &(dir->entries[dir->nEntries - 1]);

    for (e = entry; e < end; e++)
	*e = *(e + 1);

    if (!(--dir->nEntries))
	return;

    n = dir - &(SFdirs[SFdirPtr]);
    if ((n < 0) || (n > 2))
	return;

#ifdef FEAT_GUI_NEXTAW
    XawScrollbarSetThumb(
	    selFileVScrolls[n],
	    (float) (((double) dir->vOrigin) / dir->nEntries),
	    (float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries));
#else
    vim_XawScrollbarSetThumb(
	    selFileVScrolls[n],
	    (float) (((double) dir->vOrigin) / dir->nEntries),
	    (float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries),
	    (double)dir->nEntries);
#endif
}

static void SFwriteStatChar(char *name, int last, stat_T *statBuf);

    static void
SFwriteStatChar(
    char	*name,
    int		last,
    stat_T	*statBuf)
{
    name[last] = SFstatChar(statBuf);
}

static int SFstatAndCheck(SFDir *dir, SFEntry *entry);

    static int
SFstatAndCheck(SFDir *dir, SFEntry *entry)
{
    stat_T	statBuf;
    char	save;
    int		last;

    /*
     * must be restored before returning
     */
    save = *(dir->path);
    *(dir->path) = 0;

    if (!SFchdir(SFcurrentPath))
    {
	last = strlen(entry->real) - 1;
	entry->real[last] = 0;
	entry->statDone = 1;
	if ((!mch_stat(entry->real, &statBuf))
#ifdef S_IFLNK
		|| (!mch_lstat(entry->real, &statBuf))
#endif
	   )
	{
	    if (SFfunc)
	    {
		char *shown;

		shown = NULL;
		if (SFfunc(entry->real, &shown, &statBuf))
		{
		    if (shown)
		    {
			int len;

			len = strlen(shown);
			entry->shown = XtMalloc((unsigned) (len + 2));
			(void) strcpy(entry->shown, shown);
			SFwriteStatChar(entry->shown, len, &statBuf);
			entry->shown[len + 1] = 0;
		    }
		}
		else
		{
		    SFdeleteEntry(dir, entry);

		    *(dir->path) = save;
		    return 1;
		}
	    }
	    SFwriteStatChar(entry->real, last, &statBuf);
	}
	else
	    entry->real[last] = ' ';
    }

    *(dir->path) = save;
    return 0;
}


    static void
SFdrawStrings(
    Window	w,
    SFDir	*dir,
    int		from,
    int		to)
{
    int		i;
    SFEntry	*entry;
    int		x;

    x = SFtextX - dir->hOrigin * SFcharWidth;

    if (dir->vOrigin + to >= dir->nEntries)
	to = dir->nEntries - dir->vOrigin - 1;
    for (i = from; i <= to; i++)
    {
	entry = &(dir->entries[dir->vOrigin + i]);
	if (!(entry->statDone))
	{
	    if (SFstatAndCheck(dir, entry))
	    {
		if (dir->vOrigin + to >= dir->nEntries)
		    to = dir->nEntries - dir->vOrigin - 1;
		i--;
		continue;
	    }
	}
#ifdef FEAT_XFONTSET
	XmbDrawImageString(
		SFdisplay,
		w,
		SFfont,
		SFtextGC,
		x,
		SFtextYoffset + i * SFentryHeight,
		entry->shown,
		strlen(entry->shown));
#else
	XDrawImageString(
		SFdisplay,
		w,
		SFtextGC,
		x,
		SFtextYoffset + i * SFentryHeight,
		entry->shown,
		strlen(entry->shown));
#endif
	if (dir->vOrigin + i == dir->beginSelection)
	{
	    XDrawLine(
		    SFdisplay,
		    w,
		    SFlineGC,
		    SFlineToTextH + 1,
		    SFlowerY + i * SFentryHeight,
		    SFlineToTextH + SFentryWidth - 2,
		    SFlowerY + i * SFentryHeight);
	}
	if ((dir->vOrigin + i >= dir->beginSelection) &&
		(dir->vOrigin + i <= dir->endSelection))
	{
	    SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 =
		SFlowerY + i * SFentryHeight;
	    SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 =
		SFlowerY + (i + 1) * SFentryHeight - 1;
	    XDrawSegments(
		    SFdisplay,
		    w,
		    SFlineGC,
		    SFcompletionSegs,
		    2);
	}
	if (dir->vOrigin + i == dir->endSelection)
	{
	    XDrawLine(
		    SFdisplay,
		    w,
		    SFlineGC,
		    SFlineToTextH + 1,
		    SFlowerY + (i + 1) * SFentryHeight - 1,
		    SFlineToTextH + SFentryWidth - 2,
		    SFlowerY + (i + 1) * SFentryHeight - 1);
	}
    }
}

    static void
SFdrawList(int n, int doScroll)
{
    SFDir	*dir;
    Window	w;

    SFclearList(n, doScroll);

    if (SFdirPtr + n < SFdirEnd)
    {
	dir = &(SFdirs[SFdirPtr + n]);
	w = XtWindow(selFileLists[n]);
#ifdef FEAT_XFONTSET
	XmbDrawImageString(
		SFdisplay,
		w,
		SFfont,
		SFtextGC,
		SFtextX - dir->hOrigin * SFcharWidth,
		SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
		dir->dir,
		strlen(dir->dir));
#else
	XDrawImageString(
		SFdisplay,
		w,
		SFtextGC,
		SFtextX - dir->hOrigin * SFcharWidth,
		SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
		dir->dir,
		strlen(dir->dir));
#endif
	SFdrawStrings(w, dir, 0, SFlistSize - 1);
    }
}

    static void
SFdrawLists(int doScroll)
{
    int	i;

    for (i = 0; i < 3; i++)
	SFdrawList(i, doScroll);
}

    static void
SFinvertEntry(int n)
{
    XFillRectangle(
	    SFdisplay,
	    XtWindow(selFileLists[n]),
	    SFinvertGC,
	    SFlineToTextH,
	    SFcurrentInvert[n] * SFentryHeight + SFlowerY,
	    SFentryWidth,
	    SFentryHeight);
}

static unsigned long SFscrollTimerInterval(void);

    static unsigned long
SFscrollTimerInterval(void)
{
    static int	maxVal = 200;
    static int	varyDist = 50;
    static int	minDist = 50;
    int		t;
    int		dist;

    if (SFcurrentListY < SFlowerY)
	dist = SFlowerY - SFcurrentListY;
    else if (SFcurrentListY > SFupperY)
	dist = SFcurrentListY - SFupperY;
    else
	return (unsigned long) 1;

    t = maxVal - ((maxVal / varyDist) * (dist - minDist));

    if (t < 1)
	t = 1;

    if (t > maxVal)
	t = maxVal;

    return (unsigned long)t;
}

static void SFscrollTimer(XtPointer p, XtIntervalId *id);

    static void
SFscrollTimer(XtPointer p, XtIntervalId *id UNUSED)
{
    SFDir	*dir;
    int		save;
    int		n;

    n = (long)p;

    dir = &(SFdirs[SFdirPtr + n]);
    save = dir->vOrigin;

    if (SFcurrentListY < SFlowerY)
    {
	if (dir->vOrigin > 0)
	    SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin - 1);
    }
    else if (SFcurrentListY > SFupperY)
    {
	if (dir->vOrigin < dir->nEntries - SFlistSize)
	    SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin + 1);
    }

    if (dir->vOrigin != save)
    {
	if (dir->nEntries)
	{
#ifdef FEAT_GUI_NEXTAW
	    XawScrollbarSetThumb(
		    selFileVScrolls[n],
		    (float) (((double) dir->vOrigin) / dir->nEntries),
		    (float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries));
#else
	    vim_XawScrollbarSetThumb(
		    selFileVScrolls[n],
		    (float) (((double) dir->vOrigin) / dir->nEntries),
		    (float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries),
		    (double)dir->nEntries);
#endif
	}
    }

    if (SFbuttonPressed)
	SFscrollTimerId = XtAppAddTimeOut(SFapp,
		       SFscrollTimerInterval(), SFscrollTimer,
		       (XtPointer)(long_u)n);
}

    static int
SFnewInvertEntry(int n, XMotionEvent *event)
{
    int			x, y;
    int			nw;
    static int		SFscrollTimerAdded = 0;

    x = event->x;
    y = event->y;

    if (SFdirPtr + n >= SFdirEnd)
	return -1;

    if ((x >= 0) && (x <= SFupperX) && (y >= SFlowerY) && (y <= SFupperY))
    {
	SFDir *dir = &(SFdirs[SFdirPtr + n]);

	if (SFscrollTimerAdded)
	{
	    SFscrollTimerAdded = 0;
	    XtRemoveTimeOut(SFscrollTimerId);
	}

	nw = (y - SFlowerY) / SFentryHeight;
	if (dir->vOrigin + nw >= dir->nEntries)
	    return -1;
	return nw;
    }
    else
    {
	if (SFbuttonPressed)
	{
	    SFcurrentListY = y;
	    if (!SFscrollTimerAdded)
	    {
		SFscrollTimerAdded = 1;
		SFscrollTimerId = XtAppAddTimeOut(SFapp,
			SFscrollTimerInterval(), SFscrollTimer,
			(XtPointer)(long_u)n);
	    }
	}
	return -1;
    }
}

    static void
SFenterList(Widget w UNUSED, int n, XEnterWindowEvent *event)
{
    int			nw;

    /* sanity */
    if (SFcurrentInvert[n] != -1)
    {
	SFinvertEntry(n);
	SFcurrentInvert[n] = -1;
    }

    nw = SFnewInvertEntry(n, (XMotionEvent *) event);
    if (nw != -1)
    {
	SFcurrentInvert[n] = nw;
	SFinvertEntry(n);
    }
}

    static void
SFleaveList(Widget w UNUSED, int n, XEvent *event UNUSED)
{
    if (SFcurrentInvert[n] != -1)
    {
	SFinvertEntry(n);
	SFcurrentInvert[n] = -1;
    }
}

    static void
SFmotionList(Widget w UNUSED, int n, XMotionEvent *event)
{
    int		nw;

    nw = SFnewInvertEntry(n, event);

    if (nw != SFcurrentInvert[n])
    {
	if (SFcurrentInvert[n] != -1)
	    SFinvertEntry(n);
	SFcurrentInvert[n] = nw;
	if (nw != -1)
	    SFinvertEntry(n);
    }
}

    static void
SFvFloatSliderMovedCallback(Widget w, XtPointer n, XtPointer fnew)
{
    int		nw;

    nw = (*(float *)fnew) * SFdirs[SFdirPtr + (int)(long)n].nEntries;
    SFvSliderMovedCallback(w, (int)(long)n, nw);
}

    static void
SFvSliderMovedCallback(Widget w UNUSED, int n, int nw)
{
    int		old;
    Window	win;
    SFDir	*dir;

    dir = &(SFdirs[SFdirPtr + n]);

    old = dir->vOrigin;
    dir->vOrigin = nw;

    if (old == nw)
	return;

    win = XtWindow(selFileLists[n]);

    if (ABS(nw - old) < SFlistSize)
    {
	if (nw > old)
	{
	    XCopyArea(
		    SFdisplay,
		    win,
		    win,
		    SFscrollGC,
		    SFlineToTextH,
		    SFlowerY + (nw - old) * SFentryHeight,
		    SFentryWidth + SFlineToTextH,
		    (SFlistSize - (nw - old)) * SFentryHeight,
		    SFlineToTextH,
		    SFlowerY);
	    XClearArea(
		    SFdisplay,
		    win,
		    SFlineToTextH,
		    SFlowerY + (SFlistSize - (nw - old)) *
		    SFentryHeight,
		    SFentryWidth + SFlineToTextH,
		    (nw - old) * SFentryHeight,
		    False);
	    SFdrawStrings(win, dir, SFlistSize - (nw - old),
		    SFlistSize - 1);
	}
	else
	{
	    XCopyArea(
		    SFdisplay,
		    win,
		    win,
		    SFscrollGC,
		    SFlineToTextH,
		    SFlowerY,
		    SFentryWidth + SFlineToTextH,
		    (SFlistSize - (old - nw)) * SFentryHeight,
		    SFlineToTextH,
		    SFlowerY + (old - nw) * SFentryHeight);
	    XClearArea(
		    SFdisplay,
		    win,
		    SFlineToTextH,
		    SFlowerY,
		    SFentryWidth + SFlineToTextH,
		    (old - nw) * SFentryHeight,
		    False);
	    SFdrawStrings(win, dir, 0, old - nw);
	}
    }
    else
    {
	XClearArea(
		SFdisplay,
		win,
		SFlineToTextH,
		SFlowerY,
		SFentryWidth + SFlineToTextH,
		SFlistSize * SFentryHeight,
		False);
	SFdrawStrings(win, dir, 0, SFlistSize - 1);
    }
}

    static void
SFvAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew)
{
    SFDir	*dir;
    int		nw = (int)(long)pnew;

    dir = &(SFdirs[SFdirPtr + (int)(long)n]);

#ifdef FEAT_GUI_NEXTAW
    if (nw < 0)
    {
	if (nw > -SFvScrollHeight)
	    nw = -1;
	else
	    nw = -SFlistSize;
    }
    else if (nw > 0)
    {
	if (nw < SFvScrollHeight)
	    nw = 1;
	else
	    nw = SFlistSize;
    }
#endif
    nw += dir->vOrigin;

    if (nw > dir->nEntries - SFlistSize)
	nw = dir->nEntries - SFlistSize;

    if (nw < 0)
	nw = 0;

    if (dir->nEntries)
    {
	float	f;

	f = ((double) nw) / dir->nEntries;

#ifdef FEAT_GUI_NEXTAW
	XawScrollbarSetThumb(
		w,
		f,
		(float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries));
#else
	vim_XawScrollbarSetThumb(
		w,
		f,
		(float) (((double) ((dir->nEntries < SFlistSize) ?
				dir->nEntries : SFlistSize)) / dir->nEntries),
		(double)dir->nEntries);
#endif
    }

    SFvSliderMovedCallback(w, (int)(long)n, nw);
}

    static void
SFhSliderMovedCallback(Widget w UNUSED, XtPointer n, XtPointer nw)
{
    SFDir	*dir;
    int	save;

    dir = &(SFdirs[SFdirPtr + (int)(long)n]);
    save = dir->hOrigin;
    dir->hOrigin = (*(float *)nw) * dir->nChars;
    if (dir->hOrigin == save)
	return;

    SFdrawList((int)(long)n, SF_DO_NOT_SCROLL);
}

    static void
SFhAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew)
{
    SFDir	*dir;
    int		nw = (int)(long)pnew;

    dir = &(SFdirs[SFdirPtr + (int)(long)n]);

#ifdef FEAT_GUI_NEXTAW
    if (nw < 0)
    {
	if (nw > -SFhScrollWidth)
	    nw = -1;
	else
	    nw = -SFcharsPerEntry;
    }
    else if (nw > 0)
    {
	if (nw < SFhScrollWidth)
	    nw = 1;
	else
	    nw = SFcharsPerEntry;
    }
#endif
    nw += dir->hOrigin;

    if (nw > dir->nChars - SFcharsPerEntry)
	nw = dir->nChars - SFcharsPerEntry;

    if (nw < 0)
	nw = 0;

    if (dir->nChars)
    {
	float	f;

	f = ((double) nw) / dir->nChars;

#ifdef FEAT_GUI_NEXTAW
	XawScrollbarSetThumb(
		w,
		f,
		(float) (((double) ((dir->nChars < SFcharsPerEntry) ?
			       dir->nChars : SFcharsPerEntry)) / dir->nChars));
#else
	vim_XawScrollbarSetThumb(
		w,
		f,
		(float) (((double) ((dir->nChars < SFcharsPerEntry) ?
			       dir->nChars : SFcharsPerEntry)) / dir->nChars),
		(double)dir->nChars);
#endif

	SFhSliderMovedCallback(w, n, (XtPointer)&f);
    }
}

    static void
SFpathSliderMovedCallback(
    Widget	w UNUSED,
    XtPointer	client_data UNUSED,
    XtPointer	nw)
{
    SFDir		*dir;
    int			n;
    XawTextPosition	pos;
    int			SFdirPtrSave;

    SFdirPtrSave = SFdirPtr;
    SFdirPtr = (*(float *)nw) * SFdirEnd;
    if (SFdirPtr == SFdirPtrSave)
	return;

    SFdrawLists(SF_DO_SCROLL);

    n = 2;
    while (SFdirPtr + n >= SFdirEnd)
	n--;

    dir = &(SFdirs[SFdirPtr + n]);

    pos = dir->path - SFcurrentPath;

    if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir)))
    {
	pos -= strlen(SFstartDir);
	if (pos < 0)
	    pos = 0;
    }

    XawTextSetInsertionPoint(selFileField, pos);
}

    static void
SFpathAreaSelectedCallback(
    Widget	w,
    XtPointer	client_data UNUSED,
    XtPointer	pnew)
{
    int		nw = (int)(long)pnew;
    float	f;

#ifdef FEAT_GUI_NEXTAW
    if (nw < 0)
    {
	if (nw > -SFpathScrollWidth)
	    nw = -1;
	else
	    nw = -3;
    }
    else if (nw > 0)
    {
	if (nw < SFpathScrollWidth)
	    nw = 1;
	else
	    nw = 3;
    }
#endif
    nw += SFdirPtr;

    if (nw > SFdirEnd - 3)
	nw = SFdirEnd - 3;

    if (nw < 0)
	nw = 0;

    f = ((double) nw) / SFdirEnd;

#ifdef FEAT_GUI_NEXTAW
    XawScrollbarSetThumb(
	    w,
	    f,
	    (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd));
#else
    vim_XawScrollbarSetThumb(
	    w,
	    f,
	    (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd),
	    (double)SFdirEnd);
#endif

    SFpathSliderMovedCallback(w, (XtPointer) NULL, (XtPointer)&f);
}

    static Boolean
SFworkProc(void)
{
    SFDir	*dir;
    SFEntry	*entry;

    for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--)
    {
	if (!(dir->nEntries))
	    continue;
	for (entry = &(dir->entries[dir->nEntries - 1]);
		entry >= dir->entries;
		entry--)
	{
	    if (!(entry->statDone))
	    {
		(void)SFstatAndCheck(dir, entry);
		return False;
	    }
	}
    }

    SFworkProcAdded = 0;

    return True;
}

/***************** Dir.c */

    static int
SFcompareEntries(const void *p, const void *q)
{
 return strcmp(((SFEntry *)p)->real, ((SFEntry *)q)->real);
}

    static int
SFgetDir(
    SFDir	*dir)
{
    SFEntry		*result = NULL;
    int			Alloc = 0;
    int			i;
    DIR			*dirp;
    struct dirent	*dp;
    char		*str;
    int			len;
    int			maxChars;
    stat_T		statBuf;

    maxChars = strlen(dir->dir) - 1;

    dir->entries = NULL;
    dir->nEntries = 0;
    dir->nChars = 0;

    result = NULL;
    i = 0;

    dirp = opendir(".");
    if (!dirp)
	return 1;

    (void)mch_stat(".", &statBuf);
    dir->mtime = statBuf.st_mtime;

    while ((dp = readdir(dirp)))
    {
	/* Ignore "." and ".." */
	if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
	    continue;
	if (i >= Alloc)
	{
	    Alloc = 2 * (Alloc + 1);
	    result = (SFEntry *) XtRealloc((char *) result,
		    (unsigned) (Alloc * sizeof(SFEntry)));
	}
	result[i].statDone = 0;
	str = dp->d_name;
	len = strlen(str);
	result[i].real = XtMalloc((unsigned) (len + 2));
	(void) strcat(strcpy(result[i].real, str), " ");
	if (len > maxChars)
	    maxChars = len;
	result[i].shown = result[i].real;
	i++;
    }

    qsort((char *) result, (size_t) i, sizeof(SFEntry), SFcompareEntries);

    dir->entries = result;
    dir->nEntries = i;
    dir->nChars = maxChars + 1;

    closedir(dirp);

    return 0;
}

/***************** SFinternal.h */

#include <sys/param.h>
#include <X11/cursorfont.h>
#include <X11/Composite.h>
#include <X11/Shell.h>
#ifdef FEAT_GUI_NEXTAW
# include <X11/neXtaw/Form.h>
# include <X11/neXtaw/Command.h>
# include <X11/neXtaw/Label.h>
#else
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#endif

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

static void SFexposeList(Widget w, XtPointer n, XEvent *event, Boolean *cont);

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

    SFdrawList((int)(long)n, SF_DO_NOT_SCROLL);
}

static void SFmodVerifyCallback(Widget w, XtPointer client_data, XEvent *event, Boolean *cont);

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

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

static void SFokCallback(Widget w, XtPointer cl, XtPointer cd);

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

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

static void SFcancelCallback(Widget w, XtPointer cl, XtPointer cd);

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

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

static void SFdismissAction(Widget w, XEvent *event, String *params, Cardinal *num_params);

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

    SFstatus = SEL_FILE_CANCEL;
}

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

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

    static void
SFsetColors(
    guicolor_T	bg,
    guicolor_T	fg,
    guicolor_T	scroll_bg,
    guicolor_T	scroll_fg)
{
    if (selFileForm)
    {
	XtVaSetValues(selFileForm, XtNbackground,  bg,
				   XtNforeground,  fg,
				   XtNborderColor, bg,
				   NULL);
    }
    {
	int i;

	for (i = 0; i < 3; ++i)
	{
	    if (selFileLists[i])
	    {
		XtVaSetValues(selFileLists[i], XtNbackground,  bg,
					       XtNforeground,  fg,
					       XtNborderColor, fg,
					       NULL);
	    }
	}
    }
    if (selFileOK)
    {
	XtVaSetValues(selFileOK, XtNbackground,  bg,
				 XtNforeground,  fg,
				 XtNborderColor, fg,
				 NULL);
    }
    if (selFileCancel)
    {
	XtVaSetValues(selFileCancel, XtNbackground, bg,
				     XtNforeground, fg,
				     XtNborderColor, fg,
				     NULL);
    }
    if (selFilePrompt)
    {
	XtVaSetValues(selFilePrompt, XtNbackground, bg,
				     XtNforeground, fg,
				     NULL);
    }
    if (gui.dpy)
    {
	XSetBackground(gui.dpy, SFtextGC, bg);
	XSetForeground(gui.dpy, SFtextGC, fg);
	XSetForeground(gui.dpy, SFlineGC, fg);

	/* This is an xor GC, so combine the fg and background */
	XSetBackground(gui.dpy, SFinvertGC, fg ^ bg);
	XSetForeground(gui.dpy, SFinvertGC, fg ^ bg);
    }
    if (selFileHScroll)
    {
	XtVaSetValues(selFileHScroll, XtNbackground, scroll_bg,
				      XtNforeground, scroll_fg,
				      XtNborderColor, fg,
				      NULL);
    }
    {
	int i;

	for (i = 0; i < 3; i++)
	{
	    XtVaSetValues(selFileVScrolls[i], XtNbackground, scroll_bg,
					      XtNforeground, scroll_fg,
					      XtNborderColor, fg,
					      NULL);
	    XtVaSetValues(selFileHScrolls[i], XtNbackground, scroll_bg,
					      XtNforeground, scroll_fg,
					      XtNborderColor, fg,
					      NULL);
	}
    }
}

    static void
SFcreateWidgets(
    Widget	toplevel,
    char	*prompt,
    char	*ok,
    char	*cancel)
{
    Cardinal	n;
    int		listWidth, listHeight;
    int		listSpacing = 10;
    int		scrollThickness = 15;
    int		hScrollX, hScrollY;
    int		vScrollX, vScrollY;

    selFile = XtVaAppCreateShell("selFile", "SelFile",
		transientShellWidgetClass, SFdisplay,
		XtNtransientFor, toplevel,
		XtNtitle, prompt,
		NULL);

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

    selFileForm = XtVaCreateManagedWidget("selFileForm",
		formWidgetClass, selFile,
		XtNdefaultDistance, 30,
		XtNforeground, SFfore,
		XtNbackground, SFback,
		XtNborderColor, SFback,
		NULL);

    selFilePrompt = XtVaCreateManagedWidget("selFilePrompt",
		labelWidgetClass, selFileForm,
		XtNlabel, prompt,
		XtNresizable, True,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		XtNborderWidth, 0,
		XtNforeground, SFfore,
		XtNbackground, SFback,
		NULL);

    /*
    XtVaGetValues(selFilePrompt,
		XtNforeground, &SFfore,
		XtNbackground, &SFback,
		NULL);
    */

    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;

    selFileField = XtVaCreateManagedWidget("selFileField",
		asciiTextWidgetClass, selFileForm,
		XtNwidth, 3 * listWidth + 2 * listSpacing + 4,
		XtNborderColor, SFfore,
		XtNfromVert, selFilePrompt,
		XtNvertDistance, 10,
		XtNresizable, True,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		XtNstring, SFtextBuffer,
		XtNlength, MAXPATHL,
		XtNeditType, XawtextEdit,
		XtNwrap, XawtextWrapWord,
		XtNresize, XawtextResizeHeight,
		XtNuseStringInPlace, True,
		NULL);

    XtOverrideTranslations(selFileField,
	    XtParseTranslationTable(oneLineTextEditTranslations));
    XtSetKeyboardFocus(selFileForm, selFileField);

    selFileHScroll = XtVaCreateManagedWidget("selFileHScroll",
#ifdef FEAT_GUI_NEXTAW
		scrollbarWidgetClass, selFileForm,
#else
		vim_scrollbarWidgetClass, selFileForm,
#endif
		XtNorientation, XtorientHorizontal,
		XtNwidth, SFpathScrollWidth,
		XtNheight, scrollThickness,
		XtNborderColor, SFfore,
		XtNfromVert, selFileField,
		XtNvertDistance, 30,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		XtNforeground, gui.scroll_fg_pixel,
		XtNbackground, gui.scroll_bg_pixel,
#ifndef FEAT_GUI_NEXTAW
		XtNlimitThumb, 1,
#endif
		NULL);

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

    selFileLists[0] = XtVaCreateManagedWidget("selFileList1",
		compositeWidgetClass, selFileForm,
		XtNwidth, listWidth,
		XtNheight, listHeight,
		XtNforeground,  SFfore,
		XtNbackground,  SFback,
		XtNborderColor, SFfore,
		XtNfromVert, selFileHScroll,
		XtNvertDistance, 10,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		NULL);

    selFileLists[1] = XtVaCreateManagedWidget("selFileList2",
		compositeWidgetClass, selFileForm,
		XtNwidth, listWidth,
		XtNheight, listHeight,
		XtNforeground,  SFfore,
		XtNbackground,  SFback,
		XtNborderColor, SFfore,
		XtNfromHoriz, selFileLists[0],
		XtNfromVert, selFileHScroll,
		XtNhorizDistance, listSpacing,
		XtNvertDistance, 10,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		NULL);

    selFileLists[2] = XtVaCreateManagedWidget("selFileList3",
		compositeWidgetClass, selFileForm,
		XtNwidth, listWidth,
		XtNheight, listHeight,
		XtNforeground,  SFfore,
		XtNbackground,  SFback,
		XtNborderColor, SFfore,
		XtNfromHoriz, selFileLists[1],
		XtNfromVert, selFileHScroll,
		XtNhorizDistance, listSpacing,
		XtNvertDistance, 10,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		NULL);

    for (n = 0; n < 3; n++)
    {
	selFileVScrolls[n] = XtVaCreateManagedWidget("selFileVScroll",
#ifdef FEAT_GUI_NEXTAW
		    scrollbarWidgetClass, selFileLists[n],
#else
		    vim_scrollbarWidgetClass, selFileLists[n],
#endif
		    XtNx, vScrollX,
		    XtNy, vScrollY,
		    XtNwidth, scrollThickness,
		    XtNheight, SFvScrollHeight,
		    XtNborderColor, SFfore,
		    XtNforeground, gui.scroll_fg_pixel,
		    XtNbackground, gui.scroll_bg_pixel,
#ifndef FEAT_GUI_NEXTAW
		    XtNlimitThumb, 1,
#endif
		    NULL);

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

	selFileHScrolls[n] = XtVaCreateManagedWidget("selFileHScroll",
#ifdef FEAT_GUI_NEXTAW
		    scrollbarWidgetClass, selFileLists[n],
#else
		    vim_scrollbarWidgetClass, selFileLists[n],
#endif
		    XtNorientation, XtorientHorizontal,
		    XtNx, hScrollX,
		    XtNy, hScrollY,
		    XtNwidth, SFhScrollWidth,
		    XtNheight, scrollThickness,
		    XtNborderColor, SFfore,
		    XtNforeground, gui.scroll_fg_pixel,
		    XtNbackground, gui.scroll_bg_pixel,
#ifndef FEAT_GUI_NEXTAW
		    XtNlimitThumb, 1,
#endif
		    NULL);

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

    selFileOK = XtVaCreateManagedWidget("selFileOK",
		commandWidgetClass, selFileForm,
		XtNlabel, ok,
		XtNresizable, True,
		XtNcallback, SFokSelect,
		XtNforeground,  SFfore,
		XtNbackground,  SFback,
		XtNborderColor, SFfore,
		XtNfromHoriz, selFileLists[0],
		XtNfromVert, selFileLists[0],
		XtNvertDistance, 30,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		NULL);

    selFileCancel = XtVaCreateManagedWidget("selFileCancel",
		commandWidgetClass, selFileForm,
		XtNlabel, cancel,
		XtNresizable, True,
		XtNcallback, SFcancelSelect,
		XtNforeground,  SFfore,
		XtNbackground,  SFback,
		XtNborderColor, SFfore,
		XtNfromHoriz, selFileOK,
		XtNfromVert, selFileLists[0],
		XtNhorizDistance, 30,
		XtNvertDistance, 30,
		XtNtop, XtChainTop,
		XtNbottom, XtChainTop,
		XtNleft, XtChainLeft,
		XtNright, XtChainLeft,
		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();

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

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

    SFapp = XtWidgetToApplicationContext(selFile);
}

    static void
SFtextChanged(void)
{
#if defined(FEAT_XFONTSET) && defined(XtNinternational)
    if ((unsigned long)_XawTextFormat((TextWidget)selFileField) == XawFmtWide)
    {
	wchar_t *wcbuf=(wchar_t *)SFtextBuffer;

	if ((wcbuf[0] == L'/') || (wcbuf[0] == L'~'))
	{
	    (void) wcstombs(SFcurrentPath, wcbuf, MAXPATHL);
	    SFtextPos = XawTextGetInsertionPoint(selFileField);
	}
	else
	{
	    strcpy(SFcurrentPath, SFstartDir);
	    (void) wcstombs(SFcurrentPath + strlen(SFcurrentPath), wcbuf, MAXPATHL);

	    SFtextPos = XawTextGetInsertionPoint(selFileField) + strlen(SFstartDir);
	}
    }
    else
#endif
    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, (XtWorkProc)SFworkProc, NULL);
	SFworkProcAdded = 1;
    }

    SFupdatePath();
}

    static char *
SFgetText(void)
{
#if defined(FEAT_XFONTSET) && defined(XtNinternational)
    char *buf;

    if ((unsigned long)_XawTextFormat((TextWidget)selFileField) == XawFmtWide)
    {
	wchar_t *wcbuf;
	int mbslength;

	XtVaGetValues(selFileField,
	    XtNstring, &wcbuf,
	NULL);
	mbslength = wcstombs(NULL, wcbuf, 0);
	/* Hack: some broken wcstombs() returns zero, just get a large buffer */
	if (mbslength == 0 && wcbuf != NULL && wcbuf[0] != 0)
	    mbslength = MAXPATHL;
	buf=(char *)XtMalloc(mbslength + 1);
	wcstombs(buf, wcbuf, mbslength +1);
	return buf;
    }
#endif
    return (char *)vim_strsave((char_u *)SFtextBuffer);
}

    static void
SFprepareToReturn(void)
{
    SFstatus = SEL_FILE_NULL;
    XtRemoveGrab(selFile);
    XtUnmapWidget(selFile);
    XtRemoveTimeOut(SFdirModTimerId);
    if (SFchdir(SFstartDir))
    {
	EMSG(_("E614: vim_SelFile: can't return to current directory"));
	SFstatus = SEL_FILE_CANCEL;
    }
}

    char *
vim_SelFile(
    Widget	toplevel,
    char	*prompt,
    char	*init_path,
    int		(*show_entry)(),
    int		x,
    int		y,
    guicolor_T	fg,
    guicolor_T	bg,
    guicolor_T	scroll_fg,
    guicolor_T	scroll_bg) /* The "Scrollbar" group colors */
{
    static int	firstTime = 1;
    XEvent	event;
    char	*name_return;

    if (prompt == NULL)
	prompt = _("Pathname:");
    SFfore = fg;
    SFback = bg;

    if (mch_dirname((char_u *)SFstartDir, MAXPATHL) == FAIL)
    {
	EMSG(_("E615: vim_SelFile: can't get current directory"));
	return NULL;
    }

    if (firstTime)
    {
	firstTime = 0;
	SFdisplay = XtDisplay(toplevel);
	SFcreateWidgets(toplevel, prompt, _("OK"), _("Cancel"));
    }
    else
    {
	XtVaSetValues(selFilePrompt, XtNlabel, prompt, NULL);
	XtVaSetValues(selFile, XtNtitle, prompt, NULL);
	SFsetColors(bg, fg, scroll_bg, scroll_fg);
    }

    XtVaSetValues(selFile, XtNx, x, XtNy, y, NULL);
    XtMapWidget(selFile);

    (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;

    SFtextChanged();

    XtAddGrab(selFile, True, True);

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

    for (;;)
    {
	XtAppNextEvent(SFapp, &event);
	XtDispatchEvent(&event);
	switch (SFstatus)
	{
	    case SEL_FILE_TEXT:
		SFstatus = SEL_FILE_NULL;
		SFtextChanged();
		break;
	    case SEL_FILE_OK:
		name_return = SFgetText();
		SFprepareToReturn();
		return name_return;
	    case SEL_FILE_CANCEL:
		SFprepareToReturn();
		return NULL;
	    case SEL_FILE_NULL:
		break;
	}
    }
}
#endif /* FEAT_BROWSE */