1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxDirDialog 
   8 // Copyright:   (c) AUTHOR 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13 #pragma implementation "dirdlg.h" 
  18 #include "wx/dialog.h" 
  19 #include "wx/dirdlg.h" 
  21 #include "wx/cmndata.h" 
  23 IMPLEMENT_CLASS(wxDirDialog
, wxDialog
) 
  26         kSelectItem 
= 10,                       // select button item number 
  27         kSFGetFolderDlgID 
= 250,        // dialog resource number 
  28         kStrListID 
= 250,                       // our strings 
  29         kSelectStrNum 
= 1,                      // word 'Select: ' for button 
  30         kDesktopStrNum 
= 2,                     // word 'Desktop' for button 
  31         kSelectNoQuoteStrNum 
= 3,       // word 'Select: ' for button 
  33         kUseQuotes 
= true,                      // parameter for SetButtonName 
  34         kDontUseQuotes 
= false 
  37 // the data we need to pass to our standard file hook routine 
  38 // includes a pointer to the dialog, a pointer to the standard 
  39 // file reply record (so we can inspect the current selection) 
  40 // and a copy of the "previous" file spec of the reply record 
  41 // so we can see if the selection has changed 
  44         StandardFileReply       
*sfrPtr
; 
  45         FSSpec                          oldSelectionFSSpec
; 
  48 typedef struct UserDataRec
 
  49         UserDataRec
, *UserDataRecPtr
; 
  51 static void GetLabelString(StringPtr theStr
, short stringNum
) 
  53         GetIndString(theStr
, kStrListID
, stringNum
); 
  56 static void CopyPStr(StringPtr src
, StringPtr dest
) 
  58         BlockMoveData(src
, dest
, 1 + src
[0]); 
  61 static char GetSelectKey(void) 
  63         // this is the key used to trigger the select button 
  65         // NOT INTERNATIONAL SAVVY; should at least grab it from resources 
  71 // SetButtonName sets the name of the Select button in the dialog 
  73 // To do this, we need to call the Script Manager to truncate the 
  74 // label in the middle to fit the button and to merge the button 
  75 // name with the word Select (possibly followed by quotes).  Using 
  76 // the Script Manager avoids all sorts of problems internationally. 
  78 // buttonName is the name to appear following the word Select 
  79 // quoteFlag should be true if the name is to appear in quotes 
  81 static void SetButtonName(DialogPtr theDlgPtr
, short buttonID
, StringPtr buttonName
, 
  97         // get the details of the button from the dialog 
  99         GetDialogItem(theDlgPtr
, buttonID
, &buttonType
, &buttonHandle
, &buttonRect
); 
 101         // get the string for the select button label, "Select ^0" or "Select Ò^0Ó" 
 103         GetLabelString(labelStr
, (quoteFlag 
== kUseQuotes
) ? kSelectStrNum 
: kSelectNoQuoteStrNum
); 
 105         // make string handles containing the select button label and the 
 106         // file name to be stuffed into the button 
 108         err 
= PtrToHand(&labelStr
[1], &labelHandle
, labelStr
[0]); 
 109         if (err 
!= noErr
) goto Bail
; 
 111         // cut out the middle of the file name to fit the button 
 113         // we'll temporarily use labelStr here to hold the modified button name 
 114         // since we don't own the buttonName string storage space 
 116         textWidth 
= (buttonRect
.right 
- buttonRect
.left
) - StringWidth(labelStr
); 
 118         CopyPStr(buttonName
, labelStr
); 
 119         (void) TruncString(textWidth
, labelStr
, smTruncMiddle
); 
 121         err 
= PtrToHand(&labelStr
[1], &nameHandle
, labelStr
[0]); 
 122         if (err 
!= noErr
) goto Bail
; 
 124         // replace the ^0 in the Select string with the file name 
 126         CopyPStr("\p^0", keyStr
); 
 128         (void) ReplaceText(labelHandle
, nameHandle
, keyStr
); 
 130         labelStr
[0] = (unsigned char) GetHandleSize(labelHandle
); 
 131         BlockMoveData(*labelHandle
, &labelStr
[1], labelStr
[0]); 
 133         // now set the control title, and re-validate the area 
 134         // above the control to avoid a needless redraw 
 136         SetControlTitle((ControlHandle
) buttonHandle
, labelStr
); 
 138         ValidRect(&buttonRect
); 
 141         if (nameHandle
)         DisposeHandle(nameHandle
); 
 142         if (labelHandle
)        DisposeHandle(labelHandle
); 
 146 // FlashButton briefly highlights the dialog button  
 147 // as feedback for key equivalents 
 149 static void FlashButton(DialogPtr theDlgPtr
, short buttonID
) 
 154         unsigned long   finalTicks
; 
 156         GetDialogItem(theDlgPtr
, buttonID
, &buttonType
, &buttonHandle
, &buttonRect
); 
 157         HiliteControl((ControlHandle
) buttonHandle
, kControlButtonPart
); 
 158         Delay(10, &finalTicks
); 
 159         HiliteControl((ControlHandle
) buttonHandle
, 0); 
 162 static Boolean 
SameFSSpec(FSSpecPtr spec1
, FSSpecPtr spec2
) 
 164         return (spec1
->vRefNum 
== spec2
->vRefNum
 
 165                         && spec1
->parID 
== spec2
->parID
 
 166                         && EqualString(spec1
->name
, spec2
->name
, false, false)); 
 168 // MyModalDialogFilter maps a key to the Select button, and handles 
 169 // flashing of the button when the key is hit 
 171 static pascal Boolean 
SFGetFolderModalDialogFilter(DialogPtr theDlgPtr
, EventRecord 
*eventRec
, 
 172                                                                                         short *item
, Ptr dataPtr
) 
 174 #pragma unused (dataPtr) 
 176         // make certain the proper dialog is showing, 'cause standard file 
 177         // can nest dialogs but calls the same filter for each 
 179         if (((WindowPeek
) theDlgPtr
)->refCon 
== sfMainDialogRefCon
) 
 181                 // check if the select button was hit 
 183                 if ((eventRec
->what 
== keyDown
) 
 184                         && (eventRec
->modifiers 
& cmdKey
)  
 185                         && ((eventRec
->message 
& charCodeMask
) == GetSelectKey())) 
 188                         FlashButton(theDlgPtr
, kSelectItem
); 
 197 // MyDlgHook is a hook routine that maps the select button to Open 
 198 // and sets the Select button name 
 200 static pascal short SFGetFolderDialogHook(short item
, DialogPtr theDlgPtr
, Ptr dataPtr
) 
 202         UserDataRecPtr  theUserDataRecPtr
; 
 204         short                   desktopVRefNum
; 
 209         // be sure Std File is really showing us the intended dialog, 
 210         // not a nested modal dialog 
 212         if (((WindowPeek
) theDlgPtr
)->refCon 
!= sfMainDialogRefCon
) 
 217         theUserDataRecPtr 
= (UserDataRecPtr
) dataPtr
; 
 219         // map the Select button to Open 
 221         if (item 
== kSelectItem
) 
 223                 item 
= sfItemOpenButton
; 
 226         // find the desktop folder 
 228         err 
= FindFolder(theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
, 
 229                                         kDesktopFolderType
, kDontCreateFolder
, 
 230                                         &desktopVRefNum
, &desktopDirID
); 
 234                 // for errors, get value that won't match any real vRefNum/dirID 
 239         // change the Select button label if the selection has changed or 
 240         // if this is the first call to the hook 
 242         if (item 
== sfHookFirstCall
 
 243                 || item 
== sfHookChangeSelection
 
 244                 || item 
== sfHookRebuildList
 
 245                 || ! SameFSSpec(&theUserDataRecPtr
->sfrPtr
->sfFile
, 
 246                                         &theUserDataRecPtr
->oldSelectionFSSpec
)) 
 248                 // be sure there is a file name selected 
 250                 if (theUserDataRecPtr
->sfrPtr
->sfFile
.name
[0] != '\0') 
 252                         SetButtonName(theDlgPtr
, kSelectItem
,  
 253                                                         theUserDataRecPtr
->sfrPtr
->sfFile
.name
,  
 254                                                         kUseQuotes
);    // true -> use quotes 
 258                         // is the desktop selected? 
 260                         if (theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum 
== desktopVRefNum
 
 261                                 && theUserDataRecPtr
->sfrPtr
->sfFile
.parID 
== desktopDirID
) 
 263                                 // set button to "Select Desktop" 
 265                                 GetLabelString(desktopName
, kDesktopStrNum
); 
 266                                 SetButtonName(theDlgPtr
, kSelectItem
,  
 267                                                                 desktopName
, kDontUseQuotes
);   // false -> no quotes 
 271                                 // get parent directory's name for the Select button 
 273                                 // passing an empty name string to FSMakeFSSpec gets the 
 274                                 // name of the folder specified by the parID parameter 
 276                                 (void) FSMakeFSSpec(theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
, 
 277                                         theUserDataRecPtr
->sfrPtr
->sfFile
.parID
, "\p", 
 279                                 SetButtonName(theDlgPtr
, kSelectItem
,  
 280                                                         tempSpec
.name
, kUseQuotes
); // true -> use quotes 
 285         // save the current selection as the old selection for comparison next time 
 287         // it's not valid on the first call, though, or if we don't have a  
 288         // name available from standard file 
 290         if (item 
!= sfHookFirstCall 
|| theUserDataRecPtr
->sfrPtr
->sfFile
.name
[0] != '\0') 
 292                 theUserDataRecPtr
->oldSelectionFSSpec 
= theUserDataRecPtr
->sfrPtr
->sfFile
; 
 296                 // on first call, empty string won't set the button correctly,  
 297                 // so invalidate oldSelection 
 299                 theUserDataRecPtr
->oldSelectionFSSpec
.vRefNum 
= 999; 
 300                 theUserDataRecPtr
->oldSelectionFSSpec
.parID 
= 0; 
 306 void StandardGetFolder( ConstStr255Param message 
, ConstStr255Param path 
, FileFilterYDUPP fileFilter
, StandardFileReply 
*theSFR
) 
 309         SFTypeList                      mySFTypeList
; 
 313         Boolean                         wasAliasedFlag
; 
 314         DlgHookYDUPP            dlgHookUPP
; 
 315         ModalFilterYDUPP        myModalFilterUPP
; 
 319         // presumably we're running System 7 or later so CustomGetFile is 
 322         // set initial contents of Select button to a space 
 324         CopyPStr("\p ", theSFR
->sfFile
.name
); 
 326         // point the user data parameter at the reply record so we can get to it later 
 328         myData
.sfrPtr 
= theSFR
; 
 330         // display the dialog 
 332         dlgHookUPP 
= NewDlgHookYDProc(SFGetFolderDialogHook
); 
 333         myModalFilterUPP 
= NewModalFilterYDProc(SFGetFolderModalDialogFilter
); 
 335         thePt
.h 
= thePt
.v 
= -1; // center dialog 
 337         ParamText( message 
, NULL 
, NULL 
, NULL 
) ; 
 339         CustomGetFile(  fileFilter
,  
 340                                         -1,                                     // show all types 
 344                                         thePt
,                          // top left point 
 347                                         nil
,                            // activate list 
 348                                         nil
,                            // activate proc 
 351         DisposeRoutineDescriptor(dlgHookUPP
); 
 352         DisposeRoutineDescriptor(myModalFilterUPP
); 
 354         // if cancel wasn't pressed and no fatal error occurred... 
 358                 // if no name is in the reply record file spec, 
 359                 // use the file spec of the parent folder 
 361                 if (theSFR
->sfFile
.name
[0] == '\0') 
 363                         err 
= FSMakeFSSpec(theSFR
->sfFile
.vRefNum
, theSFR
->sfFile
.parID
, 
 367                                 theSFR
->sfFile 
= tempSpec
; 
 371                                 // no name to return, forget it 
 373                                 theSFR
->sfGood 
= false; 
 377                 // if there is now a name in the file spec, check if it's 
 378                 // for a folder or a volume 
 380                 if (theSFR
->sfFile
.name
[0] != '\0') 
 382                         // the parID of the root of a disk is always fsRtParID == 1 
 384                         if (theSFR
->sfFile
.parID 
== fsRtParID
) 
 386                                 theSFR
->sfIsVolume 
= true; 
 387                                 theSFR
->sfIsFolder 
= false;     // it would be reasonable for this to be true, too 
 390                         // we have a valid FSSpec, now let's make sure it's not for an alias file 
 392                         err 
= ResolveAliasFile(&theSFR
->sfFile
, true, &folderFlag
, &wasAliasedFlag
); 
 395                                 theSFR
->sfGood 
= false; 
 398                         // did the alias resolve to a folder? 
 400                         if (folderFlag  
&& ! theSFR
->sfIsVolume
) 
 402                                 theSFR
->sfIsFolder 
= true; 
 408 static pascal Boolean 
OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr
, Ptr dataPtr
) 
 410 #pragma unused (dataPtr) 
 412         // return true if this item is invisible or a file 
 417         visibleFlag 
= ! (myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdFlags 
& kIsInvisible
); 
 418         folderFlag 
= (myCInfoPBPtr
->hFileInfo
.ioFlAttrib 
& 0x10); 
 420         // because the semantics of the filter proc are "true means don't show 
 421         // it" we need to invert the result that we return 
 423         return !(visibleFlag 
&& folderFlag
); 
 426 wxDirDialog::wxDirDialog(wxWindow 
*parent
, const wxString
& message
, 
 427         const wxString
& defaultPath
, 
 428         long style
, const wxPoint
& pos
) 
 431     m_dialogStyle 
= style
; 
 433         m_path 
= defaultPath
; 
 436 int wxDirDialog::ShowModal() 
 442                 strcpy((char *)prompt
, m_message
) ; 
 443                 c2pstr((char *)prompt 
) ; 
 445                 strcpy((char *)path
, m_path 
) ; 
 446                 c2pstr((char *)path 
) ; 
 448                 FileFilterYDUPP         invisiblesExcludedCustomFilterUPP
; 
 449                 StandardFileReply       reply 
; 
 450                 invisiblesExcludedCustomFilterUPP 
=  
 451                         NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter
); 
 453                 StandardGetFolder( prompt 
, path 
, invisiblesExcludedCustomFilterUPP
, &reply
); 
 455                 DisposeRoutineDescriptor(invisiblesExcludedCustomFilterUPP
); 
 456                 if ( reply
.sfGood 
== false ) 
 463                         m_path 
= wxMacFSSpec2UnixFilename( &reply
.sfFile 
) ;