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