Mercurial > vim
view src/gui_at_fs.c @ 27587:298b32b544ae v8.2.4320
patch 8.2.4320: Athena and Motif: when maximized scrollbar position is wrong
Commit: https://github.com/vim/vim/commit/28f1a51bde36e2770dd54c9e2ae69a26cafa5a64
Author: qsmodo <75080827+qsmodo@users.noreply.github.com>
Date: Mon Feb 7 15:57:50 2022 +0000
patch 8.2.4320: Athena and Motif: when maximized scrollbar position is wrong
Problem: Athena and Motif: when maximized scrollbar position is wrong.
Solution: Implement the scrollbar padding functions. (closes https://github.com/vim/vim/issues/9712)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Mon, 07 Feb 2022 17:00:05 +0100 |
parents | 51ddf6740ac6 |
children |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4 noet: */ /* * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Software Research Associates not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Software Research Associates * makes no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Author: Erik M. van der Poel * Software Research Associates, Inc., Tokyo, Japan * erik@sra.co.jp */ /* * Author's addresses: * erik@sra.co.jp * erik%sra.co.jp@uunet.uu.net * erik%sra.co.jp@mcvax.uucp * try junet instead of co.jp * Erik M. van der Poel * Software Research Associates, Inc. * 1-1-1 Hirakawa-cho, Chiyoda-ku * Tokyo 102 Japan. TEL +81-3-234-2692 */ /* * Heavely modified for Vim by Bram Moolenaar */ #include "vim.h" // Only include this when using the file browser #ifdef FEAT_BROWSE // Weird complication: for "make lint" Text.h doesn't combine with Xm.h #if defined(FEAT_GUI_MOTIF) && defined(FMT8BIT) # undef FMT8BIT #endif #ifndef FEAT_GUI_NEXTAW # include "gui_at_sb.h" #endif ////////////////// SFinternal.h #include <X11/Intrinsic.h> #include <X11/StringDefs.h> #include <X11/Xos.h> #ifdef FEAT_GUI_NEXTAW # include <X11/neXtaw/Text.h> # include <X11/neXtaw/AsciiText.h> # include <X11/neXtaw/Scrollbar.h> #else # include <X11/Xaw/Text.h> # include <X11/Xaw/AsciiText.h> #endif #define SEL_FILE_CANCEL -1 #define SEL_FILE_OK 0 #define SEL_FILE_NULL 1 #define SEL_FILE_TEXT 2 #define SF_DO_SCROLL 1 #define SF_DO_NOT_SCROLL 0 typedef struct { int statDone; char *real; char *shown; } SFEntry; typedef struct { char *dir; char *path; SFEntry *entries; int nEntries; int vOrigin; int nChars; int hOrigin; int changed; int beginSelection; int endSelection; time_t mtime; } SFDir; static char SFstartDir[MAXPATHL], SFcurrentPath[MAXPATHL], SFcurrentDir[MAXPATHL]; static Widget selFile, selFileField, selFileForm, selFileHScroll, selFileHScrolls[3], selFileLists[3], selFileOK, selFileCancel, selFilePrompt, selFileVScrolls[3]; static Display *SFdisplay; static int SFcharWidth, SFcharAscent, SFcharHeight; static SFDir *SFdirs = NULL; static int SFdirEnd; static int SFdirPtr; static Pixel SFfore, SFback; static Atom SFwmDeleteWindow; static XSegment SFsegs[2], SFcompletionSegs[2]; static XawTextPosition SFtextPos; static int SFupperX, SFlowerY, SFupperY; static int SFtextX, SFtextYoffset; static int SFentryWidth, SFentryHeight; static int SFlineToTextH = 3; static int SFlineToTextV = 3; static int SFbesideText = 3; static int SFaboveAndBelowText = 2; static int SFcharsPerEntry = 15; static int SFlistSize = 10; static int SFcurrentInvert[3] = { -1, -1, -1 }; static int SFworkProcAdded = 0; static XtAppContext SFapp; static int SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth; #ifdef FEAT_XFONTSET static char SFtextBuffer[MAXPATHL*sizeof(wchar_t)]; #else static char SFtextBuffer[MAXPATHL]; #endif static int SFbuttonPressed = 0; static XtIntervalId SFdirModTimerId; static int (*SFfunc)(); static int SFstatus = SEL_FILE_NULL; ///////////////// 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, XtPointer np, XMotionEvent *event, Boolean *cont); 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, XtPointer np UNUSED, XEvent *event UNUSED, Boolean *cont UNUSED) { SFbuttonPressed = 1; } static void SFbuttonReleaseList( Widget w UNUSED, XtPointer np, XEvent *event UNUSED, Boolean *cont UNUSED) { long n = (long)np; 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, (XtPointer)(long)n, (XMotionEvent *)event, 0); } } 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(_(e_vim_selfile_cant_get_font_str), 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, XtPointer np, XEvent *event, Boolean *cont UNUSED) { long n = (long)np; 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, XtPointer np, XEvent *event UNUSED, Boolean *cont UNUSED) { long n = (long)np; if (SFcurrentInvert[n] != -1) { SFinvertEntry(n); SFcurrentInvert[n] = -1; } } static void SFmotionList( Widget w UNUSED, XtPointer np, XMotionEvent *event UNUSED, Boolean *cont UNUSED) { long n = (long)np; 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 *arg UNUSED) { 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(_(e_vim_selfile_cant_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(_(e_vim_selfile_cant_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