]> git.saurik.com Git - wxWidgets.git/blob - src/mac/dirdlg.cpp
fixed menu accelerators
[wxWidgets.git] / src / mac / dirdlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dirdlg.cpp
3 // Purpose: wxDirDialog
4 // Author: AUTHOR
5 // Modified by:
6 // Created: ??/??/98
7 // RCS-ID: $Id$
8 // Copyright: (c) AUTHOR
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "dirdlg.h"
14 #endif
15
16 #include "wx/defs.h"
17 #include "wx/utils.h"
18 #include "wx/dialog.h"
19 #include "wx/dirdlg.h"
20
21 #include "wx/cmndata.h"
22
23 #if !USE_SHARED_LIBRARY
24 IMPLEMENT_CLASS(wxDirDialog, wxDialog)
25 #endif
26
27 enum {
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
34
35 kUseQuotes = true, // parameter for SetButtonName
36 kDontUseQuotes = false
37 };
38
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
44
45 struct UserDataRec {
46 StandardFileReply *sfrPtr;
47 FSSpec oldSelectionFSSpec;
48 DialogPtr theDlgPtr;
49 };
50 typedef struct UserDataRec
51 UserDataRec, *UserDataRecPtr;
52
53 static void GetLabelString(StringPtr theStr, short stringNum)
54 {
55 GetIndString(theStr, kStrListID, stringNum);
56 }
57
58 static void CopyPStr(StringPtr src, StringPtr dest)
59 {
60 BlockMoveData(src, dest, 1 + src[0]);
61 }
62
63 static char GetSelectKey(void)
64 {
65 // this is the key used to trigger the select button
66
67 // NOT INTERNATIONAL SAVVY; should at least grab it from resources
68
69 return 's';
70 }
71
72
73 // SetButtonName sets the name of the Select button in the dialog
74 //
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.
79 //
80 // buttonName is the name to appear following the word Select
81 // quoteFlag should be true if the name is to appear in quotes
82
83 static void SetButtonName(DialogPtr theDlgPtr, short buttonID, StringPtr buttonName,
84 Boolean quoteFlag)
85 {
86 short buttonType;
87 Handle buttonHandle;
88 Rect buttonRect;
89 short textWidth;
90 Handle labelHandle;
91 Handle nameHandle;
92 Str15 keyStr;
93 Str255 labelStr;
94 OSErr err;
95
96 nameHandle = nil;
97 labelHandle = nil;
98
99 // get the details of the button from the dialog
100
101 GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
102
103 // get the string for the select button label, "Select ^0" or "Select Ò^0Ó"
104
105 GetLabelString(labelStr, (quoteFlag == kUseQuotes) ? kSelectStrNum : kSelectNoQuoteStrNum);
106
107 // make string handles containing the select button label and the
108 // file name to be stuffed into the button
109
110 err = PtrToHand(&labelStr[1], &labelHandle, labelStr[0]);
111 if (err != noErr) goto Bail;
112
113 // cut out the middle of the file name to fit the button
114 //
115 // we'll temporarily use labelStr here to hold the modified button name
116 // since we don't own the buttonName string storage space
117
118 textWidth = (buttonRect.right - buttonRect.left) - StringWidth(labelStr);
119
120 CopyPStr(buttonName, labelStr);
121 (void) TruncString(textWidth, labelStr, smTruncMiddle);
122
123 err = PtrToHand(&labelStr[1], &nameHandle, labelStr[0]);
124 if (err != noErr) goto Bail;
125
126 // replace the ^0 in the Select string with the file name
127
128 CopyPStr("\p^0", keyStr);
129
130 (void) ReplaceText(labelHandle, nameHandle, keyStr);
131
132 labelStr[0] = (unsigned char) GetHandleSize(labelHandle);
133 BlockMoveData(*labelHandle, &labelStr[1], labelStr[0]);
134
135 // now set the control title, and re-validate the area
136 // above the control to avoid a needless redraw
137
138 SetControlTitle((ControlHandle) buttonHandle, labelStr);
139
140 ValidRect(&buttonRect);
141
142 Bail:
143 if (nameHandle) DisposeHandle(nameHandle);
144 if (labelHandle) DisposeHandle(labelHandle);
145
146 }
147
148 // FlashButton briefly highlights the dialog button
149 // as feedback for key equivalents
150
151 static void FlashButton(DialogPtr theDlgPtr, short buttonID)
152 {
153 short buttonType;
154 Handle buttonHandle;
155 Rect buttonRect;
156 unsigned long finalTicks;
157
158 GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
159 HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
160 Delay(10, &finalTicks);
161 HiliteControl((ControlHandle) buttonHandle, 0);
162 }
163
164 static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
165 {
166 return (spec1->vRefNum == spec2->vRefNum
167 && spec1->parID == spec2->parID
168 && EqualString(spec1->name, spec2->name, false, false));
169 }
170 // MyModalDialogFilter maps a key to the Select button, and handles
171 // flashing of the button when the key is hit
172
173 static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
174 short *item, Ptr dataPtr)
175 {
176 #pragma unused (dataPtr)
177
178 // make certain the proper dialog is showing, 'cause standard file
179 // can nest dialogs but calls the same filter for each
180
181 if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
182 {
183 // check if the select button was hit
184
185 if ((eventRec->what == keyDown)
186 && (eventRec->modifiers & cmdKey)
187 && ((eventRec->message & charCodeMask) == GetSelectKey()))
188 {
189 *item = kSelectItem;
190 FlashButton(theDlgPtr, kSelectItem);
191 return true;
192 }
193 }
194
195 return false;
196 }
197
198
199 // MyDlgHook is a hook routine that maps the select button to Open
200 // and sets the Select button name
201
202 static pascal short SFGetFolderDialogHook(short item, DialogPtr theDlgPtr, Ptr dataPtr)
203 {
204 UserDataRecPtr theUserDataRecPtr;
205 long desktopDirID;
206 short desktopVRefNum;
207 FSSpec tempSpec;
208 Str63 desktopName;
209 OSErr err;
210
211 // be sure Std File is really showing us the intended dialog,
212 // not a nested modal dialog
213
214 if (((WindowPeek) theDlgPtr)->refCon != sfMainDialogRefCon)
215 {
216 return item;
217 }
218
219 theUserDataRecPtr = (UserDataRecPtr) dataPtr;
220
221 // map the Select button to Open
222
223 if (item == kSelectItem)
224 {
225 item = sfItemOpenButton;
226 }
227
228 // find the desktop folder
229
230 err = FindFolder(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
231 kDesktopFolderType, kDontCreateFolder,
232 &desktopVRefNum, &desktopDirID);
233
234 if (err != noErr)
235 {
236 // for errors, get value that won't match any real vRefNum/dirID
237 desktopVRefNum = 0;
238 desktopDirID = 0;
239 }
240
241 // change the Select button label if the selection has changed or
242 // if this is the first call to the hook
243
244 if (item == sfHookFirstCall
245 || item == sfHookChangeSelection
246 || item == sfHookRebuildList
247 || ! SameFSSpec(&theUserDataRecPtr->sfrPtr->sfFile,
248 &theUserDataRecPtr->oldSelectionFSSpec))
249 {
250 // be sure there is a file name selected
251
252 if (theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
253 {
254 SetButtonName(theDlgPtr, kSelectItem,
255 theUserDataRecPtr->sfrPtr->sfFile.name,
256 kUseQuotes); // true -> use quotes
257 }
258 else
259 {
260 // is the desktop selected?
261
262 if (theUserDataRecPtr->sfrPtr->sfFile.vRefNum == desktopVRefNum
263 && theUserDataRecPtr->sfrPtr->sfFile.parID == desktopDirID)
264 {
265 // set button to "Select Desktop"
266
267 GetLabelString(desktopName, kDesktopStrNum);
268 SetButtonName(theDlgPtr, kSelectItem,
269 desktopName, kDontUseQuotes); // false -> no quotes
270 }
271 else
272 {
273 // get parent directory's name for the Select button
274 //
275 // passing an empty name string to FSMakeFSSpec gets the
276 // name of the folder specified by the parID parameter
277
278 (void) FSMakeFSSpec(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
279 theUserDataRecPtr->sfrPtr->sfFile.parID, "\p",
280 &tempSpec);
281 SetButtonName(theDlgPtr, kSelectItem,
282 tempSpec.name, kUseQuotes); // true -> use quotes
283 }
284 }
285 }
286
287 // save the current selection as the old selection for comparison next time
288 //
289 // it's not valid on the first call, though, or if we don't have a
290 // name available from standard file
291
292 if (item != sfHookFirstCall || theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
293 {
294 theUserDataRecPtr->oldSelectionFSSpec = theUserDataRecPtr->sfrPtr->sfFile;
295 }
296 else
297 {
298 // on first call, empty string won't set the button correctly,
299 // so invalidate oldSelection
300
301 theUserDataRecPtr->oldSelectionFSSpec.vRefNum = 999;
302 theUserDataRecPtr->oldSelectionFSSpec.parID = 0;
303 }
304
305 return item;
306 }
307
308 void StandardGetFolder( ConstStr255Param message , ConstStr255Param path , FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
309 {
310 Point thePt;
311 SFTypeList mySFTypeList;
312 UserDataRec myData;
313 FSSpec tempSpec;
314 Boolean folderFlag;
315 Boolean wasAliasedFlag;
316 DlgHookYDUPP dlgHookUPP;
317 ModalFilterYDUPP myModalFilterUPP;
318 OSErr err;
319
320
321 // presumably we're running System 7 or later so CustomGetFile is
322 // available
323
324 // set initial contents of Select button to a space
325
326 CopyPStr("\p ", theSFR->sfFile.name);
327
328 // point the user data parameter at the reply record so we can get to it later
329
330 myData.sfrPtr = theSFR;
331
332 // display the dialog
333
334 dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
335 myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
336
337 thePt.h = thePt.v = -1; // center dialog
338
339 ParamText( message , NULL , NULL , NULL ) ;
340
341 CustomGetFile( fileFilter,
342 -1, // show all types
343 mySFTypeList,
344 theSFR,
345 kSFGetFolderDlgID,
346 thePt, // top left point
347 dlgHookUPP,
348 myModalFilterUPP,
349 nil, // activate list
350 nil, // activate proc
351 &myData);
352
353 DisposeRoutineDescriptor(dlgHookUPP);
354 DisposeRoutineDescriptor(myModalFilterUPP);
355
356 // if cancel wasn't pressed and no fatal error occurred...
357
358 if (theSFR->sfGood)
359 {
360 // if no name is in the reply record file spec,
361 // use the file spec of the parent folder
362
363 if (theSFR->sfFile.name[0] == '\0')
364 {
365 err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
366 "\p", &tempSpec);
367 if (err == noErr)
368 {
369 theSFR->sfFile = tempSpec;
370 }
371 else
372 {
373 // no name to return, forget it
374
375 theSFR->sfGood = false;
376 }
377 }
378
379 // if there is now a name in the file spec, check if it's
380 // for a folder or a volume
381
382 if (theSFR->sfFile.name[0] != '\0')
383 {
384 // the parID of the root of a disk is always fsRtParID == 1
385
386 if (theSFR->sfFile.parID == fsRtParID)
387 {
388 theSFR->sfIsVolume = true;
389 theSFR->sfIsFolder = false; // it would be reasonable for this to be true, too
390 }
391
392 // we have a valid FSSpec, now let's make sure it's not for an alias file
393
394 err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
395 if (err != noErr)
396 {
397 theSFR->sfGood = false;
398 }
399
400 // did the alias resolve to a folder?
401
402 if (folderFlag && ! theSFR->sfIsVolume)
403 {
404 theSFR->sfIsFolder = true;
405 }
406 }
407 }
408 }
409
410 static pascal Boolean OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr, Ptr dataPtr)
411 {
412 #pragma unused (dataPtr)
413
414 // return true if this item is invisible or a file
415
416 Boolean visibleFlag;
417 Boolean folderFlag;
418
419 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
420 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
421
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
424
425 return !(visibleFlag && folderFlag);
426 }
427
428 wxDirDialog::wxDirDialog(wxWindow *parent, const wxString& message,
429 const wxString& defaultPath,
430 long style, const wxPoint& pos)
431 {
432 m_message = message;
433 m_dialogStyle = style;
434 m_parent = parent;
435 m_path = defaultPath;
436 }
437
438 int wxDirDialog::ShowModal()
439 {
440 {
441 Str255 prompt ;
442 Str255 path ;
443
444 strcpy((char *)prompt, m_message) ;
445 c2pstr((char *)prompt ) ;
446
447 strcpy((char *)path, m_path ) ;
448 c2pstr((char *)path ) ;
449
450 FileFilterYDUPP invisiblesExcludedCustomFilterUPP;
451 StandardFileReply reply ;
452 invisiblesExcludedCustomFilterUPP =
453 NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter);
454
455 StandardGetFolder( prompt , path , invisiblesExcludedCustomFilterUPP, &reply);
456
457 DisposeRoutineDescriptor(invisiblesExcludedCustomFilterUPP);
458 if ( reply.sfGood == false )
459 {
460 m_path = "" ;
461 return wxID_CANCEL ;
462 }
463 else
464 {
465 m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
466 return wxID_OK ;
467 }
468 }
469 return wxID_CANCEL;
470 }
471