]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/filedlg.cpp
fix for preserving the clip rgn (control redraws missing after switch to faster redra...
[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
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 }
613b6bb9 321 #if TARGET_CARBON
f65d4b83
SC
322 else if ( theItem->descriptorType == typeFSRef )
323 {
324 FSRef fsref ;
325 memcpy( &fsref , *theItem->dataHandle , sizeof(FSRef) ) ;
326 wxString file ;
327 const short maxpath = 1024 ;
328 FSRefMakePath( &fsref , (UInt8*) file.GetWriteBuf(maxpath+1),maxpath) ;
329 file.UngetWriteBuf() ;
330 display = CheckFile( file , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
331 }
613b6bb9 332#endif
2d4e4f80
GD
333 }
334 }
335
336 return display;
4d4d8bbf
SC
337}
338
e9576ca5
SC
339int wxFileDialog::ShowModal()
340{
f65d4b83
SC
341#if TARGET_CARBON
342 NavDialogCreationOptions mNavOptions;
343 NavDialogRef navDialogRef = NULL ;
344// since the same field has been renamed ...
345#define dialogOptionFlags optionFlags
346#else
e40298d5 347 NavDialogOptions mNavOptions;
f65d4b83 348#endif
e40298d5
JS
349 NavObjectFilterUPP mNavFilterUPP = NULL;
350 NavPreviewUPP mNavPreviewUPP = NULL ;
351 NavReplyRecord mNavReply;
352 AEDesc mDefaultLocation ;
353 bool mSelectDefault = false ;
f65d4b83 354 OSStatus err = noErr ;
e40298d5
JS
355 // setup dialog
356
e40298d5
JS
357 mNavFilterUPP = nil;
358 mNavPreviewUPP = nil;
359 mSelectDefault = false;
f65d4b83
SC
360 mDefaultLocation.descriptorType = typeNull;
361 mDefaultLocation.dataHandle = nil;
362
363#if TARGET_CARBON
364 NavGetDefaultDialogCreationOptions( &mNavOptions ) ;
365 wxMacCFStringHolder cfMessage(m_message) ;
366 wxMacCFStringHolder cfFileName(m_fileName) ;
367 mNavOptions.saveFileName = cfFileName ;
368 mNavOptions.message = cfMessage ;
369#else
370 NavGetDefaultDialogOptions(&mNavOptions);
371 wxMacStringToPascal( m_message , (StringPtr)mNavOptions.message ) ;
372 wxMacStringToPascal( m_fileName , (StringPtr)mNavOptions.savedFileName ) ;
373
2d4e4f80
GD
374 // Set default location, the location
375 // that's displayed when the dialog
376 // first appears
e40298d5 377
2d4e4f80
GD
378 FSSpec location ;
379 wxMacFilename2FSSpec( m_dir , &location ) ;
e40298d5 380
2d4e4f80 381 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
5b781a67 382
2d4e4f80 383 if ( mDefaultLocation.dataHandle ) {
e40298d5 384
2d4e4f80
GD
385 if (mSelectDefault) {
386 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
387 } else {
388 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
389 }
390 }
f65d4b83 391#endif
5b781a67 392
f65d4b83
SC
393 memset( &mNavReply , 0 , sizeof( mNavReply ) ) ;
394 mNavReply.validRecord = false;
395 mNavReply.replacing = false;
396 mNavReply.isStationery = false;
397 mNavReply.translationNeeded = false;
398 mNavReply.selection.descriptorType = typeNull;
399 mNavReply.selection.dataHandle = nil;
400 mNavReply.keyScript = smSystemScript;
401 mNavReply.fileTranslation = nil;
402 mNavReply.version = kNavReplyRecordVersion ;
403
7ed2b47b
SC
404 // zero all data
405
406 m_path = wxEmptyString ;
407 m_fileName = wxEmptyString ;
408 m_paths.Empty();
409 m_fileNames.Empty();
410
e40298d5 411 OpenUserDataRec myData;
2d4e4f80
GD
412 MakeUserDataRec( &myData , m_wildCard ) ;
413 myData.currentfilter = m_filterIndex ;
414 if ( myData.extensions.GetCount() > 0 )
415 {
f65d4b83
SC
416#if TARGET_CARBON
417 CFMutableArrayRef popup = CFArrayCreateMutable( kCFAllocatorDefault ,
418 myData.extensions.GetCount() , &kCFTypeArrayCallBacks ) ;
419 mNavOptions.popupExtension = popup ;
420 myData.menuitems = mNavOptions.popupExtension ;
421 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
422 {
423 CFArrayAppendValue( popup , (CFStringRef) wxMacCFStringHolder( myData.name[i] ) ) ;
424 }
425#else
2d4e4f80
GD
426 mNavOptions.popupExtension = (NavMenuItemSpecArrayHandle) NewHandle( sizeof( NavMenuItemSpec ) * myData.extensions.GetCount() ) ;
427 myData.menuitems = mNavOptions.popupExtension ;
428 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
429 {
430 (*mNavOptions.popupExtension)[i].version = kNavMenuItemSpecVersion ;
431 (*mNavOptions.popupExtension)[i].menuCreator = 'WXNG' ;
f65d4b83 432 // TODO : according to the new docs -1 to 10 are reserved for the OS
2d4e4f80 433 (*mNavOptions.popupExtension)[i].menuType = i ;
427ff662 434 wxMacStringToPascal( myData.name[i] , (StringPtr)(*mNavOptions.popupExtension)[i].menuItemName ) ;
2d4e4f80 435 }
f65d4b83 436#endif
2d4e4f80
GD
437 }
438 if ( m_dialogStyle & wxSAVE )
439 {
440 myData.saveMode = true ;
2b5f62a0 441
2d4e4f80
GD
442 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
443 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
e40298d5 444
f65d4b83
SC
445#if TARGET_CARBON
446 err = NavCreatePutFileDialog( &mNavOptions , NULL , kNavGenericSignature , sStandardNavEventFilter ,
447 &myData , &navDialogRef ) ;
448 if ( err == noErr )
449 {
450 err = NavDialogRun( navDialogRef ) ;
451 NavUserAction userAction = NavDialogGetUserAction( navDialogRef ) ;
452 if ( userAction != kNavUserActionCancel && userAction != kNavUserActionNone )
453 {
454 NavDialogGetReply( navDialogRef, &mNavReply ) ;
455 }
456 }
457#else
2d4e4f80
GD
458 err = ::NavPutFile(
459 &mDefaultLocation,
460 &mNavReply,
461 &mNavOptions,
462 sStandardNavEventFilter ,
463 NULL,
464 kNavGenericSignature,
e40298d5 465 &myData); // User Data
f65d4b83 466#endif
2d4e4f80
GD
467 m_filterIndex = myData.currentfilter ;
468 }
469 else
470 {
471 myData.saveMode = false ;
4d4d8bbf 472
2d4e4f80
GD
473 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
474 if ( m_dialogStyle & wxMULTIPLE )
475 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
476 else
477 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
e40298d5 478
f65d4b83
SC
479#if TARGET_CARBON
480 err = NavCreateGetFileDialog( &mNavOptions , NULL , sStandardNavEventFilter ,
481 mNavPreviewUPP , mNavFilterUPP , &myData , &navDialogRef ) ;
482 if ( err == noErr )
483 {
484 err = NavDialogRun( navDialogRef ) ;
485 NavUserAction userAction = NavDialogGetUserAction( navDialogRef ) ;
486 if ( userAction != kNavUserActionCancel && userAction != kNavUserActionNone )
487 {
488 NavDialogGetReply( navDialogRef, &mNavReply ) ;
489 }
490 }
491#else
2d4e4f80
GD
492 err = ::NavGetFile(
493 &mDefaultLocation,
494 &mNavReply,
495 &mNavOptions,
496 sStandardNavEventFilter ,
497 mNavPreviewUPP,
498 mNavFilterUPP,
499 NULL ,
500 &myData);
f65d4b83 501#endif
2d4e4f80
GD
502 m_filterIndex = myData.currentfilter ;
503 }
e40298d5 504
2d4e4f80
GD
505 DisposeNavObjectFilterUPP(mNavFilterUPP);
506 if ( mDefaultLocation.dataHandle != nil )
507 {
508 ::AEDisposeDesc(&mDefaultLocation);
509 }
e40298d5 510
2d4e4f80 511 if ( (err != noErr) && (err != userCanceledErr) ) {
2d4e4f80
GD
512 return wxID_CANCEL ;
513 }
5b781a67 514
2d4e4f80 515 if (mNavReply.validRecord) {
e40298d5 516
2d4e4f80
GD
517 FSSpec outFileSpec ;
518 AEDesc specDesc ;
519 AEKeyword keyWord ;
e40298d5 520
2d4e4f80
GD
521 long count ;
522 ::AECountItems( &mNavReply.selection , &count ) ;
523 for ( long i = 1 ; i <= count ; ++i )
524 {
525 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, &keyWord , &specDesc);
526 if ( err != noErr ) {
427ff662 527 m_path = wxT("") ;
2d4e4f80 528 return wxID_CANCEL ;
e40298d5 529 }
2d4e4f80
GD
530 outFileSpec = **(FSSpec**) specDesc.dataHandle;
531 if (specDesc.dataHandle != nil) {
532 ::AEDisposeDesc(&specDesc);
533 }
534 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
f65d4b83
SC
535#if TARGET_CARBON
536 if ( m_dialogStyle & wxSAVE )
537 {
538 wxMacCFStringHolder cfString = NavDialogGetSaveFileName( navDialogRef ) ;
539 m_path += wxFILE_SEP_PATH + cfString.AsString() ;
540 }
541#endif
2d4e4f80 542 m_paths.Add( m_path ) ;
24fe8dc7 543 m_fileName = wxFileNameFromPath(m_path);
2d4e4f80
GD
544 m_fileNames.Add(m_fileName);
545 }
546 // set these to the first hit
547 m_path = m_paths[ 0 ] ;
548 m_fileName = wxFileNameFromPath(m_path);
549 m_dir = wxPathOnly(m_path);
550 NavDisposeReply( &mNavReply ) ;
613b6bb9 551#if TARGET_CARBON
f65d4b83
SC
552 if ( navDialogRef )
553 NavDialogDispose( navDialogRef ) ;
613b6bb9 554#endif
2d4e4f80
GD
555 return wxID_OK ;
556 }
613b6bb9 557#if TARGET_CARBON
f65d4b83
SC
558 if ( navDialogRef )
559 NavDialogDispose( navDialogRef ) ;
613b6bb9 560#endif
2d4e4f80 561 return wxID_CANCEL;
5b781a67 562}
e9576ca5 563