Important: The information in this document is obsolete and should not be used for new development.
Selecting a Directory
You can present the recommended user interface for selecting a directory by calling theCustomGetFile
procedure and passing it the addresses of a custom file filter function and a dialog hook function. See "Selecting Volumes and Directories" on page 3-10 for a description of the appearance and behavior of the directory selection dialog box.The file filter function used to select directories is quite simple; it ensures that only directories, not files, are listed in the dialog box displayed by
CustomGetFile
.
Listing 3-16 defines a file filter function you can use for this purpose.Listing 3-16 A file filter function that lists only directories
FUNCTION MyCustomFileFilter (pb: CInfoPBPtr; myDataPtr: Ptr): Boolean; CONST kFolderBit = 4; {bit set in ioFlAttrib for a directory} BEGIN {list directories only} MyCustomFileFilter := NOT BTst(pb^.ioFlAttrib, kFolderBit); END;The functionMyCustomFileFilter
simply inspects the appropriate bit in the file attributes (ioFlAttrib
) field of the catalog information parameter block passed to it. If the directory bit is set, the file filter function returnsFALSE
, indicating that the item should appear in the list; otherwise, the file filter function returnsTRUE
to exclude the item from the list. Because a volume is identified via its root directory, volumes also appear in the list of items in the dialog box.The title of the Select button should identify which directory is available for selection. You can use the
SetButtonTitle
procedure defined in Listing 3-17 to set the title
of a button.Your dialog hook function calls the
SetButtonTitle
procedure to copy the truncated title of the selected item into the Select button. This title eliminates possible user confusion about which directory is available for selection. If no item in the list is selected, the dialog hook function uses the name of the directory shown in the pop-up menu as the title of the Select button.Listing 3-17 Setting a button's title
PROCEDURE SetButtonTitle (ButtonHdl: Handle; name: Str255; ButtonRect: Rect); VAR result: Integer; {result of TruncString} width: Integer; {width available for name of directory} BEGIN gPrevSelectedName := name; WITH ButtonRect DO width := (right - left) - (StringWidth('Select ""') + CharWidth('\xA0')); result := TruncString(width, name, smTruncMiddle); SetCTitle(ControlHandle(ButtonHdl), CONCAT('Select "', name, '"')); ValidRect(ButtonRect); END;TheSetButtonTitle
procedure is passed a handle to the button whose title is to be changed, the name of the directory available for selection, and the button's enclosing rectangle. The global variablegPrevSelectedName
holds the full directory name, before truncation.A dialog hook function manages most of the process of letting the user select a director. Listing 3-18 defines a dialog hook function that handles user selections in the dialog box.
Listing 3-18 Handling user selections in the directory selection dialog box
FUNCTION MyDlgHook (item: Integer; theDialog: DialogPtr; myDataPtr: Ptr): Integer; CONST kGetDirBTN = 10; {Select directory button} TYPE SFRPtr = ^StandardFileReply; VAR myType: Integer; {menu item selected} myHandle: Handle; {needed for GetDItem} myRect: Rect; {needed for GetDItem} myName: Str255; myPB: CInfoPBRec; mySFRPtr: SFRPtr; myErr: OSErr; BEGIN MyDlgHook := item; {default, except in special cases below} IF GetWRefCon(WindowPtr(theDialog)) <> LongInt(sfMainDialogRefCon) THEN Exit(MyDlgHook); {this function is only for main dialog box} GetDItem(theDialog, kGetDirBTN, myType, myHandle, myRect); IF item = sfHookFirstCall THEN BEGIN {Determine current folder name and set title of Select button.} WITH myPB DO BEGIN ioCompletion := NIL; ioNamePtr := @myName; ioVRefNum := GetSFCurVol; ioFDirIndex := - 1; ioDirID := GetSFCurDir; END; myErr := PBGetCatInfo(@myPB, FALSE); SetButtonTitle(myHandle, myName, myRect); END ELSE BEGIN {Get mySFRPtr from 3rd parameter to hook function.} mySFRPtr := SFRPtr(myDataPtr); {Track name of folder that can be selected.} IF (mySFRPtr^.sfIsFolder) OR (mySFRPtr^.sfIsVolume) THEN myName := mySFRPtr^.sfFile.name ELSE BEGIN WITH myPB DO BEGIN ioCompletion := NIL; ioNamePtr := @myName; ioVRefNum := mySFRPtr^.sfFile.vRefNum; ioFDirIndex := -1; ioDrDirID := mySFRPtr^.sfFile.parID; END; myErr := PBGetCatInfo(@myPB, FALSE); END; {Change directory name in button title as needed.} IF myName <> gPrevSelectedName THEN SetButtonTitle(myHandle, myName, myRect); CASE item OF kGetDirBTN: {force return by faking a cancel} MyDlgHook := sfItemCancelButton; sfItemCancelButton: gDirSelectionFlag := FALSE;{flag no directory was selected} OTHERWISE ; END; {CASE} END; END;TheMyDlgHook
dialog hook function defined in Listing 3-18 calls the File Manager functionPBGetCatInfo
to retrieve the name of the directory to be selected. When the dialog hook function is first called (that is, whenitem
is set tosfHookFirstCall
),MyDlgHook
determines the current volume and directory by calling the functionsGetSFCurVol
andGetSFCurDir
. WhenMyDlgHook
is called each subsequent time,MyDlgHook
callsPBGetCatInfo
with the volume reference number and directory ID
of the previously opened directory.When the user clicks the Select button,
MyDlgHook
returns the itemsfItemCancelButton
. When the user clicks the real Cancel button,MyDlgHook
sets the global variable gDirSelectionFlag toFALSE
, indicating that the user
didn't select a directory. The functionDoGetDirectory
uses that variable to distinguish between clicks of Cancel and clicks of Select.The function
DoGetDirectory
defined in Listing 3-19 uses the file filter function
and the dialog hook functions defined above to manage the directory selection dialog box. On exit,DoGetDirectory
returns a standard file reply record describing the selected directory.Listing 3-19 Presenting the directory selection dialog box
FUNCTION DoGetDirectory: StandardFileReply; VAR myReply: StandardFileReply; myTypes: SFTypeList; {types of files to display} myPoint: Point; {upper-left corner of box} myNumTypes: Integer; myModalFilter: ModalFilterYDProcPtr; myActiveList: Ptr; myActivateProc: ActivateYDProcPtr; myName: Str255; CONST rGetDirectoryDLOG = 128; {resource ID of custom dialog box} BEGIN gPrevSelectedName := ''; {initialize name of previous selection} gDirSelectionFlag := TRUE; {initialize directory selection flag} myNumTypes := -1; {pass all types of files to file filter} myPoint.h := -1; {center dialog box on screen} myPoint.v := -1; myModalFilter := NIL; myActiveList := NIL; myActivateProc := NIL; CustomGetFile(@MyCustomFileFilter, myNumTypes, myTypes, myReply, rGetDirectoryDLOG, myPoint, @MyDlgHook, myModalFilter, myActiveList, myActivateProc, @myReply); {Get the name of the directory.} IF gDirSelectionFlag AND myReply.sfIsVolume THEN myName := Concat(myReply.sfFile.name, ':') ELSE myName := myReply.sfFile.name; IF gDirSelectionFlag AND myReply.sfIsVolume THEN myReply.sfFile.name := myName ELSE IF gDirSelectionFlag THEN myReply.sfFile.name := gPrevSelectedName; gDirSelectionFlag := FALSE; DoGetDirectory := myReply; END;TheDoGetDirectory
function initializes the two global variables gPrevSelectedName and gDirSelectionFlag. As you have seen, these two variables are used by the custom dialog hook function. ThenDoGetDirectory
callsCustomGetFile
to display the directory selection dialog box and handle user selections. When the user selects a directory or clicks the Cancel button, the dialog
hook function returns sfItemCancelButton andCustomGetFile
exits. At that
point, the reply record contains information about the last item selected in the list of available items.