1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxFileDialog 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13 #pragma implementation "filedlg.h" 
  19 #include "wx/dialog.h" 
  20 #include "wx/filedlg.h" 
  22 #include "wx/tokenzr.h" 
  23 #include "wx/filename.h" 
  26   #include "PLStringFuncs.h" 
  29 #if !USE_SHARED_LIBRARY 
  30 IMPLEMENT_CLASS(wxFileDialog
, wxFileDialogBase
) 
  35 #include "wx/mac/private.h" 
  37 #include <Navigation.h> 
  40 #  include "MoreFilesX.h" 
  42 #  include "MoreFiles.h" 
  43 #  include "MoreFilesExtras.h" 
  46 extern bool gUseNavServices 
; 
  48 // the data we need to pass to our standard file hook routine 
  49 // includes a pointer to the dialog, a pointer to the standard 
  50 // file reply record (so we can inspect the current selection) 
  51 // and a copy of the "previous" file spec of the reply record 
  52 // so we can see if the selection has changed 
  54 struct OpenUserDataRec 
{ 
  58   wxArrayString      extensions 
; 
  59   wxArrayLong        filtermactypes 
; 
  60   wxString           defaultLocation
; 
  62   CFArrayRef         menuitems 
; 
  64   NavMenuItemSpecArrayHandle menuitems 
; 
  68 typedef struct OpenUserDataRec
 
  69 OpenUserDataRec
, *OpenUserDataRecPtr
; 
  71 static pascal void    NavEventProc( 
  72                                 NavEventCallbackMessage        inSelector
, 
  74                                 NavCallBackUserData            ioUserData
); 
  77         static NavEventUPP    sStandardNavEventFilter 
= NewNavEventUPP(NavEventProc
); 
  79         static NavEventUPP    sStandardNavEventFilter 
= NewNavEventProc(NavEventProc
); 
  84     NavEventCallbackMessage        inSelector
, 
  86     NavCallBackUserData    ioUserData    
) 
  88     OpenUserDataRec 
* data 
= ( OpenUserDataRec 
*) ioUserData 
; 
  89     if (inSelector 
== kNavCBEvent
) { 
  92         wxTheApp
->MacHandleOneEvent(ioParams
->eventData
.eventDataParms
.event
); 
  95     else if ( inSelector 
== kNavCBStart 
) 
  98         if (data 
&& !(data
->defaultLocation
).IsEmpty()) 
 100             // Set default location for the modern Navigation APIs 
 101             // Apple Technical Q&A 1151 
 103             wxMacFilename2FSSpec(data
->defaultLocation
, &theFSSpec
); 
 104             AEDesc theLocation 
= {typeNull
, NULL
}; 
 105             if (noErr 
== ::AECreateDesc(typeFSS
, &theFSSpec
, sizeof(FSSpec
), &theLocation
)) 
 106                 ::NavCustomControl(ioParams
->context
, kNavCtlSetLocation
, (void *) &theLocation
); 
 109         if ( data
->menuitems 
) 
 110             NavCustomControl(ioParams
->context
, kNavCtlSelectCustomType
, &(*data
->menuitems
)[data
->currentfilter
]); 
 113     else if ( inSelector 
== kNavCBPopupMenuSelect 
) 
 115         NavMenuItemSpec 
* menu 
= (NavMenuItemSpec 
*) ioParams
->eventData
.eventDataParms
.param 
; 
 118         if ( menu
->menuCreator 
== 'WXNG' ) 
 121             data
->currentfilter 
= menu
->menuType 
; 
 122             if ( data
->saveMode 
) 
 124                 int i 
= menu
->menuType 
; 
 125                 wxString extension 
=  data
->extensions
[i
].AfterLast('.') ; 
 126                 extension
.MakeLower() ; 
 130                 wxMacCFStringHolder 
cfString( NavDialogGetSaveFileName( ioParams
->context 
) , false  ); 
 131                 sfilename 
= cfString
.AsString() ; 
 134                 // get the current filename 
 135                 NavCustomControl(ioParams
->context
, kNavCtlGetEditFileName
, &filename
); 
 136                 sfilename 
= wxMacMakeStringFromPascal( filename 
) ; 
 139                 int pos 
= sfilename
.Find('.', true) ; 
 140                 if ( pos 
!= wxNOT_FOUND 
) 
 142                     sfilename 
= sfilename
.Left(pos
+1)+extension 
; 
 144                     cfString
.Assign( sfilename 
, wxFONTENCODING_DEFAULT 
) ; 
 145                     NavDialogSetSaveFileName( ioParams
->context 
, cfString 
) ; 
 147                     wxMacStringToPascal( sfilename 
, filename 
) ; 
 148                     NavCustomControl(ioParams
->context
, kNavCtlSetEditFileName
, &filename
); 
 157 void MakeUserDataRec(OpenUserDataRec    
*myData 
, const wxString
& filter 
) 
 159     myData
->menuitems 
= NULL 
; 
 160     myData
->currentfilter 
= 0 ; 
 161     myData
->saveMode 
= false ; 
 163     if ( filter 
&& filter
[0] ) 
 165         wxString 
filter2(filter
) ; 
 169         for( unsigned int i 
= 0; i 
< filter2
.Len() ; i
++ ) 
 171             if( filter2
.GetChar(i
) == wxT('|') ) 
 174                     myData
->name
.Add( current 
) ; 
 177                     myData
->extensions
.Add( current
.MakeUpper() ) ; 
 181                 current 
= wxEmptyString 
; 
 185                 current 
+= filter2
.GetChar(i
) ; 
 188         // we allow for compatibility reason to have a single filter expression (like *.*) without 
 189         // an explanatory text, in that case the first part is name and extension at the same time 
 191         wxASSERT_MSG( filterIndex 
== 0 || !isName 
, wxT("incorrect format of format string") ) ; 
 192         if ( current
.IsEmpty() ) 
 193             myData
->extensions
.Add( myData
->name
[filterIndex
] ) ; 
 195             myData
->extensions
.Add( current
.MakeUpper() ) ; 
 196         if ( filterIndex 
== 0 || isName 
) 
 197             myData
->name
.Add( current
.MakeUpper() ) ; 
 201         const size_t extCount 
= myData
->extensions
.GetCount(); 
 202         for ( size_t i 
= 0 ; i 
< extCount
; i
++ ) 
 206             wxString extension 
= myData
->extensions
[i
]; 
 208             if (extension
.GetChar(0) == '*') 
 209                 extension 
= extension
.Mid(1);   // Remove leading * 
 211             if (extension
.GetChar(0) == '.') 
 213                 extension 
= extension
.Mid(1);   // Remove leading . 
 216             if (wxFileName::MacFindDefaultTypeAndCreator( extension
, &fileType
, &creator 
)) 
 218                 myData
->filtermactypes
.Add( (OSType
)fileType 
); 
 222                 myData
->filtermactypes
.Add( '****' ) ;          // We'll fail safe if it's not recognized 
 228 static Boolean 
CheckFile( const wxString 
&filename 
, OSType type 
, OpenUserDataRecPtr data
) 
 230     wxString 
file(filename
) ; 
 233     if ( data
->extensions
.GetCount() > 0 ) 
 235         //for ( int i = 0 ; i < data->numfilters ; ++i ) 
 236         int i 
= data
->currentfilter 
; 
 237         if ( data
->extensions
[i
].Right(2) == wxT(".*") ) 
 241             if ( type 
== (OSType
)data
->filtermactypes
[i
] ) 
 244             wxStringTokenizer 
tokenizer( data
->extensions
[i
] , wxT(";") ) ; 
 245             while( tokenizer
.HasMoreTokens() ) 
 247                 wxString extension 
= tokenizer
.GetNextToken() ; 
 248                 if ( extension
.GetChar(0) == '*' ) 
 249                     extension 
= extension
.Mid(1) ; 
 251                 if ( file
.Len() >= extension
.Len() && extension 
== file
.Right(extension
.Len() ) ) 
 261 static pascal Boolean 
CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr
, void *dataPtr
) 
 263     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) dataPtr 
; 
 264     // return true if this item is invisible or a file 
 269     visibleFlag 
= ! (myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdFlags 
& kIsInvisible
); 
 270     folderFlag 
= (myCInfoPBPtr
->hFileInfo
.ioFlAttrib 
& 0x10); 
 272     // because the semantics of the filter proc are "true means don't show 
 273     // it" we need to invert the result that we return 
 280         wxString file 
= wxMacMakeStringFromPascal( myCInfoPBPtr
->hFileInfo
.ioNamePtr 
) ; 
 281         return !CheckFile( file 
, myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdType 
, data 
) ; 
 290 wxFileDialog::wxFileDialog(wxWindow 
*parent
, const wxString
& message
, 
 291         const wxString
& defaultDir
, const wxString
& defaultFileName
, const wxString
& wildCard
, 
 292         long style
, const wxPoint
& pos
) 
 293              :wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
, wildCard
, style
, pos
) 
 295     wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ; 
 298 pascal Boolean 
CrossPlatformFilterCallback ( 
 302     NavFilterModes filterMode
 
 306     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) callBackUD 
; 
 308     if (filterMode 
== kNavFilteringBrowserList
) 
 310         NavFileOrFolderInfo
* theInfo 
= (NavFileOrFolderInfo
*) info 
; 
 311         if ( !theInfo
->isFolder 
) 
 313             if (theItem
->descriptorType 
== typeFSS 
) 
 316                 memcpy( &spec 
, *theItem
->dataHandle 
, sizeof(FSSpec
) ) ; 
 317                 wxString file 
= wxMacMakeStringFromPascal( spec
.name 
) ; 
 318                 display 
= CheckFile( file 
, theInfo
->fileAndFolder
.fileInfo
.finderInfo
.fdType 
, data 
) ; 
 321             else if ( theItem
->descriptorType 
== typeFSRef 
) 
 324                 memcpy( &fsref 
, *theItem
->dataHandle 
, sizeof(FSRef
) ) ; 
 329                 fullURLRef 
= ::CFURLCreateFromFSRef(NULL
, &fsref
); 
 331                 CFURLPathStyle pathstyle 
= kCFURLPOSIXPathStyle
; 
 333                 CFURLPathStyle pathstyle 
= kCFURLHFSPathStyle
; 
 335                 CFStringRef cfString 
= CFURLCopyFileSystemPath(fullURLRef
, pathstyle
); 
 336                 ::CFRelease( fullURLRef 
) ; 
 337                 wxString file 
= wxMacCFStringHolder(cfString
).AsString(wxFont::GetDefaultEncoding()); 
 339                 display 
= CheckFile( file 
, theInfo
->fileAndFolder
.fileInfo
.finderInfo
.fdType 
, data 
) ; 
 348 int wxFileDialog::ShowModal() 
 352     NavDialogCreationOptions dialogCreateOptions
; 
 353     // set default options 
 354     ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions
); 
 356     // this was always unset in the old code 
 357     dialogCreateOptions
.optionFlags 
&= ~kNavSelectDefaultLocation
; 
 359     wxMacCFStringHolder 
message(m_message
, m_font
.GetEncoding()); 
 360     dialogCreateOptions
.windowTitle 
= message
; 
 362     wxMacCFStringHolder 
defaultFileName(m_fileName
, m_font
.GetEncoding()); 
 363     dialogCreateOptions
.saveFileName 
= defaultFileName
; 
 367     NavObjectFilterUPP navFilterUPP 
= NULL
; 
 368     CFArrayRef cfArray 
= NULL
; // for popupExtension 
 369     OpenUserDataRec myData
; 
 370     myData
.defaultLocation 
= m_dir
; 
 372     if (m_dialogStyle 
& wxSAVE
) 
 374         dialogCreateOptions
.optionFlags 
|= kNavNoTypePopup
; 
 375         dialogCreateOptions
.optionFlags 
|= kNavDontAutoTranslate
; 
 376         dialogCreateOptions
.optionFlags 
|= kNavDontAddTranslateItems
; 
 378         // The extension is important 
 379         dialogCreateOptions
.optionFlags 
|= kNavPreserveSaveFileExtension
; 
 381         err 
= ::NavCreatePutFileDialog(&dialogCreateOptions
, 
 384                                        sStandardNavEventFilter
, 
 385                                        &myData
, // for defaultLocation 
 390         MakeUserDataRec(&myData 
, m_wildCard
); 
 391         size_t numfilters 
= myData
.extensions
.GetCount(); 
 394             CFMutableArrayRef popup 
= CFArrayCreateMutable( kCFAllocatorDefault 
, 
 395                 numfilters 
, &kCFTypeArrayCallBacks 
) ; 
 396             dialogCreateOptions
.popupExtension 
= popup 
; 
 397             myData
.menuitems 
= dialogCreateOptions
.popupExtension 
; 
 398             for ( size_t i 
= 0 ; i 
< numfilters 
; ++i 
) 
 400                 CFArrayAppendValue( popup 
, (CFStringRef
) wxMacCFStringHolder( myData
.name
[i
] , m_font
.GetEncoding() ) ) ; 
 404         navFilterUPP 
= NewNavObjectFilterUPP(CrossPlatformFilterCallback
); 
 405         err 
= ::NavCreateGetFileDialog(&dialogCreateOptions
, 
 406                                        NULL
, // NavTypeListHandle 
 407                                        sStandardNavEventFilter
, 
 408                                        NULL
, // NavPreviewUPP 
 410                                        (void *) &myData
, // inClientData 
 415         err 
= ::NavDialogRun(dialog
); 
 417     // clean up filter related data, etc. 
 419         ::DisposeNavObjectFilterUPP(navFilterUPP
); 
 421         ::CFRelease(cfArray
); 
 426     NavReplyRecord navReply
; 
 427     err 
= ::NavDialogGetReply(dialog
, &navReply
); 
 428     if (err 
== noErr 
&& navReply
.validRecord
) 
 430         AEKeyword   theKeyword
; 
 436         ::AECountItems(&navReply
.selection 
, &count
); 
 437         for (long i 
= 1; i 
<= count
; ++i
) 
 439             err 
= ::AEGetNthPtr(&(navReply
.selection
), i
, typeFSRef
, &theKeyword
, &actualType
, 
 440                                 &theFSRef
, sizeof(theFSRef
), &actualSize
); 
 445             if (m_dialogStyle 
& wxSAVE
) 
 447                 CFURLRef parentURLRef 
= ::CFURLCreateFromFSRef(NULL
, &theFSRef
); 
 452                         ::CFURLCreateCopyAppendingPathComponent(NULL
, 
 454                                                                 navReply
.saveFileName
, 
 456                     ::CFRelease(parentURLRef
); 
 461                 fullURLRef 
= ::CFURLCreateFromFSRef(NULL
, &theFSRef
); 
 464             CFURLPathStyle pathstyle 
= kCFURLPOSIXPathStyle
; 
 466             CFURLPathStyle pathstyle 
= kCFURLHFSPathStyle
; 
 468             CFStringRef cfString 
= CFURLCopyFileSystemPath(fullURLRef
, pathstyle
); 
 469             thePath 
= wxMacCFStringHolder(cfString
).AsString(m_font
.GetEncoding()); 
 472                 ::NavDisposeReply(&navReply
); 
 477             m_fileName 
= wxFileNameFromPath(m_path
); 
 478             m_fileNames
.Add(m_fileName
); 
 480         // set these to the first hit 
 482         m_fileName 
= wxFileNameFromPath(m_path
); 
 483         m_dir 
= wxPathOnly(m_path
); 
 485     ::NavDisposeReply(&navReply
); 
 487     return (err 
== noErr
) ? wxID_OK 
: wxID_CANCEL
; 
 488 #else // TARGET_CARBON 
 490     NavDialogOptions           mNavOptions
; 
 491     NavObjectFilterUPP           mNavFilterUPP 
= NULL
; 
 492     NavPreviewUPP           mNavPreviewUPP 
= NULL 
; 
 493     NavReplyRecord           mNavReply
; 
 494     AEDesc               mDefaultLocation 
; 
 495     bool               mSelectDefault 
= false ; 
 496     OSStatus            err 
= noErr 
; 
 500     mNavPreviewUPP    
= nil
; 
 501     mSelectDefault    
= false; 
 502     mDefaultLocation
.descriptorType 
= typeNull
; 
 503     mDefaultLocation
.dataHandle     
= nil
; 
 505     NavGetDefaultDialogOptions(&mNavOptions
); 
 506     wxMacStringToPascal( m_message 
, (StringPtr
)mNavOptions
.message 
) ; 
 507     wxMacStringToPascal( m_fileName 
, (StringPtr
)mNavOptions
.savedFileName 
) ; 
 509     // Set default location, the location 
 510     //   that's displayed when the dialog 
 514     wxMacFilename2FSSpec( m_dir 
, &location 
) ; 
 516     err 
= ::AECreateDesc(typeFSS
, &location
, sizeof(FSSpec
), &mDefaultLocation 
); 
 518     if ( mDefaultLocation
.dataHandle 
) 
 522             mNavOptions
.dialogOptionFlags 
|= kNavSelectDefaultLocation
; 
 524             mNavOptions
.dialogOptionFlags 
&= ~kNavSelectDefaultLocation
; 
 528     memset( &mNavReply 
, 0 , sizeof( mNavReply 
) ) ; 
 529     mNavReply
.validRecord 
= false; 
 530     mNavReply
.replacing 
= false; 
 531     mNavReply
.isStationery 
= false; 
 532     mNavReply
.translationNeeded 
= false; 
 533     mNavReply
.selection
.descriptorType 
= typeNull
; 
 534     mNavReply
.selection
.dataHandle 
= nil
; 
 535     mNavReply
.keyScript 
= smSystemScript
; 
 536     mNavReply
.fileTranslation 
= nil
; 
 537     mNavReply
.version 
= kNavReplyRecordVersion 
; 
 541     m_path 
= wxEmptyString 
; 
 542     m_fileName 
= wxEmptyString 
; 
 546     OpenUserDataRec            myData
; 
 547     MakeUserDataRec( &myData 
, m_wildCard 
) ; 
 548     myData
.currentfilter 
= m_filterIndex 
; 
 549     if ( myData
.extensions
.GetCount() > 0 ) 
 551         mNavOptions
.popupExtension 
= (NavMenuItemSpecArrayHandle
) NewHandle( sizeof( NavMenuItemSpec 
) * myData
.extensions
.GetCount() ) ; 
 552         myData
.menuitems 
= mNavOptions
.popupExtension 
; 
 553         for ( size_t i 
= 0 ; i 
< myData
.extensions
.GetCount() ; ++i 
) 
 555             (*mNavOptions
.popupExtension
)[i
].version     
= kNavMenuItemSpecVersion 
; 
 556             (*mNavOptions
.popupExtension
)[i
].menuCreator 
= 'WXNG' ; 
 557             // TODO : according to the new docs  -1 to 10 are reserved for the OS 
 558             (*mNavOptions
.popupExtension
)[i
].menuType    
= i 
; 
 559             wxMacStringToPascal( myData
.name
[i
] , (StringPtr
)(*mNavOptions
.popupExtension
)[i
].menuItemName 
) ; 
 562     if ( m_dialogStyle 
& wxSAVE 
) 
 564         myData
.saveMode 
= true ; 
 566         mNavOptions
.dialogOptionFlags 
|= kNavDontAutoTranslate 
; 
 567         mNavOptions
.dialogOptionFlags 
|= kNavDontAddTranslateItems 
; 
 573                            sStandardNavEventFilter 
, 
 575                            kNavGenericSignature
, 
 576                            &myData
);                    // User Data 
 577         m_filterIndex 
= myData
.currentfilter 
; 
 581         myData
.saveMode 
= false ; 
 583         mNavFilterUPP 
= NewNavObjectFilterUPP( CrossPlatformFilterCallback 
) ; 
 584         if ( m_dialogStyle 
& wxMULTIPLE 
) 
 585             mNavOptions
.dialogOptionFlags 
|= kNavAllowMultipleFiles 
; 
 587             mNavOptions
.dialogOptionFlags 
&= ~kNavAllowMultipleFiles 
; 
 593                            sStandardNavEventFilter 
, 
 598         m_filterIndex 
= myData
.currentfilter 
; 
 601     DisposeNavObjectFilterUPP(mNavFilterUPP
); 
 602     if ( mDefaultLocation
.dataHandle 
!= nil 
) 
 604         ::AEDisposeDesc(&mDefaultLocation
); 
 607     if ( (err 
!= noErr
) && (err 
!= userCanceledErr
) ) { 
 611     if (mNavReply
.validRecord
) 
 618         ::AECountItems( &mNavReply
.selection 
, &count 
) ; 
 619         for ( long i 
= 1 ; i 
<= count 
; ++i 
) 
 621             OSErr err 
= ::AEGetNthDesc( &mNavReply
.selection 
, i 
, typeFSS
, &keyWord 
, &specDesc
); 
 627             outFileSpec 
= **(FSSpec
**) specDesc
.dataHandle
; 
 628             if (specDesc
.dataHandle 
!= nil
) { 
 629                 ::AEDisposeDesc(&specDesc
); 
 631             m_path 
= wxMacFSSpec2MacFilename( &outFileSpec 
) ; 
 633             m_paths
.Add( m_path 
) ; 
 634             m_fileName 
= wxFileNameFromPath(m_path
); 
 635             m_fileNames
.Add(m_fileName
); 
 637         // set these to the first hit 
 638         m_path 
= m_paths
[ 0 ] ; 
 639         m_fileName 
= wxFileNameFromPath(m_path
); 
 640         m_dir 
= wxPathOnly(m_path
); 
 641         NavDisposeReply( &mNavReply 
) ; 
 645 #endif // TARGET_CARBON