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