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
) ;