1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/carbon/filedlg.cpp 
   3 // Purpose:     wxFileDialog 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  16 #include "wx/filedlg.h" 
  22     #include "wx/dialog.h" 
  25 #include "wx/tokenzr.h" 
  26 #include "wx/filename.h" 
  28 #include "wx/mac/private.h" 
  31     #include <Navigation.h> 
  32     #include "PLStringFuncs.h" 
  35 IMPLEMENT_CLASS(wxFileDialog
, wxFileDialogBase
) 
  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 
  43 struct OpenUserDataRec
 
  48   wxArrayString      extensions 
; 
  49   wxArrayLong        filtermactypes 
; 
  50   wxString           defaultLocation
; 
  51   CFArrayRef         menuitems 
; 
  54 typedef struct OpenUserDataRec
 
  55 OpenUserDataRec
, *OpenUserDataRecPtr
; 
  57 static pascal void NavEventProc( 
  58     NavEventCallbackMessage inSelector
, 
  60     NavCallBackUserData ioUserData 
); 
  62 static NavEventUPP sStandardNavEventFilter 
= NewNavEventUPP(NavEventProc
); 
  64 static pascal void NavEventProc( 
  65     NavEventCallbackMessage inSelector
, 
  67     NavCallBackUserData ioUserData 
) 
  69     OpenUserDataRec 
* data 
= ( OpenUserDataRec 
*) ioUserData 
; 
  70     if (inSelector 
== kNavCBEvent
) 
  73     else if ( inSelector 
== kNavCBStart 
) 
  75         if (data 
&& !(data
->defaultLocation
).empty()) 
  77             // Set default location for the modern Navigation APIs 
  78             // Apple Technical Q&A 1151 
  80             wxMacPathToFSRef(data
->defaultLocation
, &theFile
); 
  81             AEDesc theLocation 
= { typeNull
, NULL 
}; 
  82             if (noErr 
== ::AECreateDesc(typeFSRef
, &theFile
, sizeof(FSRef
), &theLocation
)) 
  83                 ::NavCustomControl(ioParams
->context
, kNavCtlSetLocation
, (void *) &theLocation
); 
  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
); 
  93     else if ( inSelector 
== kNavCBPopupMenuSelect 
) 
  95         NavMenuItemSpec 
* menu 
= (NavMenuItemSpec 
*) ioParams
->eventData
.eventDataParms
.param 
; 
  96         const size_t numFilters 
= data
->extensions
.GetCount(); 
  98         if ( menu
->menuType 
< numFilters 
) 
 100             data
->currentfilter 
= menu
->menuType 
; 
 101             if ( data
->saveMode 
) 
 103                 int i 
= menu
->menuType 
; 
 104                 wxString extension 
=  data
->extensions
[i
].AfterLast('.') ; 
 105                 extension
.MakeLower() ; 
 108                 wxMacCFStringHolder 
cfString( NavDialogGetSaveFileName( ioParams
->context 
) , false  ); 
 109                 sfilename 
= cfString
.AsString() ; 
 111                 int pos 
= sfilename
.Find('.', true) ; 
 112                 if ( pos 
!= wxNOT_FOUND 
) 
 114                     sfilename 
= sfilename
.Left(pos
+1)+extension 
; 
 115                     cfString
.Assign( sfilename 
, wxFONTENCODING_DEFAULT 
) ; 
 116                     NavDialogSetSaveFileName( ioParams
->context 
, cfString 
) ; 
 123 void MakeUserDataRec(OpenUserDataRec 
*myData 
, const wxString
& filter 
) 
 125     myData
->menuitems 
= NULL 
; 
 126     myData
->currentfilter 
= 0 ; 
 127     myData
->saveMode 
= false ; 
 129     if ( filter 
&& filter
[0] ) 
 131         wxString 
filter2(filter
) ; 
 136         for ( unsigned int i 
= 0; i 
< filter2
.length() ; i
++ ) 
 138             if ( filter2
.GetChar(i
) == wxT('|') ) 
 142                     myData
->name
.Add( current 
) ; 
 146                     myData
->extensions
.Add( current
.MakeUpper() ) ; 
 151                 current 
= wxEmptyString 
; 
 155                 current 
+= filter2
.GetChar(i
) ; 
 158         // we allow for compatibility reason to have a single filter expression (like *.*) without 
 159         // an explanatory text, in that case the first part is name and extension at the same time 
 161         wxASSERT_MSG( filterIndex 
== 0 || !isName 
, wxT("incorrect format of format string") ) ; 
 162         if ( current
.empty() ) 
 163             myData
->extensions
.Add( myData
->name
[filterIndex
] ) ; 
 165             myData
->extensions
.Add( current
.MakeUpper() ) ; 
 166         if ( filterIndex 
== 0 || isName 
) 
 167             myData
->name
.Add( current
.MakeUpper() ) ; 
 171         const size_t extCount 
= myData
->extensions
.GetCount(); 
 172         for ( size_t i 
= 0 ; i 
< extCount
; i
++ ) 
 174             wxUint32 fileType
, creator
; 
 175             wxString extension 
= myData
->extensions
[i
]; 
 177             // Remove leading '*' 
 178             if (extension
.length() && (extension
.GetChar(0) == '*')) 
 179                 extension 
= extension
.Mid( 1 ); 
 181             // Remove leading '.' 
 182             if (extension
.length() && (extension
.GetChar(0) == '.')) 
 183                 extension 
= extension
.Mid( 1 ); 
 185             if (wxFileName::MacFindDefaultTypeAndCreator( extension
, &fileType
, &creator 
)) 
 186                 myData
->filtermactypes
.Add( (OSType
)fileType 
); 
 188                 myData
->filtermactypes
.Add( '****' ); // We'll fail safe if it's not recognized 
 193 static Boolean 
CheckFile( const wxString 
&filename 
, OSType type 
, OpenUserDataRecPtr data
) 
 195     wxString 
file(filename
) ; 
 198     if ( data
->extensions
.GetCount() > 0 ) 
 200         //for ( int i = 0 ; i < data->numfilters ; ++i ) 
 201         int i 
= data
->currentfilter 
; 
 202         if ( data
->extensions
[i
].Right(2) == wxT(".*") ) 
 206             if ( type 
== (OSType
)data
->filtermactypes
[i
] ) 
 209             wxStringTokenizer 
tokenizer( data
->extensions
[i
] , wxT(";") ) ; 
 210             while ( tokenizer
.HasMoreTokens() ) 
 212                 wxString extension 
= tokenizer
.GetNextToken() ; 
 213                 if ( extension
.GetChar(0) == '*' ) 
 214                     extension 
= extension
.Mid(1) ; 
 216                 if ( file
.length() >= extension
.length() && extension 
== file
.Right(extension
.length() ) ) 
 227 #if !TARGET_API_MAC_OSX 
 228 static pascal Boolean 
CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr
, void *dataPtr
) 
 230     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) dataPtr 
; 
 231     // return true if this item is invisible or a file 
 236     visibleFlag 
= ! (myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdFlags 
& kIsInvisible
); 
 237     folderFlag 
= (myCInfoPBPtr
->hFileInfo
.ioFlAttrib 
& 0x10); 
 239     // because the semantics of the filter proc are "true means don't show 
 240     // it" we need to invert the result that we return 
 247         wxString file 
= wxMacMakeStringFromPascal( myCInfoPBPtr
->hFileInfo
.ioNamePtr 
) ; 
 248         return !CheckFile( file 
, myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdType 
, data 
) ; 
 257 wxFileDialog::wxFileDialog( 
 258     wxWindow 
*parent
, const wxString
& message
, 
 259     const wxString
& defaultDir
, const wxString
& defaultFileName
, const wxString
& wildCard
, 
 260     long style
, const wxPoint
& pos
, const wxSize
& sz
, const wxString
& name
) 
 261     : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
, wildCard
, style
, pos
, sz
, name
) 
 263     wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ; 
 266 pascal Boolean 
CrossPlatformFilterCallback( 
 270     NavFilterModes filterMode 
) 
 273     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) callBackUD 
; 
 275     if (filterMode 
== kNavFilteringBrowserList
) 
 277         NavFileOrFolderInfo
* theInfo 
= (NavFileOrFolderInfo
*) info 
; 
 278         if ( !theInfo
->isFolder 
) 
 280             AECoerceDesc (theItem
, typeFSRef
, theItem
);  
 283             if ( AEGetDescData (theItem
, &fsref
, sizeof (FSRef
)) == noErr 
) 
 285                 memcpy( &fsref 
, *theItem
->dataHandle 
, sizeof(FSRef
) ) ; 
 286                 wxString file 
= wxMacFSRefToPath( &fsref 
) ; 
 287                 display 
= CheckFile( file 
, theInfo
->fileAndFolder
.fileInfo
.finderInfo
.fdType 
, data 
) ; 
 295 int wxFileDialog::ShowModal() 
 298     NavDialogCreationOptions dialogCreateOptions
; 
 300     // set default options 
 301     ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions
); 
 303     // this was always unset in the old code 
 304     dialogCreateOptions
.optionFlags 
&= ~kNavSelectDefaultLocation
; 
 306     wxMacCFStringHolder 
message(m_message
, m_font
.GetEncoding()); 
 307     dialogCreateOptions
.windowTitle 
= message
; 
 309     wxMacCFStringHolder 
defaultFileName(m_fileName
, m_font
.GetEncoding()); 
 310     dialogCreateOptions
.saveFileName 
= defaultFileName
; 
 314     NavObjectFilterUPP navFilterUPP 
= NULL
; 
 315     OpenUserDataRec myData
; 
 316     myData
.defaultLocation 
= m_dir
; 
 318     MakeUserDataRec(&myData 
, m_wildCard
); 
 319     myData
.currentfilter 
= m_filterIndex
; 
 320     size_t numFilters 
= myData
.extensions
.GetCount(); 
 323         CFMutableArrayRef popup 
= CFArrayCreateMutable( kCFAllocatorDefault 
, 
 324             numFilters 
, &kCFTypeArrayCallBacks 
) ; 
 325         dialogCreateOptions
.popupExtension 
= popup 
; 
 326         myData
.menuitems 
= dialogCreateOptions
.popupExtension 
; 
 327         for ( size_t i 
= 0 ; i 
< numFilters 
; ++i 
) 
 329             CFArrayAppendValue( popup 
, (CFStringRef
) wxMacCFStringHolder( myData
.name
[i
] , m_font
.GetEncoding() ) ) ; 
 333     if (HasFdFlag(wxFD_SAVE
)) 
 335         myData
.saveMode 
= true; 
 337         dialogCreateOptions
.optionFlags 
|= kNavDontAutoTranslate
; 
 338         dialogCreateOptions
.optionFlags 
|= kNavDontAddTranslateItems
; 
 340             dialogCreateOptions
.optionFlags 
|= kNavNoTypePopup
; 
 342         // The extension is important 
 344             dialogCreateOptions
.optionFlags 
|= kNavPreserveSaveFileExtension
; 
 346 #if TARGET_API_MAC_OSX 
 347         if (!(m_windowStyle 
& wxFD_OVERWRITE_PROMPT
)) 
 348             dialogCreateOptions
.optionFlags 
|= kNavDontConfirmReplacement
; 
 351         err 
= ::NavCreatePutFileDialog( 
 352             &dialogCreateOptions
, 
 353             kNavGenericSignature
, // Suppresses the 'Default' (top) menu item 
 354             kNavGenericSignature
, 
 355             sStandardNavEventFilter
, 
 356             &myData
, // for defaultLocation 
 361         // let the user select bundles/programs in dialogs 
 362         dialogCreateOptions
.optionFlags 
|= kNavSupportPackages
; 
 364         navFilterUPP 
= NewNavObjectFilterUPP(CrossPlatformFilterCallback
); 
 365         err 
= ::NavCreateGetFileDialog( 
 366             &dialogCreateOptions
, 
 367             NULL
, // NavTypeListHandle 
 368             sStandardNavEventFilter
, 
 369             NULL
, // NavPreviewUPP 
 371             (void *) &myData
, // inClientData 
 376         err 
= ::NavDialogRun(dialog
); 
 378     // clean up filter related data, etc. 
 380         ::DisposeNavObjectFilterUPP(navFilterUPP
); 
 385     NavReplyRecord navReply
; 
 386     err 
= ::NavDialogGetReply(dialog
, &navReply
); 
 387     if (err 
== noErr 
&& navReply
.validRecord
) 
 389         AEKeyword   theKeyword
; 
 396         m_filterIndex 
= myData
.currentfilter
; 
 397         ::AECountItems( &navReply
.selection
, &count 
); 
 398         for (long i 
= 1; i 
<= count
; ++i
) 
 401                 &(navReply
.selection
), i
, typeFSRef
, &theKeyword
, &actualType
, 
 402                 &theFSRef
, sizeof(theFSRef
), &actualSize 
); 
 406             if (HasFdFlag(wxFD_SAVE
)) 
 407                 thePath 
= wxMacFSRefToPath( &theFSRef
, navReply
.saveFileName 
); 
 409                 thePath 
= wxMacFSRefToPath( &theFSRef 
); 
 413                 ::NavDisposeReply(&navReply
); 
 419             m_fileName 
= wxFileNameFromPath(m_path
); 
 420             m_fileNames
.Add(m_fileName
); 
 423         // set these to the first hit 
 425         m_fileName 
= wxFileNameFromPath(m_path
); 
 426         m_dir 
= wxPathOnly(m_path
); 
 429     ::NavDisposeReply(&navReply
); 
 431     return (err 
== noErr
) ? wxID_OK 
: wxID_CANCEL
; 
 434 #endif // wxUSE_FILEDLG