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         if( data
->extensions
.GetCount() > 0 ) 
  88             NavMenuItemSpec  menuItem
; 
  89             memset( &menuItem
, 0, sizeof(menuItem
) ); 
  90             menuItem
.version 
= kNavMenuItemSpecVersion
; 
  91             menuItem
.menuType 
= data
->currentfilter
; 
  92             ::NavCustomControl(ioParams
->context
, kNavCtlSelectCustomType
, &menuItem
); 
  95     else if ( inSelector 
== kNavCBPopupMenuSelect 
) 
  97         NavMenuItemSpec 
* menu 
= (NavMenuItemSpec 
*) ioParams
->eventData
.eventDataParms
.param 
; 
  98         const size_t numFilters 
= data
->extensions
.GetCount(); 
 100         if ( menu
->menuType 
< numFilters 
) 
 102             data
->currentfilter 
= menu
->menuType 
; 
 103             if ( data
->saveMode 
) 
 105                 int i 
= menu
->menuType 
; 
 107                 // isolate the first extension string 
 108                 wxString firstExtension 
= data
->extensions
[i
].BeforeFirst('|').BeforeFirst(';'); 
 110                 wxString extension 
= firstExtension
.AfterLast('.') ; 
 113                 wxCFStringRef 
cfString( wxCFRetain( NavDialogGetSaveFileName( ioParams
->context 
) ) ); 
 114                 sfilename 
= cfString
.AsString() ; 
 116                 int pos 
= sfilename
.Find('.', true) ; 
 117                 if ( pos 
!= wxNOT_FOUND 
&& extension 
!= wxT("*") ) 
 119                     sfilename 
= sfilename
.Left(pos
+1)+extension 
; 
 120                     cfString 
= wxCFStringRef( sfilename 
, wxFONTENCODING_DEFAULT 
) ; 
 121                     NavDialogSetSaveFileName( ioParams
->context 
, cfString 
) ; 
 128 void MakeUserDataRec(OpenUserDataRec 
*myData 
, const wxString
& filter 
) 
 130     myData
->menuitems 
= NULL 
; 
 131     myData
->currentfilter 
= 0 ; 
 132     myData
->saveMode 
= false ; 
 134     if ( !filter
.empty() ) 
 136         wxString 
filter2(filter
) ; 
 141         for ( unsigned int i 
= 0; i 
< filter2
.length() ; i
++ ) 
 143             if ( filter2
.GetChar(i
) == wxT('|') ) 
 147                     myData
->name
.Add( current 
) ; 
 151                     myData
->extensions
.Add( current 
) ; 
 156                 current 
= wxEmptyString 
; 
 160                 current 
+= filter2
.GetChar(i
) ; 
 163         // we allow for compatibility reason to have a single filter expression (like *.*) without 
 164         // an explanatory text, in that case the first part is name and extension at the same time 
 166         wxASSERT_MSG( filterIndex 
== 0 || !isName 
, wxT("incorrect format of format string") ) ; 
 167         if ( current
.empty() ) 
 168             myData
->extensions
.Add( myData
->name
[filterIndex
] ) ; 
 170             myData
->extensions
.Add( current 
) ; 
 171         if ( filterIndex 
== 0 || isName 
) 
 172             myData
->name
.Add( current 
) ; 
 176         const size_t extCount 
= myData
->extensions
.GetCount(); 
 177         for ( size_t i 
= 0 ; i 
< extCount
; i
++ ) 
 179             wxUint32 fileType
, creator
; 
 180             wxString extension 
= myData
->extensions
[i
]; 
 182             // Remove leading '*' 
 183             if (extension
.length() && (extension
.GetChar(0) == '*')) 
 184                 extension 
= extension
.Mid( 1 ); 
 186             // Remove leading '.' 
 187             if (extension
.length() && (extension
.GetChar(0) == '.')) 
 188                 extension 
= extension
.Mid( 1 ); 
 190             if (wxFileName::MacFindDefaultTypeAndCreator( extension
, &fileType
, &creator 
)) 
 191                 myData
->filtermactypes
.Add( (OSType
)fileType 
); 
 193                 myData
->filtermactypes
.Add( '****' ); // We'll fail safe if it's not recognized 
 198 static Boolean 
CheckFile( const wxString 
&filename 
, OSType type 
, OpenUserDataRecPtr data
) 
 200     wxString 
file(filename
) ; 
 203     if ( data
->extensions
.GetCount() > 0 ) 
 205         //for ( int i = 0 ; i < data->numfilters ; ++i ) 
 206         int i 
= data
->currentfilter 
; 
 207         if ( data
->extensions
[i
].Right(2) == wxT(".*") ) 
 211             if ( type 
== (OSType
)data
->filtermactypes
[i
] ) 
 214             wxStringTokenizer 
tokenizer( data
->extensions
[i
] , wxT(";") ) ; 
 215             while ( tokenizer
.HasMoreTokens() ) 
 217                 wxString extension 
= tokenizer
.GetNextToken() ; 
 218                 if ( extension
.GetChar(0) == '*' ) 
 219                     extension 
= extension
.Mid(1) ; 
 220                 extension
.MakeUpper(); 
 222                 if ( file
.length() >= extension
.length() && extension 
== file
.Right(extension
.length() ) ) 
 235 wxFileDialog::wxFileDialog( 
 236     wxWindow 
*parent
, const wxString
& message
, 
 237     const wxString
& defaultDir
, const wxString
& defaultFileName
, const wxString
& wildCard
, 
 238     long style
, const wxPoint
& pos
, const wxSize
& sz
, const wxString
& name
) 
 239     : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
, wildCard
, style
, pos
, sz
, name
) 
 241     wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ; 
 244 pascal Boolean 
CrossPlatformFilterCallback( 
 248     NavFilterModes filterMode 
) 
 250     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) callBackUD 
; 
 252     if (filterMode 
== kNavFilteringBrowserList
) 
 254         // We allow navigation to all folders. For files, we check against the current 
 256         // However, packages should be dealt with like files and not like folders. So 
 257         // check if a folder is a package before deciding what to do. 
 258         NavFileOrFolderInfo
* theInfo 
= (NavFileOrFolderInfo
*) info 
; 
 261         if ( theInfo
->isFolder 
) 
 263             // check bundle bit (using Finder Services - used by OS9 on some bundles) 
 264             FSCatalogInfo catalogInfo
; 
 265             if (FSGetCatalogInfo (&fsref
, kFSCatInfoFinderInfo
, &catalogInfo
, NULL
, NULL
, NULL
) != noErr
) 
 268             // Check bundle item (using Launch Services - used by OS-X through info.plist or APP) 
 269             LSItemInfoRecord lsInfo
; 
 270             if (LSCopyItemInfoForRef(&fsref
, kLSRequestBasicFlagsOnly
, &lsInfo 
) != noErr
) 
 273             // If it's not a bundle, then it's a normal folder and it passes our filter 
 274             FileInfo 
*fileInfo 
= (FileInfo 
*) catalogInfo
.finderInfo
; 
 275             if ( !(fileInfo
->finderFlags 
& kHasBundle
) && 
 276                  !(lsInfo
.flags 
& (kLSItemInfoIsApplication 
| kLSItemInfoIsPackage
)) ) 
 281             AECoerceDesc (theItem
, typeFSRef
, theItem
); 
 282             if ( AEGetDescData (theItem
, &fsref
, sizeof (FSRef
)) == noErr
) 
 284                 wxString file 
= wxMacFSRefToPath( &fsref 
) ; 
 285                 return CheckFile( file 
, theInfo
->fileAndFolder
.fileInfo
.finderInfo
.fdType 
, data 
) ; 
 293 int wxFileDialog::ShowModal() 
 296     NavDialogCreationOptions dialogCreateOptions
; 
 298     // set default options 
 299     ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions
); 
 301     // this was always unset in the old code 
 302     dialogCreateOptions
.optionFlags 
&= ~kNavSelectDefaultLocation
; 
 304     wxCFStringRef 
message(m_message
, m_font
.GetEncoding()); 
 305     dialogCreateOptions
.windowTitle 
= message
; 
 307     wxCFStringRef 
defaultFileName(m_fileName
, m_font
.GetEncoding()); 
 308     dialogCreateOptions
.saveFileName 
= defaultFileName
; 
 312     NavObjectFilterUPP navFilterUPP 
= NULL
; 
 313     OpenUserDataRec myData
; 
 314     myData
.defaultLocation 
= m_dir
; 
 316     MakeUserDataRec(&myData 
, m_wildCard
); 
 317     myData
.currentfilter 
= m_filterIndex
; 
 318     size_t numFilters 
= myData
.extensions
.GetCount(); 
 321         CFMutableArrayRef popup 
= CFArrayCreateMutable( kCFAllocatorDefault 
, 
 322             numFilters 
, &kCFTypeArrayCallBacks 
) ; 
 323         dialogCreateOptions
.popupExtension 
= popup 
; 
 324         myData
.menuitems 
= dialogCreateOptions
.popupExtension 
; 
 325         for ( size_t i 
= 0 ; i 
< numFilters 
; ++i 
) 
 327             CFArrayAppendValue( popup 
, (CFStringRef
) wxCFStringRef( myData
.name
[i
] , m_font
.GetEncoding() ) ) ; 
 331     if (HasFdFlag(wxFD_SAVE
)) 
 333         myData
.saveMode 
= true; 
 335         dialogCreateOptions
.optionFlags 
|= kNavDontAutoTranslate
; 
 336         dialogCreateOptions
.optionFlags 
|= kNavDontAddTranslateItems
; 
 338             dialogCreateOptions
.optionFlags 
|= kNavNoTypePopup
; 
 340         // The extension is important 
 342             dialogCreateOptions
.optionFlags 
|= kNavPreserveSaveFileExtension
; 
 344         if (!(m_windowStyle 
& wxFD_OVERWRITE_PROMPT
)) 
 345             dialogCreateOptions
.optionFlags 
|= kNavDontConfirmReplacement
; 
 347         err 
= ::NavCreatePutFileDialog( 
 348             &dialogCreateOptions
, 
 349             kNavGenericSignature
, // Suppresses the 'Default' (top) menu item 
 350             kNavGenericSignature
, 
 351             sStandardNavEventFilter
, 
 352             &myData
, // for defaultLocation 
 357         // let the user select bundles/programs in dialogs 
 358         dialogCreateOptions
.optionFlags 
|= kNavSupportPackages
; 
 360         navFilterUPP 
= NewNavObjectFilterUPP(CrossPlatformFilterCallback
); 
 361         err 
= ::NavCreateGetFileDialog( 
 362             &dialogCreateOptions
, 
 363             NULL
, // NavTypeListHandle 
 364             sStandardNavEventFilter
, 
 365             NULL
, // NavPreviewUPP 
 367             (void *) &myData
, // inClientData 
 372         err 
= ::NavDialogRun(dialog
); 
 374     // clean up filter related data, etc. 
 376         ::DisposeNavObjectFilterUPP(navFilterUPP
); 
 380         ::NavDialogDispose(dialog
); 
 384     NavReplyRecord navReply
; 
 385     err 
= ::NavDialogGetReply(dialog
, &navReply
); 
 386     if (err 
== noErr 
&& navReply
.validRecord
) 
 388         AEKeyword   theKeyword
; 
 395         m_filterIndex 
= myData
.currentfilter
; 
 396         ::AECountItems( &navReply
.selection
, &count 
); 
 397         for (long i 
= 1; i 
<= count
; ++i
) 
 400                 &(navReply
.selection
), i
, typeFSRef
, &theKeyword
, &actualType
, 
 401                 &theFSRef
, sizeof(theFSRef
), &actualSize 
); 
 405             if (HasFdFlag(wxFD_SAVE
)) 
 406                 thePath 
= wxMacFSRefToPath( &theFSRef
, navReply
.saveFileName 
); 
 408                 thePath 
= wxMacFSRefToPath( &theFSRef 
); 
 412                 ::NavDisposeReply(&navReply
); 
 413                 ::NavDialogDispose(dialog
); 
 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
); 
 430     ::NavDialogDispose(dialog
); 
 432     return (err 
== noErr
) ? wxID_OK 
: wxID_CANCEL
; 
 435 #endif // wxUSE_FILEDLG