text: /* 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(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(struct stat *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)
  {
      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(SFDir *dir);
  
      static int
  SFcheckFiles(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(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(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(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, struct stat *statBuf);
  
      static void
  SFwriteStatChar(
      char	*name,
      int		last,
      struct stat	*statBuf)
  {
      name[last] = SFstatChar(statBuf);
  }
  
  static int SFstatAndCheck(SFDir *dir, SFEntry *entry);
  
      static int
  SFstatAndCheck(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(
      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;
      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(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 */
  