]> git.saurik.com Git - wxWidgets.git/blame - src/mac/filedlg.cpp
faster portsetting backpatched
[wxWidgets.git] / src / mac / filedlg.cpp
CommitLineData
e9576ca5
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: filedlg.cpp
2f1ae414 3// Purpose: wxFileDialog
a31a5f85 4// Author: Stefan Csomor
e9576ca5 5// Modified by:
a31a5f85 6// Created: 1998-01-01
e9576ca5 7// RCS-ID: $Id$
a31a5f85 8// Copyright: (c) Stefan Csomor
e9576ca5
SC
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13#pragma implementation "filedlg.h"
14#endif
15
16#include "wx/defs.h"
5fde6fcc 17#include "wx/app.h"
e9576ca5
SC
18#include "wx/utils.h"
19#include "wx/dialog.h"
20#include "wx/filedlg.h"
21#include "wx/intl.h"
fe35d097 22#include "wx/tokenzr.h"
e9576ca5 23
f11bdd03 24#ifndef __DARWIN__
03e11df5
GD
25 #include "PLStringFuncs.h"
26#endif
5b781a67 27
2f1ae414 28#if !USE_SHARED_LIBRARY
f74172ab 29IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
2f1ae414 30#endif
e9576ca5 31
519cb848
SC
32// begin wxmac
33
76a5e5d2
SC
34#include "wx/mac/private.h"
35
bb378910 36#include <Navigation.h>
5b781a67 37
2d4e4f80
GD
38#ifdef __DARWIN__
39# include "MoreFilesX.h"
40#else
41# include "MoreFiles.h"
42# include "MoreFilesExtras.h"
43#endif
44
5b781a67
SC
45extern bool gUseNavServices ;
46
4d4d8bbf
SC
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
4d4d8bbf
SC
53struct OpenUserDataRec {
54 int currentfilter ;
e40298d5 55 bool saveMode ;
2b5f62a0
VZ
56 wxArrayString name ;
57 wxArrayString extensions ;
e40298d5 58 wxArrayLong filtermactypes ;
f65d4b83
SC
59#if TARGET_CARBON
60 CFArrayRef menuitems ;
61#else
2b5f62a0 62 NavMenuItemSpecArrayHandle menuitems ;
f65d4b83 63#endif
4d4d8bbf 64};
2b5f62a0 65
4d4d8bbf 66typedef struct OpenUserDataRec
e40298d5 67 OpenUserDataRec, *OpenUserDataRecPtr;
4d4d8bbf 68
e40298d5
JS
69static pascal void NavEventProc(
70 NavEventCallbackMessage inSelector,
71 NavCBRecPtr ioParams,
72 NavCallBackUserData ioUserData);
5b781a67
SC
73
74#if TARGET_CARBON
e40298d5 75 static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
5b781a67 76#else
e40298d5 77 static NavEventUPP sStandardNavEventFilter = NewNavEventProc(NavEventProc);
5b781a67
SC
78#endif
79
80static pascal void
81NavEventProc(
e40298d5
JS
82 NavEventCallbackMessage inSelector,
83 NavCBRecPtr ioParams,
84 NavCallBackUserData ioUserData )
5b781a67 85{
e40298d5 86 OpenUserDataRec * data = ( OpenUserDataRec *) ioUserData ;
f65d4b83
SC
87 if (inSelector == kNavCBEvent) {
88#if TARGET_CARBON
89#else
5fde6fcc 90 wxTheApp->MacHandleOneEvent(ioParams->eventData.eventDataParms.event);
ac9b5f98 91#endif
e40298d5
JS
92 }
93 else if ( inSelector == kNavCBStart )
94 {
f65d4b83
SC
95#if TARGET_CARBON
96#else
e40298d5
JS
97 if ( data->menuitems )
98 NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &(*data->menuitems)[data->currentfilter]);
f65d4b83 99#endif
e40298d5
JS
100 }
101 else if ( inSelector == kNavCBPopupMenuSelect )
102 {
103 NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ;
f65d4b83
SC
104#if TARGET_CARBON
105#else
e40298d5 106 if ( menu->menuCreator == 'WXNG' )
f65d4b83 107#endif
e40298d5
JS
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() ;
f65d4b83
SC
115 wxString sfilename ;
116
117#if TARGET_CARBON
118 wxMacCFStringHolder cfString = NavDialogGetSaveFileName( ioParams->context ) ;
119 sfilename = cfString.AsString() ;
120#else
e40298d5
JS
121 Str255 filename ;
122 // get the current filename
123 NavCustomControl(ioParams->context, kNavCtlGetEditFileName, &filename);
f65d4b83
SC
124 sfilename = wxMacMakeStringFromPascal( filename ) ;
125#endif
126
e40298d5
JS
127 int pos = sfilename.Find('.',TRUE) ;
128 if ( pos != wxNOT_FOUND )
129 {
130 sfilename = sfilename.Left(pos+1)+extension ;
f65d4b83
SC
131#if TARGET_CARBON
132 cfString = sfilename ;
133 NavDialogSetSaveFileName( ioParams->context , cfString ) ;
134#else
427ff662 135 wxMacStringToPascal( sfilename , filename ) ;
e40298d5 136 NavCustomControl(ioParams->context, kNavCtlSetEditFileName, &filename);
f65d4b83 137#endif
e40298d5
JS
138 }
139 }
f65d4b83 140 }
e40298d5 141 }
5b781a67
SC
142}
143
427ff662 144const wxChar * gfilters[] =
519cb848 145{
427ff662
SC
146 wxT("*.TXT") ,
147 wxT("*.TIF") ,
148 wxT("*.JPG") ,
e40298d5
JS
149
150 NULL
519cb848
SC
151} ;
152
153OSType gfiltersmac[] =
154{
e40298d5
JS
155 'TEXT' ,
156 'TIFF' ,
157 'JPEG' ,
158
159 '****'
519cb848
SC
160} ;
161
2f1ae414 162
519cb848 163
e40298d5 164void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
4d4d8bbf 165{
e40298d5
JS
166 myData->menuitems = NULL ;
167 myData->currentfilter = 0 ;
168 myData->saveMode = FALSE ;
169
170 if ( filter && filter[0] )
4d4d8bbf 171 {
e40298d5
JS
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 ;
427ff662 188 current = wxEmptyString ;
e40298d5
JS
189 }
190 else
191 {
192 current += filter2.GetChar(i) ;
193 }
4d4d8bbf 194 }
e40298d5
JS
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
427ff662 198 wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ;
e40298d5
JS
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
2b5f62a0 208
2b5f62a0 209 const size_t extCount = myData->extensions.GetCount();
e40298d5
JS
210 for ( size_t i = 0 ; i < extCount; i++ )
211 {
212 int j ;
213 for ( j = 0 ; gfilters[j] ; j++ )
214 {
427ff662 215 if ( myData->extensions[i] == gfilters[j] )
e40298d5
JS
216 {
217 myData->filtermactypes.Add( gfiltersmac[j] ) ;
218 break ;
219 }
220 }
221 if( gfilters[j] == NULL )
222 {
223 myData->filtermactypes.Add( '****' ) ;
224 }
225 }
226 }
4d4d8bbf 227}
bb378910 228
f65d4b83 229static Boolean CheckFile( const wxString &filename , OSType type , OpenUserDataRecPtr data)
4d4d8bbf 230{
f65d4b83 231 wxString file = filename ;
9f92f6fb
SC
232 file.MakeUpper() ;
233
2b5f62a0 234 if ( data->extensions.GetCount() > 0 )
da2b4b7a 235 {
e40298d5
JS
236 //for ( int i = 0 ; i < data->numfilters ; ++i )
237 int i = data->currentfilter ;
427ff662 238 if ( data->extensions[i].Right(2) == wxT(".*") )
e40298d5
JS
239 return true ;
240
241 {
242 if ( type == (OSType)data->filtermactypes[i] )
243 return true ;
244
427ff662 245 wxStringTokenizer tokenizer( data->extensions[i] , wxT(";") ) ;
e40298d5
JS
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 ;
da2b4b7a
GD
257 }
258 return true ;
4d4d8bbf
SC
259}
260
bb378910 261#ifndef __DARWIN__
5fde6fcc 262static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
e40298d5
JS
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 {
f65d4b83
SC
281 wxString file = wxMacMakeStringFromPascal( myCInfoPBPtr->hFileInfo.ioNamePtr ) ;
282 return !CheckFile( file , myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType , data ) ;
e40298d5
JS
283 }
284
285 return false ;
519cb848 286}
bb378910 287#endif
519cb848
SC
288
289// end wxmac
290
e9576ca5
SC
291wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
292 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
293 long style, const wxPoint& pos)
f74172ab 294 :wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos)
e9576ca5 295{
427ff662 296 wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ;
e9576ca5
SC
297}
298
f11bdd03 299pascal Boolean CrossPlatformFilterCallback (
4d4d8bbf
SC
300 AEDesc *theItem,
301 void *info,
302 void *callBackUD,
303 NavFilterModes filterMode
304)
305{
2d4e4f80
GD
306 bool display = true;
307 OpenUserDataRecPtr data = (OpenUserDataRecPtr) callBackUD ;
308
309 if (filterMode == kNavFilteringBrowserList)
310 {
311 NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*) info ;
f65d4b83 312 if ( !theInfo->isFolder )
2d4e4f80 313 {
f65d4b83
SC
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 }
2d4e4f80
GD
331 }
332 }
333
334 return display;
4d4d8bbf
SC
335}
336
e9576ca5
SC
337int wxFileDialog::ShowModal()
338{
f65d4b83
SC
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
e40298d5 345 NavDialogOptions mNavOptions;
f65d4b83 346#endif
e40298d5
JS
347 NavObjectFilterUPP mNavFilterUPP = NULL;
348 NavPreviewUPP mNavPreviewUPP = NULL ;
349 NavReplyRecord mNavReply;
350 AEDesc mDefaultLocation ;
351 bool mSelectDefault = false ;
f65d4b83 352 OSStatus err = noErr ;
e40298d5
JS
353 // setup dialog
354
e40298d5
JS
355 mNavFilterUPP = nil;
356 mNavPreviewUPP = nil;
357 mSelectDefault = false;
f65d4b83
SC
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
2d4e4f80
GD
372 // Set default location, the location
373 // that's displayed when the dialog
374 // first appears
e40298d5 375
2d4e4f80
GD
376 FSSpec location ;
377 wxMacFilename2FSSpec( m_dir , &location ) ;
e40298d5 378
2d4e4f80 379 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
5b781a67 380
2d4e4f80 381 if ( mDefaultLocation.dataHandle ) {
e40298d5 382
2d4e4f80
GD
383 if (mSelectDefault) {
384 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
385 } else {
386 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
387 }
388 }
f65d4b83 389#endif
5b781a67 390
f65d4b83
SC
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
7ed2b47b
SC
402 // zero all data
403
404 m_path = wxEmptyString ;
405 m_fileName = wxEmptyString ;
406 m_paths.Empty();
407 m_fileNames.Empty();
408
e40298d5 409 OpenUserDataRec myData;
2d4e4f80
GD
410 MakeUserDataRec( &myData , m_wildCard ) ;
411 myData.currentfilter = m_filterIndex ;
412 if ( myData.extensions.GetCount() > 0 )
413 {
f65d4b83
SC
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
2d4e4f80
GD
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' ;
f65d4b83 430 // TODO : according to the new docs -1 to 10 are reserved for the OS
2d4e4f80 431 (*mNavOptions.popupExtension)[i].menuType = i ;
427ff662 432 wxMacStringToPascal( myData.name[i] , (StringPtr)(*mNavOptions.popupExtension)[i].menuItemName ) ;
2d4e4f80 433 }
f65d4b83 434#endif
2d4e4f80
GD
435 }
436 if ( m_dialogStyle & wxSAVE )
437 {
438 myData.saveMode = true ;
2b5f62a0 439
2d4e4f80
GD
440 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
441 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
e40298d5 442
f65d4b83
SC
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
2d4e4f80
GD
456 err = ::NavPutFile(
457 &mDefaultLocation,
458 &mNavReply,
459 &mNavOptions,
460 sStandardNavEventFilter ,
461 NULL,
462 kNavGenericSignature,
e40298d5 463 &myData); // User Data
f65d4b83 464#endif
2d4e4f80
GD
465 m_filterIndex = myData.currentfilter ;
466 }
467 else
468 {
469 myData.saveMode = false ;
4d4d8bbf 470
2d4e4f80
GD
471 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
472 if ( m_dialogStyle & wxMULTIPLE )
473 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
474 else
475 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
e40298d5 476
f65d4b83
SC
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
2d4e4f80
GD
490 err = ::NavGetFile(
491 &mDefaultLocation,
492 &mNavReply,
493 &mNavOptions,
494 sStandardNavEventFilter ,
495 mNavPreviewUPP,
496 mNavFilterUPP,
497 NULL ,
498 &myData);
f65d4b83 499#endif
2d4e4f80
GD
500 m_filterIndex = myData.currentfilter ;
501 }
e40298d5 502
2d4e4f80
GD
503 DisposeNavObjectFilterUPP(mNavFilterUPP);
504 if ( mDefaultLocation.dataHandle != nil )
505 {
506 ::AEDisposeDesc(&mDefaultLocation);
507 }
e40298d5 508
2d4e4f80 509 if ( (err != noErr) && (err != userCanceledErr) ) {
2d4e4f80
GD
510 return wxID_CANCEL ;
511 }
5b781a67 512
2d4e4f80 513 if (mNavReply.validRecord) {
e40298d5 514
2d4e4f80
GD
515 FSSpec outFileSpec ;
516 AEDesc specDesc ;
517 AEKeyword keyWord ;
e40298d5 518
2d4e4f80
GD
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 ) {
427ff662 525 m_path = wxT("") ;
2d4e4f80 526 return wxID_CANCEL ;
e40298d5 527 }
2d4e4f80
GD
528 outFileSpec = **(FSSpec**) specDesc.dataHandle;
529 if (specDesc.dataHandle != nil) {
530 ::AEDisposeDesc(&specDesc);
531 }
532 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
f65d4b83
SC
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
2d4e4f80 540 m_paths.Add( m_path ) ;
24fe8dc7 541 m_fileName = wxFileNameFromPath(m_path);
2d4e4f80
GD
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 ) ;
f65d4b83
SC
549 if ( navDialogRef )
550 NavDialogDispose( navDialogRef ) ;
551
2d4e4f80
GD
552 return wxID_OK ;
553 }
f65d4b83
SC
554 if ( navDialogRef )
555 NavDialogDispose( navDialogRef ) ;
556
2d4e4f80 557 return wxID_CANCEL;
5b781a67 558}
e9576ca5 559