updated mac sources (CW 5.3 working , CW6 still having code gen problems)
[wxWidgets.git] / src / mac / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: filedlg.cpp
3 // Purpose: wxFileDialog
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 "filedlg.h"
14 #endif
15
16 #include "wx/defs.h"
17 #include "wx/utils.h"
18 #include "wx/dialog.h"
19 #include "wx/filedlg.h"
20 #include "wx/intl.h"
21
22 #include "PLStringFuncs.h"
23
24 #if !USE_SHARED_LIBRARY
25 IMPLEMENT_CLASS(wxFileDialog, wxDialog)
26 #endif
27
28 // begin wxmac
29
30 #include "Navigation.h"
31
32 #include "morefile.h"
33 #include "moreextr.h"
34 #include "fullpath.h"
35 #include "fspcompa.h"
36 #include "PLStringFuncs.h"
37
38 extern bool gUseNavServices ;
39
40 static pascal void NavEventProc(
41 NavEventCallbackMessage inSelector,
42 NavCBRecPtr ioParams,
43 NavCallBackUserData ioUserData);
44
45 #if TARGET_CARBON
46 static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
47 #else
48 static NavEventUPP sStandardNavEventFilter = NewNavEventProc(NavEventProc);
49 #endif
50
51 static pascal void
52 NavEventProc(
53 NavEventCallbackMessage inSelector,
54 NavCBRecPtr ioParams,
55 NavCallBackUserData /* ioUserData */)
56 {
57 if (inSelector == kNavCBEvent) {
58 // In Universal Headers 3.2, Apple changed the definition of
59 /*
60 #if UNIVERSAL_INTERFACES_VERSION >= 0x0320 // Universal Headers 3.2
61 UModalAlerts::ProcessModalEvent(*(ioParams->eventData.eventDataParms.event));
62
63 #else
64 UModalAlerts::ProcessModalEvent(*(ioParams->eventData.event));
65 #endif
66 */
67
68 }
69 }
70
71 char * gfilters[] =
72 {
73 "*.TXT" ,
74
75 NULL
76 } ;
77
78 OSType gfiltersmac[] =
79 {
80 'TEXT' ,
81
82 '****'
83 } ;
84
85 // the data we need to pass to our standard file hook routine
86 // includes a pointer to the dialog, a pointer to the standard
87 // file reply record (so we can inspect the current selection)
88 // and a copy of the "previous" file spec of the reply record
89 // so we can see if the selection has changed
90
91 const int kwxMacFileTypes = 10 ;
92
93 struct OpenUserDataRec {
94 StandardFileReply *sfrPtr;
95 FSSpec oldSelectionFSSpec;
96 char filter[kwxMacFileTypes][10] ;
97 OSType filtermactypes[kwxMacFileTypes] ;
98 int numfilters ;
99 DialogPtr theDlgPtr;
100 };
101 typedef struct OpenUserDataRec
102 OpenUserDataRec, *OpenUserDataRecPtr;
103
104 #if !TARGET_CARBON
105
106 static void wxMacSetupStandardFile(short newVRefNum, long newDirID)
107 {
108 enum
109 { SFSaveDisk = 0x214, CurDirStore = 0x398 };
110 *(short *) SFSaveDisk = -1 * newVRefNum;
111 *(long *) CurDirStore = newDirID;
112 }
113
114 static void wxMacSetupStandardFileFromPath( const char* s )
115 {
116 Str255 volume ;
117 Str255 path ;
118 short vRefNum ;
119 long dirRef ;
120 short i,j ;
121 Boolean isDirectory ;
122
123 for (i=0 ; (s[i]!=0) && (s[i]!=':') ;i++)
124 {
125 volume[i]=s[i] ;
126 }
127 volume[i]=':' ;
128 volume[i+1]=0 ;
129
130 // then copy the rest of the filename
131
132 for (j=0;(s[i]!=0);i++,j++)
133 {
134 path[j]=s[i] ;
135 }
136 path[j]=0 ;
137
138 c2pstr((Ptr) volume) ;
139 c2pstr((Ptr) path) ;
140
141 SetVol(volume, 0) ;
142 GetVol( NULL, &vRefNum ) ;
143
144 GetDirectoryID( vRefNum , fsRtDirID , path , &dirRef , &isDirectory ) ;
145 wxMacSetupStandardFile(vRefNum, dirRef) ;
146 }
147
148 enum {
149 kSelectItem = 10, // select button item number
150 kSFGetFileDlgID = 251, // dialog resource number
151 kStrListID = 251, // our strings
152 kSelectStrNum = 1, // word 'Select: ' for button
153 kDesktopStrNum = 2, // word 'Desktop' for button
154 kSelectNoQuoteStrNum = 3, // word 'Select: ' for button
155
156 kUseQuotes = true, // parameter for SetButtonName
157 kDontUseQuotes = false
158 };
159
160 static void GetLabelString(StringPtr theStr, short stringNum)
161 {
162 GetIndString(theStr, kStrListID, stringNum);
163 }
164
165 static void CopyPStr(StringPtr src, StringPtr dest)
166 {
167 BlockMoveData(src, dest, 1 + src[0]);
168 }
169
170 static char GetSelectKey(void)
171 {
172 // this is the key used to trigger the select button
173
174 // NOT INTERNATIONAL SAVVY; should at least grab it from resources
175
176 return 's';
177 }
178
179 // FlashButton briefly highlights the dialog button
180 // as feedback for key equivalents
181
182 static void FlashButton(DialogPtr theDlgPtr, short buttonID)
183 {
184 short buttonType;
185 Handle buttonHandle;
186 Rect buttonRect;
187 unsigned long finalTicks;
188
189 GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
190 HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
191 Delay(10, &finalTicks);
192 HiliteControl((ControlHandle) buttonHandle, 0);
193 }
194
195 static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
196 {
197 return (spec1->vRefNum == spec2->vRefNum
198 && spec1->parID == spec2->parID
199 && EqualString(spec1->name, spec2->name, false, false));
200 }
201 // MyModalDialogFilter maps a key to the Select button, and handles
202 // flashing of the button when the key is hit
203
204 static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
205 short *item, Ptr dataPtr)
206 {
207 #pragma unused (dataPtr)
208
209 // make certain the proper dialog is showing, 'cause standard file
210 // can nest dialogs but calls the same filter for each
211
212 if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
213 {
214 // check if the select button was hit
215 /*
216 if ((eventRec->what == keyDown)
217 && (eventRec->modifiers & cmdKey)
218 && ((eventRec->message & charCodeMask) == GetSelectKey()))
219 {
220 *item = kSelectItem;
221 FlashButton(theDlgPtr, kSelectItem);
222 return true;
223 }
224 */
225 }
226
227 return false;
228 }
229 #endif !TARGET_CARBON
230
231 void ExtendedOpenFile( ConstStr255Param message , ConstStr255Param path , const char *filter , FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
232 {
233 Point thePt;
234 OpenUserDataRec myData;
235 FSSpec tempSpec;
236 Boolean folderFlag;
237 Boolean wasAliasedFlag;
238 DlgHookYDUPP dlgHookUPP;
239 ModalFilterYDUPP myModalFilterUPP;
240 OSErr err;
241 SFTypeList types ;
242
243
244 // presumably we're running System 7 or later so CustomGetFile is
245 // available
246
247 // set initial contents of Select button to a space
248
249 memcpy( theSFR->sfFile.name , "\p " , 2 ) ;
250
251 // point the user data parameter at the reply record so we can get to it later
252
253 myData.sfrPtr = theSFR;
254 if ( filter && filter[0] )
255 {
256 myData.numfilters = 1 ;
257 for ( int i = 0 ; i < myData.numfilters ; i++ )
258 {
259 int j ;
260
261 strcpy( myData.filter[i] , filter ) ;
262 for( j = 0 ; myData.filter[i][j] ; j++ )
263 {
264 myData.filter[i][j] = toupper( myData.filter[i][j] ) ;
265 }
266 for ( j = 0 ; gfilters[j] ; j++ )
267 {
268 if ( strcmp( myData.filter[i] , gfilters[j] ) == 0 )
269 {
270 myData.filtermactypes[i] = gfiltersmac[j] ;
271 break ;
272 }
273 }
274 if( gfilters[j] == NULL )
275 {
276 myData.filtermactypes[i] = '****' ;
277 }
278 }
279 }
280 else
281 {
282 myData.numfilters = 0 ;
283 }
284 // display the dialog
285
286 #if !TARGET_CARBON
287
288 dlgHookUPP = NULL ;
289 // dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
290 myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
291
292 thePt.h = thePt.v = -1; // center dialog
293
294 ParamText( message , NULL , NULL , NULL ) ;
295
296 CustomGetFile( fileFilter,
297 -1, // show all types
298 NULL,
299 theSFR,
300 kSFGetFileDlgID,
301 thePt, // top left point
302 dlgHookUPP,
303 myModalFilterUPP,
304 nil, // activate list
305 nil, // activate proc
306 &myData);
307
308 DisposeRoutineDescriptor(dlgHookUPP);
309 DisposeRoutineDescriptor(myModalFilterUPP);
310 #else
311 #endif
312 // if cancel wasn't pressed and no fatal error occurred...
313
314 if (theSFR->sfGood)
315 {
316 // if no name is in the reply record file spec,
317 // use the file spec of the parent folder
318
319 if (theSFR->sfFile.name[0] == '\0')
320 {
321 err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
322 "\p", &tempSpec);
323 if (err == noErr)
324 {
325 theSFR->sfFile = tempSpec;
326 }
327 else
328 {
329 // no name to return, forget it
330
331 theSFR->sfGood = false;
332 }
333 }
334
335 // if there is now a name in the file spec, check if it's
336 // for a folder or a volume
337
338 if (theSFR->sfFile.name[0] != '\0')
339 {
340 // the parID of the root of a disk is always fsRtParID == 1
341
342 if (theSFR->sfFile.parID == fsRtParID)
343 {
344 theSFR->sfIsVolume = true;
345 theSFR->sfIsFolder = false; // it would be reasonable for this to be true, too
346 }
347
348 // we have a valid FSSpec, now let's make sure it's not for an alias file
349
350 err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
351 if (err != noErr)
352 {
353 theSFR->sfGood = false;
354 }
355
356 // did the alias resolve to a folder?
357
358 if (folderFlag && ! theSFR->sfIsVolume)
359 {
360 theSFR->sfIsFolder = true;
361 }
362 }
363 }
364 }
365
366 static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, Ptr dataPtr)
367 {
368 Str255 filename ;
369 OpenUserDataRecPtr data = (OpenUserDataRecPtr) dataPtr ;
370 // return true if this item is invisible or a file
371
372 Boolean visibleFlag;
373 Boolean folderFlag;
374
375 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
376 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
377
378 // because the semantics of the filter proc are "true means don't show
379 // it" we need to invert the result that we return
380
381 if ( !visibleFlag )
382 return true ;
383
384 if ( !folderFlag )
385 {
386 if ( data->numfilters > 0 )
387 {
388 PLstrcpy( filename ,myCInfoPBPtr->hFileInfo.ioNamePtr ) ;
389 if ( filename[0] >= 4 )
390 {
391 for( int j = 1 ; j <= filename[0] ; j++ )
392 {
393 filename[j] = toupper( filename[j] ) ;
394 }
395 for ( int i = 0 ; i < data->numfilters ; ++i )
396 {
397 if ( myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType == data->filtermactypes[i] )
398 return false ;
399
400 if ( strncmp( (char*) filename + 1 + filename[0] - 4 ,
401 & data->filter[i][ strlen(data->filter[i]) - 4 ] , 4 ) == 0 )
402 return false ;
403 }
404 }
405 return true ;
406 }
407 }
408
409 return false ;
410 }
411
412 // end wxmac
413
414 wxString wxFileSelector(const char *title,
415 const char *defaultDir, const char *defaultFileName,
416 const char *defaultExtension, const char *filter, int flags,
417 wxWindow *parent, int x, int y)
418 {
419 // If there's a default extension specified but no filter, we create a suitable
420 // filter.
421
422 wxString filter2("");
423 if ( defaultExtension && !filter )
424 filter2 = wxString("*.") + wxString(defaultExtension) ;
425 else if ( filter )
426 filter2 = filter;
427
428 wxString defaultDirString;
429 if (defaultDir)
430 defaultDirString = defaultDir;
431 else
432 defaultDirString = "";
433
434 wxString defaultFilenameString;
435 if (defaultFileName)
436 defaultFilenameString = defaultFileName;
437 else
438 defaultFilenameString = "";
439
440 wxFileDialog fileDialog(parent, title, defaultDirString, defaultFilenameString, filter2, flags, wxPoint(x, y));
441
442 if ( fileDialog.ShowModal() == wxID_OK )
443 {
444 strcpy(wxBuffer, (const char *)fileDialog.GetPath());
445 return wxBuffer;
446 }
447 else
448 return wxGetEmptyString();
449 }
450
451 WXDLLEXPORT wxString wxFileSelectorEx(const char *title,
452 const char *defaultDir,
453 const char *defaultFileName,
454 int* defaultFilterIndex,
455 const char *filter,
456 int flags,
457 wxWindow* parent,
458 int x,
459 int y)
460
461 {
462 wxFileDialog fileDialog(parent, title ? title : "", defaultDir ? defaultDir : "",
463 defaultFileName ? defaultFileName : "", filter ? filter : "", flags, wxPoint(x, y));
464
465 if ( fileDialog.ShowModal() == wxID_OK )
466 {
467 *defaultFilterIndex = fileDialog.GetFilterIndex();
468 strcpy(wxBuffer, (const char *)fileDialog.GetPath());
469 return wxBuffer;
470 }
471 else
472 return wxGetEmptyString();
473 }
474
475 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
476 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
477 long style, const wxPoint& pos)
478 {
479 m_message = message;
480 m_dialogStyle = style;
481 m_parent = parent;
482 m_path = "";
483 m_fileName = defaultFileName;
484 m_dir = defaultDir;
485 m_wildCard = wildCard;
486 m_filterIndex = 1;
487 }
488
489 int wxFileDialog::ShowModal()
490 {
491 #if !TARGET_CARBON
492 if ( !gUseNavServices )
493 {
494 if ( m_dialogStyle & wxSAVE )
495 {
496 StandardFileReply reply ;
497 Str255 prompt ;
498 Str255 filename ;
499
500 strcpy((char *)prompt, m_message) ;
501 c2pstr((char *)prompt ) ;
502
503 strcpy((char *)filename, m_fileName) ;
504 c2pstr((char *)filename ) ;
505 #if !TARGET_CARBON
506
507 StandardPutFile( prompt , filename , &reply ) ;
508
509 #else
510 #endif
511 if ( reply.sfGood == false )
512 {
513 m_path = "" ;
514 return wxID_CANCEL ;
515 }
516 else
517 {
518 m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
519 return wxID_OK ;
520 }
521 }
522 else
523 {
524 OSType types = '????' ;
525 Str255 prompt ;
526 Str255 path ;
527
528 strcpy((char *)prompt, m_message) ;
529 c2pstr((char *)prompt ) ;
530
531 strcpy((char *)path, m_dir ) ;
532 c2pstr((char *)path ) ;
533
534 StandardFileReply reply ;
535 FileFilterYDUPP crossPlatformFileFilterUPP = 0 ;
536 #if !TARGET_CARBON
537 crossPlatformFileFilterUPP =
538 NewFileFilterYDProc(CrossPlatformFileFilter);
539 #endif
540
541 ExtendedOpenFile( prompt , path , m_wildCard , crossPlatformFileFilterUPP, &reply);
542 #if !TARGET_CARBON
543 DisposeFileFilterYDUPP(crossPlatformFileFilterUPP);
544 #endif
545 if ( reply.sfGood == false )
546 {
547 m_path = "" ;
548 return wxID_CANCEL ;
549 }
550 else
551 {
552 m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
553 return wxID_OK ;
554 }
555 }
556 return wxID_CANCEL;
557 }
558 else
559 #endif
560 {
561 NavDialogOptions mNavOptions;
562 NavObjectFilterUPP mNavFilterUPP = NULL;
563 NavPreviewUPP mNavPreviewUPP = NULL ;
564 NavReplyRecord mNavReply;
565 AEDesc mDefaultLocation ;
566 bool mSelectDefault = false ;
567
568 ::NavGetDefaultDialogOptions(&mNavOptions);
569
570 mNavFilterUPP = nil;
571 mNavPreviewUPP = nil;
572 mSelectDefault = false;
573 mNavReply.validRecord = false;
574 mNavReply.replacing = false;
575 mNavReply.isStationery = false;
576 mNavReply.translationNeeded = false;
577 mNavReply.selection.descriptorType = typeNull;
578 mNavReply.selection.dataHandle = nil;
579 mNavReply.keyScript = smSystemScript;
580 mNavReply.fileTranslation = nil;
581
582 // Set default location, the location
583 // that's displayed when the dialog
584 // first appears
585
586 FSSpec location ;
587 wxUnixFilename2FSSpec( m_dir , &location ) ;
588 OSErr err = noErr ;
589
590 mDefaultLocation.descriptorType = typeNull;
591 mDefaultLocation.dataHandle = nil;
592
593 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
594
595 if ( mDefaultLocation.dataHandle ) {
596
597 if (mSelectDefault) {
598 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
599 } else {
600 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
601 }
602 }
603
604 strcpy((char *)mNavOptions.message, m_message) ;
605 c2pstr((char *)mNavOptions.message ) ;
606
607 strcpy((char *)mNavOptions.savedFileName, m_fileName) ;
608 c2pstr((char *)mNavOptions.savedFileName ) ;
609
610 if ( m_dialogStyle & wxSAVE )
611 {
612
613 mNavOptions.dialogOptionFlags |= kNavNoTypePopup ;
614 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
615 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
616
617 err = ::NavPutFile(
618 &mDefaultLocation,
619 &mNavReply,
620 &mNavOptions,
621 sStandardNavEventFilter ,
622 'TEXT',
623 'TEXT',
624 0L); // User Data
625 }
626 else
627 {
628 if ( m_dialogStyle & wxMULTIPLE )
629 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
630 else
631 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
632
633 err = ::NavGetFile(
634 &mDefaultLocation,
635 &mNavReply,
636 &mNavOptions,
637 sStandardNavEventFilter ,
638 mNavPreviewUPP,
639 mNavFilterUPP,
640 0L /*inFileTypes.TypeListHandle() */,
641 0L); // User Data
642 }
643
644 if ( mDefaultLocation.dataHandle != nil )
645 {
646 ::AEDisposeDesc(&mDefaultLocation);
647 }
648
649 if ( (err != noErr) && (err != userCanceledErr) ) {
650 m_path = "" ;
651 return wxID_CANCEL ;
652 }
653
654 if (mNavReply.validRecord) {
655
656 FSSpec outFileSpec ;
657 AEDesc specDesc ;
658
659 long count ;
660 ::AECountItems( &mNavReply.selection , &count ) ;
661 for ( long i = 1 ; i <= count ; ++i )
662 {
663 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, NULL , &specDesc);
664 if ( err != noErr ) {
665 m_path = "" ;
666 return wxID_CANCEL ;
667 }
668 outFileSpec = **(FSSpec**) specDesc.dataHandle;
669 if (specDesc.dataHandle != nil) {
670 ::AEDisposeDesc(&specDesc);
671 }
672
673
674 // outFolderDirID = thePB.dirInfo.ioDrDirID;
675 m_path = wxMacFSSpec2UnixFilename( &outFileSpec ) ;
676 m_paths.Add( m_path ) ;
677 m_fileNames.Add(m_fileName);
678 }
679 // set these to the first hit
680 m_path = m_paths[ 0 ] ;
681 m_fileName = wxFileNameFromPath(m_path);
682 m_dir = wxPathOnly(m_path);
683
684 return wxID_OK ;
685 }
686 return wxID_CANCEL;
687 }
688 }
689
690 // Generic file load/save dialog
691 static wxString
692 wxDefaultFileSelector(bool load, const char *what, const char *extension, const char *default_name, wxWindow *parent)
693 {
694 char *ext = (char *)extension;
695
696 char prompt[50];
697 wxString str;
698 if (load)
699 str = "Load %s file";
700 else
701 str = "Save %s file";
702 sprintf(prompt, wxGetTranslation(str), what);
703
704 if (*ext == '.') ext++;
705 char wild[60];
706 sprintf(wild, "*.%s", ext);
707
708 return wxFileSelector (prompt, NULL, default_name, ext, wild, 0, parent);
709 }
710
711 // Generic file load dialog
712 wxString
713 wxLoadFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
714 {
715 return wxDefaultFileSelector(TRUE, what, extension, default_name, parent);
716 }
717
718
719 // Generic file save dialog
720 wxString
721 wxSaveFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
722 {
723 return wxDefaultFileSelector(FALSE, what, extension, default_name, parent);
724 }
725
726