]> git.saurik.com Git - wxWidgets.git/blob - src/mac/filedlg.cpp
Don't leave behind trails from glowing OS X button (By extending invisible button...
[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 // there is no way to set this in the new API
97 #else
98 if ( data->menuitems )
99 NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &(*data->menuitems)[data->currentfilter]);
100 #endif
101 }
102 else if ( inSelector == kNavCBPopupMenuSelect )
103 {
104 NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ;
105 #if TARGET_CARBON
106 #else
107 if ( menu->menuCreator == 'WXNG' )
108 #endif
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() ;
116 wxString sfilename ;
117
118 #if TARGET_CARBON
119 wxMacCFStringHolder cfString( NavDialogGetSaveFileName( ioParams->context ) , false );
120 sfilename = cfString.AsString() ;
121 #else
122 Str255 filename ;
123 // get the current filename
124 NavCustomControl(ioParams->context, kNavCtlGetEditFileName, &filename);
125 sfilename = wxMacMakeStringFromPascal( filename ) ;
126 #endif
127
128 int pos = sfilename.Find('.',TRUE) ;
129 if ( pos != wxNOT_FOUND )
130 {
131 sfilename = sfilename.Left(pos+1)+extension ;
132 #if TARGET_CARBON
133 cfString = sfilename ;
134 NavDialogSetSaveFileName( ioParams->context , cfString ) ;
135 #else
136 wxMacStringToPascal( sfilename , filename ) ;
137 NavCustomControl(ioParams->context, kNavCtlSetEditFileName, &filename);
138 #endif
139 }
140 }
141 }
142 }
143 }
144
145 const wxChar * gfilters[] =
146 {
147 wxT("*.TXT") ,
148 wxT("*.TIF") ,
149 wxT("*.JPG") ,
150
151 NULL
152 } ;
153
154 OSType gfiltersmac[] =
155 {
156 'TEXT' ,
157 'TIFF' ,
158 'JPEG' ,
159
160 '****'
161 } ;
162
163
164
165 void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
166 {
167 myData->menuitems = NULL ;
168 myData->currentfilter = 0 ;
169 myData->saveMode = FALSE ;
170
171 if ( filter && filter[0] )
172 {
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 ;
189 current = wxEmptyString ;
190 }
191 else
192 {
193 current += filter2.GetChar(i) ;
194 }
195 }
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
199 wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ;
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
209
210 const size_t extCount = myData->extensions.GetCount();
211 for ( size_t i = 0 ; i < extCount; i++ )
212 {
213 int j ;
214 for ( j = 0 ; gfilters[j] ; j++ )
215 {
216 if ( myData->extensions[i] == gfilters[j] )
217 {
218 myData->filtermactypes.Add( gfiltersmac[j] ) ;
219 break ;
220 }
221 }
222 if( gfilters[j] == NULL )
223 {
224 myData->filtermactypes.Add( '****' ) ;
225 }
226 }
227 }
228 }
229
230 static Boolean CheckFile( const wxString &filename , OSType type , OpenUserDataRecPtr data)
231 {
232 wxString file = filename ;
233 file.MakeUpper() ;
234
235 if ( data->extensions.GetCount() > 0 )
236 {
237 //for ( int i = 0 ; i < data->numfilters ; ++i )
238 int i = data->currentfilter ;
239 if ( data->extensions[i].Right(2) == wxT(".*") )
240 return true ;
241
242 {
243 if ( type == (OSType)data->filtermactypes[i] )
244 return true ;
245
246 wxStringTokenizer tokenizer( data->extensions[i] , wxT(";") ) ;
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 ;
258 }
259 return true ;
260 }
261
262 #ifndef __DARWIN__
263 static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
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 {
282 wxString file = wxMacMakeStringFromPascal( myCInfoPBPtr->hFileInfo.ioNamePtr ) ;
283 return !CheckFile( file , myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType , data ) ;
284 }
285
286 return false ;
287 }
288 #endif
289
290 // end wxmac
291
292 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
293 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
294 long style, const wxPoint& pos)
295 :wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos)
296 {
297 wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ;
298 }
299
300 pascal Boolean CrossPlatformFilterCallback (
301 AEDesc *theItem,
302 void *info,
303 void *callBackUD,
304 NavFilterModes filterMode
305 )
306 {
307 bool display = true;
308 OpenUserDataRecPtr data = (OpenUserDataRecPtr) callBackUD ;
309
310 if (filterMode == kNavFilteringBrowserList)
311 {
312 NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*) info ;
313 if ( !theInfo->isFolder )
314 {
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 }
322 #if TARGET_CARBON
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 }
333 #endif
334 }
335 }
336
337 return display;
338 }
339
340 int wxFileDialog::ShowModal()
341 {
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
348 NavDialogOptions mNavOptions;
349 #endif
350 NavObjectFilterUPP mNavFilterUPP = NULL;
351 NavPreviewUPP mNavPreviewUPP = NULL ;
352 NavReplyRecord mNavReply;
353 AEDesc mDefaultLocation ;
354 bool mSelectDefault = false ;
355 OSStatus err = noErr ;
356 // setup dialog
357
358 mNavFilterUPP = nil;
359 mNavPreviewUPP = nil;
360 mSelectDefault = false;
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
375 // Set default location, the location
376 // that's displayed when the dialog
377 // first appears
378
379 FSSpec location ;
380 wxMacFilename2FSSpec( m_dir , &location ) ;
381
382 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
383
384 if ( mDefaultLocation.dataHandle ) {
385
386 if (mSelectDefault) {
387 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
388 } else {
389 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
390 }
391 }
392 #endif
393
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
405 // zero all data
406
407 m_path = wxEmptyString ;
408 m_fileName = wxEmptyString ;
409 m_paths.Empty();
410 m_fileNames.Empty();
411
412 OpenUserDataRec myData;
413 MakeUserDataRec( &myData , m_wildCard ) ;
414 myData.currentfilter = m_filterIndex ;
415 if ( myData.extensions.GetCount() > 0 )
416 {
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
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' ;
433 // TODO : according to the new docs -1 to 10 are reserved for the OS
434 (*mNavOptions.popupExtension)[i].menuType = i ;
435 wxMacStringToPascal( myData.name[i] , (StringPtr)(*mNavOptions.popupExtension)[i].menuItemName ) ;
436 }
437 #endif
438 }
439 if ( m_dialogStyle & wxSAVE )
440 {
441 myData.saveMode = true ;
442
443 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
444 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
445
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
459 err = ::NavPutFile(
460 &mDefaultLocation,
461 &mNavReply,
462 &mNavOptions,
463 sStandardNavEventFilter ,
464 NULL,
465 kNavGenericSignature,
466 &myData); // User Data
467 #endif
468 m_filterIndex = myData.currentfilter ;
469 }
470 else
471 {
472 myData.saveMode = false ;
473
474 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
475 if ( m_dialogStyle & wxMULTIPLE )
476 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
477 else
478 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
479
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
493 err = ::NavGetFile(
494 &mDefaultLocation,
495 &mNavReply,
496 &mNavOptions,
497 sStandardNavEventFilter ,
498 mNavPreviewUPP,
499 mNavFilterUPP,
500 NULL ,
501 &myData);
502 #endif
503 m_filterIndex = myData.currentfilter ;
504 }
505
506 DisposeNavObjectFilterUPP(mNavFilterUPP);
507 if ( mDefaultLocation.dataHandle != nil )
508 {
509 ::AEDisposeDesc(&mDefaultLocation);
510 }
511
512 if ( (err != noErr) && (err != userCanceledErr) ) {
513 return wxID_CANCEL ;
514 }
515
516 if (mNavReply.validRecord) {
517
518 FSSpec outFileSpec ;
519 AEDesc specDesc ;
520 AEKeyword keyWord ;
521
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 ) {
528 m_path = wxT("") ;
529 return wxID_CANCEL ;
530 }
531 outFileSpec = **(FSSpec**) specDesc.dataHandle;
532 if (specDesc.dataHandle != nil) {
533 ::AEDisposeDesc(&specDesc);
534 }
535 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
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
543 m_paths.Add( m_path ) ;
544 m_fileName = wxFileNameFromPath(m_path);
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 ) ;
552 #if TARGET_CARBON
553 if ( navDialogRef )
554 NavDialogDispose( navDialogRef ) ;
555 #endif
556 return wxID_OK ;
557 }
558 #if TARGET_CARBON
559 if ( navDialogRef )
560 NavDialogDispose( navDialogRef ) ;
561 #endif
562 return wxID_CANCEL;
563 }
564