diff options
Diffstat (limited to 'src/gui_at_fs.c')
-rw-r--r-- | src/gui_at_fs.c | 2734 |
1 files changed, 2734 insertions, 0 deletions
diff --git a/src/gui_at_fs.c b/src/gui_at_fs.c new file mode 100644 index 0000000..e65a266 --- /dev/null +++ b/src/gui_at_fs.c @@ -0,0 +1,2734 @@ +/* vi:set ts=8 sts=4 sw=4 noet: */ + +/* + * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Software Research Associates + * makes no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Erik M. van der Poel + * Software Research Associates, Inc., Tokyo, Japan + * erik@sra.co.jp + */ +/* + * Author's addresses: + * erik@sra.co.jp + * erik%sra.co.jp@uunet.uu.net + * erik%sra.co.jp@mcvax.uucp + * try junet instead of co.jp + * Erik M. van der Poel + * Software Research Associates, Inc. + * 1-1-1 Hirakawa-cho, Chiyoda-ku + * Tokyo 102 Japan. TEL +81-3-234-2692 + */ + +/* + * Heavely modified for Vim by Bram Moolenaar + */ + +#include "vim.h" + +/* Only include this when using the file browser */ + +#ifdef FEAT_BROWSE + +/* Weird complication: for "make lint" Text.h doesn't combine with Xm.h */ +#if defined(FEAT_GUI_MOTIF) && defined(FMT8BIT) +# undef FMT8BIT +#endif + +#ifndef FEAT_GUI_NEXTAW +# include "gui_at_sb.h" +#endif + +/***************** SFinternal.h */ + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xos.h> +#ifdef FEAT_GUI_NEXTAW +# include <X11/neXtaw/Text.h> +# include <X11/neXtaw/AsciiText.h> +# include <X11/neXtaw/Scrollbar.h> +#else +# include <X11/Xaw/Text.h> +# include <X11/Xaw/AsciiText.h> +#endif + +#define SEL_FILE_CANCEL -1 +#define SEL_FILE_OK 0 +#define SEL_FILE_NULL 1 +#define SEL_FILE_TEXT 2 + +#define SF_DO_SCROLL 1 +#define SF_DO_NOT_SCROLL 0 + +typedef struct +{ + int statDone; + char *real; + char *shown; +} SFEntry; + +typedef struct +{ + char *dir; + char *path; + SFEntry *entries; + int nEntries; + int vOrigin; + int nChars; + int hOrigin; + int changed; + int beginSelection; + int endSelection; + time_t mtime; +} SFDir; + +static char SFstartDir[MAXPATHL], + SFcurrentPath[MAXPATHL], + SFcurrentDir[MAXPATHL]; + +static Widget selFile, + selFileField, + selFileForm, + selFileHScroll, + selFileHScrolls[3], + selFileLists[3], + selFileOK, + selFileCancel, + selFilePrompt, + selFileVScrolls[3]; + +static Display *SFdisplay; + +static int SFcharWidth, SFcharAscent, SFcharHeight; + +static SFDir *SFdirs = NULL; + +static int SFdirEnd; +static int SFdirPtr; + +static Pixel SFfore, SFback; + +static Atom SFwmDeleteWindow; + +static XSegment SFsegs[2], SFcompletionSegs[2]; + +static XawTextPosition SFtextPos; + +static int SFupperX, SFlowerY, SFupperY; + +static int SFtextX, SFtextYoffset; + +static int SFentryWidth, SFentryHeight; + +static int SFlineToTextH = 3; +static int SFlineToTextV = 3; + +static int SFbesideText = 3; +static int SFaboveAndBelowText = 2; + +static int SFcharsPerEntry = 15; + +static int SFlistSize = 10; + +static int SFcurrentInvert[3] = { -1, -1, -1 }; + +static int SFworkProcAdded = 0; + +static XtAppContext SFapp; + +static int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth; + +#ifdef FEAT_XFONTSET +static char SFtextBuffer[MAXPATHL*sizeof(wchar_t)]; +#else +static char SFtextBuffer[MAXPATHL]; +#endif + +static int SFbuttonPressed = 0; + +static XtIntervalId SFdirModTimerId; + +static int (*SFfunc)(); + +static int SFstatus = SEL_FILE_NULL; + +/***************** forward declare static functions */ + +static void SFsetText(char *path); +static void SFtextChanged(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 char SFstatChar(stat_T *statBuf); +static void SFmotionList(Widget w, int n, XMotionEvent *event); +static void SFvSliderMovedCallback(Widget w, int n, int nw); +static Boolean SFworkProc(void); +static int SFcompareEntries(const void *p, const void *q); + +/***************** 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) +{ + 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) +{ + 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) +{ + *s1 = strcpy(XtMalloc((unsigned)(strlen(s2) + 1)), s2); +} + + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + return strcmp(((SFLogin *)p)->name, ((SFLogin *)q)->name); +} + + 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) +{ + 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) +{ + stat_T statBuf; + int i; + + if ((!mch_stat(".", &statBuf)) && (statBuf.st_mtime != dir->mtime)) + { + /* + * If the pointer is currently in the window that we are about + * to update, we must warp it to prevent the user from + * accidentally selecting the wrong file. + */ + if (SFcurrentInvert[n] != -1) + { + XWarpPointer( + SFdisplay, + None, + XtWindow(selFileLists[n]), + 0, + 0, + 0, + 0, + 0, + 0); + } + + for (i = dir->nEntries - 1; i >= 0; i--) + { + if (dir->entries[i].shown != dir->entries[i].real) + XtFree(dir->entries[i].shown); + XtFree(dir->entries[i].real); + } + XtFree((char *) dir->entries); + if (SFgetDir(dir)) + SFunreadableDir(dir); + if (dir->vOrigin > dir->nEntries - SFlistSize) + dir->vOrigin = dir->nEntries - SFlistSize; + if (dir->vOrigin < 0) + dir->vOrigin = 0; + if (dir->hOrigin > dir->nChars - SFcharsPerEntry) + dir->hOrigin = dir->nChars - SFcharsPerEntry; + if (dir->hOrigin < 0) + dir->hOrigin = 0; + dir->beginSelection = -1; + dir->endSelection = -1; + SFdoNotTouchVorigin = 1; + if ((dir + 1)->dir) + (void) SFfindFile(dir, (dir + 1)->dir); + else + (void) SFfindFile(dir, dir->path); + + if (!SFworkProcAdded) + { + (void) XtAppAddWorkProc(SFapp, (XtWorkProc)SFworkProc, NULL); + SFworkProcAdded = 1; + } + return 1; + } + return 0; +} + + static int +SFcheckFiles(SFDir *dir) +{ + int from, to; + int result; + char oldc, newc; + int i; + char *str; + int last; + stat_T statBuf; + + result = 0; + + from = dir->vOrigin; + to = dir->vOrigin + SFlistSize; + if (to > dir->nEntries) + to = dir->nEntries; + + for (i = from; i < to; i++) + { + str = dir->entries[i].real; + last = strlen(str) - 1; + oldc = str[last]; + str[last] = 0; + if (mch_stat(str, &statBuf)) + newc = ' '; + else + newc = SFstatChar(&statBuf); + str[last] = newc; + if (newc != oldc) + result = 1; + } + + return result; +} + + static void +SFdirModTimer(XtPointer cl UNUSED, XtIntervalId *id UNUSED) +{ + static int n = -1; + static int f = 0; + char save; + SFDir *dir; + + if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) + { + n++; + if ((n > 2) || (SFdirPtr + n >= SFdirEnd)) + { + n = 0; + f++; + if ((f > 2) || (SFdirPtr + f >= SFdirEnd)) + f = 0; + } + dir = &(SFdirs[SFdirPtr + n]); + save = *(dir->path); + *(dir->path) = 0; + if (SFchdir(SFcurrentPath)) + { + *(dir->path) = save; + + /* + * force a re-read + */ + *(dir->dir) = 0; + + SFupdatePath(); + } + else + { + *(dir->path) = save; + if (SFcheckDir(n, dir) || ((f == n) && SFcheckFiles(dir))) + SFdrawList(n, SF_DO_SCROLL); + } + } + + SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000, + SFdirModTimer, (XtPointer) NULL); +} + +/* Return a single character describing what kind of file STATBUF is. */ + + static char +SFstatChar(stat_T *statBuf) +{ + if (S_ISDIR (statBuf->st_mode)) + return '/'; + if (S_ISREG (statBuf->st_mode)) + return S_ISXXX (statBuf->st_mode) ? '*' : ' '; +#ifdef S_ISSOCK + if (S_ISSOCK (statBuf->st_mode)) + return '='; +#endif /* S_ISSOCK */ + return ' '; +} + +/***************** Draw.c */ + +#ifdef FEAT_GUI_NEXTAW +# include <X11/neXtaw/Cardinals.h> +#else +# include <X11/Xaw/Cardinals.h> +#endif + +#ifdef FEAT_XFONTSET +# define SF_DEFAULT_FONT "-misc-fixed-medium-r-normal--14-*" +#else +# define SF_DEFAULT_FONT "9x15" +#endif + +#ifdef ABS +# undef ABS +#endif +#define ABS(x) (((x) < 0) ? (-(x)) : (x)) + +typedef struct +{ + char *fontname; +} TextData; + +static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC; + +static XtResource textResources[] = +{ +#ifdef FEAT_XFONTSET + {XtNfontSet, XtCFontSet, XtRString, sizeof (char *), + XtOffsetOf(TextData, fontname), XtRString, SF_DEFAULT_FONT}, +#else + {XtNfont, XtCFont, XtRString, sizeof (char *), + XtOffsetOf(TextData, fontname), XtRString, SF_DEFAULT_FONT}, +#endif +}; + +#ifdef FEAT_XFONTSET +static XFontSet SFfont; +#else +static XFontStruct *SFfont; +#endif + +static int SFcurrentListY; + +static XtIntervalId SFscrollTimerId; + + static void +SFinitFont(void) +{ + 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) + { + semsg(_("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) +{ + 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) +{ + SFEntry *e; + SFEntry *end; + int n; + int idx; + + idx = entry - dir->entries; + + if (idx < dir->beginSelection) + dir->beginSelection--; + if (idx <= dir->endSelection) + dir->endSelection--; + if (dir->beginSelection > dir->endSelection) + dir->beginSelection = dir->endSelection = -1; + + if (idx < dir->vOrigin) + dir->vOrigin--; + + XtFree(entry->real); + + end = &(dir->entries[dir->nEntries - 1]); + + for (e = entry; e < end; e++) + *e = *(e + 1); + + if (!(--dir->nEntries)) + return; + + n = dir - &(SFdirs[SFdirPtr]); + if ((n < 0) || (n > 2)) + return; + +#ifdef FEAT_GUI_NEXTAW + XawScrollbarSetThumb( + selFileVScrolls[n], + (float) (((double) dir->vOrigin) / dir->nEntries), + (float) (((double) ((dir->nEntries < SFlistSize) ? + dir->nEntries : SFlistSize)) / dir->nEntries)); +#else + vim_XawScrollbarSetThumb( + selFileVScrolls[n], + (float) (((double) dir->vOrigin) / dir->nEntries), + (float) (((double) ((dir->nEntries < SFlistSize) ? + dir->nEntries : SFlistSize)) / dir->nEntries), + (double)dir->nEntries); +#endif +} + + static void +SFwriteStatChar( + char *name, + int last, + stat_T *statBuf) +{ + name[last] = SFstatChar(statBuf); +} + + static int +SFstatAndCheck(SFDir *dir, SFEntry *entry) +{ + stat_T statBuf; + char save; + int last; + + /* + * must be restored before returning + */ + save = *(dir->path); + *(dir->path) = 0; + + if (!SFchdir(SFcurrentPath)) + { + last = strlen(entry->real) - 1; + entry->real[last] = 0; + entry->statDone = 1; + if ((!mch_stat(entry->real, &statBuf)) +#ifdef S_IFLNK + || (!mch_lstat(entry->real, &statBuf)) +#endif + ) + { + if (SFfunc) + { + char *shown; + + shown = NULL; + if (SFfunc(entry->real, &shown, &statBuf)) + { + if (shown) + { + int len; + + len = strlen(shown); + entry->shown = XtMalloc((unsigned) (len + 2)); + (void) strcpy(entry->shown, shown); + SFwriteStatChar(entry->shown, len, &statBuf); + entry->shown[len + 1] = 0; + } + } + else + { + SFdeleteEntry(dir, entry); + + *(dir->path) = save; + return 1; + } + } + SFwriteStatChar(entry->real, last, &statBuf); + } + else + entry->real[last] = ' '; + } + + *(dir->path) = save; + return 0; +} + + + static void +SFdrawStrings( + Window w, + SFDir *dir, + int from, + int to) +{ + int i; + SFEntry *entry; + int x; + + x = SFtextX - dir->hOrigin * SFcharWidth; + + if (dir->vOrigin + to >= dir->nEntries) + to = dir->nEntries - dir->vOrigin - 1; + for (i = from; i <= to; i++) + { + entry = &(dir->entries[dir->vOrigin + i]); + if (!(entry->statDone)) + { + if (SFstatAndCheck(dir, entry)) + { + if (dir->vOrigin + to >= dir->nEntries) + to = dir->nEntries - dir->vOrigin - 1; + i--; + continue; + } + } +#ifdef FEAT_XFONTSET + XmbDrawImageString( + SFdisplay, + w, + SFfont, + SFtextGC, + x, + SFtextYoffset + i * SFentryHeight, + entry->shown, + strlen(entry->shown)); +#else + XDrawImageString( + SFdisplay, + w, + SFtextGC, + x, + SFtextYoffset + i * SFentryHeight, + entry->shown, + strlen(entry->shown)); +#endif + if (dir->vOrigin + i == dir->beginSelection) + { + XDrawLine( + SFdisplay, + w, + SFlineGC, + SFlineToTextH + 1, + SFlowerY + i * SFentryHeight, + SFlineToTextH + SFentryWidth - 2, + SFlowerY + i * SFentryHeight); + } + if ((dir->vOrigin + i >= dir->beginSelection) && + (dir->vOrigin + i <= dir->endSelection)) + { + SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 = + SFlowerY + i * SFentryHeight; + SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 = + SFlowerY + (i + 1) * SFentryHeight - 1; + XDrawSegments( + SFdisplay, + w, + SFlineGC, + SFcompletionSegs, + 2); + } + if (dir->vOrigin + i == dir->endSelection) + { + XDrawLine( + SFdisplay, + w, + SFlineGC, + SFlineToTextH + 1, + SFlowerY + (i + 1) * SFentryHeight - 1, + SFlineToTextH + SFentryWidth - 2, + SFlowerY + (i + 1) * SFentryHeight - 1); + } + } +} + + static void +SFdrawList(int n, int doScroll) +{ + SFDir *dir; + Window w; + + SFclearList(n, doScroll); + + if (SFdirPtr + n < SFdirEnd) + { + dir = &(SFdirs[SFdirPtr + n]); + w = XtWindow(selFileLists[n]); +#ifdef FEAT_XFONTSET + XmbDrawImageString( + SFdisplay, + w, + SFfont, + SFtextGC, + SFtextX - dir->hOrigin * SFcharWidth, + SFlineToTextV + SFaboveAndBelowText + SFcharAscent, + dir->dir, + strlen(dir->dir)); +#else + XDrawImageString( + SFdisplay, + w, + SFtextGC, + SFtextX - dir->hOrigin * SFcharWidth, + SFlineToTextV + SFaboveAndBelowText + SFcharAscent, + dir->dir, + strlen(dir->dir)); +#endif + SFdrawStrings(w, dir, 0, SFlistSize - 1); + } +} + + static void +SFdrawLists(int doScroll) +{ + int i; + + for (i = 0; i < 3; i++) + SFdrawList(i, doScroll); +} + + static void +SFinvertEntry(int n) +{ + XFillRectangle( + SFdisplay, + XtWindow(selFileLists[n]), + SFinvertGC, + SFlineToTextH, + SFcurrentInvert[n] * SFentryHeight + SFlowerY, + SFentryWidth, + SFentryHeight); +} + + static unsigned long +SFscrollTimerInterval(void) +{ + static 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 UNUSED) +{ + SFDir *dir; + int save; + int n; + + n = (long)p; + + dir = &(SFdirs[SFdirPtr + n]); + save = dir->vOrigin; + + if (SFcurrentListY < SFlowerY) + { + if (dir->vOrigin > 0) + SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin - 1); + } + else if (SFcurrentListY > SFupperY) + { + if (dir->vOrigin < dir->nEntries - SFlistSize) + SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin + 1); + } + + if (dir->vOrigin != save) + { + if (dir->nEntries) + { +#ifdef FEAT_GUI_NEXTAW + XawScrollbarSetThumb( + selFileVScrolls[n], + (float) (((double) dir->vOrigin) / dir->nEntries), + (float) (((double) ((dir->nEntries < SFlistSize) ? + dir->nEntries : SFlistSize)) / dir->nEntries)); +#else + vim_XawScrollbarSetThumb( + selFileVScrolls[n], + (float) (((double) dir->vOrigin) / dir->nEntries), + (float) (((double) ((dir->nEntries < SFlistSize) ? + dir->nEntries : SFlistSize)) / dir->nEntries), + (double)dir->nEntries); +#endif + } + } + + if (SFbuttonPressed) + SFscrollTimerId = XtAppAddTimeOut(SFapp, + SFscrollTimerInterval(), SFscrollTimer, + (XtPointer)(long_u)n); +} + + static int +SFnewInvertEntry(int n, XMotionEvent *event) +{ + int x, y; + int nw; + static int SFscrollTimerAdded = 0; + + x = event->x; + y = event->y; + + if (SFdirPtr + n >= SFdirEnd) + return -1; + + if ((x >= 0) && (x <= SFupperX) && (y >= SFlowerY) && (y <= SFupperY)) + { + SFDir *dir = &(SFdirs[SFdirPtr + n]); + + if (SFscrollTimerAdded) + { + SFscrollTimerAdded = 0; + XtRemoveTimeOut(SFscrollTimerId); + } + + nw = (y - SFlowerY) / SFentryHeight; + if (dir->vOrigin + nw >= dir->nEntries) + return -1; + return nw; + } + else + { + if (SFbuttonPressed) + { + SFcurrentListY = y; + if (!SFscrollTimerAdded) + { + SFscrollTimerAdded = 1; + SFscrollTimerId = XtAppAddTimeOut(SFapp, + SFscrollTimerInterval(), SFscrollTimer, + (XtPointer)(long_u)n); + } + } + return -1; + } +} + + static void +SFenterList(Widget w UNUSED, int n, XEnterWindowEvent *event) +{ + int nw; + + /* sanity */ + if (SFcurrentInvert[n] != -1) + { + SFinvertEntry(n); + SFcurrentInvert[n] = -1; + } + + nw = SFnewInvertEntry(n, (XMotionEvent *) event); + if (nw != -1) + { + SFcurrentInvert[n] = nw; + SFinvertEntry(n); + } +} + + static void +SFleaveList(Widget w UNUSED, int n, XEvent *event UNUSED) +{ + if (SFcurrentInvert[n] != -1) + { + SFinvertEntry(n); + SFcurrentInvert[n] = -1; + } +} + + static void +SFmotionList(Widget w UNUSED, int n, XMotionEvent *event) +{ + int nw; + + nw = SFnewInvertEntry(n, event); + + if (nw != SFcurrentInvert[n]) + { + if (SFcurrentInvert[n] != -1) + SFinvertEntry(n); + SFcurrentInvert[n] = nw; + if (nw != -1) + SFinvertEntry(n); + } +} + + static void +SFvFloatSliderMovedCallback(Widget w, XtPointer n, XtPointer fnew) +{ + int nw; + + nw = (*(float *)fnew) * SFdirs[SFdirPtr + (int)(long)n].nEntries; + SFvSliderMovedCallback(w, (int)(long)n, nw); +} + + static void +SFvSliderMovedCallback(Widget w UNUSED, int n, int nw) +{ + int old; + Window win; + SFDir *dir; + + dir = &(SFdirs[SFdirPtr + n]); + + old = dir->vOrigin; + dir->vOrigin = nw; + + if (old == nw) + return; + + win = XtWindow(selFileLists[n]); + + if (ABS(nw - old) < SFlistSize) + { + if (nw > old) + { + XCopyArea( + SFdisplay, + win, + win, + SFscrollGC, + SFlineToTextH, + SFlowerY + (nw - old) * SFentryHeight, + SFentryWidth + SFlineToTextH, + (SFlistSize - (nw - old)) * SFentryHeight, + SFlineToTextH, + SFlowerY); + XClearArea( + SFdisplay, + win, + SFlineToTextH, + SFlowerY + (SFlistSize - (nw - old)) * + SFentryHeight, + SFentryWidth + SFlineToTextH, + (nw - old) * SFentryHeight, + False); + SFdrawStrings(win, dir, SFlistSize - (nw - old), + SFlistSize - 1); + } + else + { + XCopyArea( + SFdisplay, + win, + win, + SFscrollGC, + SFlineToTextH, + SFlowerY, + SFentryWidth + SFlineToTextH, + (SFlistSize - (old - nw)) * SFentryHeight, + SFlineToTextH, + SFlowerY + (old - nw) * SFentryHeight); + XClearArea( + SFdisplay, + win, + SFlineToTextH, + SFlowerY, + SFentryWidth + SFlineToTextH, + (old - nw) * SFentryHeight, + False); + SFdrawStrings(win, dir, 0, old - nw); + } + } + else + { + XClearArea( + SFdisplay, + win, + SFlineToTextH, + SFlowerY, + SFentryWidth + SFlineToTextH, + SFlistSize * SFentryHeight, + False); + SFdrawStrings(win, dir, 0, SFlistSize - 1); + } +} + + static void +SFvAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew) +{ + SFDir *dir; + int nw = (int)(long)pnew; + + dir = &(SFdirs[SFdirPtr + (int)(long)n]); + +#ifdef FEAT_GUI_NEXTAW + if (nw < 0) + { + if (nw > -SFvScrollHeight) + nw = -1; + else + nw = -SFlistSize; + } + else if (nw > 0) + { + if (nw < SFvScrollHeight) + nw = 1; + else + nw = SFlistSize; + } +#endif + nw += dir->vOrigin; + + if (nw > dir->nEntries - SFlistSize) + nw = dir->nEntries - SFlistSize; + + if (nw < 0) + nw = 0; + + if (dir->nEntries) + { + float f; + + f = ((double) nw) / dir->nEntries; + +#ifdef FEAT_GUI_NEXTAW + XawScrollbarSetThumb( + w, + f, + (float) (((double) ((dir->nEntries < SFlistSize) ? + dir->nEntries : SFlistSize)) / dir->nEntries)); +#else + vim_XawScrollbarSetThumb( + w, + f, + (float) (((double) ((dir->nEntries < SFlistSize) ? + dir->nEntries : SFlistSize)) / dir->nEntries), + (double)dir->nEntries); +#endif + } + + SFvSliderMovedCallback(w, (int)(long)n, nw); +} + + static void +SFhSliderMovedCallback(Widget w UNUSED, XtPointer n, XtPointer nw) +{ + SFDir *dir; + int save; + + dir = &(SFdirs[SFdirPtr + (int)(long)n]); + save = dir->hOrigin; + dir->hOrigin = (*(float *)nw) * dir->nChars; + if (dir->hOrigin == save) + return; + + SFdrawList((int)(long)n, SF_DO_NOT_SCROLL); +} + + static void +SFhAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew) +{ + SFDir *dir; + int nw = (int)(long)pnew; + + dir = &(SFdirs[SFdirPtr + (int)(long)n]); + +#ifdef FEAT_GUI_NEXTAW + if (nw < 0) + { + if (nw > -SFhScrollWidth) + nw = -1; + else + nw = -SFcharsPerEntry; + } + else if (nw > 0) + { + if (nw < SFhScrollWidth) + nw = 1; + else + nw = SFcharsPerEntry; + } +#endif + nw += dir->hOrigin; + + if (nw > dir->nChars - SFcharsPerEntry) + nw = dir->nChars - SFcharsPerEntry; + + if (nw < 0) + nw = 0; + + if (dir->nChars) + { + float f; + + f = ((double) nw) / dir->nChars; + +#ifdef FEAT_GUI_NEXTAW + XawScrollbarSetThumb( + w, + f, + (float) (((double) ((dir->nChars < SFcharsPerEntry) ? + dir->nChars : SFcharsPerEntry)) / dir->nChars)); +#else + vim_XawScrollbarSetThumb( + w, + f, + (float) (((double) ((dir->nChars < SFcharsPerEntry) ? + dir->nChars : SFcharsPerEntry)) / dir->nChars), + (double)dir->nChars); +#endif + + SFhSliderMovedCallback(w, n, (XtPointer)&f); + } +} + + static void +SFpathSliderMovedCallback( + Widget w UNUSED, + XtPointer client_data UNUSED, + XtPointer nw) +{ + SFDir *dir; + int n; + XawTextPosition pos; + int SFdirPtrSave; + + SFdirPtrSave = SFdirPtr; + SFdirPtr = (*(float *)nw) * SFdirEnd; + if (SFdirPtr == SFdirPtrSave) + return; + + SFdrawLists(SF_DO_SCROLL); + + n = 2; + while (SFdirPtr + n >= SFdirEnd) + n--; + + dir = &(SFdirs[SFdirPtr + n]); + + pos = dir->path - SFcurrentPath; + + if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) + { + pos -= strlen(SFstartDir); + if (pos < 0) + pos = 0; + } + + XawTextSetInsertionPoint(selFileField, pos); +} + + static void +SFpathAreaSelectedCallback( + Widget w, + XtPointer client_data UNUSED, + XtPointer pnew) +{ + int nw = (int)(long)pnew; + float f; + +#ifdef FEAT_GUI_NEXTAW + if (nw < 0) + { + if (nw > -SFpathScrollWidth) + nw = -1; + else + nw = -3; + } + else if (nw > 0) + { + if (nw < SFpathScrollWidth) + nw = 1; + else + nw = 3; + } +#endif + nw += SFdirPtr; + + if (nw > SFdirEnd - 3) + nw = SFdirEnd - 3; + + if (nw < 0) + nw = 0; + + f = ((double) nw) / SFdirEnd; + +#ifdef FEAT_GUI_NEXTAW + XawScrollbarSetThumb( + w, + f, + (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd)); +#else + vim_XawScrollbarSetThumb( + w, + f, + (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd), + (double)SFdirEnd); +#endif + + SFpathSliderMovedCallback(w, (XtPointer) NULL, (XtPointer)&f); +} + + static Boolean +SFworkProc(void) +{ + SFDir *dir; + SFEntry *entry; + + for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) + { + if (!(dir->nEntries)) + continue; + for (entry = &(dir->entries[dir->nEntries - 1]); + entry >= dir->entries; + entry--) + { + if (!(entry->statDone)) + { + (void)SFstatAndCheck(dir, entry); + return False; + } + } + } + + SFworkProcAdded = 0; + + return True; +} + +/***************** Dir.c */ + + static int +SFcompareEntries(const void *p, const void *q) +{ + return strcmp(((SFEntry *)p)->real, ((SFEntry *)q)->real); +} + + static int +SFgetDir( + SFDir *dir) +{ + SFEntry *result = NULL; + int Alloc = 0; + int i; + DIR *dirp; + struct dirent *dp; + char *str; + int len; + int maxChars; + stat_T statBuf; + + maxChars = strlen(dir->dir) - 1; + + dir->entries = NULL; + dir->nEntries = 0; + dir->nChars = 0; + + result = NULL; + i = 0; + + dirp = opendir("."); + if (!dirp) + return 1; + + (void)mch_stat(".", &statBuf); + dir->mtime = statBuf.st_mtime; + + while ((dp = readdir(dirp))) + { + /* Ignore "." and ".." */ + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; + if (i >= Alloc) + { + Alloc = 2 * (Alloc + 1); + result = (SFEntry *) XtRealloc((char *) result, + (unsigned) (Alloc * sizeof(SFEntry))); + } + result[i].statDone = 0; + str = dp->d_name; + len = strlen(str); + result[i].real = XtMalloc((unsigned) (len + 2)); + (void) strcat(strcpy(result[i].real, str), " "); + if (len > maxChars) + maxChars = len; + result[i].shown = result[i].real; + i++; + } + + qsort((char *) result, (size_t) i, sizeof(SFEntry), SFcompareEntries); + + dir->entries = result; + dir->nEntries = i; + dir->nChars = maxChars + 1; + + closedir(dirp); + + return 0; +} + +/***************** SFinternal.h */ + +#include <sys/param.h> +#include <X11/cursorfont.h> +#include <X11/Composite.h> +#include <X11/Shell.h> +#ifdef FEAT_GUI_NEXTAW +# include <X11/neXtaw/Form.h> +# include <X11/neXtaw/Command.h> +# include <X11/neXtaw/Label.h> +#else +#include <X11/Xaw/Form.h> +#include <X11/Xaw/Command.h> +#include <X11/Xaw/Label.h> +#endif + +static char *oneLineTextEditTranslations = "\ + <Key>Return: redraw-display()\n\ + Ctrl<Key>M: redraw-display()\n\ +"; + + static void +SFexposeList( + Widget w 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 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 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 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 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 */ |