xref: /vim-8.2.3635/src/gui_at_fs.c (revision 12ee7ff0)
1 /* vi:set ts=8 sts=4 sw=4 noet: */
2 
3 /*
4  * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
5  *
6  * Permission to use, copy, modify, and distribute this software and its
7  * documentation for any purpose and without fee is hereby granted, provided
8  * that the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Software Research Associates not be used
11  * in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission.  Software Research Associates
13  * makes no representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
18  * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
19  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
21  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: Erik M. van der Poel
25  *	   Software Research Associates, Inc., Tokyo, Japan
26  *	   [email protected]
27  */
28 /*
29  * Author's addresses:
30  *	[email protected]
31  *	erik%[email protected]
32  *	erik%[email protected]
33  *	try junet instead of co.jp
34  *	Erik M. van der Poel
35  *	Software Research Associates, Inc.
36  *	1-1-1 Hirakawa-cho, Chiyoda-ku
37  *	Tokyo 102 Japan. TEL +81-3-234-2692
38  */
39 
40 /*
41  * Heavely modified for Vim by Bram Moolenaar
42  */
43 
44 #include "vim.h"
45 
46 /* Only include this when using the file browser */
47 
48 #ifdef FEAT_BROWSE
49 
50 /* Weird complication: for "make lint" Text.h doesn't combine with Xm.h */
51 #if defined(FEAT_GUI_MOTIF) && defined(FMT8BIT)
52 # undef FMT8BIT
53 #endif
54 
55 #ifndef FEAT_GUI_NEXTAW
56 # include "gui_at_sb.h"
57 #endif
58 
59 /***************** SFinternal.h */
60 
61 #include <X11/Intrinsic.h>
62 #include <X11/StringDefs.h>
63 #include <X11/Xos.h>
64 #ifdef FEAT_GUI_NEXTAW
65 # include <X11/neXtaw/Text.h>
66 # include <X11/neXtaw/AsciiText.h>
67 # include <X11/neXtaw/Scrollbar.h>
68 #else
69 # include <X11/Xaw/Text.h>
70 # include <X11/Xaw/AsciiText.h>
71 #endif
72 
73 #define SEL_FILE_CANCEL		-1
74 #define SEL_FILE_OK		0
75 #define SEL_FILE_NULL		1
76 #define SEL_FILE_TEXT		2
77 
78 #define SF_DO_SCROLL		1
79 #define SF_DO_NOT_SCROLL	0
80 
81 typedef struct
82 {
83     int		statDone;
84     char	*real;
85     char	*shown;
86 } SFEntry;
87 
88 typedef struct
89 {
90     char	*dir;
91     char	*path;
92     SFEntry	*entries;
93     int		nEntries;
94     int		vOrigin;
95     int		nChars;
96     int		hOrigin;
97     int		changed;
98     int		beginSelection;
99     int		endSelection;
100     time_t	mtime;
101 } SFDir;
102 
103 static char	SFstartDir[MAXPATHL],
104 		SFcurrentPath[MAXPATHL],
105 		SFcurrentDir[MAXPATHL];
106 
107 static Widget	selFile,
108 		selFileField,
109 		selFileForm,
110 		selFileHScroll,
111 		selFileHScrolls[3],
112 		selFileLists[3],
113 		selFileOK,
114 		selFileCancel,
115 		selFilePrompt,
116 		selFileVScrolls[3];
117 
118 static Display	*SFdisplay;
119 
120 static int	SFcharWidth, SFcharAscent, SFcharHeight;
121 
122 static SFDir	*SFdirs = NULL;
123 
124 static int	SFdirEnd;
125 static int	SFdirPtr;
126 
127 static Pixel	SFfore, SFback;
128 
129 static Atom	SFwmDeleteWindow;
130 
131 static XSegment SFsegs[2], SFcompletionSegs[2];
132 
133 static XawTextPosition SFtextPos;
134 
135 static int	SFupperX, SFlowerY, SFupperY;
136 
137 static int	SFtextX, SFtextYoffset;
138 
139 static int	SFentryWidth, SFentryHeight;
140 
141 static int	SFlineToTextH = 3;
142 static int	SFlineToTextV = 3;
143 
144 static int	SFbesideText = 3;
145 static int	SFaboveAndBelowText = 2;
146 
147 static int	SFcharsPerEntry = 15;
148 
149 static int	SFlistSize = 10;
150 
151 static int	SFcurrentInvert[3] = { -1, -1, -1 };
152 
153 static int	SFworkProcAdded = 0;
154 
155 static XtAppContext SFapp;
156 
157 static int	SFpathScrollWidth, SFvScrollHeight, SFhScrollWidth;
158 
159 #ifdef FEAT_XFONTSET
160 static char	SFtextBuffer[MAXPATHL*sizeof(wchar_t)];
161 #else
162 static char	SFtextBuffer[MAXPATHL];
163 #endif
164 
165 static int	SFbuttonPressed = 0;
166 
167 static XtIntervalId SFdirModTimerId;
168 
169 static int	(*SFfunc)();
170 
171 static int	SFstatus = SEL_FILE_NULL;
172 
173 /***************** forward declare static functions */
174 
175 static void SFsetText(char *path);
176 static void SFtextChanged(void);
177 static int SFgetDir(SFDir *dir);
178 static void SFdrawLists(int doScroll);
179 static void SFdrawList(int n, int doScroll);
180 static void SFclearList(int n, int doScroll);
181 static char SFstatChar(stat_T *statBuf);
182 static void SFmotionList(Widget w, int n, XMotionEvent *event);
183 static void SFvSliderMovedCallback(Widget w, int n, int nw);
184 static Boolean SFworkProc(void);
185 static int SFcompareEntries(const void *p, const void *q);
186 
187 /***************** xstat.h */
188 
189 #ifndef S_IXUSR
190 # define S_IXUSR 0100
191 #endif
192 #ifndef S_IXGRP
193 # define S_IXGRP 0010
194 #endif
195 #ifndef S_IXOTH
196 # define S_IXOTH 0001
197 #endif
198 
199 #define S_ISXXX(m) ((m) & (S_IXUSR | S_IXGRP | S_IXOTH))
200 
201 /***************** Path.c */
202 
203 #include <pwd.h>
204 
205 typedef struct
206 {
207     char	*name;
208     char	*dir;
209 } SFLogin;
210 
211 static int	SFdoNotTouchDirPtr = 0;
212 
213 static int	SFdoNotTouchVorigin = 0;
214 
215 static SFDir	SFrootDir, SFhomeDir;
216 
217 static SFLogin	*SFlogins;
218 
219 static int	SFtwiddle = 0;
220 
221     static int
222 SFchdir(char *path)
223 {
224     int		result;
225 
226     result = 0;
227 
228     if (strcmp(path, SFcurrentDir))
229     {
230 	result = mch_chdir(path);
231 	if (!result)
232 	    (void) strcpy(SFcurrentDir, path);
233     }
234 
235     return result;
236 }
237 
238     static void
239 SFfree(int i)
240 {
241     SFDir	*dir;
242     int		j;
243 
244     dir = &(SFdirs[i]);
245 
246     for (j = dir->nEntries - 1; j >= 0; j--)
247     {
248 	if (dir->entries[j].shown != dir->entries[j].real)
249 	    XtFree(dir->entries[j].shown);
250 	XtFree(dir->entries[j].real);
251     }
252 
253     XtFree((char *)dir->entries);
254     XtFree(dir->dir);
255 
256     dir->dir = NULL;
257 }
258 
259     static void
260 SFstrdup(char **s1, char *s2)
261 {
262     *s1 = strcpy(XtMalloc((unsigned)(strlen(s2) + 1)), s2);
263 }
264 
265     static void
266 SFunreadableDir(SFDir *dir)
267 {
268     char	*cannotOpen = _("<cannot open> ");
269 
270     dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry));
271     dir->entries[0].statDone = 1;
272     SFstrdup(&dir->entries[0].real, cannotOpen);
273     dir->entries[0].shown = dir->entries[0].real;
274     dir->nEntries = 1;
275     dir->nChars = strlen(cannotOpen);
276 }
277 
278     static void
279 SFreplaceText(SFDir *dir, char *str)
280 {
281     int	len;
282 
283     *(dir->path) = 0;
284     len = strlen(str);
285     if (str[len - 1] == '/')
286 	(void) strcat(SFcurrentPath, str);
287     else
288 	(void) strncat(SFcurrentPath, str, len - 1);
289     if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir)))
290 	SFsetText(SFcurrentPath);
291     else
292 	SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
293 
294     SFtextChanged();
295 }
296 
297     static void
298 SFexpand(char *str)
299 {
300     int		len;
301     int		cmp;
302     char	*name, *growing;
303     SFDir	*dir;
304     SFEntry	*entry, *max;
305 
306     len = strlen(str);
307 
308     dir = &(SFdirs[SFdirEnd - 1]);
309 
310     if (dir->beginSelection == -1)
311     {
312 	SFstrdup(&str, str);
313 	SFreplaceText(dir, str);
314 	XtFree(str);
315 	return;
316     }
317     else if (dir->beginSelection == dir->endSelection)
318     {
319 	SFreplaceText(dir, dir->entries[dir->beginSelection].shown);
320 	return;
321     }
322 
323     max = &(dir->entries[dir->endSelection + 1]);
324 
325     name = dir->entries[dir->beginSelection].shown;
326     SFstrdup(&growing, name);
327 
328     cmp = 0;
329     while (!cmp)
330     {
331 	entry = &(dir->entries[dir->beginSelection]);
332 	while (entry < max)
333 	{
334 	    if ((cmp = strncmp(growing, entry->shown, len)))
335 		break;
336 	    entry++;
337 	}
338 	len++;
339     }
340 
341     /*
342      * SFreplaceText() expects filename
343      */
344     growing[len - 2] = ' ';
345 
346     growing[len - 1] = 0;
347     SFreplaceText(dir, growing);
348     XtFree(growing);
349 }
350 
351     static int
352 SFfindFile(SFDir *dir, char *str)
353 {
354     int		i, last, max;
355     char	*name, save;
356     SFEntry	*entries;
357     int		len;
358     int		begin, end;
359     int		result;
360 
361     len = strlen(str);
362 
363     if (str[len - 1] == ' ')
364     {
365 	SFexpand(str);
366 	return 1;
367     }
368     else if (str[len - 1] == '/')
369 	len--;
370 
371     max = dir->nEntries;
372 
373     entries = dir->entries;
374 
375     i = 0;
376     while (i < max)
377     {
378 	name = entries[i].shown;
379 	last = strlen(name) - 1;
380 	save = name[last];
381 	name[last] = 0;
382 
383 	result = strncmp(str, name, len);
384 
385 	name[last] = save;
386 	if (result <= 0)
387 	    break;
388 	i++;
389     }
390     begin = i;
391     while (i < max)
392     {
393 	name = entries[i].shown;
394 	last = strlen(name) - 1;
395 	save = name[last];
396 	name[last] = 0;
397 
398 	result = strncmp(str, name, len);
399 
400 	name[last] = save;
401 	if (result)
402 	    break;
403 	i++;
404     }
405     end = i;
406 
407     if (begin != end)
408     {
409 	if ((dir->beginSelection != begin) || (dir->endSelection != end - 1))
410 	{
411 	    dir->changed = 1;
412 	    dir->beginSelection = begin;
413 	    if (str[strlen(str) - 1] == '/')
414 		dir->endSelection = begin;
415 	    else
416 		dir->endSelection = end - 1;
417 	}
418     }
419     else if (dir->beginSelection != -1)
420     {
421 	dir->changed = 1;
422 	dir->beginSelection = -1;
423 	dir->endSelection = -1;
424     }
425 
426     if (SFdoNotTouchVorigin
427 	    || ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize)))
428     {
429 	SFdoNotTouchVorigin = 0;
430 	return 0;
431     }
432 
433     i = begin - 1;
434     if (i > max - SFlistSize)
435 	i = max - SFlistSize;
436     if (i < 0)
437 	i = 0;
438 
439     if (dir->vOrigin != i)
440     {
441 	dir->vOrigin = i;
442 	dir->changed = 1;
443     }
444 
445     return 0;
446 }
447 
448     static void
449 SFunselect(void)
450 {
451     SFDir	*dir;
452 
453     dir = &(SFdirs[SFdirEnd - 1]);
454     if (dir->beginSelection != -1)
455 	dir->changed = 1;
456     dir->beginSelection = -1;
457     dir->endSelection = -1;
458 }
459 
460     static int
461 SFcompareLogins(const void *p, const void *q)
462 {
463     return strcmp(((SFLogin *)p)->name, ((SFLogin *)q)->name);
464 }
465 
466     static void
467 SFgetHomeDirs(void)
468 {
469     struct	passwd	*pw;
470     int		Alloc;
471     int		i;
472     SFEntry	*entries = NULL;
473     int		len;
474     int		maxChars;
475 
476     Alloc = 1;
477     i = 1;
478     entries = (SFEntry *)XtMalloc(sizeof(SFEntry));
479     SFlogins = (SFLogin *)XtMalloc(sizeof(SFLogin));
480     entries[0].real = XtMalloc(3);
481     (void) strcpy(entries[0].real, "~");
482     entries[0].shown = entries[0].real;
483     entries[0].statDone = 1;
484     SFlogins[0].name = "";
485     pw = getpwuid((int) getuid());
486     SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/");
487     maxChars = 0;
488 
489     (void) setpwent();
490 
491     while ((pw = getpwent()) && (*(pw->pw_name)))
492     {
493 	if (i >= Alloc)
494 	{
495 	    Alloc *= 2;
496 	    entries = (SFEntry *) XtRealloc((char *)entries,
497 					 (unsigned)(Alloc * sizeof(SFEntry)));
498 	    SFlogins = (SFLogin *) XtRealloc((char *)SFlogins,
499 					 (unsigned)(Alloc * sizeof(SFLogin)));
500 	}
501 	len = strlen(pw->pw_name);
502 	entries[i].real = XtMalloc((unsigned)(len + 3));
503 	(void) strcat(strcpy(entries[i].real, "~"), pw->pw_name);
504 	entries[i].shown = entries[i].real;
505 	entries[i].statDone = 1;
506 	if (len > maxChars)
507 	    maxChars = len;
508 	SFstrdup(&SFlogins[i].name, pw->pw_name);
509 	SFstrdup(&SFlogins[i].dir, pw->pw_dir);
510 	i++;
511     }
512 
513     SFhomeDir.dir		= XtMalloc(1);
514     SFhomeDir.dir[0]		= 0;
515     SFhomeDir.path		= SFcurrentPath;
516     SFhomeDir.entries		= entries;
517     SFhomeDir.nEntries		= i;
518     SFhomeDir.vOrigin		= 0;	/* :-) */
519     SFhomeDir.nChars		= maxChars + 2;
520     SFhomeDir.hOrigin		= 0;
521     SFhomeDir.changed		= 1;
522     SFhomeDir.beginSelection	= -1;
523     SFhomeDir.endSelection	= -1;
524 
525     qsort((char *)entries, (size_t)i, sizeof(SFEntry), SFcompareEntries);
526     qsort((char *)SFlogins, (size_t)i, sizeof(SFLogin), SFcompareLogins);
527 
528     for (i--; i >= 0; i--)
529 	(void)strcat(entries[i].real, "/");
530 }
531 
532     static int
533 SFfindHomeDir(char *begin, char *end)
534 {
535     char	save;
536     char	*theRest;
537     int	i;
538 
539     save = *end;
540     *end = 0;
541 
542     for (i = SFhomeDir.nEntries - 1; i >= 0; i--)
543     {
544 	if (!strcmp(SFhomeDir.entries[i].real, begin))
545 	{
546 	    *end = save;
547 	    SFstrdup(&theRest, end);
548 	    (void) strcat(strcat(strcpy(SFcurrentPath,
549 					SFlogins[i].dir), "/"), theRest);
550 	    XtFree(theRest);
551 	    SFsetText(SFcurrentPath);
552 	    SFtextChanged();
553 	    return 1;
554 	}
555     }
556 
557     *end = save;
558 
559     return 0;
560 }
561 
562     static void
563 SFupdatePath(void)
564 {
565     static int	Alloc;
566     static int	wasTwiddle = 0;
567     char	*begin, *end;
568     int		i, j;
569     int		prevChange;
570     int		SFdirPtrSave, SFdirEndSave;
571     SFDir	*dir;
572 
573     if (!SFdirs)
574     {
575 	SFdirs = (SFDir *) XtMalloc((Alloc = 10) * sizeof(SFDir));
576 	dir = &(SFdirs[0]);
577 	SFstrdup(&dir->dir, "/");
578 	(void) SFchdir("/");
579 	(void) SFgetDir(dir);
580 	for (j = 1; j < Alloc; j++)
581 	    SFdirs[j].dir = NULL;
582 	dir->path = SFcurrentPath + 1;
583 	dir->vOrigin = 0;
584 	dir->hOrigin = 0;
585 	dir->changed = 1;
586 	dir->beginSelection = -1;
587 	dir->endSelection = -1;
588 	SFhomeDir.dir = NULL;
589     }
590 
591     SFdirEndSave = SFdirEnd;
592     SFdirEnd = 1;
593 
594     SFdirPtrSave = SFdirPtr;
595     SFdirPtr = 0;
596 
597     begin = NULL;
598 
599     if (SFcurrentPath[0] == '~')
600     {
601 	if (!SFtwiddle)
602 	{
603 	    SFtwiddle = 1;
604 	    dir = &(SFdirs[0]);
605 	    SFrootDir = *dir;
606 	    if (!SFhomeDir.dir)
607 		SFgetHomeDirs();
608 	    *dir = SFhomeDir;
609 	    dir->changed = 1;
610 	}
611 	end = SFcurrentPath;
612 	SFdoNotTouchDirPtr = 1;
613 	wasTwiddle = 1;
614     }
615     else
616     {
617 	if (SFtwiddle)
618 	{
619 	    SFtwiddle = 0;
620 	    dir = &(SFdirs[0]);
621 	    *dir = SFrootDir;
622 	    dir->changed = 1;
623 	}
624 	end = SFcurrentPath + 1;
625     }
626 
627     i = 0;
628 
629     prevChange = 0;
630 
631     while (*end)
632     {
633 	while (*end++ == '/')
634 	    ;
635 	end--;
636 	begin = end;
637 	while ((*end) && (*end++ != '/'))
638 	    ;
639 	if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/'))
640 	{
641 	    SFdirPtr = i - 1;
642 	    if (SFdirPtr < 0)
643 		SFdirPtr = 0;
644 	}
645 	if (*begin)
646 	{
647 	    if (*(end - 1) == '/')
648 	    {
649 		char save = *end;
650 
651 		if (SFtwiddle)
652 		{
653 		    if (SFfindHomeDir(begin, end))
654 			return;
655 		}
656 		*end = 0;
657 		i++;
658 		SFdirEnd++;
659 		if (i >= Alloc)
660 		{
661 		    SFdirs = (SFDir *) XtRealloc((char *) SFdirs,
662 				    (unsigned)((Alloc *= 2) * sizeof(SFDir)));
663 		    for (j = Alloc / 2; j < Alloc; j++)
664 			SFdirs[j].dir = NULL;
665 		}
666 		dir = &(SFdirs[i]);
667 		if ((!(dir->dir)) || prevChange || strcmp(dir->dir, begin))
668 		{
669 		    if (dir->dir)
670 			SFfree(i);
671 		    prevChange = 1;
672 		    SFstrdup(&dir->dir, begin);
673 		    dir->path = end;
674 		    dir->vOrigin = 0;
675 		    dir->hOrigin = 0;
676 		    dir->changed = 1;
677 		    dir->beginSelection = -1;
678 		    dir->endSelection = -1;
679 		    (void)SFfindFile(dir - 1, begin);
680 		    if (SFchdir(SFcurrentPath) || SFgetDir(dir))
681 		    {
682 			SFunreadableDir(dir);
683 			break;
684 		    }
685 		}
686 		*end = save;
687 		if (!save)
688 		    SFunselect();
689 	    }
690 	    else
691 	    {
692 		if (SFfindFile(&(SFdirs[SFdirEnd-1]), begin))
693 		    return;
694 	    }
695 	}
696 	else
697 	    SFunselect();
698     }
699 
700     if ((end == SFcurrentPath + 1) && (!SFtwiddle))
701 	SFunselect();
702 
703     for (i = SFdirEnd; i < Alloc; i++)
704 	if (SFdirs[i].dir)
705 	    SFfree(i);
706 
707     if (SFdoNotTouchDirPtr)
708     {
709 	if (wasTwiddle)
710 	{
711 	    wasTwiddle = 0;
712 	    SFdirPtr = SFdirEnd - 2;
713 	    if (SFdirPtr < 0)
714 		SFdirPtr = 0;
715 	}
716 	else
717 	    SFdirPtr = SFdirPtrSave;
718 	SFdoNotTouchDirPtr = 0;
719     }
720 
721     if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave))
722     {
723 #ifdef FEAT_GUI_NEXTAW
724 	XawScrollbarSetThumb( selFileHScroll,
725 		(float) (((double) SFdirPtr) / SFdirEnd),
726 		(float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
727 			 SFdirEnd));
728 #else
729 	vim_XawScrollbarSetThumb( selFileHScroll,
730 		(float) (((double) SFdirPtr) / SFdirEnd),
731 		(float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
732 			 SFdirEnd),
733 		(double)SFdirEnd);
734 #endif
735     }
736 
737     if (SFdirPtr != SFdirPtrSave)
738 	SFdrawLists(SF_DO_SCROLL);
739     else
740 	for (i = 0; i < 3; i++)
741 	{
742 	    if (SFdirPtr + i < SFdirEnd)
743 	    {
744 		if (SFdirs[SFdirPtr + i].changed)
745 		{
746 		    SFdirs[SFdirPtr + i].changed = 0;
747 		    SFdrawList(i, SF_DO_SCROLL);
748 		}
749 	    }
750 	    else
751 		SFclearList(i, SF_DO_SCROLL);
752 	}
753 }
754 
755 #ifdef XtNinternational
756     static int
757 WcsLen(wchar_t *p)
758 {
759     int i = 0;
760     while (*p++ != 0)
761 	i++;
762     return i;
763 }
764 #endif
765 
766     static void
767 SFsetText(char *path)
768 {
769     XawTextBlock	text;
770 
771     text.firstPos = 0;
772     text.length = strlen(path);
773     text.ptr = path;
774     text.format = FMT8BIT;
775 
776 #ifdef XtNinternational
777     if ((unsigned long)_XawTextFormat((TextWidget)selFileField) == XawFmtWide)
778     {
779 	XawTextReplace(selFileField, (XawTextPosition)0,
780 				    (XawTextPosition)WcsLen((wchar_t *)&SFtextBuffer[0]), &text);
781 	XawTextSetInsertionPoint(selFileField,
782 					   (XawTextPosition)WcsLen((wchar_t *)&SFtextBuffer[0]));
783     }
784     else
785     {
786 	XawTextReplace(selFileField, (XawTextPosition)0,
787 				    (XawTextPosition)strlen(SFtextBuffer), &text);
788 	XawTextSetInsertionPoint(selFileField,
789 					   (XawTextPosition)strlen(SFtextBuffer));
790     }
791 #else
792     XawTextReplace(selFileField, (XawTextPosition)0,
793 				(XawTextPosition)strlen(SFtextBuffer), &text);
794     XawTextSetInsertionPoint(selFileField,
795 				       (XawTextPosition)strlen(SFtextBuffer));
796 #endif
797 }
798 
799     static void
800 SFbuttonPressList(
801     Widget		w UNUSED,
802     int			n UNUSED,
803     XButtonPressedEvent	*event UNUSED)
804 {
805     SFbuttonPressed = 1;
806 }
807 
808     static void
809 SFbuttonReleaseList(
810     Widget		 w,
811     int			 n,
812     XButtonReleasedEvent *event)
813 {
814     SFDir	*dir;
815 
816     SFbuttonPressed = 0;
817 
818     if (SFcurrentInvert[n] != -1)
819     {
820 	if (n < 2)
821 	    SFdoNotTouchDirPtr = 1;
822 	SFdoNotTouchVorigin = 1;
823 	dir = &(SFdirs[SFdirPtr + n]);
824 	SFreplaceText(dir,
825 		       dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown);
826 	SFmotionList(w, n, (XMotionEvent *) event);
827     }
828 }
829 
830     static int
831 SFcheckDir(int n, SFDir *dir)
832 {
833     stat_T	statBuf;
834     int		i;
835 
836     if ((!mch_stat(".", &statBuf)) && (statBuf.st_mtime != dir->mtime))
837     {
838 	/*
839 	 * If the pointer is currently in the window that we are about
840 	 * to update, we must warp it to prevent the user from
841 	 * accidentally selecting the wrong file.
842 	 */
843 	if (SFcurrentInvert[n] != -1)
844 	{
845 	    XWarpPointer(
846 		    SFdisplay,
847 		    None,
848 		    XtWindow(selFileLists[n]),
849 		    0,
850 		    0,
851 		    0,
852 		    0,
853 		    0,
854 		    0);
855 	}
856 
857 	for (i = dir->nEntries - 1; i >= 0; i--)
858 	{
859 	    if (dir->entries[i].shown != dir->entries[i].real)
860 		XtFree(dir->entries[i].shown);
861 	    XtFree(dir->entries[i].real);
862 	}
863 	XtFree((char *) dir->entries);
864 	if (SFgetDir(dir))
865 	    SFunreadableDir(dir);
866 	if (dir->vOrigin > dir->nEntries - SFlistSize)
867 	    dir->vOrigin = dir->nEntries - SFlistSize;
868 	if (dir->vOrigin < 0)
869 	    dir->vOrigin = 0;
870 	if (dir->hOrigin > dir->nChars - SFcharsPerEntry)
871 	    dir->hOrigin = dir->nChars - SFcharsPerEntry;
872 	if (dir->hOrigin < 0)
873 	    dir->hOrigin = 0;
874 	dir->beginSelection = -1;
875 	dir->endSelection = -1;
876 	SFdoNotTouchVorigin = 1;
877 	if ((dir + 1)->dir)
878 	    (void) SFfindFile(dir, (dir + 1)->dir);
879 	else
880 	    (void) SFfindFile(dir, dir->path);
881 
882 	if (!SFworkProcAdded)
883 	{
884 	    (void) XtAppAddWorkProc(SFapp, (XtWorkProc)SFworkProc, NULL);
885 	    SFworkProcAdded = 1;
886 	}
887 	return 1;
888     }
889     return 0;
890 }
891 
892     static int
893 SFcheckFiles(SFDir *dir)
894 {
895     int		from, to;
896     int		result;
897     char	oldc, newc;
898     int		i;
899     char	*str;
900     int		last;
901     stat_T	statBuf;
902 
903     result = 0;
904 
905     from = dir->vOrigin;
906     to = dir->vOrigin + SFlistSize;
907     if (to > dir->nEntries)
908 	to = dir->nEntries;
909 
910     for (i = from; i < to; i++)
911     {
912 	str = dir->entries[i].real;
913 	last = strlen(str) - 1;
914 	oldc = str[last];
915 	str[last] = 0;
916 	if (mch_stat(str, &statBuf))
917 	    newc = ' ';
918 	else
919 	    newc = SFstatChar(&statBuf);
920 	str[last] = newc;
921 	if (newc != oldc)
922 	    result = 1;
923     }
924 
925     return result;
926 }
927 
928     static void
929 SFdirModTimer(XtPointer cl UNUSED, XtIntervalId *id UNUSED)
930 {
931     static int		n = -1;
932     static int		f = 0;
933     char		save;
934     SFDir		*dir;
935 
936     if ((!SFtwiddle) && (SFdirPtr < SFdirEnd))
937     {
938 	n++;
939 	if ((n > 2) || (SFdirPtr + n >= SFdirEnd))
940 	{
941 	    n = 0;
942 	    f++;
943 	    if ((f > 2) || (SFdirPtr + f >= SFdirEnd))
944 		f = 0;
945 	}
946 	dir = &(SFdirs[SFdirPtr + n]);
947 	save = *(dir->path);
948 	*(dir->path) = 0;
949 	if (SFchdir(SFcurrentPath))
950 	{
951 	    *(dir->path) = save;
952 
953 	    /*
954 	     * force a re-read
955 	     */
956 	    *(dir->dir) = 0;
957 
958 	    SFupdatePath();
959 	}
960 	else
961 	{
962 	    *(dir->path) = save;
963 	    if (SFcheckDir(n, dir) || ((f == n) && SFcheckFiles(dir)))
964 		SFdrawList(n, SF_DO_SCROLL);
965 	}
966     }
967 
968     SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
969 	    SFdirModTimer, (XtPointer) NULL);
970 }
971 
972 /* Return a single character describing what kind of file STATBUF is.  */
973 
974     static char
975 SFstatChar(stat_T *statBuf)
976 {
977     if (S_ISDIR (statBuf->st_mode))
978 	return '/';
979     if (S_ISREG (statBuf->st_mode))
980 	return S_ISXXX (statBuf->st_mode) ? '*' : ' ';
981 #ifdef S_ISSOCK
982     if (S_ISSOCK (statBuf->st_mode))
983 	return '=';
984 #endif /* S_ISSOCK */
985     return ' ';
986 }
987 
988 /***************** Draw.c */
989 
990 #ifdef FEAT_GUI_NEXTAW
991 # include <X11/neXtaw/Cardinals.h>
992 #else
993 # include <X11/Xaw/Cardinals.h>
994 #endif
995 
996 #ifdef FEAT_XFONTSET
997 # define SF_DEFAULT_FONT "-misc-fixed-medium-r-normal--14-*"
998 #else
999 # define SF_DEFAULT_FONT "9x15"
1000 #endif
1001 
1002 #ifdef ABS
1003 # undef ABS
1004 #endif
1005 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
1006 
1007 typedef struct
1008 {
1009     char *fontname;
1010 } TextData;
1011 
1012 static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;
1013 
1014 static XtResource textResources[] =
1015 {
1016 #ifdef FEAT_XFONTSET
1017 	{XtNfontSet, XtCFontSet, XtRString, sizeof (char *),
1018 		XtOffsetOf(TextData, fontname), XtRString, SF_DEFAULT_FONT},
1019 #else
1020 	{XtNfont, XtCFont, XtRString, sizeof (char *),
1021 		XtOffsetOf(TextData, fontname), XtRString, SF_DEFAULT_FONT},
1022 #endif
1023 };
1024 
1025 #ifdef FEAT_XFONTSET
1026 static XFontSet SFfont;
1027 #else
1028 static XFontStruct *SFfont;
1029 #endif
1030 
1031 static int SFcurrentListY;
1032 
1033 static XtIntervalId SFscrollTimerId;
1034 
1035     static void
1036 SFinitFont(void)
1037 {
1038     TextData	*data;
1039 #ifdef FEAT_XFONTSET
1040     XFontSetExtents *extents;
1041     char **missing, *def_str;
1042     int  num_missing;
1043 #endif
1044 
1045     data = XtNew(TextData);
1046 
1047     XtGetApplicationResources(selFileForm, (XtPointer) data, textResources,
1048 	    XtNumber(textResources), (Arg *) NULL, ZERO);
1049 
1050 #ifdef FEAT_XFONTSET
1051     SFfont = XCreateFontSet(SFdisplay, data->fontname,
1052 			    &missing, &num_missing, &def_str);
1053 #else
1054     SFfont = XLoadQueryFont(SFdisplay, data->fontname);
1055 #endif
1056     if (!SFfont)
1057     {
1058 #ifdef FEAT_XFONTSET
1059 	SFfont = XCreateFontSet(SFdisplay, SF_DEFAULT_FONT,
1060 					    &missing, &num_missing, &def_str);
1061 #else
1062 	SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT);
1063 #endif
1064 	if (!SFfont)
1065 	{
1066 	    semsg(_("E616: vim_SelFile: can't get font %s"), SF_DEFAULT_FONT);
1067 	    SFstatus = SEL_FILE_CANCEL;
1068 	    return;
1069 	}
1070     }
1071 
1072 #ifdef FEAT_XFONTSET
1073     extents = XExtentsOfFontSet(SFfont);
1074     SFcharWidth = extents->max_logical_extent.width;
1075     SFcharAscent = -extents->max_logical_extent.y;
1076     SFcharHeight = extents->max_logical_extent.height;
1077 #else
1078     SFcharWidth = (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2;
1079     SFcharAscent = SFfont->max_bounds.ascent;
1080     SFcharHeight = SFcharAscent + SFfont->max_bounds.descent;
1081 #endif
1082 }
1083 
1084     static void
1085 SFcreateGC(void)
1086 {
1087     XGCValues	gcValues;
1088     XRectangle	rectangles[1];
1089 
1090     gcValues.foreground = SFfore;
1091 
1092     SFlineGC = XtGetGC(
1093 	    selFileLists[0],
1094 	    (XtGCMask)GCForeground,
1095 	    &gcValues);
1096 
1097     SFscrollGC = XtGetGC(
1098 	    selFileLists[0],
1099 	    (XtGCMask)0,
1100 	    &gcValues);
1101 
1102     gcValues.function = GXxor;
1103     gcValues.foreground = SFfore ^ SFback;
1104     gcValues.background = SFfore ^ SFback;
1105 
1106     SFinvertGC = XtGetGC(
1107 	    selFileLists[0],
1108 	    (XtGCMask)GCFunction | GCForeground | GCBackground,
1109 	    &gcValues);
1110 
1111     gcValues.foreground = SFfore;
1112     gcValues.background = SFback;
1113 #ifndef FEAT_XFONTSET
1114     gcValues.font = SFfont->fid;
1115 #endif
1116 
1117     SFtextGC = XCreateGC(
1118 	    SFdisplay,
1119 	    XtWindow(selFileLists[0]),
1120 #ifdef FEAT_XFONTSET
1121 	    (unsigned long)GCForeground | GCBackground,
1122 #else
1123 	    (unsigned long)GCForeground | GCBackground | GCFont,
1124 #endif
1125 	    &gcValues);
1126 
1127     rectangles[0].x = SFlineToTextH + SFbesideText;
1128     rectangles[0].y = 0;
1129     rectangles[0].width = SFcharsPerEntry * SFcharWidth;
1130     rectangles[0].height = SFupperY + 1;
1131 
1132     XSetClipRectangles(
1133 	    SFdisplay,
1134 	    SFtextGC,
1135 	    0,
1136 	    0,
1137 	    rectangles,
1138 	    1,
1139 	    Unsorted);
1140 }
1141 
1142     static void
1143 SFclearList(int n, int doScroll)
1144 {
1145     SFDir	*dir;
1146 
1147     SFcurrentInvert[n] = -1;
1148 
1149     XClearWindow(SFdisplay, XtWindow(selFileLists[n]));
1150 
1151     XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs, 2);
1152 
1153     if (doScroll)
1154     {
1155 	dir = &(SFdirs[SFdirPtr + n]);
1156 
1157 	if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars)
1158 	{
1159 #ifdef FEAT_GUI_NEXTAW
1160 	    XawScrollbarSetThumb(
1161 		    selFileVScrolls[n],
1162 		    (float) (((double) dir->vOrigin) /
1163 			     dir->nEntries),
1164 		    (float) (((double) ((dir->nEntries < SFlistSize)
1165 					? dir->nEntries : SFlistSize)) /
1166 			     dir->nEntries));
1167 #else
1168 	    vim_XawScrollbarSetThumb(
1169 		    selFileVScrolls[n],
1170 		    (float) (((double) dir->vOrigin) /
1171 			     dir->nEntries),
1172 		    (float) (((double) ((dir->nEntries < SFlistSize)
1173 					? dir->nEntries : SFlistSize)) /
1174 			     dir->nEntries),
1175 		    (double)dir->nEntries);
1176 #endif
1177 
1178 #ifdef FEAT_GUI_NEXTAW
1179 	    XawScrollbarSetThumb(
1180 		    selFileHScrolls[n],
1181 		    (float) (((double) dir->hOrigin) / dir->nChars),
1182 		    (float) (((double) ((dir->nChars <
1183 					 SFcharsPerEntry) ? dir->nChars :
1184 					SFcharsPerEntry)) / dir->nChars));
1185 #else
1186 	    vim_XawScrollbarSetThumb(
1187 		    selFileHScrolls[n],
1188 		    (float) (((double) dir->hOrigin) / dir->nChars),
1189 		    (float) (((double) ((dir->nChars <
1190 					 SFcharsPerEntry) ? dir->nChars :
1191 					SFcharsPerEntry)) / dir->nChars),
1192 		    (double)dir->nChars);
1193 #endif
1194 	}
1195 	else
1196 	{
1197 #ifdef FEAT_GUI_NEXTAW
1198 	    XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
1199 		    (float) 1.0);
1200 #else
1201 	    vim_XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
1202 		    (float) 1.0, 1.0);
1203 #endif
1204 #ifdef FEAT_GUI_NEXTAW
1205 	    XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
1206 		    (float) 1.0);
1207 #else
1208 	    vim_XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
1209 		    (float) 1.0, 1.0);
1210 #endif
1211 	}
1212     }
1213 }
1214 
1215     static void
1216 SFdeleteEntry(SFDir *dir, SFEntry *entry)
1217 {
1218     SFEntry	*e;
1219     SFEntry	*end;
1220     int		n;
1221     int		idx;
1222 
1223     idx = entry - dir->entries;
1224 
1225     if (idx < dir->beginSelection)
1226 	dir->beginSelection--;
1227     if (idx <= dir->endSelection)
1228 	dir->endSelection--;
1229     if (dir->beginSelection > dir->endSelection)
1230 	dir->beginSelection = dir->endSelection = -1;
1231 
1232     if (idx < dir->vOrigin)
1233 	dir->vOrigin--;
1234 
1235     XtFree(entry->real);
1236 
1237     end = &(dir->entries[dir->nEntries - 1]);
1238 
1239     for (e = entry; e < end; e++)
1240 	*e = *(e + 1);
1241 
1242     if (!(--dir->nEntries))
1243 	return;
1244 
1245     n = dir - &(SFdirs[SFdirPtr]);
1246     if ((n < 0) || (n > 2))
1247 	return;
1248 
1249 #ifdef FEAT_GUI_NEXTAW
1250     XawScrollbarSetThumb(
1251 	    selFileVScrolls[n],
1252 	    (float) (((double) dir->vOrigin) / dir->nEntries),
1253 	    (float) (((double) ((dir->nEntries < SFlistSize) ?
1254 				dir->nEntries : SFlistSize)) / dir->nEntries));
1255 #else
1256     vim_XawScrollbarSetThumb(
1257 	    selFileVScrolls[n],
1258 	    (float) (((double) dir->vOrigin) / dir->nEntries),
1259 	    (float) (((double) ((dir->nEntries < SFlistSize) ?
1260 				dir->nEntries : SFlistSize)) / dir->nEntries),
1261 	    (double)dir->nEntries);
1262 #endif
1263 }
1264 
1265     static void
1266 SFwriteStatChar(
1267     char	*name,
1268     int		last,
1269     stat_T	*statBuf)
1270 {
1271     name[last] = SFstatChar(statBuf);
1272 }
1273 
1274     static int
1275 SFstatAndCheck(SFDir *dir, SFEntry *entry)
1276 {
1277     stat_T	statBuf;
1278     char	save;
1279     int		last;
1280 
1281     /*
1282      * must be restored before returning
1283      */
1284     save = *(dir->path);
1285     *(dir->path) = 0;
1286 
1287     if (!SFchdir(SFcurrentPath))
1288     {
1289 	last = strlen(entry->real) - 1;
1290 	entry->real[last] = 0;
1291 	entry->statDone = 1;
1292 	if ((!mch_stat(entry->real, &statBuf))
1293 #ifdef S_IFLNK
1294 		|| (!mch_lstat(entry->real, &statBuf))
1295 #endif
1296 	   )
1297 	{
1298 	    if (SFfunc)
1299 	    {
1300 		char *shown;
1301 
1302 		shown = NULL;
1303 		if (SFfunc(entry->real, &shown, &statBuf))
1304 		{
1305 		    if (shown)
1306 		    {
1307 			int len;
1308 
1309 			len = strlen(shown);
1310 			entry->shown = XtMalloc((unsigned)(len + 2));
1311 			(void) strcpy(entry->shown, shown);
1312 			SFwriteStatChar(entry->shown, len, &statBuf);
1313 			entry->shown[len + 1] = 0;
1314 		    }
1315 		}
1316 		else
1317 		{
1318 		    SFdeleteEntry(dir, entry);
1319 
1320 		    *(dir->path) = save;
1321 		    return 1;
1322 		}
1323 	    }
1324 	    SFwriteStatChar(entry->real, last, &statBuf);
1325 	}
1326 	else
1327 	    entry->real[last] = ' ';
1328     }
1329 
1330     *(dir->path) = save;
1331     return 0;
1332 }
1333 
1334 
1335     static void
1336 SFdrawStrings(
1337     Window	w,
1338     SFDir	*dir,
1339     int		from,
1340     int		to)
1341 {
1342     int		i;
1343     SFEntry	*entry;
1344     int		x;
1345 
1346     x = SFtextX - dir->hOrigin * SFcharWidth;
1347 
1348     if (dir->vOrigin + to >= dir->nEntries)
1349 	to = dir->nEntries - dir->vOrigin - 1;
1350     for (i = from; i <= to; i++)
1351     {
1352 	entry = &(dir->entries[dir->vOrigin + i]);
1353 	if (!(entry->statDone))
1354 	{
1355 	    if (SFstatAndCheck(dir, entry))
1356 	    {
1357 		if (dir->vOrigin + to >= dir->nEntries)
1358 		    to = dir->nEntries - dir->vOrigin - 1;
1359 		i--;
1360 		continue;
1361 	    }
1362 	}
1363 #ifdef FEAT_XFONTSET
1364 	XmbDrawImageString(
1365 		SFdisplay,
1366 		w,
1367 		SFfont,
1368 		SFtextGC,
1369 		x,
1370 		SFtextYoffset + i * SFentryHeight,
1371 		entry->shown,
1372 		strlen(entry->shown));
1373 #else
1374 	XDrawImageString(
1375 		SFdisplay,
1376 		w,
1377 		SFtextGC,
1378 		x,
1379 		SFtextYoffset + i * SFentryHeight,
1380 		entry->shown,
1381 		strlen(entry->shown));
1382 #endif
1383 	if (dir->vOrigin + i == dir->beginSelection)
1384 	{
1385 	    XDrawLine(
1386 		    SFdisplay,
1387 		    w,
1388 		    SFlineGC,
1389 		    SFlineToTextH + 1,
1390 		    SFlowerY + i * SFentryHeight,
1391 		    SFlineToTextH + SFentryWidth - 2,
1392 		    SFlowerY + i * SFentryHeight);
1393 	}
1394 	if ((dir->vOrigin + i >= dir->beginSelection) &&
1395 		(dir->vOrigin + i <= dir->endSelection))
1396 	{
1397 	    SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 =
1398 		SFlowerY + i * SFentryHeight;
1399 	    SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 =
1400 		SFlowerY + (i + 1) * SFentryHeight - 1;
1401 	    XDrawSegments(
1402 		    SFdisplay,
1403 		    w,
1404 		    SFlineGC,
1405 		    SFcompletionSegs,
1406 		    2);
1407 	}
1408 	if (dir->vOrigin + i == dir->endSelection)
1409 	{
1410 	    XDrawLine(
1411 		    SFdisplay,
1412 		    w,
1413 		    SFlineGC,
1414 		    SFlineToTextH + 1,
1415 		    SFlowerY + (i + 1) * SFentryHeight - 1,
1416 		    SFlineToTextH + SFentryWidth - 2,
1417 		    SFlowerY + (i + 1) * SFentryHeight - 1);
1418 	}
1419     }
1420 }
1421 
1422     static void
1423 SFdrawList(int n, int doScroll)
1424 {
1425     SFDir	*dir;
1426     Window	w;
1427 
1428     SFclearList(n, doScroll);
1429 
1430     if (SFdirPtr + n < SFdirEnd)
1431     {
1432 	dir = &(SFdirs[SFdirPtr + n]);
1433 	w = XtWindow(selFileLists[n]);
1434 #ifdef FEAT_XFONTSET
1435 	XmbDrawImageString(
1436 		SFdisplay,
1437 		w,
1438 		SFfont,
1439 		SFtextGC,
1440 		SFtextX - dir->hOrigin * SFcharWidth,
1441 		SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
1442 		dir->dir,
1443 		strlen(dir->dir));
1444 #else
1445 	XDrawImageString(
1446 		SFdisplay,
1447 		w,
1448 		SFtextGC,
1449 		SFtextX - dir->hOrigin * SFcharWidth,
1450 		SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
1451 		dir->dir,
1452 		strlen(dir->dir));
1453 #endif
1454 	SFdrawStrings(w, dir, 0, SFlistSize - 1);
1455     }
1456 }
1457 
1458     static void
1459 SFdrawLists(int doScroll)
1460 {
1461     int	i;
1462 
1463     for (i = 0; i < 3; i++)
1464 	SFdrawList(i, doScroll);
1465 }
1466 
1467     static void
1468 SFinvertEntry(int n)
1469 {
1470     XFillRectangle(
1471 	    SFdisplay,
1472 	    XtWindow(selFileLists[n]),
1473 	    SFinvertGC,
1474 	    SFlineToTextH,
1475 	    SFcurrentInvert[n] * SFentryHeight + SFlowerY,
1476 	    SFentryWidth,
1477 	    SFentryHeight);
1478 }
1479 
1480     static unsigned long
1481 SFscrollTimerInterval(void)
1482 {
1483     static int	maxVal = 200;
1484     static int	varyDist = 50;
1485     static int	minDist = 50;
1486     int		t;
1487     int		dist;
1488 
1489     if (SFcurrentListY < SFlowerY)
1490 	dist = SFlowerY - SFcurrentListY;
1491     else if (SFcurrentListY > SFupperY)
1492 	dist = SFcurrentListY - SFupperY;
1493     else
1494 	return (unsigned long) 1;
1495 
1496     t = maxVal - ((maxVal / varyDist) * (dist - minDist));
1497 
1498     if (t < 1)
1499 	t = 1;
1500 
1501     if (t > maxVal)
1502 	t = maxVal;
1503 
1504     return (unsigned long)t;
1505 }
1506 
1507     static void
1508 SFscrollTimer(XtPointer p, XtIntervalId *id UNUSED)
1509 {
1510     SFDir	*dir;
1511     int		save;
1512     int		n;
1513 
1514     n = (long)p;
1515 
1516     dir = &(SFdirs[SFdirPtr + n]);
1517     save = dir->vOrigin;
1518 
1519     if (SFcurrentListY < SFlowerY)
1520     {
1521 	if (dir->vOrigin > 0)
1522 	    SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin - 1);
1523     }
1524     else if (SFcurrentListY > SFupperY)
1525     {
1526 	if (dir->vOrigin < dir->nEntries - SFlistSize)
1527 	    SFvSliderMovedCallback(selFileVScrolls[n], n, dir->vOrigin + 1);
1528     }
1529 
1530     if (dir->vOrigin != save)
1531     {
1532 	if (dir->nEntries)
1533 	{
1534 #ifdef FEAT_GUI_NEXTAW
1535 	    XawScrollbarSetThumb(
1536 		    selFileVScrolls[n],
1537 		    (float) (((double) dir->vOrigin) / dir->nEntries),
1538 		    (float) (((double) ((dir->nEntries < SFlistSize) ?
1539 				dir->nEntries : SFlistSize)) / dir->nEntries));
1540 #else
1541 	    vim_XawScrollbarSetThumb(
1542 		    selFileVScrolls[n],
1543 		    (float) (((double) dir->vOrigin) / dir->nEntries),
1544 		    (float) (((double) ((dir->nEntries < SFlistSize) ?
1545 				dir->nEntries : SFlistSize)) / dir->nEntries),
1546 		    (double)dir->nEntries);
1547 #endif
1548 	}
1549     }
1550 
1551     if (SFbuttonPressed)
1552 	SFscrollTimerId = XtAppAddTimeOut(SFapp,
1553 		       SFscrollTimerInterval(), SFscrollTimer,
1554 		       (XtPointer)(long_u)n);
1555 }
1556 
1557     static int
1558 SFnewInvertEntry(int n, XMotionEvent *event)
1559 {
1560     int			x, y;
1561     int			nw;
1562     static int		SFscrollTimerAdded = 0;
1563 
1564     x = event->x;
1565     y = event->y;
1566 
1567     if (SFdirPtr + n >= SFdirEnd)
1568 	return -1;
1569 
1570     if ((x >= 0) && (x <= SFupperX) && (y >= SFlowerY) && (y <= SFupperY))
1571     {
1572 	SFDir *dir = &(SFdirs[SFdirPtr + n]);
1573 
1574 	if (SFscrollTimerAdded)
1575 	{
1576 	    SFscrollTimerAdded = 0;
1577 	    XtRemoveTimeOut(SFscrollTimerId);
1578 	}
1579 
1580 	nw = (y - SFlowerY) / SFentryHeight;
1581 	if (dir->vOrigin + nw >= dir->nEntries)
1582 	    return -1;
1583 	return nw;
1584     }
1585     else
1586     {
1587 	if (SFbuttonPressed)
1588 	{
1589 	    SFcurrentListY = y;
1590 	    if (!SFscrollTimerAdded)
1591 	    {
1592 		SFscrollTimerAdded = 1;
1593 		SFscrollTimerId = XtAppAddTimeOut(SFapp,
1594 			SFscrollTimerInterval(), SFscrollTimer,
1595 			(XtPointer)(long_u)n);
1596 	    }
1597 	}
1598 	return -1;
1599     }
1600 }
1601 
1602     static void
1603 SFenterList(Widget w UNUSED, int n, XEnterWindowEvent *event)
1604 {
1605     int			nw;
1606 
1607     /* sanity */
1608     if (SFcurrentInvert[n] != -1)
1609     {
1610 	SFinvertEntry(n);
1611 	SFcurrentInvert[n] = -1;
1612     }
1613 
1614     nw = SFnewInvertEntry(n, (XMotionEvent *) event);
1615     if (nw != -1)
1616     {
1617 	SFcurrentInvert[n] = nw;
1618 	SFinvertEntry(n);
1619     }
1620 }
1621 
1622     static void
1623 SFleaveList(Widget w UNUSED, int n, XEvent *event UNUSED)
1624 {
1625     if (SFcurrentInvert[n] != -1)
1626     {
1627 	SFinvertEntry(n);
1628 	SFcurrentInvert[n] = -1;
1629     }
1630 }
1631 
1632     static void
1633 SFmotionList(Widget w UNUSED, int n, XMotionEvent *event)
1634 {
1635     int		nw;
1636 
1637     nw = SFnewInvertEntry(n, event);
1638 
1639     if (nw != SFcurrentInvert[n])
1640     {
1641 	if (SFcurrentInvert[n] != -1)
1642 	    SFinvertEntry(n);
1643 	SFcurrentInvert[n] = nw;
1644 	if (nw != -1)
1645 	    SFinvertEntry(n);
1646     }
1647 }
1648 
1649     static void
1650 SFvFloatSliderMovedCallback(Widget w, XtPointer n, XtPointer fnew)
1651 {
1652     int		nw;
1653 
1654     nw = (*(float *)fnew) * SFdirs[SFdirPtr + (int)(long)n].nEntries;
1655     SFvSliderMovedCallback(w, (int)(long)n, nw);
1656 }
1657 
1658     static void
1659 SFvSliderMovedCallback(Widget w UNUSED, int n, int nw)
1660 {
1661     int		old;
1662     Window	win;
1663     SFDir	*dir;
1664 
1665     dir = &(SFdirs[SFdirPtr + n]);
1666 
1667     old = dir->vOrigin;
1668     dir->vOrigin = nw;
1669 
1670     if (old == nw)
1671 	return;
1672 
1673     win = XtWindow(selFileLists[n]);
1674 
1675     if (ABS(nw - old) < SFlistSize)
1676     {
1677 	if (nw > old)
1678 	{
1679 	    XCopyArea(
1680 		    SFdisplay,
1681 		    win,
1682 		    win,
1683 		    SFscrollGC,
1684 		    SFlineToTextH,
1685 		    SFlowerY + (nw - old) * SFentryHeight,
1686 		    SFentryWidth + SFlineToTextH,
1687 		    (SFlistSize - (nw - old)) * SFentryHeight,
1688 		    SFlineToTextH,
1689 		    SFlowerY);
1690 	    XClearArea(
1691 		    SFdisplay,
1692 		    win,
1693 		    SFlineToTextH,
1694 		    SFlowerY + (SFlistSize - (nw - old)) *
1695 		    SFentryHeight,
1696 		    SFentryWidth + SFlineToTextH,
1697 		    (nw - old) * SFentryHeight,
1698 		    False);
1699 	    SFdrawStrings(win, dir, SFlistSize - (nw - old),
1700 		    SFlistSize - 1);
1701 	}
1702 	else
1703 	{
1704 	    XCopyArea(
1705 		    SFdisplay,
1706 		    win,
1707 		    win,
1708 		    SFscrollGC,
1709 		    SFlineToTextH,
1710 		    SFlowerY,
1711 		    SFentryWidth + SFlineToTextH,
1712 		    (SFlistSize - (old - nw)) * SFentryHeight,
1713 		    SFlineToTextH,
1714 		    SFlowerY + (old - nw) * SFentryHeight);
1715 	    XClearArea(
1716 		    SFdisplay,
1717 		    win,
1718 		    SFlineToTextH,
1719 		    SFlowerY,
1720 		    SFentryWidth + SFlineToTextH,
1721 		    (old - nw) * SFentryHeight,
1722 		    False);
1723 	    SFdrawStrings(win, dir, 0, old - nw);
1724 	}
1725     }
1726     else
1727     {
1728 	XClearArea(
1729 		SFdisplay,
1730 		win,
1731 		SFlineToTextH,
1732 		SFlowerY,
1733 		SFentryWidth + SFlineToTextH,
1734 		SFlistSize * SFentryHeight,
1735 		False);
1736 	SFdrawStrings(win, dir, 0, SFlistSize - 1);
1737     }
1738 }
1739 
1740     static void
1741 SFvAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew)
1742 {
1743     SFDir	*dir;
1744     int		nw = (int)(long)pnew;
1745 
1746     dir = &(SFdirs[SFdirPtr + (int)(long)n]);
1747 
1748 #ifdef FEAT_GUI_NEXTAW
1749     if (nw < 0)
1750     {
1751 	if (nw > -SFvScrollHeight)
1752 	    nw = -1;
1753 	else
1754 	    nw = -SFlistSize;
1755     }
1756     else if (nw > 0)
1757     {
1758 	if (nw < SFvScrollHeight)
1759 	    nw = 1;
1760 	else
1761 	    nw = SFlistSize;
1762     }
1763 #endif
1764     nw += dir->vOrigin;
1765 
1766     if (nw > dir->nEntries - SFlistSize)
1767 	nw = dir->nEntries - SFlistSize;
1768 
1769     if (nw < 0)
1770 	nw = 0;
1771 
1772     if (dir->nEntries)
1773     {
1774 	float	f;
1775 
1776 	f = ((double) nw) / dir->nEntries;
1777 
1778 #ifdef FEAT_GUI_NEXTAW
1779 	XawScrollbarSetThumb(
1780 		w,
1781 		f,
1782 		(float) (((double) ((dir->nEntries < SFlistSize) ?
1783 				dir->nEntries : SFlistSize)) / dir->nEntries));
1784 #else
1785 	vim_XawScrollbarSetThumb(
1786 		w,
1787 		f,
1788 		(float) (((double) ((dir->nEntries < SFlistSize) ?
1789 				dir->nEntries : SFlistSize)) / dir->nEntries),
1790 		(double)dir->nEntries);
1791 #endif
1792     }
1793 
1794     SFvSliderMovedCallback(w, (int)(long)n, nw);
1795 }
1796 
1797     static void
1798 SFhSliderMovedCallback(Widget w UNUSED, XtPointer n, XtPointer nw)
1799 {
1800     SFDir	*dir;
1801     int	save;
1802 
1803     dir = &(SFdirs[SFdirPtr + (int)(long)n]);
1804     save = dir->hOrigin;
1805     dir->hOrigin = (*(float *)nw) * dir->nChars;
1806     if (dir->hOrigin == save)
1807 	return;
1808 
1809     SFdrawList((int)(long)n, SF_DO_NOT_SCROLL);
1810 }
1811 
1812     static void
1813 SFhAreaSelectedCallback(Widget w, XtPointer n, XtPointer pnew)
1814 {
1815     SFDir	*dir;
1816     int		nw = (int)(long)pnew;
1817 
1818     dir = &(SFdirs[SFdirPtr + (int)(long)n]);
1819 
1820 #ifdef FEAT_GUI_NEXTAW
1821     if (nw < 0)
1822     {
1823 	if (nw > -SFhScrollWidth)
1824 	    nw = -1;
1825 	else
1826 	    nw = -SFcharsPerEntry;
1827     }
1828     else if (nw > 0)
1829     {
1830 	if (nw < SFhScrollWidth)
1831 	    nw = 1;
1832 	else
1833 	    nw = SFcharsPerEntry;
1834     }
1835 #endif
1836     nw += dir->hOrigin;
1837 
1838     if (nw > dir->nChars - SFcharsPerEntry)
1839 	nw = dir->nChars - SFcharsPerEntry;
1840 
1841     if (nw < 0)
1842 	nw = 0;
1843 
1844     if (dir->nChars)
1845     {
1846 	float	f;
1847 
1848 	f = ((double) nw) / dir->nChars;
1849 
1850 #ifdef FEAT_GUI_NEXTAW
1851 	XawScrollbarSetThumb(
1852 		w,
1853 		f,
1854 		(float) (((double) ((dir->nChars < SFcharsPerEntry) ?
1855 			       dir->nChars : SFcharsPerEntry)) / dir->nChars));
1856 #else
1857 	vim_XawScrollbarSetThumb(
1858 		w,
1859 		f,
1860 		(float) (((double) ((dir->nChars < SFcharsPerEntry) ?
1861 			       dir->nChars : SFcharsPerEntry)) / dir->nChars),
1862 		(double)dir->nChars);
1863 #endif
1864 
1865 	SFhSliderMovedCallback(w, n, (XtPointer)&f);
1866     }
1867 }
1868 
1869     static void
1870 SFpathSliderMovedCallback(
1871     Widget	w UNUSED,
1872     XtPointer	client_data UNUSED,
1873     XtPointer	nw)
1874 {
1875     SFDir		*dir;
1876     int			n;
1877     XawTextPosition	pos;
1878     int			SFdirPtrSave;
1879 
1880     SFdirPtrSave = SFdirPtr;
1881     SFdirPtr = (*(float *)nw) * SFdirEnd;
1882     if (SFdirPtr == SFdirPtrSave)
1883 	return;
1884 
1885     SFdrawLists(SF_DO_SCROLL);
1886 
1887     n = 2;
1888     while (SFdirPtr + n >= SFdirEnd)
1889 	n--;
1890 
1891     dir = &(SFdirs[SFdirPtr + n]);
1892 
1893     pos = dir->path - SFcurrentPath;
1894 
1895     if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir)))
1896     {
1897 	pos -= strlen(SFstartDir);
1898 	if (pos < 0)
1899 	    pos = 0;
1900     }
1901 
1902     XawTextSetInsertionPoint(selFileField, pos);
1903 }
1904 
1905     static void
1906 SFpathAreaSelectedCallback(
1907     Widget	w,
1908     XtPointer	client_data UNUSED,
1909     XtPointer	pnew)
1910 {
1911     int		nw = (int)(long)pnew;
1912     float	f;
1913 
1914 #ifdef FEAT_GUI_NEXTAW
1915     if (nw < 0)
1916     {
1917 	if (nw > -SFpathScrollWidth)
1918 	    nw = -1;
1919 	else
1920 	    nw = -3;
1921     }
1922     else if (nw > 0)
1923     {
1924 	if (nw < SFpathScrollWidth)
1925 	    nw = 1;
1926 	else
1927 	    nw = 3;
1928     }
1929 #endif
1930     nw += SFdirPtr;
1931 
1932     if (nw > SFdirEnd - 3)
1933 	nw = SFdirEnd - 3;
1934 
1935     if (nw < 0)
1936 	nw = 0;
1937 
1938     f = ((double) nw) / SFdirEnd;
1939 
1940 #ifdef FEAT_GUI_NEXTAW
1941     XawScrollbarSetThumb(
1942 	    w,
1943 	    f,
1944 	    (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd));
1945 #else
1946     vim_XawScrollbarSetThumb(
1947 	    w,
1948 	    f,
1949 	    (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd),
1950 	    (double)SFdirEnd);
1951 #endif
1952 
1953     SFpathSliderMovedCallback(w, (XtPointer) NULL, (XtPointer)&f);
1954 }
1955 
1956     static Boolean
1957 SFworkProc(void)
1958 {
1959     SFDir	*dir;
1960     SFEntry	*entry;
1961 
1962     for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--)
1963     {
1964 	if (!(dir->nEntries))
1965 	    continue;
1966 	for (entry = &(dir->entries[dir->nEntries - 1]);
1967 		entry >= dir->entries;
1968 		entry--)
1969 	{
1970 	    if (!(entry->statDone))
1971 	    {
1972 		(void)SFstatAndCheck(dir, entry);
1973 		return False;
1974 	    }
1975 	}
1976     }
1977 
1978     SFworkProcAdded = 0;
1979 
1980     return True;
1981 }
1982 
1983 /***************** Dir.c */
1984 
1985     static int
1986 SFcompareEntries(const void *p, const void *q)
1987 {
1988  return strcmp(((SFEntry *)p)->real, ((SFEntry *)q)->real);
1989 }
1990 
1991     static int
1992 SFgetDir(
1993     SFDir	*dir)
1994 {
1995     SFEntry		*result = NULL;
1996     int			Alloc = 0;
1997     int			i;
1998     DIR			*dirp;
1999     struct dirent	*dp;
2000     char		*str;
2001     int			len;
2002     int			maxChars;
2003     stat_T		statBuf;
2004 
2005     maxChars = strlen(dir->dir) - 1;
2006 
2007     dir->entries = NULL;
2008     dir->nEntries = 0;
2009     dir->nChars = 0;
2010 
2011     result = NULL;
2012     i = 0;
2013 
2014     dirp = opendir(".");
2015     if (!dirp)
2016 	return 1;
2017 
2018     (void)mch_stat(".", &statBuf);
2019     dir->mtime = statBuf.st_mtime;
2020 
2021     while ((dp = readdir(dirp)))
2022     {
2023 	/* Ignore "." and ".." */
2024 	if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
2025 	    continue;
2026 	if (i >= Alloc)
2027 	{
2028 	    Alloc = 2 * (Alloc + 1);
2029 	    result = (SFEntry *) XtRealloc((char *) result,
2030 		    (unsigned) (Alloc * sizeof(SFEntry)));
2031 	}
2032 	result[i].statDone = 0;
2033 	str = dp->d_name;
2034 	len = strlen(str);
2035 	result[i].real = XtMalloc((unsigned)(len + 2));
2036 	(void) strcat(strcpy(result[i].real, str), " ");
2037 	if (len > maxChars)
2038 	    maxChars = len;
2039 	result[i].shown = result[i].real;
2040 	i++;
2041     }
2042 
2043     qsort((char *) result, (size_t) i, sizeof(SFEntry), SFcompareEntries);
2044 
2045     dir->entries = result;
2046     dir->nEntries = i;
2047     dir->nChars = maxChars + 1;
2048 
2049     closedir(dirp);
2050 
2051     return 0;
2052 }
2053 
2054 /***************** SFinternal.h */
2055 
2056 #include <sys/param.h>
2057 #include <X11/cursorfont.h>
2058 #include <X11/Composite.h>
2059 #include <X11/Shell.h>
2060 #ifdef FEAT_GUI_NEXTAW
2061 # include <X11/neXtaw/Form.h>
2062 # include <X11/neXtaw/Command.h>
2063 # include <X11/neXtaw/Label.h>
2064 #else
2065 #include <X11/Xaw/Form.h>
2066 #include <X11/Xaw/Command.h>
2067 #include <X11/Xaw/Label.h>
2068 #endif
2069 
2070 static char *oneLineTextEditTranslations = "\
2071 	<Key>Return:	redraw-display()\n\
2072 	Ctrl<Key>M:	redraw-display()\n\
2073 ";
2074 
2075     static void
2076 SFexposeList(
2077     Widget	w UNUSED,
2078     XtPointer	n,
2079     XEvent	*event,
2080     Boolean	*cont UNUSED)
2081 {
2082     if ((event->type == NoExpose) || event->xexpose.count)
2083 	return;
2084 
2085     SFdrawList((int)(long)n, SF_DO_NOT_SCROLL);
2086 }
2087 
2088     static void
2089 SFmodVerifyCallback(
2090     Widget		w UNUSED,
2091     XtPointer		client_data UNUSED,
2092     XEvent		*event,
2093     Boolean		*cont UNUSED)
2094 {
2095     char	buf[2];
2096 
2097     if ((XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) &&
2098 	    ((*buf) == '\r'))
2099 	SFstatus = SEL_FILE_OK;
2100     else
2101 	SFstatus = SEL_FILE_TEXT;
2102 }
2103 
2104     static void
2105 SFokCallback(Widget w UNUSED, XtPointer cl UNUSED, XtPointer cd UNUSED)
2106 {
2107     SFstatus = SEL_FILE_OK;
2108 }
2109 
2110 static XtCallbackRec SFokSelect[] =
2111 {
2112     { SFokCallback, (XtPointer) NULL },
2113     { NULL, (XtPointer) NULL },
2114 };
2115 
2116     static void
2117 SFcancelCallback(Widget w UNUSED, XtPointer cl UNUSED, XtPointer cd UNUSED)
2118 {
2119     SFstatus = SEL_FILE_CANCEL;
2120 }
2121 
2122 static XtCallbackRec SFcancelSelect[] =
2123 {
2124     { SFcancelCallback, (XtPointer) NULL },
2125     { NULL, (XtPointer) NULL },
2126 };
2127 
2128     static void
2129 SFdismissAction(
2130     Widget	w UNUSED,
2131     XEvent	*event,
2132     String	*params UNUSED,
2133     Cardinal	*num_params UNUSED)
2134 {
2135     if (event->type == ClientMessage
2136 	    && (Atom)event->xclient.data.l[0] != SFwmDeleteWindow)
2137 	return;
2138 
2139     SFstatus = SEL_FILE_CANCEL;
2140 }
2141 
2142 static char *wmDeleteWindowTranslation = "\
2143 	<Message>WM_PROTOCOLS:	SelFileDismiss()\n\
2144 ";
2145 
2146 static XtActionsRec actions[] =
2147 {
2148     {"SelFileDismiss",	SFdismissAction},
2149 };
2150 
2151     static void
2152 SFsetColors(
2153     guicolor_T	bg,
2154     guicolor_T	fg,
2155     guicolor_T	scroll_bg,
2156     guicolor_T	scroll_fg)
2157 {
2158     if (selFileForm)
2159     {
2160 	XtVaSetValues(selFileForm, XtNbackground,  bg,
2161 				   XtNforeground,  fg,
2162 				   XtNborderColor, bg,
2163 				   NULL);
2164     }
2165     {
2166 	int i;
2167 
2168 	for (i = 0; i < 3; ++i)
2169 	{
2170 	    if (selFileLists[i])
2171 	    {
2172 		XtVaSetValues(selFileLists[i], XtNbackground,  bg,
2173 					       XtNforeground,  fg,
2174 					       XtNborderColor, fg,
2175 					       NULL);
2176 	    }
2177 	}
2178     }
2179     if (selFileOK)
2180     {
2181 	XtVaSetValues(selFileOK, XtNbackground,  bg,
2182 				 XtNforeground,  fg,
2183 				 XtNborderColor, fg,
2184 				 NULL);
2185     }
2186     if (selFileCancel)
2187     {
2188 	XtVaSetValues(selFileCancel, XtNbackground, bg,
2189 				     XtNforeground, fg,
2190 				     XtNborderColor, fg,
2191 				     NULL);
2192     }
2193     if (selFilePrompt)
2194     {
2195 	XtVaSetValues(selFilePrompt, XtNbackground, bg,
2196 				     XtNforeground, fg,
2197 				     NULL);
2198     }
2199     if (gui.dpy)
2200     {
2201 	XSetBackground(gui.dpy, SFtextGC, bg);
2202 	XSetForeground(gui.dpy, SFtextGC, fg);
2203 	XSetForeground(gui.dpy, SFlineGC, fg);
2204 
2205 	/* This is an xor GC, so combine the fg and background */
2206 	XSetBackground(gui.dpy, SFinvertGC, fg ^ bg);
2207 	XSetForeground(gui.dpy, SFinvertGC, fg ^ bg);
2208     }
2209     if (selFileHScroll)
2210     {
2211 	XtVaSetValues(selFileHScroll, XtNbackground, scroll_bg,
2212 				      XtNforeground, scroll_fg,
2213 				      XtNborderColor, fg,
2214 				      NULL);
2215     }
2216     {
2217 	int i;
2218 
2219 	for (i = 0; i < 3; i++)
2220 	{
2221 	    XtVaSetValues(selFileVScrolls[i], XtNbackground, scroll_bg,
2222 					      XtNforeground, scroll_fg,
2223 					      XtNborderColor, fg,
2224 					      NULL);
2225 	    XtVaSetValues(selFileHScrolls[i], XtNbackground, scroll_bg,
2226 					      XtNforeground, scroll_fg,
2227 					      XtNborderColor, fg,
2228 					      NULL);
2229 	}
2230     }
2231 }
2232 
2233     static void
2234 SFcreateWidgets(
2235     Widget	toplevel,
2236     char	*prompt,
2237     char	*ok,
2238     char	*cancel)
2239 {
2240     Cardinal	n;
2241     int		listWidth, listHeight;
2242     int		listSpacing = 10;
2243     int		scrollThickness = 15;
2244     int		hScrollX, hScrollY;
2245     int		vScrollX, vScrollY;
2246 
2247     selFile = XtVaAppCreateShell("selFile", "SelFile",
2248 		transientShellWidgetClass, SFdisplay,
2249 		XtNtransientFor, toplevel,
2250 		XtNtitle, prompt,
2251 		NULL);
2252 
2253     /* Add WM_DELETE_WINDOW protocol */
2254     XtAppAddActions(XtWidgetToApplicationContext(selFile),
2255 	    actions, XtNumber(actions));
2256     XtOverrideTranslations(selFile,
2257 	    XtParseTranslationTable(wmDeleteWindowTranslation));
2258 
2259     selFileForm = XtVaCreateManagedWidget("selFileForm",
2260 		formWidgetClass, selFile,
2261 		XtNdefaultDistance, 30,
2262 		XtNforeground, SFfore,
2263 		XtNbackground, SFback,
2264 		XtNborderColor, SFback,
2265 		NULL);
2266 
2267     selFilePrompt = XtVaCreateManagedWidget("selFilePrompt",
2268 		labelWidgetClass, selFileForm,
2269 		XtNlabel, prompt,
2270 		XtNresizable, True,
2271 		XtNtop, XtChainTop,
2272 		XtNbottom, XtChainTop,
2273 		XtNleft, XtChainLeft,
2274 		XtNright, XtChainLeft,
2275 		XtNborderWidth, 0,
2276 		XtNforeground, SFfore,
2277 		XtNbackground, SFback,
2278 		NULL);
2279 
2280     /*
2281     XtVaGetValues(selFilePrompt,
2282 		XtNforeground, &SFfore,
2283 		XtNbackground, &SFback,
2284 		NULL);
2285     */
2286 
2287     SFinitFont();
2288 
2289     SFentryWidth = SFbesideText + SFcharsPerEntry * SFcharWidth +
2290 	SFbesideText;
2291     SFentryHeight = SFaboveAndBelowText + SFcharHeight +
2292 	SFaboveAndBelowText;
2293 
2294     listWidth = SFlineToTextH + SFentryWidth + SFlineToTextH + 1 +
2295 	scrollThickness;
2296     listHeight = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
2297 	SFlineToTextV + SFlistSize * SFentryHeight +
2298 	SFlineToTextV + 1 + scrollThickness;
2299 
2300     SFpathScrollWidth = 3 * listWidth + 2 * listSpacing + 4;
2301 
2302     hScrollX = -1;
2303     hScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
2304 	SFlineToTextV + SFlistSize * SFentryHeight +
2305 	SFlineToTextV;
2306     SFhScrollWidth = SFlineToTextH + SFentryWidth + SFlineToTextH;
2307 
2308     vScrollX = SFlineToTextH + SFentryWidth + SFlineToTextH;
2309     vScrollY = SFlineToTextV + SFentryHeight + SFlineToTextV;
2310     SFvScrollHeight = SFlineToTextV + SFlistSize * SFentryHeight +
2311 	SFlineToTextV;
2312 
2313     SFupperX = SFlineToTextH + SFentryWidth + SFlineToTextH - 1;
2314     SFlowerY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
2315 	SFlineToTextV;
2316     SFupperY = SFlineToTextV + SFentryHeight + SFlineToTextV + 1 +
2317 	SFlineToTextV + SFlistSize * SFentryHeight - 1;
2318 
2319     SFtextX = SFlineToTextH + SFbesideText;
2320     SFtextYoffset = SFlowerY + SFaboveAndBelowText + SFcharAscent;
2321 
2322     SFsegs[0].x1 = 0;
2323     SFsegs[0].y1 = vScrollY;
2324     SFsegs[0].x2 = vScrollX - 1;
2325     SFsegs[0].y2 = vScrollY;
2326     SFsegs[1].x1 = vScrollX;
2327     SFsegs[1].y1 = 0;
2328     SFsegs[1].x2 = vScrollX;
2329     SFsegs[1].y2 = vScrollY - 1;
2330 
2331     SFcompletionSegs[0].x1 = SFcompletionSegs[0].x2 = SFlineToTextH;
2332     SFcompletionSegs[1].x1 = SFcompletionSegs[1].x2 =
2333 	SFlineToTextH + SFentryWidth - 1;
2334 
2335     selFileField = XtVaCreateManagedWidget("selFileField",
2336 		asciiTextWidgetClass, selFileForm,
2337 		XtNwidth, 3 * listWidth + 2 * listSpacing + 4,
2338 		XtNborderColor, SFfore,
2339 		XtNfromVert, selFilePrompt,
2340 		XtNvertDistance, 10,
2341 		XtNresizable, True,
2342 		XtNtop, XtChainTop,
2343 		XtNbottom, XtChainTop,
2344 		XtNleft, XtChainLeft,
2345 		XtNright, XtChainLeft,
2346 		XtNstring, SFtextBuffer,
2347 		XtNlength, MAXPATHL,
2348 		XtNeditType, XawtextEdit,
2349 		XtNwrap, XawtextWrapWord,
2350 		XtNresize, XawtextResizeHeight,
2351 		XtNuseStringInPlace, True,
2352 		NULL);
2353 
2354     XtOverrideTranslations(selFileField,
2355 	    XtParseTranslationTable(oneLineTextEditTranslations));
2356     XtSetKeyboardFocus(selFileForm, selFileField);
2357 
2358     selFileHScroll = XtVaCreateManagedWidget("selFileHScroll",
2359 #ifdef FEAT_GUI_NEXTAW
2360 		scrollbarWidgetClass, selFileForm,
2361 #else
2362 		vim_scrollbarWidgetClass, selFileForm,
2363 #endif
2364 		XtNorientation, XtorientHorizontal,
2365 		XtNwidth, SFpathScrollWidth,
2366 		XtNheight, scrollThickness,
2367 		XtNborderColor, SFfore,
2368 		XtNfromVert, selFileField,
2369 		XtNvertDistance, 30,
2370 		XtNtop, XtChainTop,
2371 		XtNbottom, XtChainTop,
2372 		XtNleft, XtChainLeft,
2373 		XtNright, XtChainLeft,
2374 		XtNforeground, gui.scroll_fg_pixel,
2375 		XtNbackground, gui.scroll_bg_pixel,
2376 #ifndef FEAT_GUI_NEXTAW
2377 		XtNlimitThumb, 1,
2378 #endif
2379 		NULL);
2380 
2381     XtAddCallback(selFileHScroll, XtNjumpProc,
2382 	    (XtCallbackProc) SFpathSliderMovedCallback, (XtPointer)NULL);
2383     XtAddCallback(selFileHScroll, XtNscrollProc,
2384 	    (XtCallbackProc) SFpathAreaSelectedCallback, (XtPointer)NULL);
2385 
2386     selFileLists[0] = XtVaCreateManagedWidget("selFileList1",
2387 		compositeWidgetClass, selFileForm,
2388 		XtNwidth, listWidth,
2389 		XtNheight, listHeight,
2390 		XtNforeground,  SFfore,
2391 		XtNbackground,  SFback,
2392 		XtNborderColor, SFfore,
2393 		XtNfromVert, selFileHScroll,
2394 		XtNvertDistance, 10,
2395 		XtNtop, XtChainTop,
2396 		XtNbottom, XtChainTop,
2397 		XtNleft, XtChainLeft,
2398 		XtNright, XtChainLeft,
2399 		NULL);
2400 
2401     selFileLists[1] = XtVaCreateManagedWidget("selFileList2",
2402 		compositeWidgetClass, selFileForm,
2403 		XtNwidth, listWidth,
2404 		XtNheight, listHeight,
2405 		XtNforeground,  SFfore,
2406 		XtNbackground,  SFback,
2407 		XtNborderColor, SFfore,
2408 		XtNfromHoriz, selFileLists[0],
2409 		XtNfromVert, selFileHScroll,
2410 		XtNhorizDistance, listSpacing,
2411 		XtNvertDistance, 10,
2412 		XtNtop, XtChainTop,
2413 		XtNbottom, XtChainTop,
2414 		XtNleft, XtChainLeft,
2415 		XtNright, XtChainLeft,
2416 		NULL);
2417 
2418     selFileLists[2] = XtVaCreateManagedWidget("selFileList3",
2419 		compositeWidgetClass, selFileForm,
2420 		XtNwidth, listWidth,
2421 		XtNheight, listHeight,
2422 		XtNforeground,  SFfore,
2423 		XtNbackground,  SFback,
2424 		XtNborderColor, SFfore,
2425 		XtNfromHoriz, selFileLists[1],
2426 		XtNfromVert, selFileHScroll,
2427 		XtNhorizDistance, listSpacing,
2428 		XtNvertDistance, 10,
2429 		XtNtop, XtChainTop,
2430 		XtNbottom, XtChainTop,
2431 		XtNleft, XtChainLeft,
2432 		XtNright, XtChainLeft,
2433 		NULL);
2434 
2435     for (n = 0; n < 3; n++)
2436     {
2437 	selFileVScrolls[n] = XtVaCreateManagedWidget("selFileVScroll",
2438 #ifdef FEAT_GUI_NEXTAW
2439 		    scrollbarWidgetClass, selFileLists[n],
2440 #else
2441 		    vim_scrollbarWidgetClass, selFileLists[n],
2442 #endif
2443 		    XtNx, vScrollX,
2444 		    XtNy, vScrollY,
2445 		    XtNwidth, scrollThickness,
2446 		    XtNheight, SFvScrollHeight,
2447 		    XtNborderColor, SFfore,
2448 		    XtNforeground, gui.scroll_fg_pixel,
2449 		    XtNbackground, gui.scroll_bg_pixel,
2450 #ifndef FEAT_GUI_NEXTAW
2451 		    XtNlimitThumb, 1,
2452 #endif
2453 		    NULL);
2454 
2455 	XtAddCallback(selFileVScrolls[n], XtNjumpProc,
2456 		(XtCallbackProc)SFvFloatSliderMovedCallback,
2457 		(XtPointer)(long_u)n);
2458 	XtAddCallback(selFileVScrolls[n], XtNscrollProc,
2459 		(XtCallbackProc)SFvAreaSelectedCallback, (XtPointer)(long_u)n);
2460 
2461 	selFileHScrolls[n] = XtVaCreateManagedWidget("selFileHScroll",
2462 #ifdef FEAT_GUI_NEXTAW
2463 		    scrollbarWidgetClass, selFileLists[n],
2464 #else
2465 		    vim_scrollbarWidgetClass, selFileLists[n],
2466 #endif
2467 		    XtNorientation, XtorientHorizontal,
2468 		    XtNx, hScrollX,
2469 		    XtNy, hScrollY,
2470 		    XtNwidth, SFhScrollWidth,
2471 		    XtNheight, scrollThickness,
2472 		    XtNborderColor, SFfore,
2473 		    XtNforeground, gui.scroll_fg_pixel,
2474 		    XtNbackground, gui.scroll_bg_pixel,
2475 #ifndef FEAT_GUI_NEXTAW
2476 		    XtNlimitThumb, 1,
2477 #endif
2478 		    NULL);
2479 
2480 	XtAddCallback(selFileHScrolls[n], XtNjumpProc,
2481 		(XtCallbackProc)SFhSliderMovedCallback,
2482 		(XtPointer)(long_u)n);
2483 	XtAddCallback(selFileHScrolls[n], XtNscrollProc,
2484 		(XtCallbackProc)SFhAreaSelectedCallback, (XtPointer)(long_u)n);
2485     }
2486 
2487     selFileOK = XtVaCreateManagedWidget("selFileOK",
2488 		commandWidgetClass, selFileForm,
2489 		XtNlabel, ok,
2490 		XtNresizable, True,
2491 		XtNcallback, SFokSelect,
2492 		XtNforeground,  SFfore,
2493 		XtNbackground,  SFback,
2494 		XtNborderColor, SFfore,
2495 		XtNfromHoriz, selFileLists[0],
2496 		XtNfromVert, selFileLists[0],
2497 		XtNvertDistance, 30,
2498 		XtNtop, XtChainTop,
2499 		XtNbottom, XtChainTop,
2500 		XtNleft, XtChainLeft,
2501 		XtNright, XtChainLeft,
2502 		NULL);
2503 
2504     selFileCancel = XtVaCreateManagedWidget("selFileCancel",
2505 		commandWidgetClass, selFileForm,
2506 		XtNlabel, cancel,
2507 		XtNresizable, True,
2508 		XtNcallback, SFcancelSelect,
2509 		XtNforeground,  SFfore,
2510 		XtNbackground,  SFback,
2511 		XtNborderColor, SFfore,
2512 		XtNfromHoriz, selFileOK,
2513 		XtNfromVert, selFileLists[0],
2514 		XtNhorizDistance, 30,
2515 		XtNvertDistance, 30,
2516 		XtNtop, XtChainTop,
2517 		XtNbottom, XtChainTop,
2518 		XtNleft, XtChainLeft,
2519 		XtNright, XtChainLeft,
2520 		NULL);
2521 
2522     XtSetMappedWhenManaged(selFile, False);
2523     XtRealizeWidget(selFile);
2524 
2525     /* Add WM_DELETE_WINDOW protocol */
2526     SFwmDeleteWindow = XInternAtom(SFdisplay, "WM_DELETE_WINDOW", False);
2527     XSetWMProtocols(SFdisplay, XtWindow(selFile), &SFwmDeleteWindow, 1);
2528 
2529     SFcreateGC();
2530 
2531     for (n = 0; n < 3; n++)
2532     {
2533 	XtAddEventHandler(selFileLists[n], ExposureMask, True,
2534 		(XtEventHandler)SFexposeList, (XtPointer)(long_u)n);
2535 	XtAddEventHandler(selFileLists[n], EnterWindowMask, False,
2536 		(XtEventHandler)SFenterList, (XtPointer)(long_u)n);
2537 	XtAddEventHandler(selFileLists[n], LeaveWindowMask, False,
2538 		(XtEventHandler)SFleaveList, (XtPointer)(long_u)n);
2539 	XtAddEventHandler(selFileLists[n], PointerMotionMask, False,
2540 		(XtEventHandler)SFmotionList, (XtPointer)(long_u)n);
2541 	XtAddEventHandler(selFileLists[n], ButtonPressMask, False,
2542 		(XtEventHandler)SFbuttonPressList, (XtPointer)(long_u)n);
2543 	XtAddEventHandler(selFileLists[n], ButtonReleaseMask, False,
2544 		(XtEventHandler)SFbuttonReleaseList, (XtPointer)(long_u)n);
2545     }
2546 
2547     XtAddEventHandler(selFileField, KeyPressMask, False,
2548 				       SFmodVerifyCallback, (XtPointer)NULL);
2549 
2550     SFapp = XtWidgetToApplicationContext(selFile);
2551 }
2552 
2553     static void
2554 SFtextChanged(void)
2555 {
2556 #if defined(FEAT_XFONTSET) && defined(XtNinternational)
2557     if ((unsigned long)_XawTextFormat((TextWidget)selFileField) == XawFmtWide)
2558     {
2559 	wchar_t *wcbuf=(wchar_t *)SFtextBuffer;
2560 
2561 	if ((wcbuf[0] == L'/') || (wcbuf[0] == L'~'))
2562 	{
2563 	    (void) wcstombs(SFcurrentPath, wcbuf, MAXPATHL);
2564 	    SFtextPos = XawTextGetInsertionPoint(selFileField);
2565 	}
2566 	else
2567 	{
2568 	    strcpy(SFcurrentPath, SFstartDir);
2569 	    (void) wcstombs(SFcurrentPath + strlen(SFcurrentPath), wcbuf, MAXPATHL);
2570 
2571 	    SFtextPos = XawTextGetInsertionPoint(selFileField) + strlen(SFstartDir);
2572 	}
2573     }
2574     else
2575 #endif
2576     if ((SFtextBuffer[0] == '/') || (SFtextBuffer[0] == '~'))
2577     {
2578 	(void) strcpy(SFcurrentPath, SFtextBuffer);
2579 	SFtextPos = XawTextGetInsertionPoint(selFileField);
2580     }
2581     else
2582     {
2583 	(void) strcat(strcpy(SFcurrentPath, SFstartDir), SFtextBuffer);
2584 
2585 	SFtextPos = XawTextGetInsertionPoint(selFileField) + strlen(SFstartDir);
2586     }
2587 
2588     if (!SFworkProcAdded)
2589     {
2590 	(void) XtAppAddWorkProc(SFapp, (XtWorkProc)SFworkProc, NULL);
2591 	SFworkProcAdded = 1;
2592     }
2593 
2594     SFupdatePath();
2595 }
2596 
2597     static char *
2598 SFgetText(void)
2599 {
2600 #if defined(FEAT_XFONTSET) && defined(XtNinternational)
2601     char *buf;
2602 
2603     if ((unsigned long)_XawTextFormat((TextWidget)selFileField) == XawFmtWide)
2604     {
2605 	wchar_t *wcbuf;
2606 	int mbslength;
2607 
2608 	XtVaGetValues(selFileField,
2609 	    XtNstring, &wcbuf,
2610 	NULL);
2611 	mbslength = wcstombs(NULL, wcbuf, 0);
2612 	/* Hack: some broken wcstombs() returns zero, just get a large buffer */
2613 	if (mbslength == 0 && wcbuf != NULL && wcbuf[0] != 0)
2614 	    mbslength = MAXPATHL;
2615 	buf=(char *)XtMalloc(mbslength + 1);
2616 	wcstombs(buf, wcbuf, mbslength +1);
2617 	return buf;
2618     }
2619 #endif
2620     return (char *)vim_strsave((char_u *)SFtextBuffer);
2621 }
2622 
2623     static void
2624 SFprepareToReturn(void)
2625 {
2626     SFstatus = SEL_FILE_NULL;
2627     XtRemoveGrab(selFile);
2628     XtUnmapWidget(selFile);
2629     XtRemoveTimeOut(SFdirModTimerId);
2630     if (SFchdir(SFstartDir))
2631     {
2632 	emsg(_("E614: vim_SelFile: can't return to current directory"));
2633 	SFstatus = SEL_FILE_CANCEL;
2634     }
2635 }
2636 
2637     char *
2638 vim_SelFile(
2639     Widget	toplevel,
2640     char	*prompt,
2641     char	*init_path,
2642     int		(*show_entry)(),
2643     int		x,
2644     int		y,
2645     guicolor_T	fg,
2646     guicolor_T	bg,
2647     guicolor_T	scroll_fg,
2648     guicolor_T	scroll_bg) /* The "Scrollbar" group colors */
2649 {
2650     static int	firstTime = 1;
2651     XEvent	event;
2652     char	*name_return;
2653 
2654     if (prompt == NULL)
2655 	prompt = _("Pathname:");
2656     SFfore = fg;
2657     SFback = bg;
2658 
2659     if (mch_dirname((char_u *)SFstartDir, MAXPATHL) == FAIL)
2660     {
2661 	emsg(_("E615: vim_SelFile: can't get current directory"));
2662 	return NULL;
2663     }
2664 
2665     if (firstTime)
2666     {
2667 	firstTime = 0;
2668 	SFdisplay = XtDisplay(toplevel);
2669 	SFcreateWidgets(toplevel, prompt, _("OK"), _("Cancel"));
2670     }
2671     else
2672     {
2673 	XtVaSetValues(selFilePrompt, XtNlabel, prompt, NULL);
2674 	XtVaSetValues(selFile, XtNtitle, prompt, NULL);
2675 	SFsetColors(bg, fg, scroll_bg, scroll_fg);
2676     }
2677 
2678     XtVaSetValues(selFile, XtNx, x, XtNy, y, NULL);
2679     XtMapWidget(selFile);
2680 
2681     (void)strcat(SFstartDir, "/");
2682     (void)strcpy(SFcurrentDir, SFstartDir);
2683 
2684     if (init_path)
2685     {
2686 	if (init_path[0] == '/')
2687 	{
2688 	    (void)strcpy(SFcurrentPath, init_path);
2689 	    if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir)))
2690 		SFsetText(SFcurrentPath);
2691 	    else
2692 		SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
2693 	}
2694 	else
2695 	{
2696 	    (void)strcat(strcpy(SFcurrentPath, SFstartDir), init_path);
2697 	    SFsetText(&(SFcurrentPath[strlen(SFstartDir)]));
2698 	}
2699     }
2700     else
2701 	(void)strcpy(SFcurrentPath, SFstartDir);
2702 
2703     SFfunc = show_entry;
2704 
2705     SFtextChanged();
2706 
2707     XtAddGrab(selFile, True, True);
2708 
2709     SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000,
2710 	    SFdirModTimer, (XtPointer) NULL);
2711 
2712     for (;;)
2713     {
2714 	XtAppNextEvent(SFapp, &event);
2715 	XtDispatchEvent(&event);
2716 	switch (SFstatus)
2717 	{
2718 	    case SEL_FILE_TEXT:
2719 		SFstatus = SEL_FILE_NULL;
2720 		SFtextChanged();
2721 		break;
2722 	    case SEL_FILE_OK:
2723 		name_return = SFgetText();
2724 		SFprepareToReturn();
2725 		return name_return;
2726 	    case SEL_FILE_CANCEL:
2727 		SFprepareToReturn();
2728 		return NULL;
2729 	    case SEL_FILE_NULL:
2730 		break;
2731 	}
2732     }
2733 }
2734 #endif /* FEAT_BROWSE */
2735