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