]> git.saurik.com Git - wxWidgets.git/blob - src/mac/dirdlg.cpp
updated mac sources (CW 5.3 working , CW6 still having code gen problems)
[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 #include "Navigation.h"
24
25 #if !USE_SHARED_LIBRARY
26 IMPLEMENT_CLASS(wxDirDialog, wxDialog)
27 #endif
28
29 bool gUseNavServices = NavServicesAvailable() ;
30
31 // the data we need to pass to our standard file hook routine
32 // includes a pointer to the dialog, a pointer to the standard
33 // file reply record (so we can inspect the current selection)
34 // and a copy of the "previous" file spec of the reply record
35 // so we can see if the selection has changed
36
37 #if !TARGET_CARBON
38
39 struct UserDataRec {
40 StandardFileReply *sfrPtr;
41 FSSpec oldSelectionFSSpec;
42 DialogPtr theDlgPtr;
43 };
44 typedef struct UserDataRec
45 UserDataRec, *UserDataRecPtr;
46
47 enum {
48 kSelectItem = 10, // select button item number
49 kSFGetFolderDlgID = 250, // dialog resource number
50 kStrListID = 250, // our strings
51 kSelectStrNum = 1, // word 'Select: ' for button
52 kDesktopStrNum = 2, // word 'Desktop' for button
53 kSelectNoQuoteStrNum = 3, // word 'Select: ' for button
54
55 kUseQuotes = true, // parameter for SetButtonName
56 kDontUseQuotes = false
57 };
58
59
60 static void GetLabelString(StringPtr theStr, short stringNum)
61 {
62 GetIndString(theStr, kStrListID, stringNum);
63 }
64
65 static void CopyPStr(StringPtr src, StringPtr dest)
66 {
67 BlockMoveData(src, dest, 1 + src[0]);
68 }
69
70 static char GetSelectKey(void)
71 {
72 // this is the key used to trigger the select button
73
74 // NOT INTERNATIONAL SAVVY; should at least grab it from resources
75
76 return 's';
77 }
78
79
80 // SetButtonName sets the name of the Select button in the dialog
81 //
82 // To do this, we need to call the Script Manager to truncate the
83 // label in the middle to fit the button and to merge the button
84 // name with the word Select (possibly followed by quotes). Using
85 // the Script Manager avoids all sorts of problems internationally.
86 //
87 // buttonName is the name to appear following the word Select
88 // quoteFlag should be true if the name is to appear in quotes
89
90 static void SetButtonName(DialogPtr theDlgPtr, short buttonID, StringPtr buttonName,
91 Boolean quoteFlag)
92 {
93 short buttonType;
94 Handle buttonHandle;
95 Rect buttonRect;
96 short textWidth;
97 Handle labelHandle;
98 Handle nameHandle;
99 Str15 keyStr;
100 Str255 labelStr;
101 OSErr err;
102
103 nameHandle = nil;
104 labelHandle = nil;
105
106 // get the details of the button from the dialog
107
108 GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
109
110 // get the string for the select button label, "Select ^0" or "Select Ò^0Ó"
111
112 GetLabelString(labelStr, (quoteFlag == kUseQuotes) ? kSelectStrNum : kSelectNoQuoteStrNum);
113
114 // make string handles containing the select button label and the
115 // file name to be stuffed into the button
116
117 err = PtrToHand(&labelStr[1], &labelHandle, labelStr[0]);
118 if (err != noErr) goto Bail;
119
120 // cut out the middle of the file name to fit the button
121 //
122 // we'll temporarily use labelStr here to hold the modified button name
123 // since we don't own the buttonName string storage space
124
125 textWidth = (buttonRect.right - buttonRect.left) - StringWidth(labelStr);
126
127 CopyPStr(buttonName, labelStr);
128 (void) TruncString(textWidth, labelStr, smTruncMiddle);
129
130 err = PtrToHand(&labelStr[1], &nameHandle, labelStr[0]);
131 if (err != noErr) goto Bail;
132
133 // replace the ^0 in the Select string with the file name
134
135 CopyPStr("\p^0", keyStr);
136
137 (void) ReplaceText(labelHandle, nameHandle, keyStr);
138
139 labelStr[0] = (unsigned char) GetHandleSize(labelHandle);
140 BlockMoveData(*labelHandle, &labelStr[1], labelStr[0]);
141
142 // now set the control title, and re-validate the area
143 // above the control to avoid a needless redraw
144
145 SetControlTitle((ControlHandle) buttonHandle, labelStr);
146
147 ValidRect(&buttonRect);
148
149 Bail:
150 if (nameHandle) DisposeHandle(nameHandle);
151 if (labelHandle) DisposeHandle(labelHandle);
152
153 }
154
155 // FlashButton briefly highlights the dialog button
156 // as feedback for key equivalents
157
158 static void FlashButton(DialogPtr theDlgPtr, short buttonID)
159 {
160 short buttonType;
161 Handle buttonHandle;
162 Rect buttonRect;
163 unsigned long finalTicks;
164
165 GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
166 HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
167 Delay(10, &finalTicks);
168 HiliteControl((ControlHandle) buttonHandle, 0);
169 }
170
171 static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
172 {
173 return (spec1->vRefNum == spec2->vRefNum
174 && spec1->parID == spec2->parID
175 && EqualString(spec1->name, spec2->name, false, false));
176 }
177 // MyModalDialogFilter maps a key to the Select button, and handles
178 // flashing of the button when the key is hit
179
180 static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
181 short *item, Ptr dataPtr)
182 {
183 #pragma unused (dataPtr)
184
185 // make certain the proper dialog is showing, 'cause standard file
186 // can nest dialogs but calls the same filter for each
187
188 if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
189 {
190 // check if the select button was hit
191
192 if ((eventRec->what == keyDown)
193 && (eventRec->modifiers & cmdKey)
194 && ((eventRec->message & charCodeMask) == GetSelectKey()))
195 {
196 *item = kSelectItem;
197 FlashButton(theDlgPtr, kSelectItem);
198 return true;
199 }
200 }
201
202 return false;
203 }
204
205
206 // MyDlgHook is a hook routine that maps the select button to Open
207 // and sets the Select button name
208
209 static pascal short SFGetFolderDialogHook(short item, DialogPtr theDlgPtr, Ptr dataPtr)
210 {
211 UserDataRecPtr theUserDataRecPtr;
212 long desktopDirID;
213 short desktopVRefNum;
214 FSSpec tempSpec;
215 Str63 desktopName;
216 OSErr err;
217
218 // be sure Std File is really showing us the intended dialog,
219 // not a nested modal dialog
220
221 if (((WindowPeek) theDlgPtr)->refCon != sfMainDialogRefCon)
222 {
223 return item;
224 }
225
226 theUserDataRecPtr = (UserDataRecPtr) dataPtr;
227
228 // map the Select button to Open
229
230 if (item == kSelectItem)
231 {
232 item = sfItemOpenButton;
233 }
234
235 // find the desktop folder
236
237 err = FindFolder(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
238 kDesktopFolderType, kDontCreateFolder,
239 &desktopVRefNum, &desktopDirID);
240
241 if (err != noErr)
242 {
243 // for errors, get value that won't match any real vRefNum/dirID
244 desktopVRefNum = 0;
245 desktopDirID = 0;
246 }
247
248 // change the Select button label if the selection has changed or
249 // if this is the first call to the hook
250
251 if (item == sfHookFirstCall
252 || item == sfHookChangeSelection
253 || item == sfHookRebuildList
254 || ! SameFSSpec(&theUserDataRecPtr->sfrPtr->sfFile,
255 &theUserDataRecPtr->oldSelectionFSSpec))
256 {
257 // be sure there is a file name selected
258
259 if (theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
260 {
261 SetButtonName(theDlgPtr, kSelectItem,
262 theUserDataRecPtr->sfrPtr->sfFile.name,
263 kUseQuotes); // true -> use quotes
264 }
265 else
266 {
267 // is the desktop selected?
268
269 if (theUserDataRecPtr->sfrPtr->sfFile.vRefNum == desktopVRefNum
270 && theUserDataRecPtr->sfrPtr->sfFile.parID == desktopDirID)
271 {
272 // set button to "Select Desktop"
273
274 GetLabelString(desktopName, kDesktopStrNum);
275 SetButtonName(theDlgPtr, kSelectItem,
276 desktopName, kDontUseQuotes); // false -> no quotes
277 }
278 else
279 {
280 // get parent directory's name for the Select button
281 //
282 // passing an empty name string to FSMakeFSSpec gets the
283 // name of the folder specified by the parID parameter
284
285 (void) FSMakeFSSpec(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
286 theUserDataRecPtr->sfrPtr->sfFile.parID, "\p",
287 &tempSpec);
288 SetButtonName(theDlgPtr, kSelectItem,
289 tempSpec.name, kUseQuotes); // true -> use quotes
290 }
291 }
292 }
293
294 // save the current selection as the old selection for comparison next time
295 //
296 // it's not valid on the first call, though, or if we don't have a
297 // name available from standard file
298
299 if (item != sfHookFirstCall || theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
300 {
301 theUserDataRecPtr->oldSelectionFSSpec = theUserDataRecPtr->sfrPtr->sfFile;
302 }
303 else
304 {
305 // on first call, empty string won't set the button correctly,
306 // so invalidate oldSelection
307
308 theUserDataRecPtr->oldSelectionFSSpec.vRefNum = 999;
309 theUserDataRecPtr->oldSelectionFSSpec.parID = 0;
310 }
311
312 return item;
313 }
314
315 void StandardGetFolder( ConstStr255Param message , ConstStr255Param path , FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
316 {
317 Point thePt;
318 SFTypeList mySFTypeList;
319 UserDataRec myData;
320 FSSpec tempSpec;
321 Boolean folderFlag;
322 Boolean wasAliasedFlag;
323 DlgHookYDUPP dlgHookUPP;
324 ModalFilterYDUPP myModalFilterUPP;
325 OSErr err;
326
327
328 // presumably we're running System 7 or later so CustomGetFile is
329 // available
330
331 // set initial contents of Select button to a space
332
333 memcpy(theSFR->sfFile.name, "\p ", 2);
334
335 // point the user data parameter at the reply record so we can get to it later
336
337 myData.sfrPtr = theSFR;
338
339 // display the dialog
340
341 #if !TARGET_CARBON
342
343 dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
344 myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
345
346 thePt.h = thePt.v = -1; // center dialog
347
348 ParamText( message , NULL , NULL , NULL ) ;
349
350 CustomGetFile( fileFilter,
351 -1, // show all types
352 mySFTypeList,
353 theSFR,
354 kSFGetFolderDlgID,
355 thePt, // top left point
356 dlgHookUPP,
357 myModalFilterUPP,
358 nil, // activate list
359 nil, // activate proc
360 &myData);
361
362 DisposeRoutineDescriptor(dlgHookUPP);
363 DisposeRoutineDescriptor(myModalFilterUPP);
364 #else
365 #endif
366
367 // if cancel wasn't pressed and no fatal error occurred...
368
369 if (theSFR->sfGood)
370 {
371 // if no name is in the reply record file spec,
372 // use the file spec of the parent folder
373
374 if (theSFR->sfFile.name[0] == '\0')
375 {
376 err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
377 "\p", &tempSpec);
378 if (err == noErr)
379 {
380 theSFR->sfFile = tempSpec;
381 }
382 else
383 {
384 // no name to return, forget it
385
386 theSFR->sfGood = false;
387 }
388 }
389
390 // if there is now a name in the file spec, check if it's
391 // for a folder or a volume
392
393 if (theSFR->sfFile.name[0] != '\0')
394 {
395 // the parID of the root of a disk is always fsRtParID == 1
396
397 if (theSFR->sfFile.parID == fsRtParID)
398 {
399 theSFR->sfIsVolume = true;
400 theSFR->sfIsFolder = false; // it would be reasonable for this to be true, too
401 }
402
403 // we have a valid FSSpec, now let's make sure it's not for an alias file
404
405 err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
406 if (err != noErr)
407 {
408 theSFR->sfGood = false;
409 }
410
411 // did the alias resolve to a folder?
412
413 if (folderFlag && ! theSFR->sfIsVolume)
414 {
415 theSFR->sfIsFolder = true;
416 }
417 }
418 }
419 }
420
421 static pascal Boolean OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr, Ptr dataPtr)
422 {
423 #pragma unused (dataPtr)
424
425 // return true if this item is invisible or a file
426
427 Boolean visibleFlag;
428 Boolean folderFlag;
429
430 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
431 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
432
433 // because the semantics of the filter proc are "true means don't show
434 // it" we need to invert the result that we return
435
436 return !(visibleFlag && folderFlag);
437 }
438
439 #endif
440
441
442 wxDirDialog::wxDirDialog(wxWindow *parent, const wxString& message,
443 const wxString& defaultPath,
444 long style, const wxPoint& pos)
445 {
446 m_message = message;
447 m_dialogStyle = style;
448 m_parent = parent;
449 m_path = defaultPath;
450 }
451
452 int wxDirDialog::ShowModal()
453 {
454 #if !TARGET_CARBON
455 if ( !gUseNavServices )
456 {
457 Str255 prompt ;
458 Str255 path ;
459
460 strcpy((char *)prompt, m_message) ;
461 c2pstr((char *)prompt ) ;
462
463 strcpy((char *)path, m_path ) ;
464 c2pstr((char *)path ) ;
465
466 StandardFileReply reply ;
467 FileFilterYDUPP invisiblesExcludedCustomFilterUPP = 0 ;
468 invisiblesExcludedCustomFilterUPP =
469 NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter);
470
471
472 StandardGetFolder( prompt , path , invisiblesExcludedCustomFilterUPP, &reply);
473
474
475 DisposeRoutineDescriptor(invisiblesExcludedCustomFilterUPP);
476
477 if ( reply.sfGood == false )
478 {
479 m_path = "" ;
480 return wxID_CANCEL ;
481 }
482 else
483 {
484 m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
485 return wxID_OK ;
486 }
487 return wxID_CANCEL;
488 }
489 else
490 #endif
491 {
492 NavDialogOptions mNavOptions;
493 NavObjectFilterUPP mNavFilterUPP = NULL;
494 NavPreviewUPP mNavPreviewUPP = NULL ;
495 NavReplyRecord mNavReply;
496 AEDesc* mDefaultLocation = NULL ;
497 bool mSelectDefault = false ;
498
499 ::NavGetDefaultDialogOptions(&mNavOptions);
500
501 mNavFilterUPP = nil;
502 mNavPreviewUPP = nil;
503 mSelectDefault = false;
504 mNavReply.validRecord = false;
505 mNavReply.replacing = false;
506 mNavReply.isStationery = false;
507 mNavReply.translationNeeded = false;
508 mNavReply.selection.descriptorType = typeNull;
509 mNavReply.selection.dataHandle = nil;
510 mNavReply.keyScript = smSystemScript;
511 mNavReply.fileTranslation = nil;
512
513 // Set default location, the location
514 // that's displayed when the dialog
515 // first appears
516
517 if ( mDefaultLocation ) {
518
519 if (mSelectDefault) {
520 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
521 } else {
522 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
523 }
524 }
525
526 OSErr err = ::NavChooseFolder(
527 mDefaultLocation,
528 &mNavReply,
529 &mNavOptions,
530 NULL,
531 mNavFilterUPP,
532 0L); // User Data
533
534 if ( (err != noErr) && (err != userCanceledErr) ) {
535 m_path = "" ;
536 return wxID_CANCEL ;
537 }
538
539 if (mNavReply.validRecord) { // User chose a folder
540
541 FSSpec folderInfo;
542 FSSpec outFileSpec ;
543 AEDesc specDesc ;
544
545 OSErr err = ::AECoerceDesc( &mNavReply.selection , typeFSS, &specDesc);
546 if ( err != noErr ) {
547 m_path = "" ;
548 return wxID_CANCEL ;
549 }
550 folderInfo = **(FSSpec**) specDesc.dataHandle;
551 if (specDesc.dataHandle != nil) {
552 ::AEDisposeDesc(&specDesc);
553 }
554
555 // mNavReply.GetFileSpec(folderInfo);
556
557 // The FSSpec from NavChooseFolder is NOT the file spec
558 // for the folder. The parID field is actually the DirID
559 // of the folder itself, not the folder's parent, and
560 // the name field is empty. We must call PBGetCatInfo
561 // to get the parent DirID and folder name
562
563 Str255 name;
564 CInfoPBRec thePB; // Directory Info Parameter Block
565 thePB.dirInfo.ioCompletion = nil;
566 thePB.dirInfo.ioVRefNum = folderInfo.vRefNum; // Volume is right
567 thePB.dirInfo.ioDrDirID = folderInfo.parID; // Folder's DirID
568 thePB.dirInfo.ioNamePtr = name;
569 thePB.dirInfo.ioFDirIndex = -1; // Lookup using Volume and DirID
570
571 err = ::PBGetCatInfoSync(&thePB);
572 if ( err != noErr ) {
573 m_path = "" ;
574 return wxID_CANCEL ;
575 }
576 // Create cannonical FSSpec
577 ::FSMakeFSSpec(thePB.dirInfo.ioVRefNum, thePB.dirInfo.ioDrParID,
578 name, &outFileSpec);
579
580 // outFolderDirID = thePB.dirInfo.ioDrDirID;
581 m_path = wxMacFSSpec2UnixFilename( &outFileSpec ) ;
582 return wxID_OK ;
583 }
584 return wxID_CANCEL;
585
586 }
587 }
588