Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Files /
Chapter 3 - Standard File Package / Using the Standard File Package


Selecting a Directory

You can present the recommended user interface for selecting a directory by calling the CustomGetFile 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 function MyCustomFileFilter 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 returns FALSE, indicating that the item should appear in the list; otherwise, the file filter function returns TRUE 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;
The SetButtonTitle 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 variable gPrevSelectedName 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;
The MyDlgHook dialog hook function defined in Listing 3-18 calls the File Manager function PBGetCatInfo to retrieve the name of the directory to be selected. When the dialog hook function is first called (that is, when item is set to sfHookFirstCall), MyDlgHook determines the current volume and directory by calling the functions GetSFCurVol and GetSFCurDir. When MyDlgHook is called each subsequent time, MyDlgHook calls PBGetCatInfo with the volume reference number and directory ID
of the previously opened directory.

When the user clicks the Select button, MyDlgHook returns the item sfItemCancelButton. When the user clicks the real Cancel button, MyDlgHook
sets the global variable gDirSelectionFlag to FALSE, indicating that the user
didn't select a directory. The function DoGetDirectory 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;
The DoGetDirectory function initializes the two global variables gPrevSelectedName and gDirSelectionFlag. As you have seen, these two variables are used by the custom dialog hook function. Then DoGetDirectory
calls CustomGetFile 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 and CustomGetFile exits. At that
point, the reply record contains information about the last item selected in the list of available items.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
2 JUL 1996