048fa858ebb31da66446cf1be0091916256f6e0c
[xboard.git] / filebrowser / draw.c
1 /*
2  * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan
3  *
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation for any purpose and without fee is hereby granted, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Software Research Associates not be used
9  * in advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  Software Research Associates
11  * makes no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
16  * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL,
17  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
19  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  *
22  * Author: Erik M. van der Poel
23  *         Software Research Associates, Inc., Tokyo, Japan
24  *         erik@sra.co.jp
25  */
26
27 #include <stdio.h>
28 #include "xstat.h"
29 #include "selfile.h"
30 #include <X11/StringDefs.h>
31 #include <X11/Xaw/Scrollbar.h>
32 #include <X11/Xaw/Cardinals.h>
33
34 #define SF_DEFAULT_FONT "9x15"
35
36 #ifdef ABS
37 #undef ABS
38 #endif
39 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
40
41 typedef struct {
42         char *fontname;
43 } TextData, *textPtr;
44
45 int SFcharWidth, SFcharAscent, SFcharHeight;
46
47 int SFcurrentInvert[3] = { -1, -1, -1 };
48
49 static GC SFlineGC, SFscrollGC, SFinvertGC, SFtextGC;
50
51 static XtResource textResources[] = {
52         {XtNfont, XtCFont, XtRString, sizeof (char *),
53                 XtOffset(textPtr, fontname), XtRString, SF_DEFAULT_FONT},
54 };
55
56 static XFontStruct *SFfont;
57
58 static int SFcurrentListY;
59
60 static XtIntervalId SFscrollTimerId;
61
62 void
63 SFinitFont()
64 {
65         TextData        *data;
66
67         data = XtNew(TextData);
68
69         XtGetApplicationResources(selFileForm, (XtPointer) data, textResources,
70                 XtNumber(textResources), (Arg *) NULL, ZERO);
71
72         SFfont = XLoadQueryFont(SFdisplay, data->fontname);
73         if (!SFfont) {
74                 SFfont = XLoadQueryFont(SFdisplay, SF_DEFAULT_FONT);
75                 if (!SFfont) {
76                         char    sbuf[256];
77
78                         (void) sprintf(sbuf, "XsraSelFile: can't get font %s",
79                                 SF_DEFAULT_FONT);
80
81                         XtAppError(SFapp, sbuf);
82                 }
83         }
84
85         SFcharWidth = (SFfont->max_bounds.width + SFfont->min_bounds.width) / 2;
86         SFcharAscent = SFfont->max_bounds.ascent;
87         SFcharHeight = SFcharAscent + SFfont->max_bounds.descent;
88         return;
89 }
90
91 void
92 SFcreateGC()
93 {
94         XGCValues       gcValues;
95         XRectangle      rectangles[1];
96
97         gcValues.foreground = SFfore;
98
99         SFlineGC = XtGetGC(
100                 selFileLists[0],
101                 (XtGCMask)
102                         GCForeground            |
103                         0,
104                 &gcValues
105         );
106
107         SFscrollGC = XtGetGC(
108                 selFileLists[0],
109                 (XtGCMask)
110                         0,
111                 &gcValues
112         );
113
114         gcValues.function = GXinvert;
115         gcValues.plane_mask = (SFfore ^ SFback);
116
117         SFinvertGC = XtGetGC(
118                 selFileLists[0],
119                 (XtGCMask)
120                         GCFunction              |
121                         GCPlaneMask             |
122                         0,
123                 &gcValues
124         );
125
126         gcValues.foreground = SFfore;
127         gcValues.background = SFback;
128         gcValues.font = SFfont->fid;
129
130         SFtextGC = XCreateGC(
131                 SFdisplay,
132                 XtWindow(selFileLists[0]),
133                 (unsigned long)
134                         GCForeground            |
135                         GCBackground            |
136                         GCFont                  |
137                         0,
138                 &gcValues
139         );
140
141         rectangles[0].x = SFlineToTextH + SFbesideText;
142         rectangles[0].y = 0;
143         rectangles[0].width = SFcharsPerEntry * SFcharWidth;
144         rectangles[0].height = SFupperY + 1;
145
146         XSetClipRectangles(
147                 SFdisplay,
148                 SFtextGC,
149                 0,
150                 0,
151                 rectangles,
152                 1,
153                 Unsorted
154         );
155         return;
156 }
157
158 void
159 SFclearList(n, doScroll)
160         int     n;
161         int     doScroll;
162 {
163         SFDir   *dir;
164
165         SFcurrentInvert[n] = -1;
166
167         XClearWindow(SFdisplay, XtWindow(selFileLists[n]));
168
169         XDrawSegments(SFdisplay, XtWindow(selFileLists[n]), SFlineGC, SFsegs,
170                 2);
171
172         if (doScroll) {
173                 dir = &(SFdirs[SFdirPtr + n]);
174
175                 if ((SFdirPtr + n < SFdirEnd) && dir->nEntries && dir->nChars) {
176                         XawScrollbarSetThumb(
177                                 selFileVScrolls[n],
178                                 (float) (((double) dir->vOrigin) /
179                                         dir->nEntries),
180                                 (float) (((double) ((dir->nEntries < SFlistSize)
181                                         ? dir->nEntries : SFlistSize)) /
182                                         dir->nEntries)
183                         );
184
185                         XawScrollbarSetThumb(
186                                 selFileHScrolls[n],
187                                 (float) (((double) dir->hOrigin) / dir->nChars),
188                                 (float) (((double) ((dir->nChars <
189                                         SFcharsPerEntry) ? dir->nChars :
190                                         SFcharsPerEntry)) / dir->nChars)
191                         );
192                 } else {
193                         XawScrollbarSetThumb(selFileVScrolls[n], (float) 0.0,
194                                 (float) 1.0);
195                         XawScrollbarSetThumb(selFileHScrolls[n], (float) 0.0,
196                                 (float) 1.0);
197                 }
198         }
199         return;
200 }
201
202 static void
203 SFdeleteEntry(dir, entry)
204         SFDir   *dir;
205         SFEntry *entry;
206 {
207         register SFEntry        *e;
208         register SFEntry        *end;
209         int                     n;
210         int                     idx;
211
212         idx = entry - dir->entries;
213
214         if (idx < dir->beginSelection) {
215                 dir->beginSelection--;
216         }
217         if (idx <= dir->endSelection) {
218                 dir->endSelection--;
219         }
220         if (dir->beginSelection > dir->endSelection) {
221                 dir->beginSelection = dir->endSelection = -1;
222         }
223
224         if (idx < dir->vOrigin) {
225                 dir->vOrigin--;
226         }
227
228         XtFree(entry->real);
229
230         end = &(dir->entries[dir->nEntries - 1]);
231
232         for (e = entry; e < end; e++) {
233                 *e = *(e + 1);
234         }
235
236         if (!(--dir->nEntries)) {
237                 return;
238         }
239
240         n = dir - &(SFdirs[SFdirPtr]);
241         if ((n < 0) || (n > 2)) {
242                 return;
243         }
244
245         XawScrollbarSetThumb(
246                 selFileVScrolls[n],
247                 (float) (((double) dir->vOrigin) / dir->nEntries),
248                 (float) (((double) ((dir->nEntries < SFlistSize) ?
249                         dir->nEntries : SFlistSize)) / dir->nEntries)
250         );
251         return;
252 }
253
254 static void
255 SFwriteStatChar(name, last, statBuf)
256         char            *name;
257         int             last;
258         struct stat     *statBuf;
259 {
260         name[last] = SFstatChar(statBuf);
261         return;
262 }
263
264 static int
265 SFstatAndCheck(dir, entry)
266         SFDir   *dir;
267         SFEntry *entry;
268 {
269         struct stat     statBuf;
270         char            save;
271         int             last;
272
273         /*
274          * must be restored before returning
275          */
276         save = *(dir->path);
277         *(dir->path) = 0;
278
279         if (!SFchdir(SFcurrentPath)) {
280                 last = strlen(entry->real) - 1;
281                 entry->real[last] = 0;
282                 entry->statDone = 1;
283                 if (
284                         (!stat(entry->real, &statBuf))
285
286 #ifdef S_IFLNK
287
288                      || (!lstat(entry->real, &statBuf))
289
290 #endif /* ndef S_IFLNK */
291
292                 ) {
293                         if (SFfunc) {
294                                 char *shown;
295
296                                 shown = NULL;
297                                 if (SFfunc(entry->real, &shown, &statBuf)) {
298                                         if (shown) {
299                                                 int len;
300
301                                                 len = strlen(shown);
302                                                 entry->shown = XtMalloc(
303                                                         (unsigned) (len + 2)
304                                                 );
305                                                 (void) strcpy(entry->shown,
306                                                         shown);
307                                                 SFwriteStatChar(
308                                                         entry->shown,
309                                                         len,
310                                                         &statBuf
311                                                 );
312                                                 entry->shown[len + 1] = 0;
313                                         }
314                                 } else {
315                                         SFdeleteEntry(dir, entry);
316
317                                         *(dir->path) = save;
318                                         return 1;
319                                 }
320                         }
321                         SFwriteStatChar(entry->real, last, &statBuf);
322                 } else {
323                         entry->real[last] = ' ';
324                 }
325         }
326
327         *(dir->path) = save;
328         return 0;
329 }
330
331 static void
332 SFdrawStrings(w, dir, from, to)
333         register Window w;
334         register SFDir  *dir;
335         register int    from;
336         register int    to;
337 {
338         register int            i;
339         register SFEntry        *entry;
340         int                     x;
341
342         x = SFtextX - dir->hOrigin * SFcharWidth;
343
344         if (dir->vOrigin + to >= dir->nEntries) {
345                 to = dir->nEntries - dir->vOrigin - 1;
346         }
347         for (i = from; i <= to; i++) {
348                 entry = &(dir->entries[dir->vOrigin + i]);
349                 if (!(entry->statDone)) {
350                         if (SFstatAndCheck(dir, entry)) {
351                                 if (dir->vOrigin + to >= dir->nEntries) {
352                                         to = dir->nEntries - dir->vOrigin - 1;
353                                 }
354                                 i--;
355                                 continue;
356                         }
357                 }
358                 XDrawImageString(
359                         SFdisplay,
360                         w,
361                         SFtextGC,
362                         x,
363                         SFtextYoffset + i * SFentryHeight,
364                         entry->shown,
365                         strlen(entry->shown)
366                 );
367                 if (dir->vOrigin + i == dir->beginSelection) {
368                         XDrawLine(
369                                 SFdisplay,
370                                 w,
371                                 SFlineGC,
372                                 SFlineToTextH + 1,
373                                 SFlowerY + i * SFentryHeight,
374                                 SFlineToTextH + SFentryWidth - 2,
375                                 SFlowerY + i * SFentryHeight
376                         );
377                 }
378                 if (
379                         (dir->vOrigin + i >= dir->beginSelection) &&
380                         (dir->vOrigin + i <= dir->endSelection)
381                 ) {
382                         SFcompletionSegs[0].y1 = SFcompletionSegs[1].y1 =
383                                 SFlowerY + i * SFentryHeight;
384                         SFcompletionSegs[0].y2 = SFcompletionSegs[1].y2 =
385                                 SFlowerY + (i + 1) * SFentryHeight - 1;
386                         XDrawSegments(
387                                 SFdisplay,
388                                 w,
389                                 SFlineGC,
390                                 SFcompletionSegs,
391                                 2
392                         );
393                 }
394                 if (dir->vOrigin + i == dir->endSelection) {
395                         XDrawLine(
396                                 SFdisplay,
397                                 w,
398                                 SFlineGC,
399                                 SFlineToTextH + 1,
400                                 SFlowerY + (i + 1) * SFentryHeight - 1,
401                                 SFlineToTextH + SFentryWidth - 2,
402                                 SFlowerY + (i + 1) * SFentryHeight - 1
403                         );
404                 }
405         }
406         return;
407 }
408
409 void
410 SFdrawList(n, doScroll)
411         int     n;
412         int     doScroll;
413 {
414         SFDir   *dir;
415         Window  w;
416
417         SFclearList(n, doScroll);
418
419         if (SFdirPtr + (3-NR) + n < SFdirEnd) {
420                 dir = &(SFdirs[SFdirPtr + n + (3-NR)]);
421                 w = XtWindow(selFileLists[n]);
422                 XDrawImageString(
423                         SFdisplay,
424                         w,
425                         SFtextGC,
426                         SFtextX - dir->hOrigin * SFcharWidth,
427                         SFlineToTextV + SFaboveAndBelowText + SFcharAscent,
428                         dir->dir,
429                         strlen(dir->dir)
430                 );
431                 SFdrawStrings(w, dir, 0, SFlistSize - 1);
432         }
433         return;
434 }
435
436 void
437 SFdrawLists(doScroll)
438         int     doScroll;
439 {
440         int     i;
441
442         for (i = 0; i < NR; i++) {
443                 SFdrawList(i, doScroll);
444         }
445         return;
446 }
447
448 static void
449 SFinvertEntry(n)
450         register int    n;
451 {
452         XFillRectangle(
453                 SFdisplay,
454                 XtWindow(selFileLists[n]),
455                 SFinvertGC,
456                 SFlineToTextH,
457                 SFcurrentInvert[n] * SFentryHeight + SFlowerY,
458                 SFentryWidth,
459                 SFentryHeight
460         );
461         return;
462 }
463
464 static unsigned long
465 SFscrollTimerInterval()
466 {
467         static int      maxVal = 200;
468         static int      varyDist = 50;
469         static int      minDist = 50;
470         int             t;
471         int             dist;
472
473         if (SFcurrentListY < SFlowerY) {
474                 dist = SFlowerY - SFcurrentListY;
475         } else if (SFcurrentListY > SFupperY) {
476                 dist = SFcurrentListY - SFupperY;
477         } else {
478                 return (unsigned long) 1;
479         }
480
481         t = maxVal - ((maxVal / varyDist) * (dist - minDist));
482
483         if (t < 1) {
484                 t = 1;
485         }
486
487         if (t > maxVal) {
488                 t = maxVal;
489         }
490
491         return (unsigned long) t;
492 }
493
494 static void
495 SFscrollTimer(p, id)
496         XtPointer       p;
497         XtIntervalId    *id;
498 {
499         SFDir   *dir;
500         int     save;
501         int     n;
502
503         n = (int)(intptr_t) p;
504
505         dir = &(SFdirs[SFdirPtr + n]);
506         save = dir->vOrigin;
507
508         if (SFcurrentListY < SFlowerY) {
509                 if (dir->vOrigin > 0) {
510                         SFvSliderMovedCallback(selFileVScrolls[n], n,
511                                 dir->vOrigin - 1);
512                 }
513         } else if (SFcurrentListY > SFupperY) {
514                 if (dir->vOrigin < dir->nEntries - SFlistSize) {
515                         SFvSliderMovedCallback(selFileVScrolls[n], n,
516                                 dir->vOrigin + 1);
517                 }
518         }
519
520         if (dir->vOrigin != save) {
521                 if (dir->nEntries) {
522                     XawScrollbarSetThumb(
523                         selFileVScrolls[n],
524                         (float) (((double) dir->vOrigin) / dir->nEntries),
525                         (float) (((double) ((dir->nEntries < SFlistSize) ?
526                                 dir->nEntries : SFlistSize)) / dir->nEntries)
527                     );
528                 }
529         }
530
531         if (SFbuttonPressed) {
532                 SFscrollTimerId = XtAppAddTimeOut(SFapp,
533                         SFscrollTimerInterval(), SFscrollTimer, (XtPointer)(intptr_t) n);
534         }
535 }
536
537 static int
538 SFnewInvertEntry(n, event)
539         register int            n;
540         register XMotionEvent   *event;
541 {
542         register int    x, y;
543         register int    new;
544         static int      SFscrollTimerAdded = 0;
545
546         x = event->x;
547         y = event->y;
548
549         if (SFdirPtr + n >= SFdirEnd) {
550                 return -1;
551         } else if (
552                 (x >= 0)        && (x <= SFupperX) &&
553                 (y >= SFlowerY) && (y <= SFupperY)
554         ) {
555                 register SFDir *dir = &(SFdirs[SFdirPtr + n]);
556
557                 if (SFscrollTimerAdded) {
558                         SFscrollTimerAdded = 0;
559                         XtRemoveTimeOut(SFscrollTimerId);
560                 }
561
562                 new = (y - SFlowerY) / SFentryHeight;
563                 if (dir->vOrigin + new >= dir->nEntries) {
564                         return -1;
565                 }
566                 return new;
567         } else {
568                 if (SFbuttonPressed) {
569                         SFcurrentListY = y;
570                         if (!SFscrollTimerAdded) {
571                                 SFscrollTimerAdded = 1;
572                                 SFscrollTimerId = XtAppAddTimeOut(SFapp,
573                                         SFscrollTimerInterval(), SFscrollTimer,
574                                         (XtPointer)(intptr_t) n);
575                         }
576                 }
577
578                 return -1;
579         }
580 }
581
582 /* ARGSUSED */
583 void
584 SFenterList(w, n, event)
585         Widget                          w;
586         register int                    n;
587         register XEnterWindowEvent      *event;
588 {
589         register int    new;
590
591         /* sanity */
592         if (SFcurrentInvert[n] != -1) {
593                 SFinvertEntry(n);
594                 SFcurrentInvert[n] = -1;
595         }
596
597         new = SFnewInvertEntry(n, (XMotionEvent *) event);
598         if (new != -1) {
599                 SFcurrentInvert[n] = new;
600                 SFinvertEntry(n);
601         }
602 }
603
604 /* ARGSUSED */
605 void
606 SFleaveList(w, n, event)
607         Widget          w;
608         register int    n;
609         XEvent          *event;
610 {
611         if (SFcurrentInvert[n] != -1) {
612                 SFinvertEntry(n);
613                 SFcurrentInvert[n] = -1;
614         }
615 }
616
617 /* ARGSUSED */
618 void
619 SFmotionList(w, n, event)
620         Widget                  w;
621         register int            n;
622         register XMotionEvent   *event;
623 {
624         register int    new;
625
626         new = SFnewInvertEntry(n, event);
627
628         if (new != SFcurrentInvert[n]) {
629                 if (SFcurrentInvert[n] != -1) {
630                         SFinvertEntry(n);
631                 }
632                 SFcurrentInvert[n] = new;
633                 if (new != -1) {
634                         SFinvertEntry(n);
635                 }
636         }
637         return;
638 }
639
640 /* ARGSUSED */
641 void
642 SFvSliderMovedCallback(w, n, new)
643         Widget  w;
644         int     n;
645         int     new;
646 {
647         int             old;
648         register Window win;
649         SFDir           *dir;
650
651         dir = &(SFdirs[SFdirPtr + n]);
652
653
654         old = dir->vOrigin;
655         if(new == -1) new = old + 1; else if(new == -2) new = old - 1; // [HGM] indicates scroll direction on mousewheel event
656         if(new < 0 || new > dir->nEntries - SFlistSize) return;
657         dir->vOrigin = new;
658
659         if (old == new) {
660                 return;
661         }
662
663         win = XtWindow(selFileLists[n]);
664
665         if (ABS(new - old) < SFlistSize) {
666                 if (new > old) {
667                         XCopyArea(
668                                 SFdisplay,
669                                 win,
670                                 win,
671                                 SFscrollGC,
672                                 SFlineToTextH,
673                                 SFlowerY + (new - old) * SFentryHeight,
674                                 SFentryWidth + SFlineToTextH,
675                                 (SFlistSize - (new - old)) * SFentryHeight,
676                                 SFlineToTextH,
677                                 SFlowerY
678                         );
679                         XClearArea(
680                                 SFdisplay,
681                                 win,
682                                 SFlineToTextH,
683                                 SFlowerY + (SFlistSize - (new - old)) *
684                                         SFentryHeight,
685                                 SFentryWidth + SFlineToTextH,
686                                 (new - old) * SFentryHeight,
687                                 False
688                         );
689                         SFdrawStrings(win, dir, SFlistSize - (new - old),
690                                 SFlistSize - 1);
691                 } else {
692                         XCopyArea(
693                                 SFdisplay,
694                                 win,
695                                 win,
696                                 SFscrollGC,
697                                 SFlineToTextH,
698                                 SFlowerY,
699                                 SFentryWidth + SFlineToTextH,
700                                 (SFlistSize - (old - new)) * SFentryHeight,
701                                 SFlineToTextH,
702                                 SFlowerY + (old - new) * SFentryHeight
703                         );
704                         XClearArea(
705                                 SFdisplay,
706                                 win,
707                                 SFlineToTextH,
708                                 SFlowerY,
709                                 SFentryWidth + SFlineToTextH,
710                                 (old - new) * SFentryHeight,
711                                 False
712                         );
713                         SFdrawStrings(win, dir, 0, old - new);
714                 }
715         } else {
716                 XClearArea(
717                         SFdisplay,
718                         win,
719                         SFlineToTextH,
720                         SFlowerY,
721                         SFentryWidth + SFlineToTextH,
722                         SFlistSize * SFentryHeight,
723                         False
724                 );
725                 SFdrawStrings(win, dir, 0, SFlistSize - 1);
726         }
727 }
728
729 /* ARGSUSED */
730 void
731 SFvFloatSliderMovedCallback(w, n, fnew)
732         Widget  w;
733         int     n;
734         float   *fnew;
735 {
736         int     new;
737
738         new = (*fnew) * SFdirs[SFdirPtr + n].nEntries;
739
740         SFvSliderMovedCallback(w, n, new);
741 }
742
743
744 /* ARGSUSED */
745 void
746 SFvAreaSelectedCallback(w, n, pnew)
747         Widget  w;
748         int     n;
749         int     pnew;
750 {
751         SFDir   *dir;
752         int     new;
753
754         dir = &(SFdirs[SFdirPtr + n]);
755
756         new = dir->vOrigin +
757                 (((double) pnew) / SFvScrollHeight) * dir->nEntries;
758
759         if (new > dir->nEntries - SFlistSize) {
760                 new = dir->nEntries - SFlistSize;
761         }
762
763         if (new < 0) {
764                 new = 0;
765         }
766
767         if (dir->nEntries) {
768                 float   f;
769
770                 f = ((double) new) / dir->nEntries;
771
772                 XawScrollbarSetThumb(
773                         w,
774                         f,
775                         (float) (((double) ((dir->nEntries < SFlistSize) ?
776                                 dir->nEntries : SFlistSize)) / dir->nEntries)
777                 );
778         }
779
780         SFvSliderMovedCallback(w, n, new);
781 }
782
783 /* ARGSUSED */
784 void
785 SFhSliderMovedCallback(w, n, new)
786         Widget  w;
787         int     n;
788         float   *new;
789 {
790         SFDir   *dir;
791         int     save;
792
793         dir = &(SFdirs[SFdirPtr + n]);
794         save = dir->hOrigin;
795         dir->hOrigin = (*new) * dir->nChars;
796         if (dir->hOrigin == save) {
797                 return;
798         }
799
800         SFdrawList(n, SF_DO_NOT_SCROLL);
801 }
802
803 /* ARGSUSED */
804 void
805 SFhAreaSelectedCallback(w, n, pnew)
806         Widget  w;
807         int     n;
808         int     pnew;
809 {
810         SFDir   *dir;
811         int     new;
812
813         dir = &(SFdirs[SFdirPtr + n]);
814
815         new = dir->hOrigin +
816                 (((double) pnew) / SFhScrollWidth) * dir->nChars;
817
818         if (new > dir->nChars - SFcharsPerEntry) {
819                 new = dir->nChars - SFcharsPerEntry;
820         }
821
822         if (new < 0) {
823                 new = 0;
824         }
825
826         if (dir->nChars) {
827                 float   f;
828
829                 f = ((double) new) / dir->nChars;
830
831                 XawScrollbarSetThumb(
832                         w,
833                         f,
834                         (float) (((double) ((dir->nChars < SFcharsPerEntry) ?
835                                 dir->nChars : SFcharsPerEntry)) / dir->nChars)
836                 );
837
838                 SFhSliderMovedCallback(w, n, &f);
839         }
840 }
841
842 /* ARGSUSED */
843 void
844 SFpathSliderMovedCallback(w, client_data, new)
845         Widget          w;
846         XtPointer       client_data;
847         float   *new;
848 {
849         SFDir           *dir;
850         int             n;
851         XawTextPosition pos;
852         int     SFdirPtrSave;
853
854         SFdirPtrSave = SFdirPtr;
855         SFdirPtr = (*new) * SFdirEnd;
856         if (SFdirPtr == SFdirPtrSave) {
857                 return;
858         }
859
860         SFdrawLists(SF_DO_SCROLL);
861
862         n = 2;
863         while (SFdirPtr + n >= SFdirEnd) {
864                 n--;
865         }
866
867         dir = &(SFdirs[SFdirPtr + n]);
868
869         pos = dir->path - SFcurrentPath;
870
871         if (!strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) {
872                 pos -= strlen(SFstartDir);
873                 if (pos < 0) {
874                         pos = 0;
875                 }
876         }
877
878         XawTextSetInsertionPoint(selFileField, pos);
879 }
880
881 /* ARGSUSED */
882
883 void
884 SFpathAreaSelectedCallback(w, client_data, pnew)
885         Widget          w;
886         XtPointer       client_data;
887         int             pnew;
888 {
889         int     new;
890         float   f;
891
892         new = SFdirPtr + (((double) pnew) / SFpathScrollWidth) * SFdirEnd;
893
894         if (new > SFdirEnd - 3) {
895                 new = SFdirEnd - 3;
896         }
897
898         if (new < 0) {
899                 new = 0;
900         }
901
902         f = ((double) new) / SFdirEnd;
903
904         XawScrollbarSetThumb(
905                 w,
906                 f,
907                 (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) /
908                         SFdirEnd)
909         );
910
911         SFpathSliderMovedCallback(w, (XtPointer) NULL, &f);
912 }
913
914 Boolean
915 SFworkProc()
916 {
917         register SFDir          *dir;
918         register SFEntry        *entry;
919
920         for (dir = &(SFdirs[SFdirEnd - 1]); dir >= SFdirs; dir--) {
921                 if (!(dir->nEntries)) {
922                         continue;
923                 }
924                 for (
925                         entry = &(dir->entries[dir->nEntries - 1]);
926                         entry >= dir->entries;
927                         entry--
928                 ) {
929                         if (!(entry->statDone)) {
930                                 (void) SFstatAndCheck(dir, entry);
931                                 return False;
932                         }
933                 }
934         }
935
936         SFworkProcAdded = 0;
937
938         return True;
939 }