]> git.saurik.com Git - wxWidgets.git/blob - src/mac/filedlg.cpp
use wxEventLoop in wxApp under wxMSW; factored out common code from wxX11/wxMotif...
[wxWidgets.git] / src / mac / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
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/app.h"
18 #include "wx/utils.h"
19 #include "wx/dialog.h"
20 #include "wx/filedlg.h"
21 #include "wx/intl.h"
22 #include "wx/tokenzr.h"
23
24 #ifndef __DARWIN__
25 #include "PLStringFuncs.h"
26 #endif
27
28 #if !USE_SHARED_LIBRARY
29 IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
30 #endif
31
32 // begin wxmac
33
34 #include "wx/mac/private.h"
35
36 #include <Navigation.h>
37
38 #ifdef __DARWIN__
39 # include "MoreFilesX.h"
40 #else
41 # include "MoreFiles.h"
42 # include "MoreFilesExtras.h"
43 #endif
44
45 extern bool gUseNavServices ;
46
47 // the data we need to pass to our standard file hook routine
48 // includes a pointer to the dialog, a pointer to the standard
49 // file reply record (so we can inspect the current selection)
50 // and a copy of the "previous" file spec of the reply record
51 // so we can see if the selection has changed
52
53 struct OpenUserDataRec {
54 int currentfilter ;
55 bool saveMode ;
56 wxArrayString name ;
57 wxArrayString extensions ;
58 wxArrayLong filtermactypes ;
59 #if TARGET_CARBON
60 CFArrayRef menuitems ;
61 #else
62 NavMenuItemSpecArrayHandle menuitems ;
63 #endif
64 };
65
66 typedef struct OpenUserDataRec
67 OpenUserDataRec, *OpenUserDataRecPtr;
68
69 static pascal void NavEventProc(
70 NavEventCallbackMessage inSelector,
71 NavCBRecPtr ioParams,
72 NavCallBackUserData ioUserData);
73
74 #if TARGET_CARBON
75 static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
76 #else
77 static NavEventUPP sStandardNavEventFilter = NewNavEventProc(NavEventProc);
78 #endif
79
80 static pascal void
81 NavEventProc(
82 NavEventCallbackMessage inSelector,
83 NavCBRecPtr ioParams,
84 NavCallBackUserData ioUserData )
85 {
86 OpenUserDataRec * data = ( OpenUserDataRec *) ioUserData ;
87 if (inSelector == kNavCBEvent) {
88 #if TARGET_CARBON
89 #else
90 wxTheApp->MacHandleOneEvent(ioParams->eventData.eventDataParms.event);
91 #endif
92 }
93 else if ( inSelector == kNavCBStart )
94 {
95 #if TARGET_CARBON
96 #else
97 if ( data->menuitems )
98 NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &(*data->menuitems)[data->currentfilter]);
99 #endif
100 }
101 else if ( inSelector == kNavCBPopupMenuSelect )
102 {
103 NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ;
104 #if TARGET_CARBON
105 #else
106 if ( menu->menuCreator == 'WXNG' )
107 #endif
108 {
109 data->currentfilter = menu->menuType ;
110 if ( data->saveMode )
111 {
112 int i = menu->menuType ;
113 wxString extension = data->extensions[i].AfterLast('.') ;
114 extension.MakeLower() ;
115 wxString sfilename ;
116
117 #if TARGET_CARBON
118 wxMacCFStringHolder cfString = NavDialogGetSaveFileName( ioParams->context ) ;
119 sfilename = cfString.AsString() ;
120 #else
121 Str255 filename ;
122 // get the current filename
123 NavCustomControl(ioParams->context, kNavCtlGetEditFileName, &filename);
124 sfilename = wxMacMakeStringFromPascal( filename ) ;
125 #endif
126
127 int pos = sfilename.Find('.',TRUE) ;
128 if ( pos != wxNOT_FOUND )
129 {
130 sfilename = sfilename.Left(pos+1)+extension ;
131 #if TARGET_CARBON
132 cfString = sfilename ;
133 NavDialogSetSaveFileName( ioParams->context , cfString ) ;
134 #else
135 wxMacStringToPascal( sfilename , filename ) ;
136 NavCustomControl(ioParams->context, kNavCtlSetEditFileName, &filename);
137 #endif
138 }
139 }
140 }
141 }
142 }
143
144 const wxChar * gfilters[] =
145 {
146 wxT("*.TXT") ,
147 wxT("*.TIF") ,
148 wxT("*.JPG") ,
149
150 NULL
151 } ;
152
153 OSType gfiltersmac[] =
154 {
155 'TEXT' ,
156 'TIFF' ,
157 'JPEG' ,
158
159 '****'
160 } ;
161
162
163
164 void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
165 {
166 myData->menuitems = NULL ;
167 myData->currentfilter = 0 ;
168 myData->saveMode = FALSE ;
169
170 if ( filter && filter[0] )
171 {
172 wxString filter2(filter) ;
173 int filterIndex = 0;
174 bool isName = true ;
175 wxString current ;
176 for( unsigned int i = 0; i < filter2.Len() ; i++ )
177 {
178 if( filter2.GetChar(i) == wxT('|') )
179 {
180 if( isName ) {
181 myData->name.Add( current ) ;
182 }
183 else {
184 myData->extensions.Add( current.MakeUpper() ) ;
185 ++filterIndex ;
186 }
187 isName = !isName ;
188 current = wxEmptyString ;
189 }
190 else
191 {
192 current += filter2.GetChar(i) ;
193 }
194 }
195 // we allow for compatibility reason to have a single filter expression (like *.*) without
196 // an explanatory text, in that case the first part is name and extension at the same time
197
198 wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ;
199 if ( current.IsEmpty() )
200 myData->extensions.Add( myData->name[filterIndex] ) ;
201 else
202 myData->extensions.Add( current.MakeUpper() ) ;
203 if ( filterIndex == 0 || isName )
204 myData->name.Add( current.MakeUpper() ) ;
205
206 ++filterIndex ;
207
208
209 const size_t extCount = myData->extensions.GetCount();
210 for ( size_t i = 0 ; i < extCount; i++ )
211 {
212 int j ;
213 for ( j = 0 ; gfilters[j] ; j++ )
214 {
215 if ( myData->extensions[i] == gfilters[j] )
216 {
217 myData->filtermactypes.Add( gfiltersmac[j] ) ;
218 break ;
219 }
220 }
221 if( gfilters[j] == NULL )
222 {
223 myData->filtermactypes.Add( '****' ) ;
224 }
225 }
226 }
227 }
228
229 static Boolean CheckFile( const wxString &filename , OSType type , OpenUserDataRecPtr data)
230 {
231 wxString file = filename ;
232 file.MakeUpper() ;
233
234 if ( data->extensions.GetCount() > 0 )
235 {
236 //for ( int i = 0 ; i < data->numfilters ; ++i )
237 int i = data->currentfilter ;
238 if ( data->extensions[i].Right(2) == wxT(".*") )
239 return true ;
240
241 {
242 if ( type == (OSType)data->filtermactypes[i] )
243 return true ;
244
245 wxStringTokenizer tokenizer( data->extensions[i] , wxT(";") ) ;
246 while( tokenizer.HasMoreTokens() )
247 {
248 wxString extension = tokenizer.GetNextToken() ;
249 if ( extension.GetChar(0) == '*' )
250 extension = extension.Mid(1) ;
251
252 if ( file.Len() >= extension.Len() && extension == file.Right(extension.Len() ) )
253 return true ;
254 }
255 }
256 return false ;
257 }
258 return true ;
259 }
260
261 #ifndef __DARWIN__
262 static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
263 {
264 OpenUserDataRecPtr data = (OpenUserDataRecPtr) dataPtr ;
265 // return true if this item is invisible or a file
266
267 Boolean visibleFlag;
268 Boolean folderFlag;
269
270 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
271 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
272
273 // because the semantics of the filter proc are "true means don't show
274 // it" we need to invert the result that we return
275
276 if ( !visibleFlag )
277 return true ;
278
279 if ( !folderFlag )
280 {
281 wxString file = wxMacMakeStringFromPascal( myCInfoPBPtr->hFileInfo.ioNamePtr ) ;
282 return !CheckFile( file , myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType , data ) ;
283 }
284
285 return false ;
286 }
287 #endif
288
289 // end wxmac
290
291 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
292 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
293 long style, const wxPoint& pos)
294 :wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos)
295 {
296 wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ;
297 }
298
299 pascal Boolean CrossPlatformFilterCallback (
300 AEDesc *theItem,
301 void *info,
302 void *callBackUD,
303 NavFilterModes filterMode
304 )
305 {
306 bool display = true;
307 OpenUserDataRecPtr data = (OpenUserDataRecPtr) callBackUD ;
308
309 if (filterMode == kNavFilteringBrowserList)
310 {
311 NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*) info ;
312 if ( !theInfo->isFolder )
313 {
314 if (theItem->descriptorType == typeFSS )
315 {
316 FSSpec spec;
317 memcpy( &spec , *theItem->dataHandle , sizeof(FSSpec) ) ;
318 wxString file = wxMacMakeStringFromPascal( spec.name ) ;
319 display = CheckFile( file , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
320 }
321 #if TARGET_CARBON
322 else if ( theItem->descriptorType == typeFSRef )
323 {
324 FSRef fsref ;
325 memcpy( &fsref , *theItem->dataHandle , sizeof(FSRef) ) ;
326 wxString file ;
327 const short maxpath = 1024 ;
328 FSRefMakePath( &fsref , (UInt8*) file.GetWriteBuf(maxpath+1),maxpath) ;
329 file.UngetWriteBuf() ;
330 display = CheckFile( file , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
331 }
332 #endif
333 }
334 }
335
336 return display;
337 }
338
339 int wxFileDialog::ShowModal()
340 {
341 #if TARGET_CARBON
342 NavDialogCreationOptions mNavOptions;
343 NavDialogRef navDialogRef = NULL ;
344 // since the same field has been renamed ...
345 #define dialogOptionFlags optionFlags
346 #else
347 NavDialogOptions mNavOptions;
348 #endif
349 NavObjectFilterUPP mNavFilterUPP = NULL;
350 NavPreviewUPP mNavPreviewUPP = NULL ;
351 NavReplyRecord mNavReply;
352 AEDesc mDefaultLocation ;
353 bool mSelectDefault = false ;
354 OSStatus err = noErr ;
355 // setup dialog
356
357 mNavFilterUPP = nil;
358 mNavPreviewUPP = nil;
359 mSelectDefault = false;
360 mDefaultLocation.descriptorType = typeNull;
361 mDefaultLocation.dataHandle = nil;
362
363 #if TARGET_CARBON
364 NavGetDefaultDialogCreationOptions( &mNavOptions ) ;
365 wxMacCFStringHolder cfMessage(m_message) ;
366 wxMacCFStringHolder cfFileName(m_fileName) ;
367 mNavOptions.saveFileName = cfFileName ;
368 mNavOptions.message = cfMessage ;
369 #else
370 NavGetDefaultDialogOptions(&mNavOptions);
371 wxMacStringToPascal( m_message , (StringPtr)mNavOptions.message ) ;
372 wxMacStringToPascal( m_fileName , (StringPtr)mNavOptions.savedFileName ) ;
373
374 // Set default location, the location
375 // that's displayed when the dialog
376 // first appears
377
378 FSSpec location ;
379 wxMacFilename2FSSpec( m_dir , &location ) ;
380
381 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
382
383 if ( mDefaultLocation.dataHandle ) {
384
385 if (mSelectDefault) {
386 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
387 } else {
388 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
389 }
390 }
391 #endif
392
393 memset( &mNavReply , 0 , sizeof( mNavReply ) ) ;
394 mNavReply.validRecord = false;
395 mNavReply.replacing = false;
396 mNavReply.isStationery = false;
397 mNavReply.translationNeeded = false;
398 mNavReply.selection.descriptorType = typeNull;
399 mNavReply.selection.dataHandle = nil;
400 mNavReply.keyScript = smSystemScript;
401 mNavReply.fileTranslation = nil;
402 mNavReply.version = kNavReplyRecordVersion ;
403
404 // zero all data
405
406 m_path = wxEmptyString ;
407 m_fileName = wxEmptyString ;
408 m_paths.Empty();
409 m_fileNames.Empty();
410
411 OpenUserDataRec myData;
412 MakeUserDataRec( &myData , m_wildCard ) ;
413 myData.currentfilter = m_filterIndex ;
414 if ( myData.extensions.GetCount() > 0 )
415 {
416 #if TARGET_CARBON
417 CFMutableArrayRef popup = CFArrayCreateMutable( kCFAllocatorDefault ,
418 myData.extensions.GetCount() , &kCFTypeArrayCallBacks ) ;
419 mNavOptions.popupExtension = popup ;
420 myData.menuitems = mNavOptions.popupExtension ;
421 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
422 {
423 CFArrayAppendValue( popup , (CFStringRef) wxMacCFStringHolder( myData.name[i] ) ) ;
424 }
425 #else
426 mNavOptions.popupExtension = (NavMenuItemSpecArrayHandle) NewHandle( sizeof( NavMenuItemSpec ) * myData.extensions.GetCount() ) ;
427 myData.menuitems = mNavOptions.popupExtension ;
428 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
429 {
430 (*mNavOptions.popupExtension)[i].version = kNavMenuItemSpecVersion ;
431 (*mNavOptions.popupExtension)[i].menuCreator = 'WXNG' ;
432 // TODO : according to the new docs -1 to 10 are reserved for the OS
433 (*mNavOptions.popupExtension)[i].menuType = i ;
434 wxMacStringToPascal( myData.name[i] , (StringPtr)(*mNavOptions.popupExtension)[i].menuItemName ) ;
435 }
436 #endif
437 }
438 if ( m_dialogStyle & wxSAVE )
439 {
440 myData.saveMode = true ;
441
442 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
443 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
444
445 #if TARGET_CARBON
446 err = NavCreatePutFileDialog( &mNavOptions , NULL , kNavGenericSignature , sStandardNavEventFilter ,
447 &myData , &navDialogRef ) ;
448 if ( err == noErr )
449 {
450 err = NavDialogRun( navDialogRef ) ;
451 NavUserAction userAction = NavDialogGetUserAction( navDialogRef ) ;
452 if ( userAction != kNavUserActionCancel && userAction != kNavUserActionNone )
453 {
454 NavDialogGetReply( navDialogRef, &mNavReply ) ;
455 }
456 }
457 #else
458 err = ::NavPutFile(
459 &mDefaultLocation,
460 &mNavReply,
461 &mNavOptions,
462 sStandardNavEventFilter ,
463 NULL,
464 kNavGenericSignature,
465 &myData); // User Data
466 #endif
467 m_filterIndex = myData.currentfilter ;
468 }
469 else
470 {
471 myData.saveMode = false ;
472
473 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
474 if ( m_dialogStyle & wxMULTIPLE )
475 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
476 else
477 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
478
479 #if TARGET_CARBON
480 err = NavCreateGetFileDialog( &mNavOptions , NULL , sStandardNavEventFilter ,
481 mNavPreviewUPP , mNavFilterUPP , &myData , &navDialogRef ) ;
482 if ( err == noErr )
483 {
484 err = NavDialogRun( navDialogRef ) ;
485 NavUserAction userAction = NavDialogGetUserAction( navDialogRef ) ;
486 if ( userAction != kNavUserActionCancel && userAction != kNavUserActionNone )
487 {
488 NavDialogGetReply( navDialogRef, &mNavReply ) ;
489 }
490 }
491 #else
492 err = ::NavGetFile(
493 &mDefaultLocation,
494 &mNavReply,
495 &mNavOptions,
496 sStandardNavEventFilter ,
497 mNavPreviewUPP,
498 mNavFilterUPP,
499 NULL ,
500 &myData);
501 #endif
502 m_filterIndex = myData.currentfilter ;
503 }
504
505 DisposeNavObjectFilterUPP(mNavFilterUPP);
506 if ( mDefaultLocation.dataHandle != nil )
507 {
508 ::AEDisposeDesc(&mDefaultLocation);
509 }
510
511 if ( (err != noErr) && (err != userCanceledErr) ) {
512 return wxID_CANCEL ;
513 }
514
515 if (mNavReply.validRecord) {
516
517 FSSpec outFileSpec ;
518 AEDesc specDesc ;
519 AEKeyword keyWord ;
520
521 long count ;
522 ::AECountItems( &mNavReply.selection , &count ) ;
523 for ( long i = 1 ; i <= count ; ++i )
524 {
525 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, &keyWord , &specDesc);
526 if ( err != noErr ) {
527 m_path = wxT("") ;
528 return wxID_CANCEL ;
529 }
530 outFileSpec = **(FSSpec**) specDesc.dataHandle;
531 if (specDesc.dataHandle != nil) {
532 ::AEDisposeDesc(&specDesc);
533 }
534 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
535 #if TARGET_CARBON
536 if ( m_dialogStyle & wxSAVE )
537 {
538 wxMacCFStringHolder cfString = NavDialogGetSaveFileName( navDialogRef ) ;
539 m_path += wxFILE_SEP_PATH + cfString.AsString() ;
540 }
541 #endif
542 m_paths.Add( m_path ) ;
543 m_fileName = wxFileNameFromPath(m_path);
544 m_fileNames.Add(m_fileName);
545 }
546 // set these to the first hit
547 m_path = m_paths[ 0 ] ;
548 m_fileName = wxFileNameFromPath(m_path);
549 m_dir = wxPathOnly(m_path);
550 NavDisposeReply( &mNavReply ) ;
551 #if TARGET_CARBON
552 if ( navDialogRef )
553 NavDialogDispose( navDialogRef ) ;
554 #endif
555 return wxID_OK ;
556 }
557 #if TARGET_CARBON
558 if ( navDialogRef )
559 NavDialogDispose( navDialogRef ) ;
560 #endif
561 return wxID_CANCEL;
562 }
563