/*
	File:		alDialog.c

	Contains:	Implementation of SelectAlias dialog box.

	Written by:	Prashant Patel

	Copyright:	© 1989-1990 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<13>	10/21/90	gbm		Changing hfileInfo to hFileInfo.
	    <13>	10/15/90	JL		Changing hfileInfo to hFileInfo it should have never been
									changed back.
		<12>	  6/8/90	ngk		update for changes to StandardFile.h
		<11>	 5/16/90	PP		Parameters to AL_findVolume has cahnged.
		<10>	  5/3/90	PP		Incorporate Diet tips.
		 <9>	 4/10/90	PP		Change once more hFileInfo to hfileInfo to stay consistent with
									Files.h
		 <8>	  4/5/90	PP		Change "callbackPtr" to "yourDataPtr".
		 <7>	  3/8/90	JAL		Changed hfileInfo to hFileInfo to stay consistent with change 
		 							in Files.h
		 <6>	  3/5/90	PKE		Added smTruncEnd as truncWhere parameter for TruncString call,
									since TruncString interface now matches former NTruncString
									interface.
		 <5>	 2/26/90	ngk		Changed names of new standardfile calls to match header.
		 <4>	 2/26/90	dnf		Change capitalization to match changes in files.h, rename
									positionRec to CatPositionRec.
		 <3>	 1/21/90	PP		Fix bugs related to flashing and beeping while in scroll bar;
									"Select" button hilited evenif no selection was selected.
	   <2.1>	12/15/89	prp		Restore current port correctly.
	   <2.0>	11/27/89	prp		SetArrowCursor is now a Trap selector routine.
	   <1.9>	11/16/89	prp		Cancel dialog box uses 'Stop Search' button instead of
									cmd-period. MyFilter does not bring up cancel dialog but a fake
									returned item does. StopWatch and arrow cursor work correctly
									while cancel dialog is up. If initial volume not found, curVref
									still gets set correctly.
	   <1.8>	10/30/89	prp		¥ AcceptFile has additional vref argument. This allows choices
									from multiple disks to show up correclty. ¥ Lock and Unlock
									sp->alList handle in order to not cause any purged handle
									referencing problems. ¥ Handle CatSearch returning wrgVolTypeErr
									for AppleShare volumes where it is not supported. ¥ Do not do
									AcceptFile of fastList explicitly. Reordering based on
									alFastList count.
	   <1.7>	10/17/89	prp		NewSFPGetFile is now released. No need to coerce arguments.
	   <1.6>	10/14/89	ngk		Changed call to NewSFPGetFile to not need coercing.
	   <1.5>	10/13/89	prp		Add support for folder 'Another One' selection. Works with
									NewSFPGetFile.
	   <1.4>	 10/2/89	prp		Added support for searching all mounted volumes when user pushes
									'TryHarder' button.
	   <1.3>	 9/18/89	prp		SelectAlias has additional filtering parameters.
	   <1.2>	  9/6/89	prp		Changes from CodeReview.
	   <1.1>	 8/11/89	prp		Do not call CatSearch again if previous call has returned an
									eofErr.
	   <1.0>	  8/7/89	prp		Initial Creation

	To Do:
*/

/*EASE$$$ READ ONLY COPY of file ÒalDialog.cÓ
** 2.1	prp 12/15/1989 Restore current port correctly.
** 2.0	prp 11/27/1989 SetArrowCursor is now a Trap selector routine.
** 1.9	prp 11/16/1989 Cancel dialog box uses 'Stop Search' button instead of
**		cmd-period. MyFilter does not bring up cancel dialog but a fake
**		returned item does. StopWatch and arrow cursor work correctly while
**		cancel dialog is up. If initial volume not found, curVref still gets
**		set correctly.
** 1.8	prp 10/30/1989 ¥ AcceptFile has additional vref argument. This allows
**		choices from multiple disks to show up correclty. ¥ Lock and Unlock
**		sp->alList handle in order to not cause any purged handle referencing
**		problems. ¥ Handle CatSearch returning wrgVolTypeErr for AppleShare
**		volumes where it is not supported. ¥ Do not do AcceptFile of fastList
**		explicitly. Reordering based on alFastList count.
** 1.7	prp 10/17/1989 NewSFPGetFile is now released. No need to coerce
**		arguments.
** 1.6	ngk 10/14/1989 	Changed call to NewSFPGetFile to not need coercing.
** 1.5	prp 10/13/1989 Add support for folder 'Another One' selection. Works
**		with NewSFPGetFile.
** 1.4	prp 10/02/1989 Added support for searching all mounted volumes when
**		user pushes 'TryHarder' button.
** 1.3	prp 09/18/1989 SelectAlias has additional filtering parameters.
** 1.2	prp 09/06/1989 Changes from CodeReview.
** 1.1	prp 08/11/1989 Do not call CatSearch again if previous call has
**		returned an eofErr. 
** 1.0	prp 08/07/1989 Initial Creation
** END EASE MODIFICATION HISTORY */
/*********************************************************************
*
*	File:		alDialog.c
*	Project: 	Alias Manager
*	Contains:	support for alias resolution through a dialog box
*	Written by:	Prashant Patel
*
*	Copyright 1989 by Apple Computer, Inc.
*	All Rights Reserved.
*
**********************************************************************/

/**********************************************************************
 ***************		Public Include files		*******************
 *********************************************************************/
 
 #include "Aliases.h"		
 
/**********************************************************************
 ***************		 Private Include files		*******************
 *********************************************************************/
 
 #include "aliasPriv.h"		
 #include "aliasDlogPriv.h"		

/**********************************************************************
 ***************		External C Include files		***************
 *********************************************************************/

#ifndef __ERRORS__
#include <errors.h>
#endif
#ifndef __DIALOGS__
#include <dialogs.h>
#endif
#ifndef __LISTS__
#include <Lists.h>
#endif
#ifndef __FONTS__
#include <Fonts.h>
#endif
#ifndef __RESOURCES__
#include <Resources.h>
#endif
#ifndef __STANDARDFILE__
#include <StandardFile.h>
#endif
#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif
#ifndef __SCRIPT__
#include <Script.h>
#endif

/*********************************************************************
******** pseudo static Variables, since static not allowed ***********
*********************************************************************/
/* This is a structure that holds variables that have a scope for this entire file.
   Since static variables are not allowed for stand-alone code resources, we will
   use a pointer to this struct as a way of accessing these variables.  All local
   routines needing access to it will be passed a pointer to it.  It will also be
   stored in RefCon of our dialog box record so that routines like myFilter can
   also access it.
*/
typedef struct {
	 AliasPtrPriv aptr;			// pointer to locked AliasRecord
	 DialogPtr aliasDialog;  	// Pointer to alias dialog 
	 DialogPtr cancelDialog;  	// Pointer to cancel further search dialog 
	 SICNHandle alSICNHandle; 	// handle to SICN resource
	 ControlHandle ButtonCH[kNumButtons]; // button control handles
	 Rect nameRect;				// Rectangle for names list
	 Rect locationRect;  		// Rectangle for location display list 
	 ListHandle nameList;		// handle to list of names
	 ListHandle locationList;	// handle to the list displaying location
	 short selRow;    			// Current selection row in names list
	 short prevSel;				// previous selection row in names list
	 short smallFontSize;		// size of small font in which list entry text is drawn
	 Boolean DoubleClick;  		// Flag to say that a double click on a list happened
	 Boolean TryHarder;			// Flag to say if in TryHarder mode
	 Boolean firstTry;			// Flag to say if TryingHard first time or not
	 Boolean CSrch;				// Flag to say if CatSearch available or not
	 Boolean quitFlag;			// Flag to say if client filter proc asked us to quit
	 short firstVref;			// volume ref num of first volume we searched
	 short curVref;				// current volume ref num of volume we are searching
	 long curDir;				// current directory we are searching
	 long curIdx;				// current directory index for the search
	 CatPositionRec curPrec;	// current CatSearch position record
	 Str63 curFname;			// current file name
	 CInfoPBRec asyncCPB;		// async I/O CPB record
	 unsigned long startCount;	// remember ticks when trying hard starts
	 short curCnt;				// current # of entries in alias list
	 short curLim;				// current # of entries allocated in alias list 
	 VCB *vcbPtr;				// pointer to VCB entry
	 CanonicalFileSpecHandle alList; // alias list buffer handle
	 LongsHandle dirStack;		// directory stack
	 AliasFilterProcPtr fileFilter; // client supplied file filter proc ptr
	 ModalFilterWithCallback modalFilter; // client supplied ModalDialog filter proc ptr
	 Ptr yourDataPtr;			// additional param to be passed to fileFilter & modalFilter
	 StandardFileReply mySFreply;	// reply record for CustomGetFile
	 Boolean folderSelected;	// Flag to say that a folder was selected during 'AnotherOne'
} myStaticVars;

typedef myStaticVars *myStaticPtr; // pointer to my static var record

	
/*********************************************************************
******** forward declarations ****************************************
*********************************************************************/
 static OSErr Do_findNext(const myStaticPtr sp);
 static void DoneTryingHard(Boolean forEver, const myStaticPtr sp);
 static void NextVolumeSearch(const myStaticPtr sp);

 extern pascal OSErr AL_selectAliasWithDialog(const CanonicalFileSpec *fromFile, //I
						const Str31 fTypeName,			//I
						short cnt,						//I
						CanonicalFileSpecList alFastList,//I
						AliasFilterProcPtr aliasFilter,	//I
						AliasHandlePriv Alias,			//I/O
						CanonicalFileSpec *toFile,		//O
						ModalFilterWithCallback filterProc, //I
						Ptr yourDataPtr);				//I

/*********************************************************************
******** Main Entry for dialog code. This has to be the first code ***
******** in this file.  The code contained in this file gets loaded **
******** dynamically only if needed.  The first byte of the loaded ***
******** code is assumed to be the starting code for alias dialog. ***
*********************************************************************/
pascal OSErr AL_mainEntryForDialog(const CanonicalFileSpec *fromFile, //I from file 
						const Str31 fTypeName,			//I descriptive name for file type
						short cnt,						//I #of entries found during fast search
						CanonicalFileSpecList alFastList,//I fast search answer
						AliasFilterProcPtr aliasFilter,	//I file filter proc pointer
						AliasHandlePriv Alias,			//I/O alias record handle
						CanonicalFileSpec *toFile,		//O returned toFile
						ModalFilterWithCallback filterProc, //I modal filter proc pointer
						Ptr yourDataPtr)				//I additional param to be passed to filter procs

{
	return (AL_selectAliasWithDialog (fromFile, fTypeName, cnt, alFastList,
				aliasFilter, Alias, toFile, filterProc, yourDataPtr));
}


/*********************************************************************
******** Set the Font and size to our small, informational font ******
*********************************************************************/
static void DoSmFont()
{
	TextFont(kSmallFont);
	TextSize(kSmallSize);
}

/*********************************************************************
******** Set the Font and size to the system font ********************
*********************************************************************/
static void DoDlgFont()
{
	TextFont(0);
	TextSize(0);
}

/*********************************************************************
******** Draw one small icon from a SICN List ************************
*********************************************************************/
static void PlotSICN (  short theIndex,			//I	index into SICN list
						Rect *theRect,			//I	rect in which it is drawn
						const myStaticPtr sp)   //I pointer to my static storage

{
	BitMap theBits;						// bitmap of the SICN
	SICNHandle theHandle;				//	handle to SICN list resource

	// set up the bitmap for the SICN to be drawn and copy it to the rect
	theHandle = sp->alSICNHandle;
	theBits.baseAddr = (Ptr) ((*theHandle)[theIndex]);
	theBits.rowBytes = 2;
	SetRect(&theBits.bounds, 0, 0, kSICNSize, kSICNSize);
	CopyBits(&theBits, &((sp->aliasDialog)->portBits), 
				&theBits.bounds, theRect, srcCopy, NULL);
}

/*********************************************************************
******** Flash a button for user feedback ****************************
*********************************************************************/
static void  FlashButton(const ControlHandle button) //I button's control handle
{ 
	long dummyCount;					// dummy tick count
	
	HiliteControl (button, 1);			// hiliting on
	Delay (8, &dummyCount);				// delay for 8 ticks
	HiliteControl (button, 0);			// hiliting off
} 

/*********************************************************************
******** draw or hide the default outline of Select button ***********
*********************************************************************/
static void  DefaultOutline(Boolean on,				//I	outline on or off?
 					const DialogPtr aliasDialog)	//I	my dialog box record pointer
{ 
	Rect    itemBox;   					// rectangle surrounding default item
	short    DType; 					// Type of dialog item 
	Handle    DItem;    				// Handle to the dialog item 
	 
	GetDItem(aliasDialog, kI_SelectOutline, &DType, &DItem, &itemBox);// Get the item handle 
	if (!on)
		PenMode(patBic);				// clear it
	PenSize(3, 3);  					// Change pen to draw thick default outline 
	FrameRoundRect(&itemBox, 16, 16); 	// Draw the outline 
	PenNormal();						// restore initial state of pen 
} 

/*********************************************************************
******** Change Select, Cancel and Another One button states *********
*********************************************************************/
static void ChangeOthers(short hiliteState, 	//I	new hilite state
 				const myStaticPtr sp)   		//I pointer to my static storage
{
	HiliteControl (sp->ButtonCH[kI_AnotherOne-1], hiliteState); // Another One button
	// If there is nothing to pick, don't activate Select button
	HiliteControl (sp->ButtonCH[kI_Select-1], 
	  (hiliteState == kButtonActive && sp->curCnt > 0) ? kButtonActive : kButtonInactive);	// Select button to new state
	DefaultOutline ((hiliteState == kButtonActive && sp->curCnt > 0) ? kDrawOutline : kEraseOutline,
	  sp->aliasDialog); // outline Select button
	HiliteControl (sp->ButtonCH[kI_Cancel-1], hiliteState);	// Cancel button to new state
}

/*********************************************************************
***************	Handle a list draw message   *************************
*********************************************************************/
static void ListDrawMsg(	Rect lRect,		//I	rect in which cell is drawn
						short lDataOffset, 	//I offset into cell data of the cell
						short lDataLen, 	//I length in bytes of cell data
						const ListHandle lHandle) //I list handle
{
	ListEntry *theEntry;		// ptr to entry we stored in the list
	DataHandle theCells; 		// Handle to cells 
	Rect tempRect;				// temporary rectangle
	Str63 aString; 				// String for mashing file name
	myStaticPtr sp;				// pointer to my static data
	char masterFlags;			// flags to remember before locking and unlocking handles

	sp = (myStaticPtr)(*lHandle)->userHandle;
	EraseRect(&lRect);			// we are going to redraw it
	if (lDataLen > 0) {
		tempRect.top = lRect.top;		
		tempRect.left = lRect.left;
		tempRect.bottom = lRect.bottom;
		tempRect.right = tempRect.left+kSICNSize; // boundry for SICN
		theCells = (*lHandle)->cells;
		masterFlags = HGetState ((Handle)theCells); // remember master flags
		HLock((Handle)theCells);
		theEntry = (ListEntry *) ((*theCells)+lDataOffset); // our list entry
		PlotSICN(theEntry->icon, &tempRect, sp);
		MoveTo(tempRect.right + kSpSICN, tempRect.bottom - kSIBase); // now draw text
		DoSmFont();				// small application font
		AL_copyPString (theEntry->fileName, aString);
		TruncString(lRect.right - (tempRect.right+kSpSICN), aString, smTruncEnd); // fit it in rectangle <6>
		DrawString(aString);
		DoDlgFont();			// back to default font
		HUnlock((Handle)theCells);
		HSetState((Handle)theCells, masterFlags); // restore original state
	}
	
} // end of Alias_LDEF

/*********************************************************************
***************	LDEF procedure for name list *************************
*********************************************************************/
static pascal void Name_LDEF(	short lMessage, //I	operation to be performed 
						Boolean lSelect,		//I	select the cell or not
						Rect lRect,				//I	rect in which cell is drawn
						Cell lCell,				//I	the cell which is operated on 
						short lDataOffset, 		//I offset into cell data of the cell
						short lDataLen, 		//I length in bytes of cell data
						ListHandle lHandle)		//I list handle
{
	switch (lMessage) {
	
	case lDrawMsg:
		ListDrawMsg (lRect, lDataOffset, lDataLen, lHandle);
		if (lSelect)	
			InvertRect(&lRect);
		break;
		
	case lHiliteMsg:
		InvertRect(&lRect);
		break;
		
	default:					// ignore initialize and close
		break;
		
	} // end of switch
		
} // end of Name_LDEF

/*********************************************************************
***************	LDEF procedure for location list *********************
*********************************************************************/
static pascal void Location_LDEF(	short lMessage, //I	operation to be performed 
						Boolean lSelect,		//I	select the cell or not
						Rect lRect,				//I	rect in which cell is drawn
						Cell lCell,				//I	the cell which is operated on 
						short lDataOffset, 		//I offset into cell data of the cell
						short lDataLen, 		//I length in bytes of cell data
						ListHandle lHandle)		//I list handle
{	
	if (lMessage == lDrawMsg) {
		lRect.left += kSpSICN;	// open folder looks better with offset		
		ListDrawMsg (lRect, lDataOffset, lDataLen, lHandle);
	}
	// no need to handle any other messages, since picking not allowed in LocationList
} // end of Location_LDEF

 /*********************************************************************
 ***************	Get Icon ID given a directory ID ******************
 *********************************************************************/

static short GetIconID(long dirID,			//I directory ID of list entry
 				unsigned short volType) //I current volume type 
{ 
	if (dirID != kVolRootDirID)
		return (kSIOpen);		// open folder for non-root level directory
	else if (volType == kVolFloppy400K || volType == kVolFloppy800K || volType == kVolFloppy1400K)
		return (kSIFloppy);		// root level is a folppy volume
	else
		return (kSIHard);		// root level is hard disk volume
}

 /*********************************************************************
 ***************	add an entry to a list ****************************
 *********************************************************************/

static void  Add_toList(short iconID,			//I	icon id for this list entry
						short vref,				//I volume ref num for list entry
						long dirID,				//I directory ID of list entry
						const StringPtr theString, //I text of list entry
						ListHandle theList)		//I	list handle
{ 
	short	theRow;						// the row that we are going to add
	Point   cSize;  					// Pointer to a cell in a list
	short 	len;						// length of the string
	ListEntry theEntry;					// entry to be added to the list
 
	cSize.h = 0;    				// Point to the correct column 
	theRow = LAddRow(1, 32767, theList);// Add another row at the end of the list 
	cSize.v = theRow;   			// Point to the row just added
	len = *theString;				// prepare list entry
	theEntry.icon = iconID;
	theEntry.vRefNum = vref;
	theEntry.dirID = dirID;
	BlockMove (theString, theEntry.fileName, len+1);
	if (! (len & 1))				// force it even # of bytes
		++len;
	// Place the entry in row just created
	LSetCell((Ptr)&theEntry, 
		sizeof(theEntry)-(sizeof(theEntry.fileName)-1)+len, cSize, theList);

} // end of routine Add_toList


 /*********************************************************************
 ***************	select a given name row  **************************
 *********************************************************************/

static void  SelectNameRow(short thisRow, //I name list row that should be selected
 				const myStaticPtr sp)   //I pointer to my static storage
{ 
	Point selPt;

	SetPt (&selPt, 0, sp->selRow=thisRow);
	LSetSelect (TRUE, selPt, sp->nameList);
	
} // end of routine SelectNameRow

 /*********************************************************************
 ***************	display location for given name cell **************
 *********************************************************************/

static void  DisplayLoc(short thisRow,		//I name list row that was selected
 				const myStaticPtr sp)   //I pointer to my static storage
{ 
	OSErr		err;					// return code
	ListEntry 	theEntry;				// a list entry
	Point 		theCell;				// the cell to be retrieved
	short 		vref;					// volume ref num of selected name
	long		dirID;					// parent dir ID of selected name
	Str63		localName;				// name of a parent directory
	short		len;					// length of list entry
	Rect		tempRect;				// temporary rectangle

	LDelRow (0, 0, sp->locationList);	// we will redraw location list
	LDoDraw (FALSE, sp->locationList);	// do not display while building it 
	len = sizeof (ListEntry);
	SetPt (&theCell, 0, thisRow);		// get list entry of selected row
	LGetCell ((Ptr)&theEntry, &len, theCell, sp->nameList);
	vref = theEntry.vRefNum; 			// get its volume reference number
	dirID = theEntry.dirID; 			// get its parent directory ID
	
	// get names of all directories in its path and add them to location list
	while ((err = AL_findDirByID (vref, dirID, &dirID, localName)) == noErr)
		Add_toList (GetIconID(dirID, sp->aptr->volumeType), 0, 0, 
					localName, sp->locationList);
	GetIndString (localName, kAliasResID, kDeskStrIdx); // get the "Desktop" display string
	Add_toList (kSIDesktop, 0, 0, localName, sp->locationList); // show the "Desktop"
 
	LDoDraw (TRUE, sp->locationList);	// drawing on for next update
	tempRect = sp->locationRect;
	tempRect.right = tempRect.right - kScrMinOne;
	InvalRect (&tempRect);				// invalidate to force an update
}

 /*********************************************************************
 ***************	display cancel dialog if needed *******************
 *********************************************************************/

static void  DisplayCancelIfNeeded(const myStaticPtr sp)  //I pointer to my static storage
{ 

	if (sp->startCount > 0 && (TickCount() >= (sp->startCount + kCancelWait))) {
		// put up cancel through stop search button dialog box
		sp->cancelDialog = GetNewDialog(kAlCancelDLOGID, NULL, (WindowPtr)-1);
		SetWRefCon (sp->cancelDialog, (long)sp); // store address of our static storage
		sp->startCount = 0;					// remember that cancel dailog box is up
		AL_setArrowCursor();				// arrow cursor, so 'StopSearch' can be hit
	}
			
}

/**********************************************************************
 ***************	Draw User Items  **********************************
 **********************************************************************/

static pascal void DrawUserItem(WindowPtr theDialog, //I pointer to my dialog window
								short itemNo)		//I item number
{
	myStaticPtr sp;				// pointer to my static data	 
	ListHandle	itemToChange;	// item's handle
	Rect		itemBox;		// item's rectanglular box

	sp = (myStaticPtr) (GetWRefCon (theDialog)); // get our storage pointer

	switch (itemNo) {
	
	case kI_SelectOutline:
		// draw Select button outline
		DefaultOutline (sp->curCnt > 0 ? kDrawOutline : kEraseOutline, theDialog);
		break;

	case kI_NamesList:
	case kI_LocationList:
			
		if (itemNo == kI_NamesList) {
			itemBox = sp->nameRect;
			itemToChange = sp->nameList;
		} else {
			itemBox = sp->locationRect;
			itemToChange = sp->locationList;
		}
		// draw the names or location list outside rectangle and inside rectangle
		InsetRect(&itemBox, -1, -1);   	// Set for framing 
		FrameRect(&itemBox);   			// Frame the main rectangle
		itemBox.right = itemBox.right - kScrMinOne;// Make room for the scroll bar on the right 
		FrameRect(&itemBox);   			// Frame the inside rectangle
		LUpdate(theDialog->visRgn, itemToChange); // Update names list or location list
		break;

	default:
		break;
		
	} // end of itemNo switch

} // end of DrawUserItem routine

/**********************************************************************
 ***************	dlgHook for NewSFPGetFile for folder selection ****
 **********************************************************************/

static pascal short FolderSFGetHook(short mySFItem,		//I item number from SFPGetFile
							DialogPtr theDialog,	//I SFPGetFile dialog record pointer
							myStaticPtr sp) 
{
	Handle		itemToChange;	// needed for GetDItem and SetCtlValue 
	Rect		itemBox;		// needed for GetDItem 
	short		itemType;		// needed for GetDItem 
	Str255		buttonTitle;	// needed for GetIndString
	
	if (mySFItem == sfHookFirstCall) {
		/* Before the dialog is drawn, our hook gets called with a -1.
	   This gives us the opportunity to change 'open' title to 'Open Folder'. */
	   
		GetIndString(&buttonTitle, kAliasResID, kOpenFldrStrIdx);
		if (*buttonTitle != 0) { // if we really got the resource
			GetDItem(theDialog, getOpen, &itemType, &itemToChange, &itemBox);
			SetCTitle((ControlHandle)itemToChange,&buttonTitle);
		}
	} else {
		GetDItem(theDialog, kI_SelectFolder, &itemType, &itemToChange, &itemBox);
		if (sp->mySFreply.sfType == 0 && (Length(sp->mySFreply.sfFile.name) == 0)) {
			// nothing is selected, deselect 'Select Folder' button
			if ((*(ControlHandle)itemToChange)->contrlHilite == kButtonActive) 
				HiliteControl ((ControlHandle)itemToChange, kButtonInactive);
		} else { // if 'Select Folder' not active, activate it
			if ((*(ControlHandle)itemToChange)->contrlHilite == kButtonInactive) 
				HiliteControl ((ControlHandle)itemToChange, kButtonActive);
		}
	
		if (mySFItem == kI_SelectFolder) { // the user selected a folder
			sp->folderSelected = TRUE; // mark it as such, otherwise can't distinguish from real 'Cancel'
			mySFItem = getCancel;	// tell SFPGetFile to get out
		}
	}
	
	return(mySFItem);
}

/**********************************************************************
 ***************	Filter for ModalDialog call  **********************
 **********************************************************************/

static pascal Boolean MyFilter (DialogPtr theDialog,	//I dialog record pointer
						EventRecord *theEvent,			//I event record pointer
						short *itemHit)					//O item hit
{
	Point selPt;						// current selection point
	char ch;							// key-down character
	myStaticPtr sp;						// pointer to my static data
	Boolean returnFromFilter;			// answer from client's ModalDialog filter

	sp = (myStaticPtr) (GetWRefCon (theDialog)); // get our storage pointer
	
	if (sp->modalFilter != NULL) {		// give client a chance to filter events
		returnFromFilter = ((ModalFilterWithCallback)(*(sp->modalFilter)))
									(theDialog, theEvent, itemHit, sp->yourDataPtr);
		if (returnFromFilter)	
			return (TRUE);				// client handled the event
	}

	switch (theEvent->what) {
	
	case mouseDown:
		selPt = theEvent->where;     	// Get position of click
		GlobalToLocal(&selPt);   		// Convert to local coordinates

		if (PtInRect(selPt,&sp->nameRect)) { // See if in names list
			sp->DoubleClick = LClick(selPt, theEvent->modifiers, sp->nameList); // select it
			if (selPt.h < (sp->nameRect.right - kScrMinOne)) { // not in scroll bar
				sp->prevSel = sp->selRow;	// remember previous row to avoid unnecessary redraw
				SetPt (&selPt, 0, 0);		// get the selection row > 0
				if (LGetSelect(TRUE, &selPt, sp->nameList)) { // got the selection row
					sp->selRow = selPt.v;	// newly selected row
					*itemHit = sp->DoubleClick ? kI_Select : kI_NamesList; //actual select or just a pick?
					return (TRUE);		// we will handle the event
				} else if (sp->curCnt > 0) { // pick in an empty selection and there are entries
					SelectNameRow ((sp->curCnt-1), sp); // select the last entry in the list
					*itemHit = kI_NamesList;//actual select or just a pick?
					return (TRUE);		// we will handle the event
				}
			} else { // click in scrollbar, return an item that does nothing
				*itemHit = kI_NameText;
				return (TRUE);
			}

		} else if (PtInRect(selPt,&sp->locationRect)) { // See if in display location list
		// do nothing for the contents region since this list is for display only.
		// if it is in the scroll bars, call LClick
			if (selPt.h >= (sp->locationRect.right - kScrMinOne))
				(void) LClick(selPt, theEvent->modifiers, sp->locationList);
		}  
		break;
		
	case keyDown:
		ch = theEvent->message & charCodeMask; // get the character
		switch (ch) { 					// which charcter was it?
		
		case kChrETX: // 'enter' or 'cr' means user has selected an alias
		case kChrCR: 
			if ((*(sp->ButtonCH[kI_Select-1]))->contrlHilite == kButtonActive) {
										// disabled during TryHard mode
				*itemHit = kI_Select; 	// pretend that Select button was hit 
				FlashButton (sp->ButtonCH[kI_Select-1]); // flash the Select button as feedback
				return(TRUE);			// we will handle the event 
			}
			
			
		case kChrESC: // 'ESC' or 'CMD-period' means user is canceling out
		case kChrPeriod:
			if (ch == kChrESC || (theEvent->modifiers & cmdKey)) { // 'ESC' or 'CMD-.'
				if ((*(sp->ButtonCH[kI_Cancel-1]))->contrlHilite == kButtonActive) { 
					// cancel disabled during TryHarder mode
					*itemHit = kI_Cancel; // canceling out of alias selection dialog box
					FlashButton (sp->ButtonCH[kI_Cancel-1]);// flash the Cancel button as feedback
					return(TRUE);		// we will handle the event 
				}
			}
			break;
			
		default:
			break;
			
		} // end of character code switch
		
		break; // from keydown case
		
	case updateEvt:
	case nullEvent:
		if (theEvent->what == updateEvt && theDialog == theEvent->message)
			break;	// could get an updateEvt for some other window instead of nullEvent
			
		if (sp->TryHarder) { // in TryingHarder mode
		
			if (sp->firstTry)  {// are we seeing the TryHarder mode for the first time? Then
			// TryHarder mode was set programatically, pretend that TryHarder button was pushed
				*itemHit = kI_TryHarder; // pretend that TryHarder button was hit 
				FlashButton (sp->ButtonCH[kI_TryHarder-1]); // flash the TryHarder button as feedback
			} else {
				*itemHit = kI_SearchInProgress; // TryHarder mode search is in progress
			}
			return(TRUE);			// we will handle the event 
		}
		break;

	defualt:
		break;
		
	} // end of switch statement
		
	return (FALSE);						// Let ModalDialog handle the event
	
} // end of MyFilter routine

 /**********************************************************************
 ***************	Show empty dialog box initially *******************
 *********************************************************************/

static void Display_aliasDialog (const Str31 fTypeName, //I descriptive name of file type
  				const myStaticPtr sp)   	//I pointer to my static storage
{
	Rect    	dataBounds; 				// Rect to setup the list  
	Point   	cSize;  					// Pointer to a cell in a list 
	FontInfo	thisFontInfo;				// info about application font
	Rect		inRect;						// inside rectangle for a list
	short		Dtype;						// dialog item type
	Handle		Ditem;						// dialog item handle
	Rect		box;						// dialog itm box
	register short i;						// do loop index
	char		*s;							// temp char string pointer
	Str255		localName;					// local name string

	sp->aliasDialog = GetNewDialog(kAliasDLOGID, NULL, (WindowPtr)-1);// Bring in the dialog resource 
	SetWRefCon (sp->aliasDialog, (long)sp); // store address of our static storage
	AL_setArrowCursor();					// arrow cursor
	SetPort(sp->aliasDialog);  				// Prepare to add conditional text 
	sp->alSICNHandle = (SICNHandle)(GetResource('SICN', kAliasResID)); // load all SICNs

	// display title with it's  text and two parameters.
	// first is the descrptive name for the file type, second is the file name.
	if (Length(fTypeName) > 0) // client supplied file type name
		s = fTypeName;
	else 
		GetIndString (s=localName, kAliasResID, 
			sp->aptr->thisAliasKind == kFileAlias ? kDocStrIdx : kFldrStrIdx);
	ParamText (s, sp->aptr->fileName,'','');		
	
	// get all button's control handles
	for (i=1; i<=kNumButtons; i++) {
		GetDItem (sp->aliasDialog, i, &Dtype, &Ditem, &box); // get button control item
		sp->ButtonCH[i-1] = (ControlHandle) Ditem;
	}
	// get Select button outline box and install its userItem draw proc
	GetDItem (sp->aliasDialog, kI_SelectOutline, &Dtype, &Ditem, &inRect);
	SetDItem (sp->aliasDialog, kI_SelectOutline, Dtype, (Handle)&DrawUserItem, &inRect);
	
	// get name list userItem and install its userItem draw proc
	GetDItem (sp->aliasDialog, kI_NamesList, &Dtype, &Ditem, &sp->nameRect);
	SetDItem (sp->aliasDialog, kI_NamesList, Dtype, (Handle)&DrawUserItem, &sp->nameRect);
	
	// set userItem draw proc for name list and location list rectanlges
	GetDItem (sp->aliasDialog, kI_LocationList, &Dtype, &Ditem, &sp->locationRect);
	SetDItem (sp->aliasDialog, kI_LocationList, Dtype, (Handle)&DrawUserItem, &sp->locationRect);

	SetRect(&dataBounds, 0, 0, 1, 0);		// Set list up, 1 column 
	DoSmFont();								// get info about small font
	GetFontInfo(&thisFontInfo); 			// Get the current font sizes 
	cSize.v = thisFontInfo.ascent + thisFontInfo.descent + thisFontInfo.leading;
	DoDlgFont();							// back to default font
	SetPt(&cSize, 0, (kSICNSize >= cSize.v) ? kSICNSize : cSize.v);
											// height of cell in the list
	
	// create name list
	inRect = sp->nameRect;
	inRect.right = inRect.right - kScrMinOne;// Make room for the scroll bar on the right 
	sp->nameList =  LNew(&inRect, &dataBounds, cSize, kAliasResID, sp->aliasDialog, 
				TRUE, FALSE, FALSE, TRUE);	// Make the list 
	(*(sp->nameList))->selFlags = lOnlyOne + lNoNilHilite;// Set the attributes 

	// create location list
	inRect = sp->locationRect;
	inRect.right = inRect.right - kScrMinOne;// Make room for the scroll bar on the right
 	sp->locationList =  LNew(&inRect, &dataBounds, cSize, kAliasResID, sp->aliasDialog, 
				TRUE, FALSE, FALSE, TRUE); // Make the list 
	(*(sp->locationList))->selFlags = lOnlyOne + lNoNilHilite;// Set the attributes 

 	// Patch the refcon fields in the ListHandles so that the dummy LDEFproc
	// can find the real, linked-in LDEFproc which is written in C.
	// Also, save the static storage pointer so that LDEF can access it.
	(*(sp->nameList))->refCon = &Name_LDEF;
	(*(sp->locationList))->refCon = &Location_LDEF;	
	(*(sp->nameList))->userHandle = (Handle)sp;
	(*(sp->locationList))->userHandle = (Handle)sp;

} // end of Display_dialog routine
  
/*********************************************************************
********  done trying harder for now, provide feedback ***************
*********************************************************************/ 
static void DoneTryingHard(Boolean forEver,	//I TryHard done, is it for ever done?
   				const myStaticPtr sp)   	//I pointer to my static storage
{
	SysBeep(0);								// beep to let the user know
	sp->TryHarder = FALSE;  				// set our global flag
	if (forEver && sp->dirStack != NULL)	// dispose directory stack if done forever
		DisposHandle ((Handle)sp->dirStack);
	if (sp->startCount == 0 && sp->cancelDialog != NULL) { // a cancel dialog was put up
		DisposDialog(sp->cancelDialog); 	// Flush the cancel dialog out of memory
		sp->cancelDialog = NULL;
		sp->startCount = 1;					// mark the flag
	}
	// if done forEver with TryingHard, make the button inactive, otherwise active.
	HiliteControl(sp->ButtonCH[kI_TryHarder-1], 
		(forEver) ? kButtonInactive : kButtonActive); 
	ChangeOthers(kButtonActive, sp);		// all other buttons are now active
	if (sp->selRow == -1 && sp->curCnt > 0) { // nothing was selected yet
		SelectNameRow (0, sp);				// select first entry by default
		DisplayLoc (0, sp);					// display path of default selection
	}
	AL_setArrowCursor();					// back to arrow cursor
}

/*********************************************************************
******** Accept a file as an alias ***********************************
*********************************************************************/ 
static OSErr AcceptFile (short vref,		//I volume reference number
				long dirID, 				//I directory ID of the file
				const StringPtr fname, 		//I file name
   				const myStaticPtr sp)   	//I pointer to my static storage

{
	OSErr err;								// result code
	Boolean returnFromFilter;				// client wants to filter it or not?						
	
	if (sp->fileFilter != NULL) {	// client supplied file filter proc
		if ((err = AL_filterFile (vref, dirID, fname, sp->fileFilter, sp->yourDataPtr,
				 (sp->CSrch) ? NULL : &sp->asyncCPB,
				 kRealCPB, &returnFromFilter, &sp->quitFlag)) != noErr)
			return (err);
		if (returnFromFilter) 	// client says don't display it
			return (noErr);	
	}
	
	if (sp->alList == NULL) { // allocate the slow search alias list buffer
		sp->alList = (CanonicalFileSpecHandle)
			(NewHandle(sizeof(CanonicalFileSpec) * sp->curLim));
		if (sp->alList == NULL)
			return (MemError());
		HLock((Handle)sp->alList);	
	} else if (sp->curCnt >= sp->curLim) { // ran out of room in alias list buffer
		sp->curLim += kMaxCSrchEnt;				// double its size
		HUnlock((Handle)sp->alList);	
		SetHandleSize ((Handle)sp->alList, 
			(sizeof(CanonicalFileSpec) * sp->curLim)); // grow alias list
		err = MemError(); 
		HLock((Handle)sp->alList);	
		if (err != noErr) { // could not extend block
			AL_displayAlert(kMem1StrIdx);
			return (err);
		}
	}
	
	if (AL_updateAList(vref, dirID, fname,	 // add an entry to alias list
					&sp->curCnt, sp->curLim, (*(sp->alList)))) { // room in the list & not duplicate
			Add_toList ((sp->aptr->thisAliasKind == kFileAlias) ? kSIDocument : kSIFolder, 
					vref, dirID, fname, sp->nameList);	 // add it to Names List
	}
	
	return (noErr);
}

/*********************************************************************
 ***************	Find file(s) or directory(s) by CatSearch	******
 *********************************************************************/

static OSErr FindByCatSrch( 	short vref,			//I volume reference number/WD ref
						const AliasPtrPriv aptr,	//I alias record pointer
						long specBits,				//I search specification mask
						long ioSearchTime,			//I length of time to run search
						short lim,					//I alias list buffer limit
						short *fcnt,				//I/O current num of entries in buffer
						CanonicalFileSpecList flist,//O alias list buffer
						CatPositionRec *prec)		//I/O CatSearch IO position record
						
{						
	OSErr err = paramErr;			// result code, assume CatSrch not available
	CSParam cspb;					// CatSearch parameter block
	CInfoPBRec cpb1, cpb2;			// CatSearch setup catlog param blocks
	Str63 localName;				// local temporary name
	FSSpec ans[kMaxCSrchEnt];		// maximum CatSearch return entries
	register short i;				// for loop index

	// set up CatSearch parameter block
	cspb.ioNamePtr = cspb.ioOptBuffer = NULL;
	cspb.ioVRefNum = vref;
	cspb.ioMatchPtr = ans;
	cspb.ioReqMatchCount = (lim < kMaxCSrchEnt) ? lim : kMaxCSrchEnt;
	cspb.ioSpecBits = fsSBFlAttrib + specBits;
	if (aptr->thisAliasKind == kDirAlias && (specBits & fsSBFlFndrInfo))
		// for files, type and creator are important but for folders they are not
		cspb.ioSpecBits -= fsSBFlFndrInfo;
	cspb.ioSpec1 = &cpb1;
	cspb.ioSpec2 = &cpb2;
	cspb.ioSearchTime = ioSearchTime;	// length of time to run search
									// later on, may want to refine it depending upon performance
	cspb.ioCatPosition = *prec;		// input CatSearch position record
	
	if (cspb.ioSpecBits & (fsSBFullName | fsSBPartialName)) { // by name specified
		AL_copyPString (aptr->fileName, localName);
		cpb1.hFileInfo.ioNamePtr = localName; // put name in first catalog param blk
	}
	else 
		cpb1.hFileInfo.ioNamePtr = NULL; // name was not important
	cpb2.hFileInfo.ioNamePtr = NULL;
	
	cpb1.hFileInfo.ioFlAttrib = (aptr->thisAliasKind == kDirAlias) ? kDirMask : 0;
	cpb2.hFileInfo.ioFlAttrib = kDirMask;// only bit 4 is important
	
	if (cspb.ioSpecBits & fsSBFlCrDat) // creation date mask was specified
		cpb1.hFileInfo.ioFlCrDat = cpb2.hFileInfo.ioFlCrDat = aptr->fileCrDate; 

	if (cspb.ioSpecBits & fsSBFlFndrInfo) { 
		// for files, type and creator are important
		cpb1.hFileInfo.ioFlFndrInfo.fdType = aptr->fileType;;
		cpb1.hFileInfo.ioFlFndrInfo.fdCreator = aptr->fdCreator;

		// create the mask bits so that only type and creator are important.
		cpb2.hFileInfo.ioFlFndrInfo.fdType = 0xFFFFFFFF;
		cpb2.hFileInfo.ioFlFndrInfo.fdCreator = 0xFFFFFFFF;
		cpb2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
		cpb2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
		cpb2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
		cpb2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
	}
		
	
	err = PBCatSearch (&cspb, false);
	if (err == noErr || err == eofErr) { //if reached end of volume, it is ok
		for (i = 0; i < cspb.ioActMatchCount; ++i) { // load alias list buffer
			(void) AL_updateAList (vref, ans[i].parID, &ans[i].name, fcnt, lim, flist);
			if (*fcnt >= lim)	// no room in return list
				break;
		}
		if (err == noErr) // new position not meaningful for eofErr or fnfErr
			*prec = cspb.ioCatPosition;	// return the new position
	}

	return (err);			
	
} // end of FindByCatSrch routine

/*********************************************************************
******** Perform next CatSearch **************************************
*********************************************************************/ 
static OSErr NextCatSrch(const myStaticPtr sp)   	//I pointer to my static storage		
{
	OSErr 		err, err2 = noErr; 	// result code
	CanonicalFileSpec	nextFile;	// next file that is found
	short 		fcnt = 0;			// search count

	/* currently, CatSearch is by matching partial name, type, creator and creation date.
	 Also, do one at a time.  This gives user a chance to cancel out of search.
	 curPrec maintains the current CatSearch position and hence search could be continued. */

	err = FindByCatSrch (sp->curVref, sp->aptr, fsSBFlFndrInfo + fsSBFlCrDat,
					KMaxCSrchQuant, 1, &fcnt, &nextFile, &sp->curPrec);
	if (err == noErr || err == eofErr) { // this is ok, just reached end of volume while CatSearching
		if (fcnt != 0) // if nothing found, get out. otherwise, accept the alias
			err2 = AcceptFile(sp->curVref, nextFile.dirID, nextFile.fileName, sp);
	}

	return (err ? err : err2);	
}

/*********************************************************************
******** Perform next asynchronous search ****************************
*********************************************************************/ 
static OSErr NextAsyncSrch(const myStaticPtr sp)   	//I pointer to my static storage
{
	OSErr err;					// return code
	
	err = sp->asyncCPB.hFileInfo.ioResult; // result of last async search
	if (err == noErr) { 		// last async search was successful
		if (sp->asyncCPB.hFileInfo.ioFlAttrib & kDirMask) // found a directory
			if (sp->curIdx >= (kMaxDirStack-1)) { // ran out of directory stack
				AL_displayAlert(kMem2StrIdx);	// too many dirs on volume 
				err = memFullErr;		// out of memory
			}
			else 				// add it to directory stack in order to search it later
				(*(sp->dirStack))[++sp->curIdx] = sp->asyncCPB.dirInfo.ioDrDirID;
		// does type, creator and creation date match? If yes, accept it as an alias.
		if (AL_attribMatches(sp->aptr, &sp->asyncCPB, kMatchFileNumber))
			err = AcceptFile (sp->curVref, sp->asyncCPB.hFileInfo.ioFlParID, 
								sp->asyncCPB.hFileInfo.ioNamePtr, sp);
		++sp->asyncCPB.hFileInfo.ioFDirIndex;	// search next file in the current dir

	} else if (err == fnfErr || err == dirNFErr || err == afpAccessDenied) { 
		// these are valid errors. Either a directory was completely searched, or for
		// some reason the directory has disappeared or we don't have access to search it.
		if (sp->curIdx >= 0) {	// any more directories remaining to be searched?
			sp->curDir = (*(sp->dirStack))[sp->curIdx--];	// next directory from stack
			sp->asyncCPB.hFileInfo.ioFDirIndex = 1;	// start search at the beginning
			err = noErr;		// force an async pending search 
		}

	} else
		; // ******************** serious error, get out **************
		
	if (err==noErr) { 			// new I/O request if no fatal error
		sp->asyncCPB.dirInfo.ioDrDirID = sp->curDir;// replace file number with dirID 
		(void) PBGetCatInfo (&sp->asyncCPB, TRUE); // issue an async CatInfo call
	}
	
	return (err);				// error from last async call or memFullErr
}

/*********************************************************************
******** Find next matching alias via CatSearch or slow async search *
*********************************************************************/ 
static OSErr Do_findNext(const myStaticPtr sp)   	//I pointer to my static storage	
{
	if (sp->CSrch)						// CatSearch is available
		return (NextCatSrch(sp));		// do it
	else if (sp->asyncCPB.hFileInfo.ioResult != 1) // Did last async call complete?
		return (NextAsyncSrch(sp));	// yes, start the next one
	else
		return (noErr);				// last async did not complete, just return
}

/*********************************************************************
******** User pushed the TryHarder button ****************************
*********************************************************************/ 
static OSErr Do_TryHarder(const myStaticPtr sp)   	//I pointer to my static storage
{
	OSErr err = noErr;				// return code 
	
	HiliteControl (sp->ButtonCH[kI_TryHarder-1], kButtonInactive); // make it inactive for now
	ChangeOthers(kButtonInactive, sp);	// while trying hard, all other buttons are inactive
	if (sp->startCount == 1) {			// flag says no cancel dialog up
		sp->startCount = TickCount();	// remember when try hard started
		SetCursor(*(GetCursor(watchCursor))); // cursor is in watch mode only before cancel dialog shows up
	}
	sp->TryHarder = TRUE;				// set the  flag
	if (sp->firstTry) {					// if first time, need to allocate & initialize
		err = NextCatSrch(sp);
		if (err != noErr && err != eofErr) { // CatSearch not available
			sp->CSrch = FALSE;			// remember that fact
			// allocate a directory stack to save all dirIDs found on the volume.
			// so each can be searched later on for matching alias.
			if (sp->dirStack == NULL) { // not already allocated
				sp->dirStack = (LongsHandle)(NewHandle(sizeof(long)*kMaxDirStack));
				if (sp->dirStack == NULL)
					return (MemError());
			}
			// allocated the directory stack
			(*(sp->dirStack))[0] = sp->curDir; // from root directory
			sp->curIdx = 0;			// only root directory in the stack for now
			sp->asyncCPB.hFileInfo.ioResult = fnfErr; // this will start initial search
			sp->asyncCPB.hFileInfo.ioCompletion = NULL; // no completion routine
			sp->asyncCPB.hFileInfo.ioNamePtr = &sp->curFname;// returned name from GetCatInfo
			sp->asyncCPB.hFileInfo.ioFVersNum = 0;	  // keep GetCatInfo happy
			sp->asyncCPB.hFileInfo.ioVRefNum = sp->curVref; // volume to search
			err = NextAsyncSrch(sp);// start the async search
		} else if (err == eofErr) { 	// end of volume reached while CatSearching
			NextVolumeSearch (sp); 		// search the next volume
			err = noErr;				// not a real error
		}
		sp->firstTry = FALSE;			// from now on, no allocation and initialization unless next volume is searched
	}
	return (err);
}

/*********************************************************************
 ***************	Initialize  volume search statics ****************
 *********************************************************************/

static void Init_volSearchStatics(const myStaticPtr sp) //I pointer to my static storage
{
	sp->firstTry = TRUE;			// will be TryingHard the first time
	sp->CSrch = TRUE;				// assume CatSearch available
	sp->quitFlag = FALSE;			// assume client filter proc does not want to quit
	sp->curDir = kRootDirID;		// current directory we are searching
	sp->curIdx = 0;					// current directory index for the search
	sp->curPrec.initialize = 0;		// initialize CatSearch 
}

/*********************************************************************
 ***************	Perform next mounted volume search ****************
 *********************************************************************/

static void NextVolumeSearch(const myStaticPtr sp) //I pointer to my static storage
{
	short nextVref;					// volume ref num of next volume to be searched

	for (;;) {
		if (sp->vcbPtr == NULL) {
			DoneTryingHard(kDoneForever, sp);// done forever TryingHard
			return;
		} else {
			nextVref = sp->vcbPtr->vcbVRefNum;
			sp->vcbPtr = (VCB *) sp->vcbPtr->qLink;
			if (sp->firstVref == nextVref)
				continue;
			else
				break;
		}
	}
	// we found a valid vrefNum that needs to be searched
	sp->curVref = nextVref;			// this the current one we are searching
	sp->TryHarder = TRUE;			// again trying harder on next volume
	Init_volSearchStatics (sp);		// init volume search statics
	/* before we do the next volume search, make sure the cancel dialog box
	comes up if the timer has expired during (last CatSearch that returned eofErr) */
	DisplayCancelIfNeeded (sp); // display cancel dialog box if needed
	return;	
}

/*********************************************************************
******** User pushed the AnotherOne button ***************************
*********************************************************************/ 
static void Do_AnotherOne(const myStaticPtr sp)   	//I pointer to my static storage
{
	Point where;					// display location of SFPGetFile dialog box
	SFTypeList	typeList;			// alias type passed to SFGetFile

	sp->folderSelected = FALSE;	 	// when we come back from SFPGetFile, was a Folder really selected?
	//  SFGetFile is linked here.  User presumably knows where the aliased 
	//  entity is.  Let him find it through modified SFPGetFile modal dialog.
	SetPt (&where, sp->aliasDialog->portRect.left+kSFGetHOffset, 
			sp->aliasDialog->portRect.top+kSFGetVOffset);
	LocalToGlobal(&where);	// put SFGetFile dialog box nicely offseted from alias dialog
	typeList[0] = sp->aptr->fileType; // match original alias type only
	if (sp->aptr->thisAliasKind == kDirAlias) {
		typeList[0] = kSelectNoFile; // show only folders
		
		CustomGetFile(NULL, 1, &typeList, &sp->mySFreply, kAlFldrSFGetID,
						where, FolderSFGetHook, NULL, NULL, NULL, sp);		// put up the folder selection dialog
	} else 
		CustomGetFile(NULL, 1, &typeList, &sp->mySFreply, kAlFileSFGetID,
						where, NULL, NULL, NULL, NULL, sp);					// put up the file/volume selection dialog 	
	return;
}

/********************************************************************
 ***************	Initialize  my static storage once ***************
 *********************************************************************/

static void Init_s(const myStaticPtr sp)   	//I pointer to my static storage
{
	sp->aliasDialog = NULL; 		// Pointer to alias dialog
	sp->cancelDialog = NULL;		// Pointer to cancel further search dialog
	sp->alSICNHandle = NULL; 		// handle to SICN resource
	sp->alList = NULL;				// handle to alias list buffer
	sp->nameList = NULL;			// handle to list of names
	sp->locationList = NULL;		// handle to the list displaying location
	sp->DoubleClick = FALSE;  		// Flag to say that a double click on a list happened
	sp->TryHarder = FALSE;			// Flag to say if in TryHarder mode
	sp->startCount = 1;				// mark the flag to say that the cancel dialog box is not up
	sp->selRow = -1;    			// Current selection row in names list
	sp->prevSel = -1;				// previous selection row in names list
	sp->dirStack = NULL;			// handle to directory stack
	sp->curCnt = 0;					// current count of alias list buffer
	sp->curLim = kMaxCSrchEnt;		// current max entries allocated in alias list
	sp->vcbPtr = (VCB *)GetVCBQHdr()->qHead; // VCB Q header
	Init_volSearchStatics (sp);		// init volume search statics
}

 /*********************************************************************
 ***************	Select an alias	with dialog box *******************
 *********************************************************************/

pascal OSErr AL_selectAliasWithDialog(const CanonicalFileSpec *fromFile, //I from file 
						const Str31 fTypeName,			//I descriptive name for file type
						short cnt,						//I #of entries found during fast search
						CanonicalFileSpecList alFastList, //I fast search answer
						AliasFilterProcPtr aliasFilter,	//I file filter proc pointer
						AliasHandlePriv Alias,			//I/O alias record handle
						CanonicalFileSpec *toFile, 		//O returned toFile
						ModalFilterWithCallback filterProc, //I modal filter proc pointer
						Ptr yourDataPtr)				//I additional param passed to filter procs

{
	OSErr 		err = noErr;		 // result code	
	short    	itemHit;   			// Get selection from ModalDialog
	register 	int i;				// do loop index
	char 		masterFlags;		// flags to remember before locking and unlocking handles
	Boolean   	ExitDialog = FALSE; // Flag used to exit the Dialog 
	long		fnum;				// file number
	long		parDirID;			// parent directory ID
	Boolean		needsUpdate;		// alias record needs update or not?
	GrafPtr		savePort;			// save current port for restoring later
	register CanonicalFileSpec *fptr; // pointer into alias list 

	/* the variables in structure myStaticVars are used by many routines in this file.  
		They are meant as static variables.  Since static is not allowed in stand-alone
		code resource, we are using pointer to this structure to simulate having static
		variables.  None of these variables are needed across SelectAlias calls.  They
		are valid while the dialog box is up.
	*/
	myStaticPtr sp;					// pointer to our static storage
		
	if ((sp = (myStaticPtr)NewPtr(sizeof(myStaticVars))) == NULL)
		return (MemError());

	// do initialization of our storage variables
	Init_s(sp);
	sp->fileFilter = aliasFilter;	// remember client supplied file filter proc ptr
	sp->modalFilter = filterProc;	// remember client supplied modal filter proc ptr
	sp->yourDataPtr = yourDataPtr;	// remember additional param for both filter procs	

	masterFlags = HGetState ((Handle)Alias); // remember master flags
	HLock((Handle)Alias);			// lock the passed alias record	
	sp->aptr = *Alias;				// pointer to alias record as a  variable

	// obtain volume reference number for the volume that we are searching
	if (cnt > 0) 					// found >=2 during fast search
		// remember the first volume that we will search
		sp->curVref = alFastList[0].vRefNum;
	else {
		err = AL_findVolume(sp->aptr, 0 /*no autoMount*/, &sp->curVref, &needsUpdate, NULL); // obtain curVref
		if (err == nsvErr)
			// could not find original aliase's volume, start with first in VCB chain
			sp->curVref = sp->vcbPtr->vcbVRefNum;
		else if (err != noErr) 		// fatal error
			goto EXIT;
	}
	sp->firstVref = sp->curVref;	// remember the first volume that we searched

	// display initial dialog box
	GetPort (&savePort);			// save the current port
	Display_aliasDialog(fTypeName, sp); // pass the descriptive file type name

	if (cnt > 0) { 					// was anything found during fast search?
		// fill entries of name list
		LDoDraw(FALSE, sp->nameList);// don't draw list while creating it
		
		for (i=0; i<cnt; i++)		// add matching entries to name scroll list
			if ((err = AcceptFile (alFastList[i].vRefNum, alFastList[i].dirID, &alFastList[i].fileName, sp)) != noErr)
				goto EXIT;
				
		SelectNameRow (0, sp);     // select the first entry by default

		// fill location list with the path of first alias (the default selection)
		DisplayLoc (sp->selRow, sp);
		
	} else 	{ // nothing matching was found, kick in TryHarder search automatically
		sp->TryHarder = TRUE; // let MyFilter convert this flag into 'TryHarder' button hit
	}

	LDoDraw(TRUE, sp->nameList);	// let us see list boxes initially
	LDoDraw(TRUE, sp->locationList);
	
	do {							// Start of dialog handle loop 
		ModalDialog(MyFilter, &itemHit); // Wait until an item is hit 
		 
		switch (itemHit) {
		
		case kI_Select:				// Select Button OR Stop Search Button of cancel dialog
			
			if (sp->TryHarder && (sp->startCount == 0)) { // was Stop Search button hit?
			// canceling out of TryHard mode with cancel Dialog Box up?
				DoneTryingHard(kNotDoneForever, sp); // not done forever, user may push TryHarder again
			} else {				// Select button of main dialog was hit
				
				ExitDialog = TRUE;  	// Exit the dialog when this selection is made
				fptr = &(*(sp->alList))[sp->selRow];
				toFile->vRefNum = fptr->vRefNum;
				toFile->dirID = fptr->dirID;
				AL_copyPString (fptr->fileName, toFile->fileName);
				err = AL_findByName (toFile->vRefNum, toFile->dirID, toFile->fileName,
										&fnum, &parDirID); // make sure that the file still exists
			}
			break;

		case kI_Cancel: 			// Handle the Cancel Button
			ExitDialog =TRUE;   	// Exit the dialog when this selection is made 
			err = userCanceledErr;	// return user cancelling out of operation status
			break;
		
		case kI_TryHarder: 		// Handle the Try Harder Button
			if ((err = Do_TryHarder(sp)) != noErr) // start further search
				ExitDialog =TRUE;   // Exit the dialog, serious error occurred
			break;
			
		case kI_SearchInProgress: 	// searching during TryHarder mode is in progress
		
			if (sp->quitFlag) { // client filter proc told us to quit current 'TryHard' seacrh
				DoneTryingHard(kNotDoneForever, sp); // not done forever, user may push TryHarder again
				sp->quitFlag = FALSE; // reset the flag

			} else {
			
				DisplayCancelIfNeeded (sp); // display cancel dialog box if needed
				// find the next matching alias and display it. If can't find any more,
				// provide feedback to the user.
				sp->TryHarder = (Do_findNext(sp) == noErr) ? TRUE : FALSE;
				if (! sp->TryHarder) 	// done searching this volume
					NextVolumeSearch(sp);// try searching next mounted volume
			}
			break;

		case kI_AnotherOne:			// Handle the AnotherOne button 
			
			Do_AnotherOne (sp);		// do it!
			if (sp->mySFreply.sfGood || sp->folderSelected) { // return the selected file/folder as the returned alias
				BlockMove ((Ptr)&sp->mySFreply.sfFile, (Ptr)toFile, sizeof(CanonicalFileSpec));
				ExitDialog = TRUE; 	// user chose the alias, now get out
			} 						//  else user cancelled out of SFPGetFile dialog
			break;
		
		case kI_NamesList:			// Handle a pick in the Names List
			if (sp->prevSel != sp->selRow)	// display its location if a new row was picked
				DisplayLoc (sp->selRow, sp);
			break;
					
		case kI_LocationText:		// if any of the presumably disabled items
		case kI_NameText:			// nothing to do, just go back in loop
		case kI_Title:
		case kI_LocationList:
			break;
		
		default:					// what was it?
			ExitDialog = TRUE;
			err = fnfErr;			// alias not found
			break;
			
		} // end of switch statement
		 
	} while (! ExitDialog);    		// Handle dialog items until exit selected 
	 	
EXIT:
	// release resources, dispose of handles and dialogs.
	if (sp->alSICNHandle != NULL)
		ReleaseResource ((Handle)sp->alSICNHandle); // release SICN resource
	if (sp->nameList != NULL)			// names list
		LDispose (sp->nameList);
	if (sp->locationList != NULL)		// location list
		LDispose (sp->locationList);
	if (sp->alList != NULL)				// deallocate alias list buffer
		DisposHandle ((Handle)sp->alList);
	if (sp->startCount == 0 && sp->cancelDialog != NULL) // a cancel dialog was put up
		DisposDialog(sp->cancelDialog); // Flush the cancel dialog out of memory
	if (sp->aliasDialog != NULL) {
		DisposDialog(sp->aliasDialog); 	// dispose of our dialog
		SetPort(savePort);				// restore the original port
	}

	DisposPtr ((Ptr)sp);				// dispose of our static storage
		
	HUnlock((Handle)Alias);				// unlock the alias record
	HSetState((Handle)Alias, masterFlags); // restore original state

	return (err);
	
} // end of routine AL_selectAliasWithDialog

/***************************** end of alDialog.c file *****************************/

