Tizen 2.0 Release
[external/vim.git] / src / gui_at_fs.c
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
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 /***************** static functions */
174
175 static void SFsetText __ARGS((char *path));
176 static void SFtextChanged __ARGS((void));
177 static char *SFgetText __ARGS((void));
178 static void SFupdatePath __ARGS((void));
179 static int SFgetDir __ARGS((SFDir *dir));
180 static void SFdrawLists __ARGS((int doScroll));
181 static void SFdrawList __ARGS((int n, int doScroll));
182 static void SFclearList __ARGS((int n, int doScroll));
183 static void SFbuttonPressList __ARGS((Widget w, int n, XButtonPressedEvent *event));
184 static void SFbuttonReleaseList __ARGS((Widget w, int n, XButtonReleasedEvent *event));
185 static void SFdirModTimer __ARGS((XtPointer cl, XtIntervalId *id));
186 static char SFstatChar __ARGS((struct stat *statBuf));
187 static void SFdrawStrings __ARGS((Window w, SFDir *dir, int from, int to));
188 static int SFnewInvertEntry __ARGS((int n, XMotionEvent *event));
189 static void SFinvertEntry __ARGS((int n));
190 static void SFenterList __ARGS((Widget w, int n, XEnterWindowEvent *event));
191 static void SFleaveList __ARGS((Widget w, int n, XEvent *event));
192 static void SFmotionList __ARGS((Widget w, int n, XMotionEvent *event));
193 static void SFvFloatSliderMovedCallback __ARGS((Widget w, XtPointer n, XtPointer fnew));
194 static void SFvSliderMovedCallback __ARGS((Widget w, int n, int nw));
195 static void SFvAreaSelectedCallback __ARGS((Widget w, XtPointer n, XtPointer pnew));
196 static void SFhSliderMovedCallback __ARGS((Widget w, XtPointer n, XtPointer nw));
197 static void SFhAreaSelectedCallback __ARGS((Widget w, XtPointer n, XtPointer pnew));
198 static void SFpathSliderMovedCallback __ARGS((Widget w, XtPointer client_data, XtPointer nw));
199 static void SFpathAreaSelectedCallback __ARGS((Widget w, XtPointer client_data, XtPointer pnew));
200 static Boolean SFworkProc __ARGS((void));
201 static int SFcompareEntries __ARGS((const void *p, const void *q));
202 static void SFprepareToReturn __ARGS((void));
203 static void SFcreateWidgets __ARGS((Widget toplevel, char *prompt, char *ok, char *cancel));
204 static 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
224 typedef struct
225 {
226     char        *name;
227     char        *dir;
228 } SFLogin;
229
230 static int      SFdoNotTouchDirPtr = 0;
231
232 static int      SFdoNotTouchVorigin = 0;
233
234 static SFDir    SFrootDir, SFhomeDir;
235
236 static SFLogin  *SFlogins;
237
238 static int      SFtwiddle = 0;
239
240 static int SFchdir __ARGS((char *path));
241
242     static int
243 SFchdir(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
260 static void SFfree __ARGS((int i));
261
262     static void
263 SFfree(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
284 static void SFstrdup __ARGS((char **s1, char *s2));
285
286     static void
287 SFstrdup(s1, s2)
288     char        **s1;
289     char        *s2;
290 {
291     *s1 = strcpy(XtMalloc((unsigned)(strlen(s2) + 1)), s2);
292 }
293
294 static void SFunreadableDir __ARGS((SFDir *dir));
295
296     static void
297 SFunreadableDir(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
310 static void SFreplaceText __ARGS((SFDir *dir, char *str));
311
312     static void
313 SFreplaceText(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
333 static void SFexpand __ARGS((char *str));
334
335     static void
336 SFexpand(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
390 static int SFfindFile __ARGS((SFDir *dir, char *str));
391
392     static int
393 SFfindFile(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
491 static void SFunselect __ARGS((void));
492
493     static void
494 SFunselect()
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
505 static int SFcompareLogins __ARGS((const void *p, const void *q));
506
507     static int
508 SFcompareLogins(p, q)
509     const void *p, *q;
510 {
511     return strcmp(((SFLogin *)p)->name, ((SFLogin *)q)->name);
512 }
513
514 static void SFgetHomeDirs __ARGS((void));
515
516     static void
517 SFgetHomeDirs()
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
582 static int SFfindHomeDir __ARGS((char *begin, char *end));
583
584     static int
585 SFfindHomeDir(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
616 SFupdatePath()
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
810 WcsLen(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
821 SFsetText(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
855 SFbuttonPressList(w, n, event)
856     Widget              w UNUSED;
857     int                 n UNUSED;
858     XButtonPressedEvent *event UNUSED;
859 {
860     SFbuttonPressed = 1;
861 }
862
863     static void
864 SFbuttonReleaseList(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
885 static int SFcheckDir __ARGS((int n, SFDir *dir));
886
887     static int
888 SFcheckDir(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
951 static int SFcheckFiles __ARGS((SFDir *dir));
952
953     static int
954 SFcheckFiles(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
991 SFdirModTimer(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
1039 SFstatChar(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
1072 typedef struct
1073 {
1074     char *fontname;
1075 } TextData;
1076
1077 static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;
1078
1079 static 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
1091 static XFontSet SFfont;
1092 #else
1093 static XFontStruct *SFfont;
1094 #endif
1095
1096 static int SFcurrentListY;
1097
1098 static XtIntervalId SFscrollTimerId;
1099
1100 static void SFinitFont __ARGS((void));
1101
1102     static void
1103 SFinitFont()
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
1151 static void SFcreateGC __ARGS((void));
1152
1153     static void
1154 SFcreateGC()
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
1212 SFclearList(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
1286 static void SFdeleteEntry __ARGS((SFDir *dir, SFEntry *entry));
1287
1288     static void
1289 SFdeleteEntry(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
1340 static void SFwriteStatChar __ARGS((char *name, int last, struct stat *statBuf));
1341
1342     static void
1343 SFwriteStatChar(name, last, statBuf)
1344     char        *name;
1345     int         last;
1346     struct stat *statBuf;
1347 {
1348     name[last] = SFstatChar(statBuf);
1349 }
1350
1351 static int SFstatAndCheck __ARGS((SFDir *dir, SFEntry *entry));
1352
1353     static int
1354 SFstatAndCheck(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
1417 SFdrawStrings(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
1504 SFdrawList(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
1542 SFdrawLists(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
1552 SFinvertEntry(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
1565 static unsigned long SFscrollTimerInterval __ARGS((void));
1566
1567     static unsigned long
1568 SFscrollTimerInterval()
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
1594 static void SFscrollTimer __ARGS((XtPointer p, XtIntervalId *id));
1595
1596     static void
1597 SFscrollTimer(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
1649 SFnewInvertEntry(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
1696 SFenterList(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
1719 SFleaveList(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
1732 SFmotionList(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
1752 SFvFloatSliderMovedCallback(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
1764 SFvSliderMovedCallback(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
1849 SFvAreaSelectedCallback(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
1909 SFhSliderMovedCallback(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
1927 SFhAreaSelectedCallback(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
1987 SFpathSliderMovedCallback(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
2023 SFpathAreaSelectedCallback(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
2074 SFworkProc()
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
2103 SFcompareEntries(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
2111 SFgetDir(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
2189 static char *oneLineTextEditTranslations = "\
2190         <Key>Return:    redraw-display()\n\
2191         Ctrl<Key>M:     redraw-display()\n\
2192 ";
2193
2194 static void SFexposeList __ARGS((Widget w, XtPointer n, XEvent *event, Boolean *cont));
2195
2196     static void
2197 SFexposeList(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
2209 static void SFmodVerifyCallback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont));
2210
2211     static void
2212 SFmodVerifyCallback(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
2227 static void SFokCallback __ARGS((Widget w, XtPointer cl, XtPointer cd));
2228
2229     static void
2230 SFokCallback(w, cl, cd)
2231     Widget      w UNUSED;
2232     XtPointer   cl UNUSED;
2233     XtPointer   cd UNUSED;
2234 {
2235     SFstatus = SEL_FILE_OK;
2236 }
2237
2238 static XtCallbackRec SFokSelect[] =
2239 {
2240     { SFokCallback, (XtPointer) NULL },
2241     { NULL, (XtPointer) NULL },
2242 };
2243
2244 static void SFcancelCallback __ARGS((Widget w, XtPointer cl, XtPointer cd));
2245
2246     static void
2247 SFcancelCallback(w, cl, cd)
2248     Widget      w UNUSED;
2249     XtPointer   cl UNUSED;
2250     XtPointer   cd UNUSED;
2251 {
2252     SFstatus = SEL_FILE_CANCEL;
2253 }
2254
2255 static XtCallbackRec SFcancelSelect[] =
2256 {
2257     { SFcancelCallback, (XtPointer) NULL },
2258     { NULL, (XtPointer) NULL },
2259 };
2260
2261 static void SFdismissAction __ARGS((Widget w, XEvent *event, String *params, Cardinal *num_params));
2262
2263     static void
2264 SFdismissAction(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
2277 static char *wmDeleteWindowTranslation = "\
2278         <Message>WM_PROTOCOLS:  SelFileDismiss()\n\
2279 ";
2280
2281 static XtActionsRec actions[] =
2282 {
2283     {"SelFileDismiss",  SFdismissAction},
2284 };
2285
2286     static void
2287 SFsetColors(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
2369 SFcreateWidgets(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
2689 SFtextChanged()
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 *
2733 SFgetText()
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
2759 SFprepareToReturn()
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 *
2773 vim_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 */