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