From: Stefan Csomor Date: Fri, 18 Feb 2011 17:29:31 +0000 (+0000) Subject: support for file-type popup, compatible for 10.4+, solves #12429 X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/13390af486a8b9b72408260f6d89309870a43812?ds=inline support for file-type popup, compatible for 10.4+, solves #12429 git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66952 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/osx/filedlg.h b/include/wx/osx/filedlg.h index f427d032e3..aa9f39da24 100644 --- a/include/wx/osx/filedlg.h +++ b/include/wx/osx/filedlg.h @@ -16,6 +16,11 @@ // wxFileDialog //------------------------------------------------------------------------- +// set this system option to 1 in order to always show the filetypes popup in +// file open dialogs if possible + +#define wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES wxT("osx.filedlg.always-show-types") + class WXDLLIMPEXP_CORE wxFileDialog: public wxFileDialogBase { DECLARE_DYNAMIC_CLASS(wxFileDialog) @@ -45,6 +50,13 @@ public: #endif virtual bool SupportsExtraControl() const; + + // implementation only + +#if wxOSX_USE_COCOA + // returns true if the file can be shown as active + bool CheckFile( const wxString& filename ); +#endif protected: // not supported for file dialog, RR @@ -53,6 +65,20 @@ protected: int WXUNUSED(sizeFlags) = wxSIZE_AUTO) {} void SetupExtraControls(WXWindow nativeWindow); + +#if wxOSX_USE_COCOA + virtual wxWindow* CreateFilterPanel(wxWindow *extracontrol); + virtual void OnFilterSelected(wxCommandEvent &event); + + wxArrayString m_filterExtensions; + wxArrayString m_filterNames; + wxChoice* m_filterChoice; + wxWindow* m_filterPanel; + bool m_useFileTypeFilter; + int m_firstFileTypeFilter; + wxArrayString m_currentExtensions; + WX_NSObject m_delegate; +#endif }; #endif // _WX_FILEDLG_H_ diff --git a/interface/wx/sysopt.h b/interface/wx/sysopt.h index 46cc1579c2..a3bc77240f 100644 --- a/interface/wx/sysopt.h +++ b/interface/wx/sysopt.h @@ -121,6 +121,10 @@ @flag{mac.textcontrol-use-spell-checker} This option only has effect for Mac OS X 10.4 and higher. If 1 activates the spell checking in wxTextCtrl. + @flag{osx.openfiledialog.always-show-types} + Per default a wxFileDialog with wxFD_OPEN does not show a types-popup on OSX but allows + the selection of files from any of the supported types. Setting this to 1 shows a wxChoice + for selection (if there is more than one supported filetype). @endFlagTable diff --git a/src/osx/cocoa/filedlg.mm b/src/osx/cocoa/filedlg.mm index 19c84eee0a..12fe95f056 100644 --- a/src/osx/cocoa/filedlg.mm +++ b/src/osx/cocoa/filedlg.mm @@ -33,14 +33,124 @@ #include "wx/tokenzr.h" #include "wx/osx/private.h" +#include "wx/sysopt.h" // ============================================================================ // implementation // ============================================================================ // Open Items: -// - support for old style MacOS creator / type combos // - parameter support for descending into packages as directories (setTreatsFilePackagesAsDirectories) +// - as setAllowedFileTypes is only functional for NSOpenPanel on 10.6+, on earlier systems, the file +// type choice will not be shown, but all possible file items will be shown, if a popup must be working +// then the delegate method - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename will have to +// be implemented + +@interface wxOpenPanelDelegate : NSObject wxOSX_10_6_AND_LATER() +{ + wxFileDialog* _dialog; +} + +- (wxFileDialog*) fileDialog; +- (void) setFileDialog:(wxFileDialog*) dialog; + +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; + +@end + +@implementation wxOpenPanelDelegate + +- (id) init +{ + [super init]; + _dialog = NULL; + return self; +} + +- (wxFileDialog*) fileDialog +{ + return _dialog; +} + +- (void) setFileDialog:(wxFileDialog*) dialog +{ + _dialog = dialog; +} + +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename +{ + BOOL showObject = YES; + + NSString* resolvedLink = [[NSFileManager defaultManager] pathContentOfSymbolicLinkAtPath:filename]; + if ( resolvedLink != nil ) + filename = resolvedLink; + + NSDictionary* fileAttribs = [[NSFileManager defaultManager] + fileAttributesAtPath:filename traverseLink:YES]; + if (fileAttribs) + { + // check for packages + if ([NSFileTypeDirectory isEqualTo:[fileAttribs objectForKey:NSFileType]]) + { + if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO) + showObject = YES; // it's a folder, OK to show + else + { + // it's a packaged directory, apply check + wxCFStringRef filecf([filename retain]); + showObject = _dialog->CheckFile(filecf.AsString()); + } + } + else + { + // the code above only solves links, not aliases, do this here: + + NSString* resolvedAlias = nil; + + CFURLRef url = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, + (CFStringRef)filename, + kCFURLPOSIXPathStyle, + NO); + if (url != NULL) + { + FSRef fsRef; + if (CFURLGetFSRef(url, &fsRef)) + { + Boolean targetIsFolder, wasAliased; + OSErr err = FSResolveAliasFile (&fsRef, true, &targetIsFolder, &wasAliased); + + if ((err == noErr) && wasAliased) + { + CFURLRef resolvedUrl = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsRef); + if (resolvedUrl != NULL) + { + resolvedAlias = (NSString*) CFURLCopyFileSystemPath(resolvedUrl, + kCFURLPOSIXPathStyle); + CFRelease(resolvedUrl); + } + } + } + CFRelease(url); + } + + if (resolvedAlias != nil) + { + // recursive call + [resolvedAlias autorelease]; + showObject = [self panel:sender shouldShowFilename:resolvedAlias]; + } + else + { + wxCFStringRef filecf([filename retain]); + showObject = _dialog->CheckFile(filecf.AsString()); + } + } + } + + return showObject; +} + +@end IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase) @@ -57,95 +167,107 @@ bool wxFileDialog::SupportsExtraControl() const return true; } -NSArray* CopyTypesFromFilter( const wxString filter ) +NSArray* GetTypesFromExtension( const wxString extensiongroup, wxArrayString& extensions ) { NSMutableArray* types = nil; - if ( !filter.empty() ) + extensions.Clear(); + + wxStringTokenizer tokenizer( extensiongroup, wxT(";") ) ; + while ( tokenizer.HasMoreTokens() ) { - wxArrayString names ; - wxArrayString extensions; + wxString extension = tokenizer.GetNextToken() ; + // Remove leading '*' + if ( extension.length() && (extension.GetChar(0) == '*') ) + extension = extension.Mid( 1 ); - wxString filter2(filter) ; - int filterIndex = 0; - bool isName = true ; - wxString current ; + // Remove leading '.' + if ( extension.length() && (extension.GetChar(0) == '.') ) + extension = extension.Mid( 1 ); - for ( unsigned int i = 0; i < filter2.length() ; i++ ) + // Remove leading '*', this is for handling *.* + if ( extension.length() && (extension.GetChar(0) == '*') ) + extension = extension.Mid( 1 ); + + if ( extension.IsEmpty() ) { - if ( filter2.GetChar(i) == wxT('|') ) - { - if ( isName ) - { - names.Add( current ) ; - } - else - { - extensions.Add( current ) ; - ++filterIndex ; - } + extensions.Clear(); + [types release]; + types = nil; + return nil; + } - isName = !isName ; - current = wxEmptyString ; - } - else + if ( types == nil ) + types = [[NSMutableArray alloc] init]; + + extensions.Add(extension.Lower()); + wxCFStringRef cfext(extension); + [types addObject: (NSString*)cfext.AsNSString() ]; +#if 0 + // add support for classic fileType / creator here + wxUint32 fileType, creator; + // extension -> mactypes +#endif + } + [types autorelease]; + return types; +} + +NSArray* GetTypesFromFilter( const wxString& filter, wxArrayString& names, wxArrayString& extensiongroups ) +{ + NSMutableArray* types = nil; + bool allowAll = false; + + names.Clear(); + extensiongroups.Clear(); + + if ( !filter.empty() ) + { + wxStringTokenizer tokenizer( filter, wxT("|") ); + int numtokens = (int)tokenizer.CountTokens(); + if(numtokens == 1) + { + // we allow for compatibility reason to have a single filter expression (like *.*) without + // an explanatory text, in that case the first part is name and extension at the same time + wxString extension = tokenizer.GetNextToken(); + names.Add( extension ); + extensiongroups.Add( extension ); + } + else + { + int numextensions = numtokens / 2; + for(int i = 0; i < numextensions; i++) { - current += filter2.GetChar(i) ; + wxString name = tokenizer.GetNextToken(); + wxString extension = tokenizer.GetNextToken(); + names.Add( name ); + extensiongroups.Add( extension ); } } - // we allow for compatibility reason to have a single filter expression (like *.*) without - // an explanatory text, in that case the first part is name and extension at the same time - - wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ; - if ( current.empty() ) - extensions.Add( names[filterIndex] ) ; - else - extensions.Add( current ) ; - if ( filterIndex == 0 || isName ) - names.Add( current ) ; - ++filterIndex ; - - const size_t extCount = extensions.GetCount(); + const size_t extCount = extensiongroups.GetCount(); + wxArrayString extensions; for ( size_t i = 0 ; i < extCount; i++ ) { - wxString extensiongroup = extensions[i]; - wxStringTokenizer tokenizer( extensiongroup , wxT(";") ) ; - while ( tokenizer.HasMoreTokens() ) + NSArray* exttypes = GetTypesFromExtension(extensiongroups[i], extensions); + if ( exttypes != nil ) { - wxString extension = tokenizer.GetNextToken() ; - // Remove leading '*' - if (extension.length() && (extension.GetChar(0) == '*')) - extension = extension.Mid( 1 ); - - // Remove leading '.' - if (extension.length() && (extension.GetChar(0) == '.')) - extension = extension.Mid( 1 ); - - // Remove leading '*', this is for handling *.* - if (extension.length() && (extension.GetChar(0) == '*')) - extension = extension.Mid( 1 ); - - if ( extension.IsEmpty() ) + if ( allowAll == false ) { - [types release]; - types = nil; - return nil; - } - - if ( types == nil ) - types = [[NSMutableArray alloc] init]; + if ( types == nil ) + types = [[NSMutableArray alloc] init]; - wxCFStringRef cfext(extension); - [types addObject: (NSString*)cfext.AsNSString() ]; -#if 0 - // add support for classic fileType / creator here - wxUint32 fileType, creator; - // extension -> mactypes -#endif + [types addObjectsFromArray:exttypes]; + } + } + else + { + allowAll = true; + [types release]; + types = nil; } - } } + [types autorelease]; return types; } @@ -163,9 +285,9 @@ void wxFileDialog::ShowWindowModal() parentWindow = dynamic_cast(wxGetTopLevelParent(GetParent())); wxASSERT_MSG(parentWindow, "Window modal display requires parent."); - - NSArray* types = CopyTypesFromFilter( m_wildCard ) ; - if (HasFlag(wxFD_SAVE)) + + NSArray* types = GetTypesFromFilter( m_wildCard, m_filterNames, m_filterExtensions ) ; + if ( HasFlag(wxFD_SAVE) ) { NSSavePanel* sPanel = [NSSavePanel savePanel]; @@ -211,8 +333,76 @@ void wxFileDialog::ShowWindowModal() didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:) contextInfo: nil]; } - [types release]; - types = nil; +} + +// Create a panel with the file type drop down list +// If extra controls need to be added (see wxFileDialog::SetExtraControlCreator), add +// them to the panel as well +// Returns the newly created wxPanel + +wxWindow* wxFileDialog::CreateFilterPanel(wxWindow *extracontrol) +{ + wxPanel *extrapanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); + wxBoxSizer *verticalSizer = new wxBoxSizer(wxVERTICAL); + extrapanel->SetSizer(verticalSizer); + + // the file type control + { + wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL); + verticalSizer->Add(horizontalSizer, 0, wxEXPAND, 0); + wxStaticText *stattext = new wxStaticText( extrapanel, wxID_ANY, _("File type:") ); + horizontalSizer->Add(stattext, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + m_filterChoice = new wxChoice(extrapanel, wxID_ANY); + horizontalSizer->Add(m_filterChoice, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5); + m_filterChoice->Append(m_filterNames); + if( m_filterNames.GetCount() > 0) + { + if ( m_firstFileTypeFilter >= 0 ) + m_filterChoice->SetSelection(m_firstFileTypeFilter); + } + m_filterChoice->Connect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(wxFileDialog::OnFilterSelected), NULL, this); + } + + if(extracontrol) + { + wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL); + verticalSizer->Add(horizontalSizer, 0, wxEXPAND, 0); + + extracontrol->Reparent(extrapanel); + horizontalSizer->Add(extracontrol); + } + + verticalSizer->Layout(); + verticalSizer->SetSizeHints(extrapanel); + return extrapanel; +} + +// An item has been selected in the file filter wxChoice: +void wxFileDialog::OnFilterSelected( wxCommandEvent &WXUNUSED(event) ) +{ + int index = m_filterChoice->GetSelection(); + + NSArray* types = GetTypesFromExtension(m_filterExtensions[index],m_currentExtensions); + NSSavePanel* panel = (NSSavePanel*) GetWXWindow(); + if ( m_delegate ) + [panel validateVisibleColumns]; + else + [panel setAllowedFileTypes:types]; +} + +bool wxFileDialog::CheckFile( const wxString& filename ) +{ + if ( m_currentExtensions.GetCount() == 0 ) + return true; + + wxString ext = filename.AfterLast('.').Lower(); + + for ( size_t i = 0; i < m_currentExtensions.GetCount(); ++i ) + { + if ( ext == m_currentExtensions[i] ) + return true; + } + return false; } void wxFileDialog::SetupExtraControls(WXWindow nativeWindow) @@ -220,22 +410,48 @@ void wxFileDialog::SetupExtraControls(WXWindow nativeWindow) NSSavePanel* panel = (NSSavePanel*) nativeWindow; wxNonOwnedWindow::Create( GetParent(), nativeWindow ); - - if (HasExtraControlCreator()) + wxWindow* extracontrol = NULL; + if ( HasExtraControlCreator() ) { CreateExtraControl(); - wxWindow* control = GetExtraControl(); - if ( control ) - { - NSView* accView = control->GetHandle(); - [accView removeFromSuperview]; - [panel setAccessoryView:accView]; - } - else + extracontrol = GetExtraControl(); + } + + NSView* accView = nil; + m_delegate = nil; + + if ( m_useFileTypeFilter ) + { + m_filterPanel = CreateFilterPanel(extracontrol); + accView = m_filterPanel->GetHandle(); + if( HasFlag(wxFD_OPEN) ) { - [panel setAccessoryView:nil]; + if ( 1 /* UMAGetSystemVersion() < 0x1060 */ ) + { + wxOpenPanelDelegate* del = [[wxOpenPanelDelegate alloc]init]; + [del setFileDialog:this]; + [panel setDelegate:del]; + m_delegate = del; + } } } + else + { + m_filterPanel = NULL; + m_filterChoice = NULL; + if ( extracontrol != nil ) + accView = extracontrol->GetHandle(); + } + + if ( accView != nil ) + { + [accView removeFromSuperview]; + [panel setAccessoryView:accView]; + } + else + { + [panel setAccessoryView:nil]; + } } int wxFileDialog::ShowModal() @@ -259,8 +475,53 @@ int wxFileDialog::ShowModal() parentWindow = dynamic_cast(wxGetTopLevelParent(GetParent())); } - NSArray* types = CopyTypesFromFilter( m_wildCard ) ; - if (HasFlag(wxFD_SAVE)) + + NSArray* types = GetTypesFromFilter( m_wildCard, m_filterNames, m_filterExtensions ) ; + + m_useFileTypeFilter = m_filterExtensions.GetCount() > 1; + + if( HasFlag(wxFD_OPEN) ) + { + if ( !(wxSystemOptions::HasOption( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES ) && (wxSystemOptions::GetOptionInt( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES ) == 1)) ) + m_useFileTypeFilter = false; + } + + m_firstFileTypeFilter = -1; + + if ( m_useFileTypeFilter ) + { + types = nil; + bool useDefault = true; + for ( size_t i = 0; i < m_filterExtensions.GetCount(); ++i ) + { + types = GetTypesFromExtension(m_filterExtensions[i], m_currentExtensions); + if ( m_currentExtensions.GetCount() == 0 ) + { + useDefault = false; + m_firstFileTypeFilter = i; + break; + } + + for ( size_t j = 0; j < m_currentExtensions.GetCount(); ++j ) + { + if ( m_fileName.EndsWith(m_currentExtensions[j]) ) + { + m_firstFileTypeFilter = i; + useDefault = false; + break; + } + } + if ( !useDefault ) + break; + } + if ( useDefault ) + { + types = GetTypesFromExtension(m_filterExtensions[0], m_currentExtensions); + m_firstFileTypeFilter = 0; + } + } + + if ( HasFlag(wxFD_SAVE) ) { NSSavePanel* sPanel = [NSSavePanel savePanel]; @@ -282,9 +543,6 @@ int wxFileDialog::ShowModal() returnCode = [sPanel runModalForDirectory:dir.AsNSString() file:file.AsNSString() ]; ModalFinishedCallback(sPanel, returnCode); - - UnsubclassWin(); - [sPanel setAccessoryView:nil]; } else { @@ -302,24 +560,18 @@ int wxFileDialog::ShowModal() if ( UMAGetSystemVersion() < 0x1060 ) { returnCode = [oPanel runModalForDirectory:dir.AsNSString() - file:file.AsNSString() types:types]; + file:file.AsNSString() types:(m_delegate == nil ? types : nil)]; } else { - [oPanel setAllowedFileTypes:types]; + [oPanel setAllowedFileTypes: (m_delegate == nil ? types : nil)]; [oPanel setDirectoryURL:[NSURL fileURLWithPath:dir.AsNSString() isDirectory:YES]]; returnCode = [oPanel runModal]; } ModalFinishedCallback(oPanel, returnCode); - - UnsubclassWin(); - [oPanel setAccessoryView:nil]; - } - [types release]; - types = nil; return GetReturnCode(); } @@ -360,12 +612,19 @@ void wxFileDialog::ModalFinishedCallback(void* panel, int returnCode) } } } + if ( m_delegate ) + { + [oPanel setDelegate:nil]; + [m_delegate release]; + m_delegate = nil; + } } SetReturnCode(result); if (GetModality() == wxDIALOG_MODALITY_WINDOW_MODAL) SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED ); + UnsubclassWin(); [(NSSavePanel*) panel setAccessoryView:nil]; }