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 
; 
 105                 // isolate the first extension string 
 106                 wxString firstExtension 
= data
->extensions
[i
].BeforeFirst('|').BeforeFirst(';'); 
 108                 wxString extension 
= firstExtension
.AfterLast('.') ; 
 111                 wxMacCFStringHolder 
cfString( NavDialogGetSaveFileName( ioParams
->context 
) , false  ); 
 112                 sfilename 
= cfString
.AsString() ; 
 114                 int pos 
= sfilename
.Find('.', true) ; 
 115                 if ( pos 
!= wxNOT_FOUND 
) 
 117                     sfilename 
= sfilename
.Left(pos
+1)+extension 
; 
 118                     cfString
.Assign( sfilename 
, wxFONTENCODING_DEFAULT 
) ; 
 119                     NavDialogSetSaveFileName( ioParams
->context 
, cfString 
) ; 
 126 void MakeUserDataRec(OpenUserDataRec 
*myData 
, const wxString
& filter 
) 
 128     myData
->menuitems 
= NULL 
; 
 129     myData
->currentfilter 
= 0 ; 
 130     myData
->saveMode 
= false ; 
 132     if ( !filter
.empty() ) 
 134         wxString 
filter2(filter
) ; 
 139         for ( unsigned int i 
= 0; i 
< filter2
.length() ; i
++ ) 
 141             if ( filter2
.GetChar(i
) == wxT('|') ) 
 145                     myData
->name
.Add( current 
) ; 
 149                     myData
->extensions
.Add( current 
) ; 
 154                 current 
= wxEmptyString 
; 
 158                 current 
+= filter2
.GetChar(i
) ; 
 161         // we allow for compatibility reason to have a single filter expression (like *.*) without 
 162         // an explanatory text, in that case the first part is name and extension at the same time 
 164         wxASSERT_MSG( filterIndex 
== 0 || !isName 
, wxT("incorrect format of format string") ) ; 
 165         if ( current
.empty() ) 
 166             myData
->extensions
.Add( myData
->name
[filterIndex
] ) ; 
 168             myData
->extensions
.Add( current 
) ; 
 169         if ( filterIndex 
== 0 || isName 
) 
 170             myData
->name
.Add( current 
) ; 
 174         const size_t extCount 
= myData
->extensions
.GetCount(); 
 175         for ( size_t i 
= 0 ; i 
< extCount
; i
++ ) 
 177             wxUint32 fileType
, creator
; 
 178             wxString extension 
= myData
->extensions
[i
]; 
 180             // Remove leading '*' 
 181             if (extension
.length() && (extension
.GetChar(0) == '*')) 
 182                 extension 
= extension
.Mid( 1 ); 
 184             // Remove leading '.' 
 185             if (extension
.length() && (extension
.GetChar(0) == '.')) 
 186                 extension 
= extension
.Mid( 1 ); 
 188             if (wxFileName::MacFindDefaultTypeAndCreator( extension
, &fileType
, &creator 
)) 
 189                 myData
->filtermactypes
.Add( (OSType
)fileType 
); 
 191                 myData
->filtermactypes
.Add( '****' ); // We'll fail safe if it's not recognized 
 196 static Boolean 
CheckFile( const wxString 
&filename 
, OSType type 
, OpenUserDataRecPtr data
) 
 198     wxString 
file(filename
) ; 
 201     if ( data
->extensions
.GetCount() > 0 ) 
 203         //for ( int i = 0 ; i < data->numfilters ; ++i ) 
 204         int i 
= data
->currentfilter 
; 
 205         if ( data
->extensions
[i
].Right(2) == wxT(".*") ) 
 209             if ( type 
== (OSType
)data
->filtermactypes
[i
] ) 
 212             wxStringTokenizer 
tokenizer( data
->extensions
[i
] , wxT(";") ) ; 
 213             while ( tokenizer
.HasMoreTokens() ) 
 215                 wxString extension 
= tokenizer
.GetNextToken() ; 
 216                 if ( extension
.GetChar(0) == '*' ) 
 217                     extension 
= extension
.Mid(1) ; 
 218                 extension
.MakeUpper(); 
 220                 if ( file
.length() >= extension
.length() && extension 
== file
.Right(extension
.length() ) ) 
 231 #if !TARGET_API_MAC_OSX 
 232 static pascal Boolean 
CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr
, void *dataPtr
) 
 234     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) dataPtr 
; 
 235     // return true if this item is invisible or a file 
 240     visibleFlag 
= ! (myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdFlags 
& kIsInvisible
); 
 241     folderFlag 
= (myCInfoPBPtr
->hFileInfo
.ioFlAttrib 
& 0x10); 
 243     // because the semantics of the filter proc are "true means don't show 
 244     // it" we need to invert the result that we return 
 251         wxString file 
= wxMacMakeStringFromPascal( myCInfoPBPtr
->hFileInfo
.ioNamePtr 
) ; 
 252         return !CheckFile( file 
, myCInfoPBPtr
->hFileInfo
.ioFlFndrInfo
.fdType 
, data 
) ; 
 261 wxFileDialog::wxFileDialog( 
 262     wxWindow 
*parent
, const wxString
& message
, 
 263     const wxString
& defaultDir
, const wxString
& defaultFileName
, const wxString
& wildCard
, 
 264     long style
, const wxPoint
& pos
, const wxSize
& sz
, const wxString
& name
) 
 265     : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
, wildCard
, style
, pos
, sz
, name
) 
 267     wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ; 
 270 pascal Boolean 
CrossPlatformFilterCallback( 
 274     NavFilterModes filterMode 
) 
 276     OpenUserDataRecPtr data 
= (OpenUserDataRecPtr
) callBackUD 
; 
 278     if (filterMode 
== kNavFilteringBrowserList
) 
 280         // We allow navigation to all folders. For files, we check against the current 
 282         // However, packages should be dealt with like files and not like folders. So 
 283         // check if a folder is a package before deciding what to do. 
 285         NavFileOrFolderInfo
* theInfo 
= (NavFileOrFolderInfo
*) info 
; 
 286         AECoerceDesc (theItem
, typeFSRef
, theItem
); 
 287         if ( AEGetDescData (theItem
, &fsref
, sizeof (FSRef
)) != noErr
) 
 290         if ( theInfo
->isFolder 
) 
 292             // check bundle bit (using Finder Services - used by OS9 on some bundles) 
 293             FSCatalogInfo catalogInfo
; 
 294             if (FSGetCatalogInfo (&fsref
, kFSCatInfoFinderInfo
, &catalogInfo
, NULL
, NULL
, NULL
) != noErr
) 
 297             // Check bundle item (using Launch Services - used by OS-X through info.plist or APP) 
 298             LSItemInfoRecord lsInfo
; 
 299             if (LSCopyItemInfoForRef(&fsref
, kLSRequestBasicFlagsOnly
, &lsInfo 
) != noErr
) 
 302             // If it's not a bundle, then it's a normal folder and it passes our filter 
 303             FileInfo 
*fileInfo 
= (FileInfo 
*) catalogInfo
.finderInfo
; 
 304             if ( !(fileInfo
->finderFlags 
& kHasBundle
) && 
 305                  !(lsInfo
.flags 
& (kLSItemInfoIsApplication 
| kLSItemInfoIsPackage
)) ) 
 309         wxString file 
= wxMacFSRefToPath( &fsref 
) ; 
 310         return CheckFile( file 
, theInfo
->fileAndFolder
.fileInfo
.finderInfo
.fdType 
, data 
) ; 
 317 int wxFileDialog::ShowModal() 
 320     NavDialogCreationOptions dialogCreateOptions
; 
 322     // set default options 
 323     ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions
); 
 325     // this was always unset in the old code 
 326     dialogCreateOptions
.optionFlags 
&= ~kNavSelectDefaultLocation
; 
 328     wxMacCFStringHolder 
message(m_message
, m_font
.GetEncoding()); 
 329     dialogCreateOptions
.windowTitle 
= message
; 
 331     wxMacCFStringHolder 
defaultFileName(m_fileName
, m_font
.GetEncoding()); 
 332     dialogCreateOptions
.saveFileName 
= defaultFileName
; 
 336     NavObjectFilterUPP navFilterUPP 
= NULL
; 
 337     OpenUserDataRec myData
; 
 338     myData
.defaultLocation 
= m_dir
; 
 340     MakeUserDataRec(&myData 
, m_wildCard
); 
 341     myData
.currentfilter 
= m_filterIndex
; 
 342     size_t numFilters 
= myData
.extensions
.GetCount(); 
 345         CFMutableArrayRef popup 
= CFArrayCreateMutable( kCFAllocatorDefault 
, 
 346             numFilters 
, &kCFTypeArrayCallBacks 
) ; 
 347         dialogCreateOptions
.popupExtension 
= popup 
; 
 348         myData
.menuitems 
= dialogCreateOptions
.popupExtension 
; 
 349         for ( size_t i 
= 0 ; i 
< numFilters 
; ++i 
) 
 351             CFArrayAppendValue( popup 
, (CFStringRef
) wxMacCFStringHolder( myData
.name
[i
] , m_font
.GetEncoding() ) ) ; 
 355     if (HasFdFlag(wxFD_SAVE
)) 
 357         myData
.saveMode 
= true; 
 359         dialogCreateOptions
.optionFlags 
|= kNavDontAutoTranslate
; 
 360         dialogCreateOptions
.optionFlags 
|= kNavDontAddTranslateItems
; 
 362             dialogCreateOptions
.optionFlags 
|= kNavNoTypePopup
; 
 364         // The extension is important 
 366             dialogCreateOptions
.optionFlags 
|= kNavPreserveSaveFileExtension
; 
 368 #if TARGET_API_MAC_OSX 
 369         if (!(m_windowStyle 
& wxFD_OVERWRITE_PROMPT
)) 
 370             dialogCreateOptions
.optionFlags 
|= kNavDontConfirmReplacement
; 
 373         err 
= ::NavCreatePutFileDialog( 
 374             &dialogCreateOptions
, 
 375             kNavGenericSignature
, // Suppresses the 'Default' (top) menu item 
 376             kNavGenericSignature
, 
 377             sStandardNavEventFilter
, 
 378             &myData
, // for defaultLocation 
 383         // let the user select bundles/programs in dialogs 
 384         dialogCreateOptions
.optionFlags 
|= kNavSupportPackages
; 
 386         navFilterUPP 
= NewNavObjectFilterUPP(CrossPlatformFilterCallback
); 
 387         err 
= ::NavCreateGetFileDialog( 
 388             &dialogCreateOptions
, 
 389             NULL
, // NavTypeListHandle 
 390             sStandardNavEventFilter
, 
 391             NULL
, // NavPreviewUPP 
 393             (void *) &myData
, // inClientData 
 398         err 
= ::NavDialogRun(dialog
); 
 400     // clean up filter related data, etc. 
 402         ::DisposeNavObjectFilterUPP(navFilterUPP
); 
 407     NavReplyRecord navReply
; 
 408     err 
= ::NavDialogGetReply(dialog
, &navReply
); 
 409     if (err 
== noErr 
&& navReply
.validRecord
) 
 411         AEKeyword   theKeyword
; 
 418         m_filterIndex 
= myData
.currentfilter
; 
 419         ::AECountItems( &navReply
.selection
, &count 
); 
 420         for (long i 
= 1; i 
<= count
; ++i
) 
 423                 &(navReply
.selection
), i
, typeFSRef
, &theKeyword
, &actualType
, 
 424                 &theFSRef
, sizeof(theFSRef
), &actualSize 
); 
 428             if (HasFdFlag(wxFD_SAVE
)) 
 429                 thePath 
= wxMacFSRefToPath( &theFSRef
, navReply
.saveFileName 
); 
 431                 thePath 
= wxMacFSRefToPath( &theFSRef 
); 
 435                 ::NavDisposeReply(&navReply
); 
 441             m_fileName 
= wxFileNameFromPath(m_path
); 
 442             m_fileNames
.Add(m_fileName
); 
 445         // set these to the first hit 
 447         m_fileName 
= wxFileNameFromPath(m_path
); 
 448         m_dir 
= wxPathOnly(m_path
); 
 451     ::NavDisposeReply(&navReply
); 
 453     return (err 
== noErr
) ? wxID_OK 
: wxID_CANCEL
; 
 456 #endif // wxUSE_FILEDLG