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
)
27 // the data we need to pass to our standard file hook routine
28 // includes a pointer to the dialog, a pointer to the standard
29 // file reply record (so we can inspect the current selection)
30 // and a copy of the "previous" file spec of the reply record
31 // so we can see if the selection has changed
34 StandardFileReply
*sfrPtr
;
35 FSSpec oldSelectionFSSpec
;
38 typedef struct UserDataRec
39 UserDataRec
, *UserDataRecPtr
;
44 kSelectItem
= 10, // select button item number
45 kSFGetFolderDlgID
= 250, // dialog resource number
46 kStrListID
= 250, // our strings
47 kSelectStrNum
= 1, // word 'Select: ' for button
48 kDesktopStrNum
= 2, // word 'Desktop' for button
49 kSelectNoQuoteStrNum
= 3, // word 'Select: ' for button
51 kUseQuotes
= true, // parameter for SetButtonName
52 kDontUseQuotes
= false
56 static void GetLabelString(StringPtr theStr
, short stringNum
)
58 GetIndString(theStr
, kStrListID
, stringNum
);
61 static void CopyPStr(StringPtr src
, StringPtr dest
)
63 BlockMoveData(src
, dest
, 1 + src
[0]);
66 static char GetSelectKey(void)
68 // this is the key used to trigger the select button
70 // NOT INTERNATIONAL SAVVY; should at least grab it from resources
76 // SetButtonName sets the name of the Select button in the dialog
78 // To do this, we need to call the Script Manager to truncate the
79 // label in the middle to fit the button and to merge the button
80 // name with the word Select (possibly followed by quotes). Using
81 // the Script Manager avoids all sorts of problems internationally.
83 // buttonName is the name to appear following the word Select
84 // quoteFlag should be true if the name is to appear in quotes
86 static void SetButtonName(DialogPtr theDlgPtr
, short buttonID
, StringPtr buttonName
,
102 // get the details of the button from the dialog
104 GetDialogItem(theDlgPtr
, buttonID
, &buttonType
, &buttonHandle
, &buttonRect
);
106 // get the string for the select button label, "Select ^0" or "Select Ò^0Ó"
108 GetLabelString(labelStr
, (quoteFlag
== kUseQuotes
) ? kSelectStrNum
: kSelectNoQuoteStrNum
);
110 // make string handles containing the select button label and the
111 // file name to be stuffed into the button
113 err
= PtrToHand(&labelStr
[1], &labelHandle
, labelStr
[0]);
114 if (err
!= noErr
) goto Bail
;
116 // cut out the middle of the file name to fit the button
118 // we'll temporarily use labelStr here to hold the modified button name
119 // since we don't own the buttonName string storage space
121 textWidth
= (buttonRect
.right
- buttonRect
.left
) - StringWidth(labelStr
);
123 CopyPStr(buttonName
, labelStr
);
124 (void) TruncString(textWidth
, labelStr
, smTruncMiddle
);
126 err
= PtrToHand(&labelStr
[1], &nameHandle
, labelStr
[0]);
127 if (err
!= noErr
) goto Bail
;
129 // replace the ^0 in the Select string with the file name
131 CopyPStr("\p^0", keyStr
);
133 (void) ReplaceText(labelHandle
, nameHandle
, keyStr
);
135 labelStr
[0] = (unsigned char) GetHandleSize(labelHandle
);
136 BlockMoveData(*labelHandle
, &labelStr
[1], labelStr
[0]);
138 // now set the control title, and re-validate the area
139 // above the control to avoid a needless redraw
141 SetControlTitle((ControlHandle
) buttonHandle
, labelStr
);
143 ValidRect(&buttonRect
);
146 if (nameHandle
) DisposeHandle(nameHandle
);
147 if (labelHandle
) DisposeHandle(labelHandle
);
151 // FlashButton briefly highlights the dialog button
152 // as feedback for key equivalents
154 static void FlashButton(DialogPtr theDlgPtr
, short buttonID
)
159 unsigned long finalTicks
;
161 GetDialogItem(theDlgPtr
, buttonID
, &buttonType
, &buttonHandle
, &buttonRect
);
162 HiliteControl((ControlHandle
) buttonHandle
, kControlButtonPart
);
163 Delay(10, &finalTicks
);
164 HiliteControl((ControlHandle
) buttonHandle
, 0);
167 static Boolean
SameFSSpec(FSSpecPtr spec1
, FSSpecPtr spec2
)
169 return (spec1
->vRefNum
== spec2
->vRefNum
170 && spec1
->parID
== spec2
->parID
171 && EqualString(spec1
->name
, spec2
->name
, false, false));
173 // MyModalDialogFilter maps a key to the Select button, and handles
174 // flashing of the button when the key is hit
176 static pascal Boolean
SFGetFolderModalDialogFilter(DialogPtr theDlgPtr
, EventRecord
*eventRec
,
177 short *item
, Ptr dataPtr
)
179 #pragma unused (dataPtr)
181 // make certain the proper dialog is showing, 'cause standard file
182 // can nest dialogs but calls the same filter for each
184 if (((WindowPeek
) theDlgPtr
)->refCon
== sfMainDialogRefCon
)
186 // check if the select button was hit
188 if ((eventRec
->what
== keyDown
)
189 && (eventRec
->modifiers
& cmdKey
)
190 && ((eventRec
->message
& charCodeMask
) == GetSelectKey()))
193 FlashButton(theDlgPtr
, kSelectItem
);
202 // MyDlgHook is a hook routine that maps the select button to Open
203 // and sets the Select button name
205 static pascal short SFGetFolderDialogHook(short item
, DialogPtr theDlgPtr
, Ptr dataPtr
)
207 UserDataRecPtr theUserDataRecPtr
;
209 short desktopVRefNum
;
214 // be sure Std File is really showing us the intended dialog,
215 // not a nested modal dialog
217 if (((WindowPeek
) theDlgPtr
)->refCon
!= sfMainDialogRefCon
)
222 theUserDataRecPtr
= (UserDataRecPtr
) dataPtr
;
224 // map the Select button to Open
226 if (item
== kSelectItem
)
228 item
= sfItemOpenButton
;
231 // find the desktop folder
233 err
= FindFolder(theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
,
234 kDesktopFolderType
, kDontCreateFolder
,
235 &desktopVRefNum
, &desktopDirID
);
239 // for errors, get value that won't match any real vRefNum/dirID
244 // change the Select button label if the selection has changed or
245 // if this is the first call to the hook
247 if (item
== sfHookFirstCall
248 || item
== sfHookChangeSelection
249 || item
== sfHookRebuildList
250 || ! SameFSSpec(&theUserDataRecPtr
->sfrPtr
->sfFile
,
251 &theUserDataRecPtr
->oldSelectionFSSpec
))
253 // be sure there is a file name selected
255 if (theUserDataRecPtr
->sfrPtr
->sfFile
.name
[0] != '\0')
257 SetButtonName(theDlgPtr
, kSelectItem
,
258 theUserDataRecPtr
->sfrPtr
->sfFile
.name
,
259 kUseQuotes
); // true -> use quotes
263 // is the desktop selected?
265 if (theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
== desktopVRefNum
266 && theUserDataRecPtr
->sfrPtr
->sfFile
.parID
== desktopDirID
)
268 // set button to "Select Desktop"
270 GetLabelString(desktopName
, kDesktopStrNum
);
271 SetButtonName(theDlgPtr
, kSelectItem
,
272 desktopName
, kDontUseQuotes
); // false -> no quotes
276 // get parent directory's name for the Select button
278 // passing an empty name string to FSMakeFSSpec gets the
279 // name of the folder specified by the parID parameter
281 (void) FSMakeFSSpec(theUserDataRecPtr
->sfrPtr
->sfFile
.vRefNum
,
282 theUserDataRecPtr
->sfrPtr
->sfFile
.parID
, "\p",
284 SetButtonName(theDlgPtr
, kSelectItem
,
285 tempSpec
.name
, kUseQuotes
); // true -> use quotes
290 // save the current selection as the old selection for comparison next time
292 // it's not valid on the first call, though, or if we don't have a
293 // name available from standard file
295 if (item
!= sfHookFirstCall
|| theUserDataRecPtr
->sfrPtr
->sfFile
.name
[0] != '\0')
297 theUserDataRecPtr
->oldSelectionFSSpec
= theUserDataRecPtr
->sfrPtr
->sfFile
;
301 // on first call, empty string won't set the button correctly,
302 // so invalidate oldSelection
304 theUserDataRecPtr
->oldSelectionFSSpec
.vRefNum
= 999;
305 theUserDataRecPtr
->oldSelectionFSSpec
.parID
= 0;
312 void StandardGetFolder( ConstStr255Param message
, ConstStr255Param path
, FileFilterYDUPP fileFilter
, StandardFileReply
*theSFR
)
315 SFTypeList mySFTypeList
;
319 Boolean wasAliasedFlag
;
320 DlgHookYDUPP dlgHookUPP
;
321 ModalFilterYDUPP myModalFilterUPP
;
325 // presumably we're running System 7 or later so CustomGetFile is
328 // set initial contents of Select button to a space
330 memcpy(theSFR
->sfFile
.name
, "\p ", 2);
332 // point the user data parameter at the reply record so we can get to it later
334 myData
.sfrPtr
= theSFR
;
336 // display the dialog
340 dlgHookUPP
= NewDlgHookYDProc(SFGetFolderDialogHook
);
341 myModalFilterUPP
= NewModalFilterYDProc(SFGetFolderModalDialogFilter
);
343 thePt
.h
= thePt
.v
= -1; // center dialog
345 ParamText( message
, NULL
, NULL
, NULL
) ;
347 CustomGetFile( fileFilter
,
348 -1, // show all types
352 thePt
, // top left point
355 nil
, // activate list
356 nil
, // activate proc
359 DisposeRoutineDescriptor(dlgHookUPP
);
360 DisposeRoutineDescriptor(myModalFilterUPP
);
364 // if cancel wasn't pressed and no fatal error occurred...
368 // if no name is in the reply record file spec,
369 // use the file spec of the parent folder
371 if (theSFR
->sfFile
.name
[0] == '\0')
373 err
= FSMakeFSSpec(theSFR
->sfFile
.vRefNum
, theSFR
->sfFile
.parID
,
377 theSFR
->sfFile
= tempSpec
;
381 // no name to return, forget it
383 theSFR
->sfGood
= false;
387 // if there is now a name in the file spec, check if it's
388 // for a folder or a volume
390 if (theSFR
->sfFile
.name
[0] != '\0')
392 // the parID of the root of a disk is always fsRtParID == 1
394 if (theSFR
->sfFile
.parID
== fsRtParID
)
396 theSFR
->sfIsVolume
= true;
397 theSFR
->sfIsFolder
= false; // it would be reasonable for this to be true, too
400 // we have a valid FSSpec, now let's make sure it's not for an alias file
402 err
= ResolveAliasFile(&theSFR
->sfFile
, true, &folderFlag
, &wasAliasedFlag
);
405 theSFR
->sfGood
= false;
408 // did the alias resolve to a folder?
410 if (folderFlag
&& ! theSFR
->sfIsVolume
)
412 theSFR
->sfIsFolder
= true;
418 static pascal Boolean
OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr
, Ptr dataPtr
)
420 #pragma unused (dataPtr)
422 // return true if this item is invisible or a file
427 visibleFlag
= ! (myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdFlags
& kIsInvisible
);
428 folderFlag
= (myCInfoPBPtr
->hFileInfo
.ioFlAttrib
& 0x10);
430 // because the semantics of the filter proc are "true means don't show
431 // it" we need to invert the result that we return
433 return !(visibleFlag
&& folderFlag
);
436 wxDirDialog::wxDirDialog(wxWindow
*parent
, const wxString
& message
,
437 const wxString
& defaultPath
,
438 long style
, const wxPoint
& pos
)
441 m_dialogStyle
= style
;
443 m_path
= defaultPath
;
446 int wxDirDialog::ShowModal()
452 strcpy((char *)prompt
, m_message
) ;
453 c2pstr((char *)prompt
) ;
455 strcpy((char *)path
, m_path
) ;
456 c2pstr((char *)path
) ;
458 StandardFileReply reply
;
459 FileFilterYDUPP invisiblesExcludedCustomFilterUPP
= 0 ;
461 invisiblesExcludedCustomFilterUPP
=
462 NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter
);
465 StandardGetFolder( prompt
, path
, invisiblesExcludedCustomFilterUPP
, &reply
);
468 DisposeRoutineDescriptor(invisiblesExcludedCustomFilterUPP
);
470 if ( reply
.sfGood
== false )
477 m_path
= wxMacFSSpec2UnixFilename( &reply
.sfFile
) ;