]> git.saurik.com Git - wxWidgets.git/blob - src/mac/filedlg.cpp
new carbon implementation (unicode support)
[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 else if ( theItem->descriptorType == typeFSRef )
322 {
323 FSRef fsref ;
324 memcpy( &fsref , *theItem->dataHandle , sizeof(FSRef) ) ;
325 wxString file ;
326 const short maxpath = 1024 ;
327 FSRefMakePath( &fsref , (UInt8*) file.GetWriteBuf(maxpath+1),maxpath) ;
328 file.UngetWriteBuf() ;
329 display = CheckFile( file , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
330 }
331 }
332 }
333
334 return display;
335 }
336
337 int wxFileDialog::ShowModal()
338 {
339 #if TARGET_CARBON
340 NavDialogCreationOptions mNavOptions;
341 NavDialogRef navDialogRef = NULL ;
342 // since the same field has been renamed ...
343 #define dialogOptionFlags optionFlags
344 #else
345 NavDialogOptions mNavOptions;
346 #endif
347 NavObjectFilterUPP mNavFilterUPP = NULL;
348 NavPreviewUPP mNavPreviewUPP = NULL ;
349 NavReplyRecord mNavReply;
350 AEDesc mDefaultLocation ;
351 bool mSelectDefault = false ;
352 OSStatus err = noErr ;
353 // setup dialog
354
355 mNavFilterUPP = nil;
356 mNavPreviewUPP = nil;
357 mSelectDefault = false;
358 mDefaultLocation.descriptorType = typeNull;
359 mDefaultLocation.dataHandle = nil;
360
361 #if TARGET_CARBON
362 NavGetDefaultDialogCreationOptions( &mNavOptions ) ;
363 wxMacCFStringHolder cfMessage(m_message) ;
364 wxMacCFStringHolder cfFileName(m_fileName) ;
365 mNavOptions.saveFileName = cfFileName ;
366 mNavOptions.message = cfMessage ;
367 #else
368 NavGetDefaultDialogOptions(&mNavOptions);
369 wxMacStringToPascal( m_message , (StringPtr)mNavOptions.message ) ;
370 wxMacStringToPascal( m_fileName , (StringPtr)mNavOptions.savedFileName ) ;
371
372 // Set default location, the location
373 // that's displayed when the dialog
374 // first appears
375
376 FSSpec location ;
377 wxMacFilename2FSSpec( m_dir , &location ) ;
378
379 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
380
381 if ( mDefaultLocation.dataHandle ) {
382
383 if (mSelectDefault) {
384 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
385 } else {
386 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
387 }
388 }
389 #endif
390
391 memset( &mNavReply , 0 , sizeof( mNavReply ) ) ;
392 mNavReply.validRecord = false;
393 mNavReply.replacing = false;
394 mNavReply.isStationery = false;
395 mNavReply.translationNeeded = false;
396 mNavReply.selection.descriptorType = typeNull;
397 mNavReply.selection.dataHandle = nil;
398 mNavReply.keyScript = smSystemScript;
399 mNavReply.fileTranslation = nil;
400 mNavReply.version = kNavReplyRecordVersion ;
401
402 // zero all data
403
404 m_path = wxEmptyString ;
405 m_fileName = wxEmptyString ;
406 m_paths.Empty();
407 m_fileNames.Empty();
408
409 OpenUserDataRec myData;
410 MakeUserDataRec( &myData , m_wildCard ) ;
411 myData.currentfilter = m_filterIndex ;
412 if ( myData.extensions.GetCount() > 0 )
413 {
414 #if TARGET_CARBON
415 CFMutableArrayRef popup = CFArrayCreateMutable( kCFAllocatorDefault ,
416 myData.extensions.GetCount() , &kCFTypeArrayCallBacks ) ;
417 mNavOptions.popupExtension = popup ;
418 myData.menuitems = mNavOptions.popupExtension ;
419 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
420 {
421 CFArrayAppendValue( popup , (CFStringRef) wxMacCFStringHolder( myData.name[i] ) ) ;
422 }
423 #else
424 mNavOptions.popupExtension = (NavMenuItemSpecArrayHandle) NewHandle( sizeof( NavMenuItemSpec ) * myData.extensions.GetCount() ) ;
425 myData.menuitems = mNavOptions.popupExtension ;
426 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
427 {
428 (*mNavOptions.popupExtension)[i].version = kNavMenuItemSpecVersion ;
429 (*mNavOptions.popupExtension)[i].menuCreator = 'WXNG' ;
430 // TODO : according to the new docs -1 to 10 are reserved for the OS
431 (*mNavOptions.popupExtension)[i].menuType = i ;
432 wxMacStringToPascal( myData.name[i] , (StringPtr)(*mNavOptions.popupExtension)[i].menuItemName ) ;
433 }
434 #endif
435 }
436 if ( m_dialogStyle & wxSAVE )
437 {
438 myData.saveMode = true ;
439
440 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
441 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
442
443 #if TARGET_CARBON
444 err = NavCreatePutFileDialog( &mNavOptions , NULL , kNavGenericSignature , sStandardNavEventFilter ,
445 &myData , &navDialogRef ) ;
446 if ( err == noErr )
447 {
448 err = NavDialogRun( navDialogRef ) ;
449 NavUserAction userAction = NavDialogGetUserAction( navDialogRef ) ;
450 if ( userAction != kNavUserActionCancel && userAction != kNavUserActionNone )
451 {
452 NavDialogGetReply( navDialogRef, &mNavReply ) ;
453 }
454 }
455 #else
456 err = ::NavPutFile(
457 &mDefaultLocation,
458 &mNavReply,
459 &mNavOptions,
460 sStandardNavEventFilter ,
461 NULL,
462 kNavGenericSignature,
463 &myData); // User Data
464 #endif
465 m_filterIndex = myData.currentfilter ;
466 }
467 else
468 {
469 myData.saveMode = false ;
470
471 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
472 if ( m_dialogStyle & wxMULTIPLE )
473 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
474 else
475 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
476
477 #if TARGET_CARBON
478 err = NavCreateGetFileDialog( &mNavOptions , NULL , sStandardNavEventFilter ,
479 mNavPreviewUPP , mNavFilterUPP , &myData , &navDialogRef ) ;
480 if ( err == noErr )
481 {
482 err = NavDialogRun( navDialogRef ) ;
483 NavUserAction userAction = NavDialogGetUserAction( navDialogRef ) ;
484 if ( userAction != kNavUserActionCancel && userAction != kNavUserActionNone )
485 {
486 NavDialogGetReply( navDialogRef, &mNavReply ) ;
487 }
488 }
489 #else
490 err = ::NavGetFile(
491 &mDefaultLocation,
492 &mNavReply,
493 &mNavOptions,
494 sStandardNavEventFilter ,
495 mNavPreviewUPP,
496 mNavFilterUPP,
497 NULL ,
498 &myData);
499 #endif
500 m_filterIndex = myData.currentfilter ;
501 }
502
503 DisposeNavObjectFilterUPP(mNavFilterUPP);
504 if ( mDefaultLocation.dataHandle != nil )
505 {
506 ::AEDisposeDesc(&mDefaultLocation);
507 }
508
509 if ( (err != noErr) && (err != userCanceledErr) ) {
510 return wxID_CANCEL ;
511 }
512
513 if (mNavReply.validRecord) {
514
515 FSSpec outFileSpec ;
516 AEDesc specDesc ;
517 AEKeyword keyWord ;
518
519 long count ;
520 ::AECountItems( &mNavReply.selection , &count ) ;
521 for ( long i = 1 ; i <= count ; ++i )
522 {
523 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, &keyWord , &specDesc);
524 if ( err != noErr ) {
525 m_path = wxT("") ;
526 return wxID_CANCEL ;
527 }
528 outFileSpec = **(FSSpec**) specDesc.dataHandle;
529 if (specDesc.dataHandle != nil) {
530 ::AEDisposeDesc(&specDesc);
531 }
532 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
533 #if TARGET_CARBON
534 if ( m_dialogStyle & wxSAVE )
535 {
536 wxMacCFStringHolder cfString = NavDialogGetSaveFileName( navDialogRef ) ;
537 m_path += wxFILE_SEP_PATH + cfString.AsString() ;
538 }
539 #endif
540 m_paths.Add( m_path ) ;
541 m_fileName = wxFileNameFromPath(m_path);
542 m_fileNames.Add(m_fileName);
543 }
544 // set these to the first hit
545 m_path = m_paths[ 0 ] ;
546 m_fileName = wxFileNameFromPath(m_path);
547 m_dir = wxPathOnly(m_path);
548 NavDisposeReply( &mNavReply ) ;
549 if ( navDialogRef )
550 NavDialogDispose( navDialogRef ) ;
551
552 return wxID_OK ;
553 }
554 if ( navDialogRef )
555 NavDialogDispose( navDialogRef ) ;
556
557 return wxID_CANCEL;
558 }
559