view src/gui_at_fs.c @ 3087:3ecf9e91d88a v7.3.315

updated for version 7.3.315 Problem: Opening a window before forking causes problems for GTK. Solution: Fork first, create the window in the child and report back to the parent process whether it worked. If successful the parent exits, if unsuccessful the child exits and the parent continues in the terminal. (Tim Starling)
author Bram Moolenaar <bram@vim.org>
date Wed, 14 Sep 2011 19:04:39 +0200
parents e5265e4d4725
children f8d307ebd74a
line wrap: on
line source

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

/*
 * 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 __ARGS((char *path));
static void SFtextChanged __ARGS((void));
static char *SFgetText __ARGS((void));
static void SFupdatePath __ARGS((void));
static int SFgetDir __ARGS((SFDir *dir));
static void SFdrawLists __ARGS((int doScroll));
static void SFdrawList __ARGS((int n, int doScroll));
static void SFclearList __ARGS((int n, int doScroll));
static void SFbuttonPressList __ARGS((Widget w, int n, XButtonPressedEvent *event));
static void SFbuttonReleaseList __ARGS((Widget w, int n, XButtonReleasedEvent *event));
static void SFdirModTimer __ARGS((XtPointer cl, XtIntervalId *id));
static char SFstatChar __ARGS((struct stat *statBuf));
static void SFdrawStrings __ARGS((Window w, SFDir *dir, int from, int to));
static int SFnewInvertEntry __ARGS((int n, XMotionEvent *event));
static void SFinvertEntry __ARGS((int n));
static void SFenterList __ARGS((Widget w, int n, XEnterWindowEvent *event));
static void SFleaveList __ARGS((Widget w, int n, XEvent *event));
static void SFmotionList __ARGS((Widget w, int n, XMotionEvent *event));
static void SFvFloatSliderMovedCallback __ARGS((Widget w, XtPointer n, XtPointer fnew));
static void SFvSliderMovedCallback __ARGS((Widget w, int n, int nw));
static void SFvAreaSelectedCallback __ARGS((Widget w, XtPointer n, XtPointer pnew));
static void SFhSliderMovedCallback __ARGS((Widget w, XtPointer n, XtPointer nw));
static void SFhAreaSelectedCallback __ARGS((Widget w, XtPointer n, XtPointer pnew));
static void SFpathSliderMovedCallback __ARGS((Widget w, XtPointer client_data, XtPointer nw));
static void SFpathAreaSelectedCallback __ARGS((Widget w, XtPointer client_data, XtPointer pnew));
static Boolean SFworkProc __ARGS((void));
static int SFcompareEntries __ARGS((const void *p, const void *q));
static void SFprepareToReturn __ARGS((void));
static void SFcreateWidgets __ARGS((Widget toplevel, char *prompt, char *ok, char *cancel));
static void SFsetColors __ARGS((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 __ARGS((char *path));

    static int
SFchdir(path)
    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 __ARGS((int i));

    static void
SFfree(i)
    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 __ARGS((char **s1, char *s2));

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

static void SFunreadableDir __ARGS((SFDir *dir));

    static void
SFunreadableDir(dir)
    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 __ARGS((SFDir *dir, char *str));

    static void
SFreplaceText(dir, str)
    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 __ARGS((char *str));

    static void
SFexpand(str)
    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 __ARGS((SFDir *dir, char *str));

    static int
SFfindFile(dir, str)
    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 __ARGS((void));

    static void
SFunselect()
{
    SFDir	*dir;

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

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

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

static void SFgetHomeDirs __ARGS((void));

    static void
SFgetHomeDirs()
{
    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 __ARGS((char *begin, char *end));

    static int
SFfindHomeDir(begin, end)
    char	*begin, *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()
{
    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(p)
    wchar_t *p;
{
    int i = 0;
    while (*p++ != 0)
	i++;
    return i;
}
#endif

    static void
SFsetText(path)
    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(w, n, event)
    Widget		w UNUSED;
    int			n UNUSED;
    XButtonPressedEvent	*event UNUSED;
{
    SFbuttonPressed = 1;
}

    static void
SFbuttonReleaseList(w, n, event)
    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 __ARGS((int n, SFDir *dir));

    static int
SFcheckDir(n, dir)
    int		n;
    SFDir		*dir;
{
    struct stat	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 __ARGS((SFDir *dir));

    static int
SFcheckFiles(dir)
	SFDir	*dir;
{
    int		from, to;
    int		result;
    char	oldc, newc;
    int		i;
    char	*str;
    int		last;
    struct stat	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(cl, id)
    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(statBuf)
    struct stat *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 __ARGS((void));

    static void
SFinitFont()
{
    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 __ARGS((void));

    static void
SFcreateGC()
{
    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(n, doScroll)
    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 __ARGS((SFDir *dir, SFEntry *entry));

    static void
SFdeleteEntry(dir, entry)
    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 __ARGS((char *name, int last, struct stat *statBuf));

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

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

    static int
SFstatAndCheck(dir, entry)
    SFDir	*dir;
    SFEntry	*entry;
{
    struct stat	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(w, dir, from, to)
    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(n, doScroll)
    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(doScroll)
    int	doScroll;
{
    int	i;

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

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

static unsigned long SFscrollTimerInterval __ARGS((void));

    static unsigned long
SFscrollTimerInterval()
{
    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 __ARGS((XtPointer p, XtIntervalId *id));

    static void
SFscrollTimer(p, id)
    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(n, event)
    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(w, n, event)
    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(w, n, event)
    Widget	w UNUSED;
    int		n;
    XEvent	*event UNUSED;
{
    if (SFcurrentInvert[n] != -1)
    {
	SFinvertEntry(n);
	SFcurrentInvert[n] = -1;
    }
}

    static void
SFmotionList(w, n, event)
    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(w, n, fnew)
    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(w, n, nw)
    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(w, n, pnew)
    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(w, n, nw)
    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(w, n, pnew)
    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(w, client_data, nw)
    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(w, client_data, pnew)
    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()
{
    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(p, q)
    const void	*p;
    const void	*q;
{
    return strcmp(((SFEntry *)p)->real, ((SFEntry *)q)->real);
}

    static int
SFgetDir(dir)
    SFDir	*dir;
{
    SFEntry		*result = NULL;
    int			Alloc = 0;
    int			i;
    DIR			*dirp;
    struct dirent	*dp;
    char		*str;
    int			len;
    int			maxChars;
    struct stat		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 __ARGS((Widget w, XtPointer n, XEvent *event, Boolean *cont));

    static void
SFexposeList(w, n, event, cont)
    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 __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont));

    static void
SFmodVerifyCallback(w, client_data, event, cont)
    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 __ARGS((Widget w, XtPointer cl, XtPointer cd));

    static void
SFokCallback(w, cl, cd)
    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 __ARGS((Widget w, XtPointer cl, XtPointer cd));

    static void
SFcancelCallback(w, cl, cd)
    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 __ARGS((Widget w, XEvent *event, String *params, Cardinal *num_params));

    static void
SFdismissAction(w, event, params, num_params)
    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(bg, fg, scroll_bg, scroll_fg)
    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(toplevel, prompt, ok, cancel)
    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)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)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()
{
#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()
{
#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()
{
    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(toplevel, prompt, init_path, show_entry, x, y, fg, bg, scroll_fg, scroll_bg)
    Widget	toplevel;
    char	*prompt;
    char	*init_path;
    int		(*show_entry)();
    int		x, y;
    guicolor_T	fg, bg;
    guicolor_T	scroll_fg, 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 */