1 /*
2  * FChooser.c : A widget for choosing a file
3  *
4  * George Ferguson, ferguson@cs.rochester.edu, 21 Jan 1993.
5  *
6  * This code is derived from the FileSelector widget by Brian Totty,
7  * hence the following copyright applies:
8  *
9  * Copyright 1990,1991,1992 Brian Totty
10  *
11  * Permission to use, copy, modify, distribute, and sell this software
12  * and its documentation for any purpose is hereby granted without fee,
13  * provided that the above copyright notice appears in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation, and that the name of Brian Totty or
16  * University of Illinois not be used in advertising or publicity
17  * pertaining to distribution of the software without specific, written
18  * prior permission.  Brian Totty and University of Illinois make no
19  * representations about the suitability of this software for any
20  * purpose.  It is provided "as is" without express or implied warranty.
21  *
22  * Brian Totty and University of Illinois disclaim all warranties with
23  * regard to this software, including all implied warranties of
24  * merchantability and fitness, in no event shall Brian Totty or
25  * University of Illinois be liable for any special, indirect or
26  * consequential damages or any damages whatsoever resulting from loss of
27  * use, data or profits, whether in an action of contract, negligence or
28  * other tortious action, arising out of or in connection with the use or
29  * performance of this software.
30  *
31  * Author:
32  * 	Brian Totty
33  * 	Department of Computer Science
34  * 	University Of Illinois at Urbana-Champaign
35  *	1304 West Springfield Avenue
36  * 	Urbana, IL 61801
37  *
38  * 	totty@cs.uiuc.edu
39  *
40  */
41 
42 #include <X11/Xlib.h>
43 #include <X11/Xos.h>
44 #include <X11/IntrinsicP.h>
45 #include <X11/Intrinsic.h>
46 #include <X11/StringDefs.h>
47 #include <X11/cursorfont.h>
48 #include <X11/CompositeP.h>
49 #include <X11/Composite.h>
50 #ifdef USE_XAW3D
51 #include <X11/Xaw3d/MenuButton.h>
52 #include <X11/Xaw3d/SimpleMenu.h>
53 #include <X11/Xaw3d/SmeBSB.h>
54 #include <X11/Xaw3d/Viewport.h>
55 #include <X11/Xaw3d/List.h>
56 #else
57 #include <X11/Xaw/MenuButton.h>
58 #include <X11/Xaw/SimpleMenu.h>
59 #include <X11/Xaw/SmeBSB.h>
60 #include <X11/Xaw/Viewport.h>
61 #include <X11/Xaw/List.h>
62 #endif
63 
64 #include "DirMgr.h"
65 #include "FChooserP.h"
66 #include "FChooser.h"
67 
68 /*---------------------------------------------------------------------------*
69 
70                    I N T E R N A L    R O U T I N E S
71 
72  *---------------------------------------------------------------------------*/
73 
74 #if (!NeedFunctionPrototypes)
75 
76 static void		Initialize();
77 static void		Realize();
78 static void		Destroy();
79 static void		Resize();
80 static Boolean		SetValues();
81 static XtGeometryResult	GeometryManager();
82 static void		ChildrenCreate();
83 static void		ChildrenRealize();
84 static void		ChildrenUpdate();
85 static void		DirectoryCallback();
86 static void		FileCallback();
87 static void		SelectFileByIndex();
88 static Boolean		SelectFileByName();
89 static void		UnselectAll();
90 static void		Notify();
91 static void		GotoDeepestLegalDirectory();
92 static void		UpdateLists();
93 static void		Chdir();
94 
95 #else
96 
97 static void		Initialize(Widget request, Widget new);
98 static void		Realize(Widget w, XtValueMask *valueMask,
99 				XSetWindowAttributes *attrs);
100 static void		Destroy(XfwfFileChooserWidget fcw);
101 static void		Resize(Widget w);
102 static Boolean		SetValues(Widget current, Widget request, Widget new);
103 static XtGeometryResult	GeometryManager(Widget w,
104 				XtWidgetGeometry *request,
105 				XtWidgetGeometry *reply);
106 static void		ChildrenCreate(XfwfFileChooserWidget fcw);
107 static void		ChildrenRealize(XfwfFileChooserWidget fcw);
108 static void		ChildrenUpdate(XfwfFileChooserWidget fcw);
109 static void		DirectoryCallback(Widget w, XtPointer client_data,
110 				XtPointer call_data);
111 static void		FileCallback(Widget w, XtPointer client_data,
112 				XtPointer call_data);
113 static void		SelectFileByIndex(XfwfFileChooserWidget fcw, int index);
114 static Boolean		SelectFileByName(XfwfFileChooserWidget fcw, char *name);
115 static void		UnselectAll(XfwfFileChooserWidget fcw);
116 static void		Notify(XfwfFileChooserWidget fcw);
117 static void		GotoDeepestLegalDirectory(XfwfFileChooserWidget fcw);
118 static void		UpdateLists(XfwfFileChooserWidget fcw);
119 static void		Chdir(XfwfFileChooserWidget fcw);
120 
121 #endif
122 
123 /*---------------------------------------------------------------------------*
124 
125               R E S O U R C E    I N I T I A L I Z A T I O N
126 
127  *---------------------------------------------------------------------------*/
128 
129 #define FCFieldOffset(FIELD)	XtOffset(XfwfFileChooserWidget,fileChooser.FIELD)
130 #define CoreFieldOffset(FIELD)	XtOffset(Widget,core.FIELD)
131 
132 static XtResource resources[] = {
133     { XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
134 	 CoreFieldOffset(width), XtRString, "500" },
135     { XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
136 	 CoreFieldOffset(height), XtRString, "250" },
137     { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
138 	 CoreFieldOffset(background_pixel), XtRString, "white" },
139     { XtNcurrentDirectory, XtCPathname, XtRString, sizeof(String),
140 	 FCFieldOffset(current_dir), XtRString, NULL },
141     { XtNcurrentFile, XtCFilename, XtRString, sizeof(String),
142 	 FCFieldOffset(current_file), XtRString, NULL },
143     { XtNcallback, XtCCallback, XtRCallback,
144 	 sizeof(XtCallbackList), FCFieldOffset(callbacks), XtRCallback, NULL},
145     {XtNsortMode, XtCValue, XtRInt, sizeof(int),
146 	 FCFieldOffset(sort_mode), XtRString, "2"},
147     {XtNpattern, XtCFile, XtRString, sizeof(String),
148 	 FCFieldOffset(pattern), XtRString, NULL},
149 };
150 
151 #undef FCFieldOffset
152 #undef CoreFieldOffset
153 
154 /*---------------------------------------------------------------------------*
155 
156                     C L A S S    A L L O C A T I O N
157 
158  *---------------------------------------------------------------------------*/
159 
160 XfwfFileChooserClassRec xfwfFileChooserClassRec = {
161     {
162 	/* superclass		*/	(WidgetClass)&compositeClassRec,
163 	/* class_name		*/	"XfwfFileChooser",
164 	/* widget_size		*/	sizeof(XfwfFileChooserRec),
165 	/* class_initialize	*/	NULL,
166 	/* class_part_initialize*/	NULL,
167 	/* class_inited		*/	FALSE,
168 	/* initialize		*/	(XtInitProc)Initialize,
169 	/* initialize_hook	*/	NULL,
170 	/* realize		*/	(XtRealizeProc)Realize,
171 	/* actions		*/	NULL,
172 	/* num_actions		*/	0,
173 	/* resources		*/	resources,
174 	/* resource_count	*/	XtNumber(resources),
175 	/* xrm_class		*/	NULLQUARK,
176 	/* compress_motion	*/	TRUE,
177 	/* compress_exposure	*/	TRUE,
178 	/* compress_enterleave	*/	TRUE,
179 	/* visible_interest	*/	FALSE,
180 	/* destroy		*/	(XtWidgetProc)Destroy,
181 	/* resize		*/	(XtWidgetProc)Resize,
182 	/* expose		*/	XtInheritExpose,
183 	/* set_values		*/	(XtSetValuesFunc)SetValues,
184 	/* set_values_hook	*/	NULL,
185 	/* set_values_almost	*/	XtInheritSetValuesAlmost,
186 	/* get_values_hook	*/	NULL,
187 	/* accept_focus		*/	NULL,
188 	/* version		*/	XtVersion,
189 	/* callback_private	*/	NULL,
190 	/* tm_table		*/	NULL,
191 	/* query_geometry       */	XtInheritQueryGeometry,
192 	/* display_accelerator  */	XtInheritDisplayAccelerator,
193 	/* extension            */	NULL
194     }, /* Core Part */
195     {
196 	/* geometry_manager     */	GeometryManager,
197 	/* change_managed       */	XtInheritChangeManaged,
198 	/* insert_child         */	XtInheritInsertChild,
199 	/* delete_child         */	XtInheritDeleteChild,
200 	/* extension            */	NULL
201     }, /* Composite Part */
202     {
203 	/* no extra class data  */	0
204     }  /* FileSelector Part */
205 };
206 
207 WidgetClass xfwfFileChooserWidgetClass =
208 	(WidgetClass)&xfwfFileChooserClassRec;
209 
210 /*---------------------------------------------------------------------------*
211 
212                     E X P O R T E D    M E T H O D S
213 
214  *---------------------------------------------------------------------------*/
215 
216 
217 /*---------------------------------------------------------------------------*
218 
219 	Initialize()
220 
221 	This procedure is called by the X toolkit to initialize
222 	the FileChooser widget instance.  The hook to this routine is in the
223 	initialize part of the core part of the class.
224 
225  *---------------------------------------------------------------------------*/
226 
227 /* ARGSUSED */
228 static void
Initialize(request,new)229 Initialize(request,new)
230 Widget request,new;
231 {
232     XfwfFileChooserWidget fcw = (XfwfFileChooserWidget)new;
233     char *str,*initial_file,path[MAXPATHLEN + 2];
234     static char *star = "*";
235 
236     FCBusyCursor(fcw) = XCreateFontCursor(XtDisplay(fcw),XC_watch);
237     FCDirMenu(fcw) = NULL;
238     FCDirMenuButton(fcw) = NULL;
239     FCFileList(fcw) = NULL;
240     FCDirMgr(fcw) = NULL;
241     FCFileNames(fcw) = NULL;
242     FCNumFileNames(fcw) = 0;
243     FCDirNames(fcw) = NULL;
244     FCNumDirNames(fcw) = 0;
245 
246     str = (char *)XtCalloc((MAXPATHLEN + 2),sizeof(char));
247     if (FCCurrentDirectory(fcw) != NULL) {	/* User Specified Path */
248 	strcpy(str,FCCurrentDirectory(fcw));
249     } else {
250 	getwd(path);
251 	strcpy(str,path);
252     }
253     FCCurrentDirectory(fcw) = str;
254 
255     str = (char *)XtCalloc((MAXPATHLEN + 2),sizeof(char));
256     initial_file = FCCurrentFile(fcw);
257     FCCurrentFile(fcw) = str;
258 
259     if (FCCorePart(request)->width <= 0)
260 	FCCorePart(new)->width = 500;
261     if (FCCorePart(request)->height <= 0)
262 	FCCorePart(new)->height = 200;
263 
264     if (FCPattern(fcw) == NULL)
265 	FCPattern(fcw) = star;
266     FCPattern(fcw) = XtNewString(FCPattern(fcw));
267 
268     ChildrenCreate(fcw);
269     GotoDeepestLegalDirectory(fcw);
270     if (initial_file)
271 	SelectFileByName(fcw,initial_file);
272 } /* End Initialize */
273 
274 /*---------------------------------------------------------------------------*
275 
276 	Realize()
277 
278 	This function is called to realize a FileChooser widget.
279 
280  *---------------------------------------------------------------------------*/
281 
Realize(w,valueMask,attrs)282 static void Realize(w,valueMask,attrs)
283 Widget w;
284 XtValueMask *valueMask;
285 XSetWindowAttributes *attrs;
286 {
287     XfwfFileChooserWidget fcw = (XfwfFileChooserWidget)w;
288 
289     XtCreateWindow(w,InputOutput,(Visual *)CopyFromParent,*valueMask,attrs);
290     ChildrenRealize(fcw);
291     ChildrenUpdate(fcw);
292     Resize(w);
293     Notify(fcw);
294 } /* End Realize */
295 
296 /*---------------------------------------------------------------------------*
297 
298 	Destroy()
299 
300 	This function is called to destroy a FileChooser widget.
301 
302  *---------------------------------------------------------------------------*/
303 
304 static void
Destroy(fcw)305 Destroy(fcw)
306 XfwfFileChooserWidget fcw;
307 {
308     int i;
309 
310     XtFree(FCCurrentDirectory(fcw));
311     XtFree(FCCurrentFile(fcw));
312     XtFree(FCPattern(fcw));
313     if (FCFileNames(fcw) != NULL) {
314 	for (i=0; i < FCNumFileNames(fcw); i++)
315 	    XtFree(FCFileNames(fcw)[i]);
316 	XtFree(FCFileNames(fcw));
317     }
318     if (FCDirNames(fcw) != NULL) {
319 	for (i=0; i < FCNumDirNames(fcw); i++)
320 	    XtFree(FCDirNames(fcw)[i]);
321 	XtFree(FCDirNames(fcw));
322     }
323 } /* End Destroy */
324 
325 /*---------------------------------------------------------------------------*
326 
327 	Resize()
328 
329 	This function is called to resize a FileChooser widget.
330 
331  *---------------------------------------------------------------------------*/
332 
333 static void
Resize(w)334 Resize(w)
335 Widget w;
336 {
337     XfwfFileChooserWidget fcw = (XfwfFileChooserWidget)w;
338 
339     ChildrenUpdate(fcw);
340 } /* End Resize */
341 
342 /*---------------------------------------------------------------------------*
343 
344 	SetValues()
345 
346 	This function is the external interface for setting resources.
347 
348  *---------------------------------------------------------------------------*/
349 
350 /* ARGSUSED */
351 static Boolean
SetValues(current,request,new)352 SetValues(current,request,new)
353 Widget current,request,new;
354 {
355     XfwfFileChooserWidget fcw_current = (XfwfFileChooserWidget)current;
356     XfwfFileChooserWidget fcw_new = (XfwfFileChooserWidget)new;
357     XfwfFileChooserWidget fcw_request = (XfwfFileChooserWidget)request;
358 
359     if (FCCurrentDirectory(fcw_current) != FCCurrentDirectory(fcw_new)){
360 /*	strcpy(FCCurrentDirectory(fcw_current),FCCurrentDirectory(fcw_new));
361 	FCCurrentDirectory(fcw_new) = FCCurrentDirectory(fcw_current); */
362 	/* didn't understand why to copy the new string into old space and
363 	   use this for the new widget ?! - ACZ */
364 	Chdir(fcw_new);
365     }
366     if (FCCurrentFile(fcw_current) != FCCurrentFile(fcw_new)) {
367 	char *new_name;
368 
369 	new_name = FCCurrentFile(fcw_new);
370 	FCCurrentFile(fcw_new) = FCCurrentFile(fcw_current);
371 	SelectFileByName(fcw_new,new_name);
372     }
373     if (FCPattern(fcw_current) != FCPattern(fcw_new)) {
374 /*	XtFree(FCPattern(fcw_current));
375 	FCPattern(fcw_new) = XtNewString(FCPattern(fcw_request)); */
376 		/* above was XtNewString(FCPattern(fcw_current)) - ACZ */
377 	Chdir(fcw_new);
378     }
379     if (FCSortMode(fcw_current) != FCSortMode(fcw_new)) {
380 	Chdir(fcw_new);
381     }
382     return(False);
383 } /* End SetValues() */
384 
385 /*---------------------------------------------------------------------------*
386 
387 	GeometryManager(w,request,reply)
388 
389 	This routine acts as the geometry_manager method for the
390 	FileChooser widget.  It is called when a child wants to
391 	resize/reposition itself.
392 
393 	Currently, we allow all requests.
394 
395  *---------------------------------------------------------------------------*/
396 
397 /* ARGSUSED */
398 static XtGeometryResult
GeometryManager(w,request,reply)399 GeometryManager(w,request,reply)
400 Widget w;
401 XtWidgetGeometry *request;
402 XtWidgetGeometry *reply;
403 {
404     return(XtGeometryYes);
405 } /* End GeometryManager */
406 
407 /*---------------------------------------------------------------------------*
408 
409                      L O C A L    R O U T I N E S
410 
411  *---------------------------------------------------------------------------*/
412 
413 /*---------------------------------------------------------------------------*
414 
415 	ChildrenCreate(fcw)
416 
417 	This routine creates the initial child widgets for the
418 	file selector widget and places them in the widget fcw.
419 	No placement or resizing is done.  That is done by
420 	ChildrenUpdate().
421 
422  *---------------------------------------------------------------------------*/
423 
424 static void
ChildrenCreate(fcw)425 ChildrenCreate(fcw)
426 XfwfFileChooserWidget fcw;
427 {
428     static char *str = NULL;
429 
430     FCDirMenuButton(fcw) =
431 	XtVaCreateManagedWidget(FC_DIR_MENU_BUTTON_NAME,menuButtonWidgetClass,
432 		(Widget)fcw,
433 		XtNmenuName, FC_DIR_MENU_NAME,
434 		NULL);
435 
436     FCFileViewport(fcw) =
437 	XtVaCreateManagedWidget(FC_FILE_VIEWPORT_NAME,viewportWidgetClass,
438 		(Widget)fcw,
439 		XtNallowVert, True,
440 		XtNforceBars, True,
441 		XtNuseRight, True,
442 		NULL);
443 
444     FCFileList(fcw) = XtVaCreateManagedWidget(FC_FILE_LIST_NAME,listWidgetClass,
445 		FCFileViewport(fcw),
446 		XtNlist, &str,
447 		XtNdefaultColumns, 1,
448 		XtNforceColumns, True,
449 		XtNverticalList, True,
450 		/* XtNborderWidth, 0, */
451 		NULL);
452 
453     XtAddCallback(FCFileList(fcw),XtNcallback,
454 		  (XtCallbackProc)FileCallback,(XtPointer)fcw);
455 } /* End ChildrenCreate */
456 
457 /*---------------------------------------------------------------------------*
458 
459 	ChildrenRealize(fcw)
460 
461 	This routine realizes the child widgets.  The widgets must
462 	already have been created and initialized.  Their coordinates
463 	should already have been set.
464 
465  *---------------------------------------------------------------------------*/
466 
467 static void
ChildrenRealize(fcw)468 ChildrenRealize(fcw)
469 XfwfFileChooserWidget fcw;
470 {
471     XtRealizeWidget(FCDirMenuButton(fcw));
472     XtRealizeWidget(FCFileViewport(fcw));
473     XtRealizeWidget(FCFileList(fcw));
474 } /* End ChildrenRealize */
475 
476 /*---------------------------------------------------------------------------*
477 
478 	ChildrenUpdate(fcw)
479 
480 	This routine takes a FileChooser widget fcw and updates
481 	the child widgets by recalculating their coordinates based
482 	on the current size of the FileChooser, and setting the
483 	appropriate resources.
484 
485 	We go to some trouble to get something useful displayed in the
486 	FCDirMenuButton(fcw) if the current directory name is too long
487 	to fit in the button. It would be nice if the "justify" resource
488 	of the MenuButton widget did this, but...
489 
490  *---------------------------------------------------------------------------*/
491 
492 static void
ChildrenUpdate(fcw)493 ChildrenUpdate(fcw)
494 XfwfFileChooserWidget fcw;
495 {
496     Dimension w,h;
497     int gap;
498     Dimension menuButtonW,menuButtonH,fileListW,fileListH;
499     Position menuButtonX,menuButtonY,fileListX,fileListY;
500     XtWidgetGeometry parent_idea,child_idea;
501     XFontStruct *fs;
502     char *label;
503     Dimension intw;
504 
505     if (!XtIsRealized((Widget)fcw)) {
506 	return;
507     }
508     w = CoreWidth(fcw);
509     h = CoreHeight(fcw);
510     gap = 3;
511 
512     intw = 8;
513 
514     XtVaGetValues(FCDirMenuButton(fcw), XtNfont, &fs, NULL);
515     /* Get The Child Widgets Current Widths And Heights */
516     /* (although we don't actually use the existing values... */
517     menuButtonW = CoreWidth(FCDirMenuButton(fcw));
518     menuButtonH = CoreHeight(FCDirMenuButton(fcw));
519 
520     /* Adjust Widths */
521     menuButtonW = w;
522     fileListW = w;
523 
524     /* Adjust menu button label if too small */
525     /* It would be nice if the "justify" resource of MenuButton did this... */
526     label = FCCurrentDirectory(fcw);
527     if (XTextWidth(fs,label,strlen(label)) > menuButtonW-intw) {
528 	char newLabel[MAXPATHLEN];
529 	/* cut away chars from left side until it fits into the button */
530 	while (*label && XTextWidth(fs,label,strlen(label))>menuButtonW-intw) {
531 	    label += 1;
532 	}
533 	if (*label)
534 	    label += 1;
535 	strcpy(newLabel,""); /* was "<" - ACZ */
536 	strcat(newLabel,label);
537 	XtVaSetValues(FCDirMenuButton(fcw), XtNlabel, newLabel, NULL);
538     } else {
539 	XtVaSetValues(FCDirMenuButton(fcw), XtNlabel, label, NULL);
540     }
541     /* Adjust Heights */
542     fileListH = h - menuButtonH - gap;
543     /* Listen To Child Height Request For List */
544 /*
545     parent_idea.request_mode = CWWidth | CWHeight;
546     parent_idea.width = fileListW;
547     parent_idea.height = fileListH;
548     XtQueryGeometry(FCFileList(fcw),&parent_idea,&child_idea);
549     if ((child_idea.request_mode & CWHeight) &&
550 	(child_idea.height < parent_idea.height)) {
551 	fileListH = child_idea.height;
552     }
553 */
554     /* Vertical Positions */
555     menuButtonY = 0;
556     fileListY = menuButtonH + gap;
557     /* Horizontal Positions */
558     menuButtonX = 0;
559     fileListX = 0;
560 
561     /* Move them */
562     XtMoveWidget(FCDirMenuButton(fcw),menuButtonX,menuButtonY);
563     XtMoveWidget(FCFileViewport(fcw),fileListX,fileListY);
564     /* Resize them */
565     XtResizeWidget(FCDirMenuButton(fcw),menuButtonW,menuButtonH,
566 		   CoreBorderWidth(FCDirMenuButton(fcw)));
567     XtResizeWidget(FCFileViewport(fcw),fileListW,fileListH,
568 		   CoreBorderWidth(FCFileViewport(fcw)));
569 } /* End ChildrenUpdate */
570 
571 /*---------------------------------------------------------------------------*
572 
573                  I N T E R N A L    C A L L B A C K S
574 
575  *---------------------------------------------------------------------------*/
576 
577 /*---------------------------------------------------------------------------*
578 
579 	DirectoryCallback(w,client_data,call_data)
580 
581 	This is called when the user selects an ancestor directory from
582 	the menu. The argument "w" is the selected object (hence the two
583 	calls to XtParent()) and "client_data" is it's index in the menu.
584 
585  *---------------------------------------------------------------------------*/
586 
587 /*ARGSUSED*/
588 static void
DirectoryCallback(w,client_data,call_data)589 DirectoryCallback(w,client_data,call_data)
590 Widget w;
591 XtPointer client_data;	/* index */
592 XtPointer call_data;	/* not used */
593 {
594     int index = (int)client_data;
595     XfwfFileChooserWidget fcw = (XfwfFileChooserWidget)XtParent(XtParent(w));
596     int i;
597 
598     strcpy(FCCurrentDirectory(fcw),"/");
599     for (i = 1; i <= index; i++) {
600 	strcat(FCCurrentDirectory(fcw),FCDirNames(fcw)[i]);
601 	strcat(FCCurrentDirectory(fcw),"/");
602     }
603     Chdir(fcw);
604 } /* End DirectoryCallback */
605 
606 /*---------------------------------------------------------------------------*
607 
608 	FileCallback(w,client_data,call_data)
609 
610 	This is called when the user selects a file in the fileList.
611 	The argument "client_data" is the FileChooser widget, "call_data"
612 	is a pointer to the standard List widget callback information.
613 
614  *---------------------------------------------------------------------------*/
615 
616 /*ARGSUSED*/
617 static void
FileCallback(w,client_data,call_data)618 FileCallback(w,client_data,call_data)
619 Widget w;
620 XtPointer client_data;	/* fcw */
621 XtPointer call_data;	/* return struct */
622 {
623     XfwfFileChooserWidget fcw = (XfwfFileChooserWidget)client_data;
624     XawListReturnStruct *ret = (XawListReturnStruct *)call_data;
625 
626     if (ret->list_index == -1) {
627 	UnselectAll(fcw);		/* Click On Blank Space */
628 	Notify(fcw);
629     } else {
630 	SelectFileByIndex(fcw,ret->list_index);
631     }
632 } /* End FileCallback */
633 
634 /*---------------------------------------------------------------------------*
635 
636              I N T E R N A L    S U P P O R T    R O U T I N E S
637 
638  *---------------------------------------------------------------------------*/
639 
640 static void
SelectFileByIndex(fcw,index)641 SelectFileByIndex(fcw,index)
642 XfwfFileChooserWidget fcw;
643 int index;
644 {
645     DirEntry *dir_entry;
646 
647     DirectoryMgrGotoItem(FCDirMgr(fcw),index);
648     if ((dir_entry=DirectoryMgrCurrentEntry(FCDirMgr(fcw))) == NULL) {
649 	fprintf(stderr,"SelectFileByIndex: Entry %d invalid\n",index);
650 	exit(-1);
651     }
652     if (DirEntryIsDir(dir_entry) || DirEntryIsDirectoryLink(dir_entry)) {
653 	strcat(FCCurrentDirectory(fcw),DirEntryFileName(dir_entry));
654 	Chdir(fcw);
655     } else if (!DirEntryIsBrokenLink(dir_entry)) {	/* File */
656 	strcpy(FCCurrentFile(fcw),DirEntryFileName(dir_entry));
657 	XawListHighlight(FCFileList(fcw),index);
658 	Notify(fcw);
659     } else {						/* Broken Link */
660 	XBell(XtDisplay(fcw),0);
661 	UnselectAll(fcw);
662     }
663 } /* End SelectFileByIndex */
664 
665 static Boolean
SelectFileByName(fcw,name)666 SelectFileByName(fcw,name)
667 XfwfFileChooserWidget fcw;
668 char *name;
669 {
670     if (DirectoryMgrGotoNamedItem(FCDirMgr(fcw),name) == FALSE) {
671 	return(False);
672     }
673     SelectFileByIndex(fcw,DirectoryMgrCurrentIndex(FCDirMgr(fcw)));
674     return(True);
675 } /* End SelectFileByName */
676 
677 static void
UnselectAll(fcw)678 UnselectAll(fcw)
679 XfwfFileChooserWidget fcw;
680 {
681     Boolean selected = FCCurrentFile(fcw)[0] != '\0';
682 
683     FCCurrentFile(fcw)[0] = '\0';
684     XawListUnhighlight(FCFileList(fcw));
685     if (selected)
686 	Notify(fcw);
687 } /* End UnselectAll */
688 
689 static void
Notify(fcw)690 Notify(fcw)
691 XfwfFileChooserWidget fcw;
692 {
693     XfwfFileChooserReturnStruct ret;
694 
695     if (FCCurrentFile(fcw)[0] != '\0') {
696 	ret.directory = FCCurrentDirectory(fcw);
697 	ret.file = FCCurrentFile(fcw);
698     } else {
699 	ret.directory = NULL;
700 	ret.file = NULL;
701     }
702     XtCallCallbacks((Widget)fcw,XtNcallback,(XtPointer)&ret);
703 } /* End Notify */
704 
705 /*---------------------------------------------------------------------------*
706 
707 	GotoDeepestLegalDirectory(fcw)
708 
709 	This function takes a FileChooser widget <fcw> and modifies the
710 	directory string in FCCurrentDirectory(fcw) to be the deepest
711 	legal directory above the string.  Partial or incorrect directory
712 	names are stripped starting at the end.
713 
714 	It then calls UpdateLists() to reset the information dislayed in
715 	the FileChooser.
716 
717  *---------------------------------------------------------------------------*/
718 
719 static void
GotoDeepestLegalDirectory(fcw)720 GotoDeepestLegalDirectory(fcw)
721 XfwfFileChooserWidget fcw;
722 {
723     char *dir,*end;
724     char temp[MAXPATHLEN + 2];
725 
726     dir = FCCurrentDirectory(fcw);
727     for (end=dir; *end != '\0'; ++end)
728 	/*EMPTY*/;
729     while (1) {
730 	if (DirectoryPathExpand(dir,temp) == NULL) {
731 	    while (*end != '/' && end != dir) {
732 		end -= 1;
733 	    }
734 	    *end = '\0';
735 	} else {
736 	    strcpy(FCCurrentDirectory(fcw),temp);
737 	    break;
738 	}
739     }
740     UnselectAll(fcw);
741     UpdateLists(fcw);
742 } /* End GotoDeepestLegalDirectory */
743 
744 /*---------------------------------------------------------------------------*
745 
746 	UpdateLists(fcw)
747 
748 	This routine resets the information displayed in a FileChooser
749 	widget by doing the following:
750 	1. It changes the cursor to the value of the "busyCursor" resource.
751 	2. The old directory manager (FCDirMgr(fcw)) is closed and a
752 	   new one opened based on the value of FCCurrentDirectory(fcw).
753 	3. Any old string arrays are freed, and any existing directory
754 	   menu is destroyed.
755 	4. A new array of strings (FCFileNames(fcw)) is allocated and filled
756 	   with the names of the files in the directory, then this array
757 	   is used to set what is displayed in the FCFileList(fcw) widget.
758 	5. A new array of strings (FCDirNames(fcw)) is allocated and filled
759 	   with the names of the ancestor directories. These are also used
760 	   to create a new FCDirMenu(fcw), and the label FCDirMenuButton(fcw)
761 	   is set to the name of the directory.
762 	6. Finally, the cursor is restored.
763 
764  *---------------------------------------------------------------------------*/
765 
766 static void
UpdateLists(fcw)767 UpdateLists(fcw)
768 XfwfFileChooserWidget fcw;
769 {
770     int i,count;
771     char *dir,*start;
772     DirEntry *dir_entry;
773     char temp[MAXPATHLEN + 2];
774     Widget menuItem;
775 
776     if (XtIsRealized((Widget)fcw)) {
777 	/* This is puke-ola. */
778 	XDefineCursor(XtDisplay(fcw),XtWindow(fcw),FCBusyCursor(fcw));
779 	XDefineCursor(XtDisplay(fcw),XtWindow(FCFileList(fcw)),
780 		      FCBusyCursor(fcw));
781 	XDefineCursor(XtDisplay(fcw),XtWindow(FCDirMenuButton(fcw)),
782 		      FCBusyCursor(fcw));
783 	XFlush(XtDisplay(fcw));
784     }
785     if (FCDirMgr(fcw))
786 	DirectoryMgrClose(FCDirMgr(fcw));
787     FCDirMgr(fcw) = DirectoryMgrSimpleOpen(FCCurrentDirectory(fcw),
788 					   FCSortMode(fcw),FCPattern(fcw));
789 
790     /* Throw away old info */
791     if (FCFileNames(fcw) != NULL) {
792 	for (i = 0; i < FCNumFileNames(fcw); i++)
793 	    XtFree(FCFileNames(fcw)[i]);
794 	XtFree((char *)FCFileNames(fcw));
795     }
796     if (FCDirNames(fcw) != NULL) {
797 	for (i = 0; i < FCNumDirNames(fcw); i++)
798 	    XtFree(FCDirNames(fcw)[i]);
799 	XtFree((char *)FCDirNames(fcw));
800     }
801     if (FCDirMenu(fcw) != NULL)
802 	XtDestroyWidget(FCDirMenu(fcw));
803 
804     /* Count how many files and dirs we have now */
805     FCNumFileNames(fcw) = DirectoryMgrFilteredCount(FCDirMgr(fcw));
806     FCNumDirNames(fcw) = 1;
807     for (dir=FCCurrentDirectory(fcw)+1; *dir != '\0'; dir++) {
808 	if (*dir == '/')
809 	    FCNumDirNames(fcw) += 1;
810     }
811 
812     /* Make the array of filenames and set the fileList widget */
813     FCFileNames(fcw) = (char **)XtCalloc(FCNumFileNames(fcw)+1,sizeof(char *));
814     for (i=0; i < FCNumFileNames(fcw); i++) {
815 	dir_entry = DirectoryMgrNextEntry(FCDirMgr(fcw));
816 	if (dir_entry == NULL)
817 	    XtError("Inconsistent Directory");
818 	strcpy(temp,DirEntryFileName(dir_entry));
819 	if (DirEntryIsDir(dir_entry))
820 	    strcat(temp,"/");
821 	else if (DirEntryIsBrokenLink(dir_entry))
822 	    strcat(temp," X");
823 	else if (DirEntryIsDirectoryLink(dir_entry))
824 	    strcat(temp,"/");
825 	else if (DirEntryIsSymLink(dir_entry))
826 	    strcat(temp," @");
827 	FCFileNames(fcw)[i] = XtNewString(temp);
828     }
829     FCFileNames(fcw)[i] = NULL;
830     XawListChange(FCFileList(fcw),FCFileNames(fcw),0,0,True);
831 
832     /* Make the array of dirnames and build a new dirMenu widget */
833     FCDirNames(fcw) = (char **)XtCalloc(FCNumDirNames(fcw)+1,sizeof(char *));
834     FCDirNames(fcw)[0] = XtNewString("/");
835     FCDirMenu(fcw) = XtCreatePopupShell(FC_DIR_MENU_NAME,simpleMenuWidgetClass,
836 					(Widget)fcw,NULL,0);
837     menuItem = XtCreateManagedWidget("/",smeBSBObjectClass,FCDirMenu(fcw),
838 				     NULL,0);
839     XtAddCallback(menuItem,XtNcallback,DirectoryCallback,(XtPointer)0);
840     start = FCCurrentDirectory(fcw);
841     for (i = 1; i < FCNumDirNames(fcw); i++) {
842 	while (*start != '\0' && *start == '/')
843 	    start += 1;
844 	count = 0;
845 	while (*start != '\0' && *start != '/')
846 	    temp[count++] = *start++;
847 	temp[count] = '\0';
848 	FCDirNames(fcw)[i] = XtNewString(temp);
849 	menuItem = XtVaCreateManagedWidget("dirMenuItem",smeBSBObjectClass,
850 			FCDirMenu(fcw),
851 			XtNlabel, temp,
852 			NULL);
853         XtAddCallback(menuItem,XtNcallback,DirectoryCallback,(XtPointer)i);
854     }
855     XtVaSetValues(FCDirMenuButton(fcw), XtNlabel,FCCurrentDirectory(fcw), NULL);
856     if (XtIsRealized((Widget)fcw)) {
857 	XUndefineCursor(XtDisplay(fcw),XtWindow(fcw));
858 	XUndefineCursor(XtDisplay(fcw),XtWindow(FCFileList(fcw)));
859 	XUndefineCursor(XtDisplay(fcw),XtWindow(FCDirMenuButton(fcw)));
860     }
861 } /* End UpdateLists */
862 
863 
Chdir(fcw)864 static void Chdir(fcw)
865 XfwfFileChooserWidget fcw;
866 {
867     GotoDeepestLegalDirectory(fcw);
868     ChildrenUpdate(fcw);
869 } /* End Chdir */
870 
871 /*---------------------------------------------------------------------------*
872 
873                     E X T E R N A L    R O U T I N E S
874 
875  *---------------------------------------------------------------------------*/
876 
877 void
XfwfFileChooserChangeDirectory(fcw,dir)878 XfwfFileChooserChangeDirectory(fcw,dir)
879 XfwfFileChooserWidget fcw;
880 char *dir;
881 {
882     strcpy(FCCurrentDirectory(fcw),dir);
883     Chdir(fcw);
884 } /* End XfwfFileChooserChangeDirectory */
885 
886 void
XfwfFileChooserRefresh(fcw)887 XfwfFileChooserRefresh(fcw)
888 XfwfFileChooserWidget fcw;
889 {
890     XfwfFileChooserChangeDirectory(fcw,".");
891 } /* End XfwfFileChooserRefresh */
892 
893 char *
XfwfFileChooserCurrentDirectory(fcw)894 XfwfFileChooserCurrentDirectory(fcw)
895 XfwfFileChooserWidget fcw;
896 {
897     return(FCCurrentDirectory(fcw));
898 } /* End XfwfFileChooserCurrentDirectory */
899 
900 char *
XfwfFileChooserCurrentFile(fcw)901 XfwfFileChooserCurrentFile(fcw)
902 XfwfFileChooserWidget fcw;
903 {
904     return(FCCurrentFile(fcw));
905 } /* End XfwfFileChooserCurrentFile */
906 
907