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 #if !USE_SHARED_LIBRARY
24 IMPLEMENT_CLASS(wxDirDialog
, wxDialog
)
28 kSelectItem
= 10, // select button item number
29 kSFGetFolderDlgID
= 250, // dialog resource number
30 kStrListID
= 250, // our strings
31 kSelectStrNum
= 1, // word 'Select: ' for button
32 kDesktopStrNum
= 2, // word 'Desktop' for button
33 kSelectNoQuoteStrNum
= 3, // word 'Select: ' for button
35 kUseQuotes
= true, // parameter for SetButtonName
36 kDontUseQuotes
= false
39 // the data we need to pass to our standard file hook routine
40 // includes a pointer to the dialog, a pointer to the standard
41 // file reply record (so we can inspect the current selection)
42 // and a copy of the "previous" file spec of the reply record
43 // so we can see if the selection has changed
46 StandardFileReply
*sfrPtr
;
47 FSSpec oldSelectionFSSpec
;
50 typedef struct UserDataRec
51 UserDataRec
, *UserDataRecPtr
;
53 static void GetLabelString(StringPtr theStr
, short stringNum
)
55 GetIndString(theStr
, kStrListID
, stringNum
);
58 static void CopyPStr(StringPtr src
, StringPtr dest
)
60 BlockMoveData(src
, dest
, 1 + src
[0]);
63 static char GetSelectKey(void)
65 // this is the key used to trigger the select button
67 // NOT INTERNATIONAL SAVVY; should at least grab it from resources
73 // SetButtonName sets the name of the Select button in the dialog
75 // To do this, we need to call the Script Manager to truncate the
76 // label in the middle to fit the button and to merge the button
77 // name with the word Select (possibly followed by quotes). Using
78 // the Script Manager avoids all sorts of problems internationally.
80 // buttonName is the name to appear following the word Select
81 // quoteFlag should be true if the name is to appear in quotes
83 static void SetButtonName(DialogPtr theDlgPtr
, short buttonID
, StringPtr buttonName
,
99 // get the details of the button from the dialog
101 GetDialogItem(theDlgPtr
, buttonID
, &buttonType
, &buttonHandle
, &buttonRect
);
103 // get the string for the select button label, "Select ^0" or "Select Ò^0Ó"
105 GetLabelString(labelStr
, (quoteFlag
== kUseQuotes
) ? kSelectStrNum
: kSelectNoQuoteStrNum
);
107 // make string handles containing the select button label and the
108 // file name to be stuffed into the button
110 err
= PtrToHand(&labelStr
[1], &labelHandle
, labelStr
[0]);
111 if (err
!= noErr
) goto Bail
;
113 // cut out the middle of the file name to fit the button
115 // we'll temporarily use labelStr here to hold the modified button name
116 // since we don't own the buttonName string storage space
118 textWidth
= (buttonRect
.right
- buttonRect
.left
) - StringWidth(labelStr
);
120 CopyPStr(buttonName
, labelStr
);
121 (void) TruncString(textWidth
, labelStr
, smTruncMiddle
);
123 err
= PtrToHand(&labelStr
[1], &nameHandle
, labelStr
[0]);
124 if (err
!= noErr
) goto Bail
;
126 // replace the ^0 in the Select string with the file name
128 CopyPStr("\p^0", keyStr
);
130 (void) ReplaceText(labelHandle
, nameHandle
, keyStr
);
132 labelStr
[0] = (unsigned char) GetHandleSize(labelHandle
);
133 BlockMoveData(*labelHandle
, &labelStr
[1], labelStr
[0]);
135 // now set the control title, and re-validate the area
136 // above the control to avoid a needless redraw
138 SetControlTitle((ControlHandle
) buttonHandle
, labelStr
);
140 ValidRect(&buttonRect
);
143 if (nameHandle
) DisposeHandle(nameHandle
);
144 if (labelHandle
) DisposeHandle(labelHandle
);
148 // FlashButton briefly highlights the dialog button
149 // as feedback for key equivalents
151 static void FlashButton(DialogPtr theDlgPtr
, short buttonID
)
156 unsigned long finalTicks
;
158 GetDialogItem(theDlgPtr
, buttonID
, &buttonType
, &buttonHandle
, &buttonRect
);
159 HiliteControl((ControlHandle
) buttonHandle
, kControlButtonPart
);
160 Delay(10, &finalTicks
);
161 HiliteControl((ControlHandle
) buttonHandle
, 0);
164 static Boolean
SameFSSpec(FSSpecPtr spec1
, FSSpecPtr spec2
)
166 return (spec1
->vRefNum
== spec2
->vRefNum
167 && spec1
->parID
== spec2
->parID
168 && EqualString(spec1
->name
, spec2
->name
, false, false));
170 // MyModalDialogFilter maps a key to the Select button, and handles
171 // flashing of the button when the key is hit
173 static pascal Boolean
SFGetFolderModalDialogFilter(DialogPtr theDlgPtr
, EventRecord
*eventRec
,
174 short *item
, Ptr dataPtr
)
176 #pragma unused (dataPtr)
178 // make certain the proper dialog is showing, 'cause standard file
179 // can nest dialogs but calls the same filter for each
181 if (((WindowPeek
) theDlgPtr
)->refCon
== sfMainDialogRefCon
)
183 // check if the select button was hit
185 if ((eventRec
->what
== keyDown
)
186 && (eventRec
->modifiers
& cmdKey
)
187 && ((eventRec
->message
& charCodeMask
) == GetSelectKey()))
190 FlashButton(theDlgPtr
, kSelectItem
);
199 // MyDlgHook is a hook routine that maps the select button to Open
200 // and sets the Select button name
202 static pascal short SFGetFolderDialogHook(short item
, DialogPtr theDlgPtr
, Ptr dataPtr
)
204 UserDataRecPtr theUserDataRecPtr
;
206 short desktopVRefNum
;
211 // be sure Std File is really showing us the intended dialog,
212 // not a nested modal dialog
214 if (((WindowPeek
) theDlgPtr
)->refCon
!= sfMainDialogRefCon
)
219 theUserDataRecPtr
= (UserDataRecPtr
) dataPtr
;
221 // map the Select button to Open
223 if (item
== kSelectItem
)
225 item
= sfItemOpenButton
;
228 // find the desktop folder
230 err
= FindFolder(theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
,
231 kDesktopFolderType
, kDontCreateFolder
,
232 &desktopVRefNum
, &desktopDirID
);
236 // for errors, get value that won't match any real vRefNum/dirID
241 // change the Select button label if the selection has changed or
242 // if this is the first call to the hook
244 if (item
== sfHookFirstCall
245 || item
== sfHookChangeSelection
246 || item
== sfHookRebuildList
247 || ! SameFSSpec(&theUserDataRecPtr
->sfrPtr
->sfFile
,
248 &theUserDataRecPtr
->oldSelectionFSSpec
))
250 // be sure there is a file name selected
252 if (theUserDataRecPtr
->sfrPtr
->sfFile
.name
[0] != '\0')
254 SetButtonName(theDlgPtr
, kSelectItem
,
255 theUserDataRecPtr
->sfrPtr
->sfFile
.name
,
256 kUseQuotes
); // true -> use quotes
260 // is the desktop selected?
262 if (theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
== desktopVRefNum
263 && theUserDataRecPtr
->sfrPtr
->sfFile
.parID
== desktopDirID
)
265 // set button to "Select Desktop"
267 GetLabelString(desktopName
, kDesktopStrNum
);
268 SetButtonName(theDlgPtr
, kSelectItem
,
269 desktopName
, kDontUseQuotes
); // false -> no quotes
273 // get parent directory's name for the Select button
275 // passing an empty name string to FSMakeFSSpec gets the
276 // name of the folder specified by the parID parameter
278 (void) FSMakeFSSpec(theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
,
279 theUserDataRecPtr
->sfrPtr
->sfFile
.parID
, "\p",
281 SetButtonName(theDlgPtr
, kSelectItem
,
282 tempSpec
.name
, kUseQuotes
); // true -> use quotes
287 // save the current selection as the old selection for comparison next time
289 // it's not valid on the first call, though, or if we don't have a
290 // name available from standard file
292 if (item
!= sfHookFirstCall
|| theUserDataRecPtr
->sfrPtr
->sfFile
.name
[0] != '\0')
294 theUserDataRecPtr
->oldSelectionFSSpec
= theUserDataRecPtr
->sfrPtr
->sfFile
;
298 // on first call, empty string won't set the button correctly,
299 // so invalidate oldSelection
301 theUserDataRecPtr
->oldSelectionFSSpec
.vRefNum
= 999;
302 theUserDataRecPtr
->oldSelectionFSSpec
.parID
= 0;
308 void StandardGetFolder( ConstStr255Param message
, ConstStr255Param path
, FileFilterYDUPP fileFilter
, StandardFileReply
*theSFR
)
311 SFTypeList mySFTypeList
;
315 Boolean wasAliasedFlag
;
316 DlgHookYDUPP dlgHookUPP
;
317 ModalFilterYDUPP myModalFilterUPP
;
321 // presumably we're running System 7 or later so CustomGetFile is
324 // set initial contents of Select button to a space
326 CopyPStr("\p ", theSFR
->sfFile
.name
);
328 // point the user data parameter at the reply record so we can get to it later
330 myData
.sfrPtr
= theSFR
;
332 // display the dialog
334 dlgHookUPP
= NewDlgHookYDProc(SFGetFolderDialogHook
);
335 myModalFilterUPP
= NewModalFilterYDProc(SFGetFolderModalDialogFilter
);
337 thePt
.h
= thePt
.v
= -1; // center dialog
339 ParamText( message
, NULL
, NULL
, NULL
) ;
341 CustomGetFile( fileFilter
,
342 -1, // show all types
346 thePt
, // top left point
349 nil
, // activate list
350 nil
, // activate proc
353 DisposeRoutineDescriptor(dlgHookUPP
);
354 DisposeRoutineDescriptor(myModalFilterUPP
);
356 // if cancel wasn't pressed and no fatal error occurred...
360 // if no name is in the reply record file spec,
361 // use the file spec of the parent folder
363 if (theSFR
->sfFile
.name
[0] == '\0')
365 err
= FSMakeFSSpec(theSFR
->sfFile
.vRefNum
, theSFR
->sfFile
.parID
,
369 theSFR
->sfFile
= tempSpec
;
373 // no name to return, forget it
375 theSFR
->sfGood
= false;
379 // if there is now a name in the file spec, check if it's
380 // for a folder or a volume
382 if (theSFR
->sfFile
.name
[0] != '\0')
384 // the parID of the root of a disk is always fsRtParID == 1
386 if (theSFR
->sfFile
.parID
== fsRtParID
)
388 theSFR
->sfIsVolume
= true;
389 theSFR
->sfIsFolder
= false; // it would be reasonable for this to be true, too
392 // we have a valid FSSpec, now let's make sure it's not for an alias file
394 err
= ResolveAliasFile(&theSFR
->sfFile
, true, &folderFlag
, &wasAliasedFlag
);
397 theSFR
->sfGood
= false;
400 // did the alias resolve to a folder?
402 if (folderFlag
&& ! theSFR
->sfIsVolume
)
404 theSFR
->sfIsFolder
= true;
410 static pascal Boolean
OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr
, Ptr dataPtr
)
412 #pragma unused (dataPtr)
414 // return true if this item is invisible or a file
419 visibleFlag
= ! (myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdFlags
& kIsInvisible
);
420 folderFlag
= (myCInfoPBPtr
->hFileInfo
.ioFlAttrib
& 0x10);
422 // because the semantics of the filter proc are "true means don't show
423 // it" we need to invert the result that we return
425 return !(visibleFlag
&& folderFlag
);
428 wxDirDialog::wxDirDialog(wxWindow
*parent
, const wxString
& message
,
429 const wxString
& defaultPath
,
430 long style
, const wxPoint
& pos
)
433 m_dialogStyle
= style
;
435 m_path
= defaultPath
;
438 int wxDirDialog::ShowModal()
444 strcpy((char *)prompt
, m_message
) ;
445 c2pstr((char *)prompt
) ;
447 strcpy((char *)path
, m_path
) ;
448 c2pstr((char *)path
) ;
450 FileFilterYDUPP invisiblesExcludedCustomFilterUPP
;
451 StandardFileReply reply
;
452 invisiblesExcludedCustomFilterUPP
=
453 NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter
);
455 StandardGetFolder( prompt
, path
, invisiblesExcludedCustomFilterUPP
, &reply
);
457 DisposeRoutineDescriptor(invisiblesExcludedCustomFilterUPP
);
458 if ( reply
.sfGood
== false )
465 m_path
= wxMacFSSpec2UnixFilename( &reply
.sfFile
) ;