]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/filedlg.cpp
use 2 extensions in a filter in FileSave() for testing
[wxWidgets.git] / src / mac / carbon / filedlg.cpp
CommitLineData
e9576ca5 1/////////////////////////////////////////////////////////////////////////////
88a7a4e1 2// Name: src/mac/carbon/filedlg.cpp
685a634c 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
65571936 9// Licence: wxWindows licence
e9576ca5
SC
10/////////////////////////////////////////////////////////////////////////////
11
3d1a4878
SC
12#include "wx/wxprec.h"
13
72c1ba98
VZ
14#if wxUSE_FILEDLG
15
88a7a4e1
WS
16#include "wx/filedlg.h"
17
18#ifndef WX_PRECOMP
19 #include "wx/intl.h"
670f9935 20 #include "wx/app.h"
de6185e2 21 #include "wx/utils.h"
fdf565fe 22 #include "wx/dialog.h"
88a7a4e1
WS
23#endif
24
fe35d097 25#include "wx/tokenzr.h"
5974c3cf 26#include "wx/filename.h"
e9576ca5 27
76a5e5d2
SC
28#include "wx/mac/private.h"
29
768c6e8b 30#ifndef __DARWIN__
670f9935
WS
31 #include <Navigation.h>
32 #include "PLStringFuncs.h"
768c6e8b 33#endif
5b781a67 34
f3078f07
DS
35IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
36
4d4d8bbf
SC
37// the data we need to pass to our standard file hook routine
38// includes a pointer to the dialog, a pointer to the standard
39// file reply record (so we can inspect the current selection)
40// and a copy of the "previous" file spec of the reply record
41// so we can see if the selection has changed
42
f3078f07
DS
43struct OpenUserDataRec
44{
a4f5b9b9
GD
45 int currentfilter ;
46 bool saveMode ;
2b5f62a0
VZ
47 wxArrayString name ;
48 wxArrayString extensions ;
e40298d5 49 wxArrayLong filtermactypes ;
685a634c 50 wxString defaultLocation;
a4f5b9b9 51 CFArrayRef menuitems ;
4d4d8bbf 52};
2b5f62a0 53
4d4d8bbf 54typedef struct OpenUserDataRec
a4f5b9b9 55OpenUserDataRec, *OpenUserDataRecPtr;
4d4d8bbf 56
f3078f07
DS
57static pascal void NavEventProc(
58 NavEventCallbackMessage inSelector,
59 NavCBRecPtr ioParams,
60 NavCallBackUserData ioUserData );
5b781a67 61
f3078f07 62static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
5b781a67 63
f3078f07
DS
64static pascal void NavEventProc(
65 NavEventCallbackMessage inSelector,
66 NavCBRecPtr ioParams,
67 NavCallBackUserData ioUserData )
5b781a67 68{
e40298d5 69 OpenUserDataRec * data = ( OpenUserDataRec *) ioUserData ;
f3078f07
DS
70 if (inSelector == kNavCBEvent)
71 {
685a634c 72 }
e40298d5
JS
73 else if ( inSelector == kNavCBStart )
74 {
de6185e2 75 if (data && !(data->defaultLocation).empty())
a4f5b9b9
GD
76 {
77 // Set default location for the modern Navigation APIs
78 // Apple Technical Q&A 1151
f387b80e
SC
79 FSRef theFile;
80 wxMacPathToFSRef(data->defaultLocation, &theFile);
f3078f07 81 AEDesc theLocation = { typeNull, NULL };
f387b80e 82 if (noErr == ::AECreateDesc(typeFSRef, &theFile, sizeof(FSRef), &theLocation))
a4f5b9b9
GD
83 ::NavCustomControl(ioParams->context, kNavCtlSetLocation, (void *) &theLocation);
84 }
75338075
DS
85
86 NavMenuItemSpec menuItem;
87 menuItem.version = kNavMenuItemSpecVersion;
88 menuItem.menuCreator = 'WXNG';
89 menuItem.menuType = data->currentfilter;
90 wxMacStringToPascal( data->name[data->currentfilter] , (StringPtr)(menuItem.menuItemName) ) ;
91 ::NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &menuItem);
e40298d5
JS
92 }
93 else if ( inSelector == kNavCBPopupMenuSelect )
94 {
95 NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ;
75338075
DS
96 const size_t numFilters = data->extensions.GetCount();
97
98 if ( menu->menuType < numFilters )
e40298d5
JS
99 {
100 data->currentfilter = menu->menuType ;
101 if ( data->saveMode )
102 {
103 int i = menu->menuType ;
f7aeba1c
VZ
104
105 // isolate the first extension string
106 wxString firstExtension = data->extensions[i].BeforeFirst('|').BeforeFirst(';');
107
108 wxString extension = firstExtension.AfterLast('.') ;
e40298d5 109 extension.MakeLower() ;
f65d4b83 110 wxString sfilename ;
685a634c 111
4891a3d5 112 wxMacCFStringHolder cfString( NavDialogGetSaveFileName( ioParams->context ) , false );
f65d4b83 113 sfilename = cfString.AsString() ;
f65d4b83 114
685a634c 115 int pos = sfilename.Find('.', true) ;
e40298d5
JS
116 if ( pos != wxNOT_FOUND )
117 {
118 sfilename = sfilename.Left(pos+1)+extension ;
a9412f8f 119 cfString.Assign( sfilename , wxFONTENCODING_DEFAULT ) ;
f65d4b83 120 NavDialogSetSaveFileName( ioParams->context , cfString ) ;
e40298d5
JS
121 }
122 }
f65d4b83 123 }
e40298d5 124 }
5b781a67
SC
125}
126
f3078f07 127void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
4d4d8bbf 128{
e40298d5
JS
129 myData->menuitems = NULL ;
130 myData->currentfilter = 0 ;
685a634c
DS
131 myData->saveMode = false ;
132
c3b8bf3f 133 if ( !filter.empty() )
4d4d8bbf 134 {
e40298d5
JS
135 wxString filter2(filter) ;
136 int filterIndex = 0;
137 bool isName = true ;
138 wxString current ;
f3078f07 139
e031f1df 140 for ( unsigned int i = 0; i < filter2.length() ; i++ )
e40298d5 141 {
f3078f07 142 if ( filter2.GetChar(i) == wxT('|') )
e40298d5 143 {
f3078f07
DS
144 if ( isName )
145 {
e40298d5
JS
146 myData->name.Add( current ) ;
147 }
f3078f07
DS
148 else
149 {
e40298d5
JS
150 myData->extensions.Add( current.MakeUpper() ) ;
151 ++filterIndex ;
152 }
f3078f07 153
e40298d5 154 isName = !isName ;
427ff662 155 current = wxEmptyString ;
e40298d5
JS
156 }
157 else
158 {
159 current += filter2.GetChar(i) ;
160 }
4d4d8bbf 161 }
e40298d5
JS
162 // we allow for compatibility reason to have a single filter expression (like *.*) without
163 // an explanatory text, in that case the first part is name and extension at the same time
685a634c 164
427ff662 165 wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ;
88a7a4e1 166 if ( current.empty() )
e40298d5
JS
167 myData->extensions.Add( myData->name[filterIndex] ) ;
168 else
169 myData->extensions.Add( current.MakeUpper() ) ;
170 if ( filterIndex == 0 || isName )
171 myData->name.Add( current.MakeUpper() ) ;
685a634c 172
e40298d5 173 ++filterIndex ;
685a634c 174
2b5f62a0 175 const size_t extCount = myData->extensions.GetCount();
e40298d5
JS
176 for ( size_t i = 0 ; i < extCount; i++ )
177 {
f3078f07 178 wxUint32 fileType, creator;
5974c3cf
SC
179 wxString extension = myData->extensions[i];
180
f3078f07 181 // Remove leading '*'
6264c8e2 182 if (extension.length() && (extension.GetChar(0) == '*'))
f3078f07 183 extension = extension.Mid( 1 );
5974c3cf 184
f3078f07 185 // Remove leading '.'
6264c8e2 186 if (extension.length() && (extension.GetChar(0) == '.'))
f3078f07 187 extension = extension.Mid( 1 );
88a7a4e1 188
5974c3cf 189 if (wxFileName::MacFindDefaultTypeAndCreator( extension, &fileType, &creator ))
5974c3cf 190 myData->filtermactypes.Add( (OSType)fileType );
5974c3cf 191 else
f3078f07 192 myData->filtermactypes.Add( '****' ); // We'll fail safe if it's not recognized
e40298d5
JS
193 }
194 }
4d4d8bbf 195}
bb378910 196
f65d4b83 197static Boolean CheckFile( const wxString &filename , OSType type , OpenUserDataRecPtr data)
4d4d8bbf 198{
a4f5b9b9 199 wxString file(filename) ;
9f92f6fb 200 file.MakeUpper() ;
685a634c 201
2b5f62a0 202 if ( data->extensions.GetCount() > 0 )
da2b4b7a 203 {
e40298d5
JS
204 //for ( int i = 0 ; i < data->numfilters ; ++i )
205 int i = data->currentfilter ;
427ff662 206 if ( data->extensions[i].Right(2) == wxT(".*") )
e40298d5 207 return true ;
685a634c 208
e40298d5
JS
209 {
210 if ( type == (OSType)data->filtermactypes[i] )
211 return true ;
685a634c 212
427ff662 213 wxStringTokenizer tokenizer( data->extensions[i] , wxT(";") ) ;
f3078f07 214 while ( tokenizer.HasMoreTokens() )
e40298d5
JS
215 {
216 wxString extension = tokenizer.GetNextToken() ;
217 if ( extension.GetChar(0) == '*' )
218 extension = extension.Mid(1) ;
685a634c 219
e031f1df 220 if ( file.length() >= extension.length() && extension == file.Right(extension.length() ) )
e40298d5
JS
221 return true ;
222 }
223 }
f3078f07 224
e40298d5 225 return false ;
da2b4b7a 226 }
f3078f07 227
da2b4b7a 228 return true ;
4d4d8bbf
SC
229}
230
a2b77260 231#if !TARGET_API_MAC_OSX
5fde6fcc 232static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
a4f5b9b9 233{
e40298d5
JS
234 OpenUserDataRecPtr data = (OpenUserDataRecPtr) dataPtr ;
235 // return true if this item is invisible or a file
236
237 Boolean visibleFlag;
238 Boolean folderFlag;
685a634c 239
e40298d5
JS
240 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
241 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
685a634c 242
e40298d5
JS
243 // because the semantics of the filter proc are "true means don't show
244 // it" we need to invert the result that we return
685a634c 245
e40298d5
JS
246 if ( !visibleFlag )
247 return true ;
685a634c 248
e40298d5
JS
249 if ( !folderFlag )
250 {
a4f5b9b9 251 wxString file = wxMacMakeStringFromPascal( myCInfoPBPtr->hFileInfo.ioNamePtr ) ;
f65d4b83 252 return !CheckFile( file , myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType , data ) ;
a4f5b9b9 253 }
685a634c 254
e40298d5 255 return false ;
519cb848 256}
bb378910 257#endif
519cb848
SC
258
259// end wxmac
260
f3078f07
DS
261wxFileDialog::wxFileDialog(
262 wxWindow *parent, const wxString& message,
263 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
ff3e84ff
VZ
264 long style, const wxPoint& pos, const wxSize& sz, const wxString& name)
265 : wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos, sz, name)
e9576ca5 266{
427ff662 267 wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ;
e9576ca5
SC
268}
269
f3078f07 270pascal Boolean CrossPlatformFilterCallback(
685a634c
DS
271 AEDesc *theItem,
272 void *info,
273 void *callBackUD,
f3078f07 274 NavFilterModes filterMode )
4d4d8bbf 275{
2d4e4f80
GD
276 bool display = true;
277 OpenUserDataRecPtr data = (OpenUserDataRecPtr) callBackUD ;
685a634c 278
2d4e4f80
GD
279 if (filterMode == kNavFilteringBrowserList)
280 {
281 NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*) info ;
f65d4b83 282 if ( !theInfo->isFolder )
2d4e4f80 283 {
f7aeba1c
VZ
284 AECoerceDesc (theItem, typeFSRef, theItem);
285
f387b80e
SC
286 FSRef fsref ;
287 if ( AEGetDescData (theItem, &fsref, sizeof (FSRef)) == noErr )
f65d4b83 288 {
f65d4b83 289 memcpy( &fsref , *theItem->dataHandle , sizeof(FSRef) ) ;
a2b77260 290 wxString file = wxMacFSRefToPath( &fsref ) ;
f65d4b83
SC
291 display = CheckFile( file , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
292 }
2d4e4f80
GD
293 }
294 }
685a634c 295
2d4e4f80 296 return display;
4d4d8bbf
SC
297}
298
e9576ca5
SC
299int wxFileDialog::ShowModal()
300{
a4f5b9b9
GD
301 OSErr err;
302 NavDialogCreationOptions dialogCreateOptions;
f3078f07 303
a4f5b9b9
GD
304 // set default options
305 ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions);
685a634c 306
a4f5b9b9
GD
307 // this was always unset in the old code
308 dialogCreateOptions.optionFlags &= ~kNavSelectDefaultLocation;
685a634c 309
e71800ba
SC
310 wxMacCFStringHolder message(m_message, m_font.GetEncoding());
311 dialogCreateOptions.windowTitle = message;
312
313 wxMacCFStringHolder defaultFileName(m_fileName, m_font.GetEncoding());
314 dialogCreateOptions.saveFileName = defaultFileName;
315
316
a4f5b9b9
GD
317 NavDialogRef dialog;
318 NavObjectFilterUPP navFilterUPP = NULL;
a4f5b9b9
GD
319 OpenUserDataRec myData;
320 myData.defaultLocation = m_dir;
321
75338075
DS
322 MakeUserDataRec(&myData , m_wildCard);
323 myData.currentfilter = m_filterIndex;
324 size_t numFilters = myData.extensions.GetCount();
325 if (numFilters)
326 {
327 CFMutableArrayRef popup = CFArrayCreateMutable( kCFAllocatorDefault ,
328 numFilters , &kCFTypeArrayCallBacks ) ;
329 dialogCreateOptions.popupExtension = popup ;
330 myData.menuitems = dialogCreateOptions.popupExtension ;
331 for ( size_t i = 0 ; i < numFilters ; ++i )
332 {
333 CFArrayAppendValue( popup , (CFStringRef) wxMacCFStringHolder( myData.name[i] , m_font.GetEncoding() ) ) ;
334 }
335 }
336
b014db05 337 if (HasFdFlag(wxFD_SAVE))
a4f5b9b9 338 {
75338075
DS
339 myData.saveMode = true;
340
685a634c
DS
341 dialogCreateOptions.optionFlags |= kNavDontAutoTranslate;
342 dialogCreateOptions.optionFlags |= kNavDontAddTranslateItems;
f3078f07
DS
343 if (!numFilters)
344 dialogCreateOptions.optionFlags |= kNavNoTypePopup;
685a634c
DS
345
346 // The extension is important
a485ee6f
JS
347 if (numFilters < 2)
348 dialogCreateOptions.optionFlags |= kNavPreserveSaveFileExtension;
685a634c 349
78eeb095 350#if TARGET_API_MAC_OSX
e031f1df 351 if (!(m_windowStyle & wxFD_OVERWRITE_PROMPT))
f3078f07 352 dialogCreateOptions.optionFlags |= kNavDontConfirmReplacement;
78eeb095 353#endif
f3078f07
DS
354
355 err = ::NavCreatePutFileDialog(
356 &dialogCreateOptions,
357 kNavGenericSignature, // Suppresses the 'Default' (top) menu item
358 kNavGenericSignature,
359 sStandardNavEventFilter,
360 &myData, // for defaultLocation
361 &dialog );
a4f5b9b9
GD
362 }
363 else
364 {
f3078f07 365 // let the user select bundles/programs in dialogs
c11d9cb8 366 dialogCreateOptions.optionFlags |= kNavSupportPackages;
88a7a4e1 367
a4f5b9b9 368 navFilterUPP = NewNavObjectFilterUPP(CrossPlatformFilterCallback);
f3078f07
DS
369 err = ::NavCreateGetFileDialog(
370 &dialogCreateOptions,
371 NULL, // NavTypeListHandle
372 sStandardNavEventFilter,
373 NULL, // NavPreviewUPP
374 navFilterUPP,
375 (void *) &myData, // inClientData
376 &dialog );
a4f5b9b9
GD
377 }
378
379 if (err == noErr)
380 err = ::NavDialogRun(dialog);
685a634c 381
a4f5b9b9
GD
382 // clean up filter related data, etc.
383 if (navFilterUPP)
384 ::DisposeNavObjectFilterUPP(navFilterUPP);
e71800ba 385
685a634c 386 if (err != noErr)
a4f5b9b9
GD
387 return wxID_CANCEL;
388
389 NavReplyRecord navReply;
390 err = ::NavDialogGetReply(dialog, &navReply);
685a634c 391 if (err == noErr && navReply.validRecord)
a4f5b9b9
GD
392 {
393 AEKeyword theKeyword;
394 DescType actualType;
395 Size actualSize;
396 FSRef theFSRef;
878973f1 397 wxString thePath ;
a4f5b9b9 398 long count;
f3078f07
DS
399
400 m_filterIndex = myData.currentfilter;
401 ::AECountItems( &navReply.selection, &count );
a4f5b9b9
GD
402 for (long i = 1; i <= count; ++i)
403 {
f3078f07
DS
404 err = ::AEGetNthPtr(
405 &(navReply.selection), i, typeFSRef, &theKeyword, &actualType,
406 &theFSRef, sizeof(theFSRef), &actualSize );
685a634c 407 if (err != noErr)
a4f5b9b9
GD
408 break;
409
b014db05 410 if (HasFdFlag(wxFD_SAVE))
f3078f07 411 thePath = wxMacFSRefToPath( &theFSRef, navReply.saveFileName );
685a634c 412 else
f3078f07 413 thePath = wxMacFSRefToPath( &theFSRef );
88a7a4e1 414
a449f840
DS
415 if (!thePath)
416 {
417 ::NavDisposeReply(&navReply);
418 return wxID_CANCEL;
a4f5b9b9 419 }
f3078f07 420
a4f5b9b9
GD
421 m_path = thePath;
422 m_paths.Add(m_path);
423 m_fileName = wxFileNameFromPath(m_path);
424 m_fileNames.Add(m_fileName);
425 }
f3078f07 426
a4f5b9b9
GD
427 // set these to the first hit
428 m_path = m_paths[0];
429 m_fileName = wxFileNameFromPath(m_path);
430 m_dir = wxPathOnly(m_path);
431 }
f3078f07 432
a4f5b9b9 433 ::NavDisposeReply(&navReply);
685a634c 434
a4f5b9b9 435 return (err == noErr) ? wxID_OK : wxID_CANCEL;
5b781a67 436}
72c1ba98
VZ
437
438#endif // wxUSE_FILEDLG
439