From bd7bfb002d2abcae1a87c758b1094e9f06af2f42 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Mon, 21 Jun 2010 16:36:45 +0000 Subject: [PATCH] adding mimetype patch, closes #12072 git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64671 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/osx/core/mimetype.h | 199 +++--- include/wx/osx/icon.h | 5 + src/common/mimecmn.cpp | 2 +- src/osx/carbon/icon.cpp | 352 ++++++----- src/osx/core/mimetype.cpp | 1089 ++++++++++++++------------------ 5 files changed, 772 insertions(+), 875 deletions(-) diff --git a/include/wx/osx/core/mimetype.h b/include/wx/osx/core/mimetype.h index 4255411162..21e1b6440f 100644 --- a/include/wx/osx/core/mimetype.h +++ b/include/wx/osx/core/mimetype.h @@ -1,174 +1,119 @@ ///////////////////////////////////////////////////////////////////////////// // Name: wx/osx/core/mimetype.h -// Purpose: classes and functions to manage MIME types -// Author: Vadim Zeitlin +// Purpose: Mac implementation for wx mime-related classes +// Author: Neil Perkins // Modified by: -// Created: 23.09.98 +// Created: 2010-05-15 // RCS-ID: $Id: mimetype.h 54448 2008-07-01 09:28:08Z RR $ -// Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows licence (part of wxExtra library) +// Copyright: (C) 2010 Neil Perkins +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #ifndef _MIMETYPE_IMPL_H #define _MIMETYPE_IMPL_H -#include "wx/mimetype.h" +#include "wx/defs.h" #if wxUSE_MIMETYPE -class wxMimeTypeCommands; +#include "wx/mimetype.h" +#include "wx/hashmap.h" +#include "wx/iconloc.h" -WX_DEFINE_ARRAY_PTR(wxMimeTypeCommands *, wxMimeCommandsArray); -// this is the real wxMimeTypesManager for Unix +// This class implements mime type functionality for Mac OS X using UTIs and Launch Services +// Currently only the GetFileTypeFromXXXX public functions have been implemented class WXDLLIMPEXP_BASE wxMimeTypesManagerImpl { public: - // ctor and dtor + wxMimeTypesManagerImpl(); virtual ~wxMimeTypesManagerImpl(); - // load all data into memory - done when it is needed for the first time - void Initialize(int mailcapStyles = wxMAILCAP_ALL, - const wxString& extraDir = wxEmptyString); - - // and delete the data here + // These functions are not needed on Mac OS X and have no-op implementations + void Initialize(int mailcapStyles = wxMAILCAP_STANDARD, const wxString& extraDir = wxEmptyString); void ClearData(); - // implement containing class functions + // Functions to look up types by ext, mime or UTI wxFileType *GetFileTypeFromExtension(const wxString& ext); wxFileType *GetFileTypeFromMimeType(const wxString& mimeType); + wxFileType *GetFileTypeFromUti(const wxString& uti); + // These functions are only stubs on Mac OS X size_t EnumAllFileTypes(wxArrayString& mimetypes); + wxFileType *Associate(const wxFileTypeInfo& ftInfo); + bool Unassociate(wxFileType *ft); - void AddFallback(const wxFileTypeInfo& filetype); +private: - // add information about the given mimetype - void AddMimeTypeInfo(const wxString& mimetype, - const wxString& extensions, - const wxString& description); - void AddMailcapInfo(const wxString& strType, - const wxString& strOpenCmd, - const wxString& strPrintCmd, - const wxString& strTest, - const wxString& strDesc); + // The work of querying the OS for type data is done in these two functions + void LoadTypeDataForUti(const wxString& uti); + void LoadDisplayDataForUti(const wxString& uti); - // add a new record to the user .mailcap/.mime.types files - wxFileType *Associate(const wxFileTypeInfo& ftInfo); - // remove association - bool Unassociate(wxFileType *ft); + // These functions are pass-throughs from wxFileTypeImpl + bool GetExtensions(const wxString& uti, wxArrayString& extensions); + bool GetMimeType(const wxString& uti, wxString *mimeType); + bool GetMimeTypes(const wxString& uti, wxArrayString& mimeTypes); + bool GetIcon(const wxString& uti, wxIconLocation *iconLoc); + bool GetDescription(const wxString& uti, wxString *desc); + + // Structure to represent file types + typedef struct FileTypeData + { + wxArrayString extensions; + wxArrayString mimeTypes; + wxIconLocation iconLoc; + wxString description; + } + FileTypeInfo; + + // Map types + WX_DECLARE_STRING_HASH_MAP( wxString, TagMap ); + WX_DECLARE_STRING_HASH_MAP( FileTypeData, UtiMap ); - // accessors - // get the string containing space separated extensions for the given - // file type - wxString GetExtension(size_t index) { return m_aExtensions[index]; } - -protected: - void InitIfNeeded(); - - wxArrayString m_aTypes, // MIME types - m_aDescriptions, // descriptions (just some text) - m_aExtensions, // space separated list of extensions - m_aIcons; // Icon filenames - - // verb=command pairs for this file type - wxMimeCommandsArray m_aEntries; - - // are we initialized? - bool m_initialized; - - wxString GetCommand(const wxString &verb, size_t nIndex) const; - - // Read XDG *.desktop file - void LoadXDGApp(const wxString& filename); - // Scan XDG directory - void LoadXDGAppsFilesFromDir(const wxString& dirname); - - // Load XDG globs files - void LoadXDGGlobs(const wxString& filename); - - // functions used to do associations - virtual int AddToMimeData(const wxString& strType, - const wxString& strIcon, - wxMimeTypeCommands *entry, - const wxArrayString& strExtensions, - const wxString& strDesc, - bool replaceExisting = true); - virtual bool DoAssociation(const wxString& strType, - const wxString& strIcon, - wxMimeTypeCommands *entry, - const wxArrayString& strExtensions, - const wxString& strDesc); - - // give it access to m_aXXX variables - friend class WXDLLIMPEXP_FWD_BASE wxFileTypeImpl; + // Data store + TagMap m_extMap; + TagMap m_mimeMap; + UtiMap m_utiMap; + + friend class wxFileTypeImpl; }; + +// This class provides the interface between wxFileType and wxMimeTypesManagerImple for Mac OS X +// Currently only extension, mimetype, description and icon information is available +// All other methods have no-op implementation class WXDLLIMPEXP_BASE wxFileTypeImpl { public: - // initialization functions - // this is used to construct a list of mimetypes which match; - // if built with GetFileTypeFromMimetype index 0 has the exact match and - // index 1 the type / * match - // if built with GetFileTypeFromExtension, index 0 has the mimetype for - // the first extension found, index 1 for the second and so on - - void Init(wxMimeTypesManagerImpl *manager, size_t index) - { m_manager = manager; m_index.Add(index); } - - // accessors - bool GetExtensions(wxArrayString& extensions); - bool GetMimeType(wxString *mimeType) const - { *mimeType = m_manager->m_aTypes[m_index[0]]; return true; } - bool GetMimeTypes(wxArrayString& mimeTypes) const; - bool GetIcon(wxIconLocation *iconLoc) const; - - bool GetDescription(wxString *desc) const - { *desc = m_manager->m_aDescriptions[m_index[0]]; return true; } - - bool GetOpenCommand(wxString *openCmd, - const wxFileType::MessageParameters& params) const - { - *openCmd = GetExpandedCommand(wxT("open"), params); - return (! openCmd -> IsEmpty() ); - } - - bool GetPrintCommand(wxString *printCmd, - const wxFileType::MessageParameters& params) const - { - *printCmd = GetExpandedCommand(wxT("print"), params); - return (! printCmd -> IsEmpty() ); - } - // return the number of commands defined for this file type, 0 if none - size_t GetAllCommands(wxArrayString *verbs, wxArrayString *commands, - const wxFileType::MessageParameters& params) const; + wxFileTypeImpl(); + virtual ~wxFileTypeImpl(); + bool GetExtensions(wxArrayString& extensions) const ; + bool GetMimeType(wxString *mimeType) const ; + bool GetMimeTypes(wxArrayString& mimeTypes) const ; + bool GetIcon(wxIconLocation *iconLoc) const ; + bool GetDescription(wxString *desc) const ; - // remove the record for this file type - // probably a mistake to come here, use wxMimeTypesManager.Unassociate (ft) instead - bool Unassociate(wxFileType *ft) - { - return m_manager->Unassociate(ft); - } - - // set an arbitrary command, ask confirmation if it already exists and - // overwriteprompt is TRUE - bool SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt = true); + // These functions are only stubs on Mac OS X + bool GetOpenCommand(wxString *openCmd, const wxFileType::MessageParameters& params) const; + bool GetPrintCommand(wxString *printCmd, const wxFileType::MessageParameters& params) const; + size_t GetAllCommands(wxArrayString *verbs, wxArrayString *commands, const wxFileType::MessageParameters& params) const; + bool SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt = TRUE); bool SetDefaultIcon(const wxString& strIcon = wxEmptyString, int index = 0); + bool Unassociate(wxFileType *ft); private: - wxString - GetExpandedCommand(const wxString & verb, - const wxFileType::MessageParameters& params) const; - wxMimeTypesManagerImpl *m_manager; - wxArrayInt m_index; // in the wxMimeTypesManagerImpl arrays + // All that is needed to query type info - UTI and pointer to the manager + wxString m_uti; + wxMimeTypesManagerImpl* m_manager; + + friend class wxMimeTypesManagerImpl; }; #endif // wxUSE_MIMETYPE - -#endif // _MIMETYPE_IMPL_H +#endif //_MIMETYPE_IMPL_H diff --git a/include/wx/osx/icon.h b/include/wx/osx/icon.h index 76113f7121..1728e07ec5 100644 --- a/include/wx/osx/icon.h +++ b/include/wx/osx/icon.h @@ -58,6 +58,11 @@ protected: private: DECLARE_DYNAMIC_CLASS(wxIcon) + + bool LoadIconFromSystemResource(const wxString& resourceName, int desiredWidth, int desiredHeight); + bool LoadIconFromBundleResource(const wxString& resourceName, int desiredWidth, int desiredHeight); + bool LoadIconFromFile(const wxString& filename, int desiredWidth, int desiredHeight); + bool LoadIconAsBitmap(const wxString& filename, wxBitmapType flags = wxICON_DEFAULT_TYPE, int desiredWidth = -1, int desiredHeight = -1); }; class WXDLLIMPEXP_CORE wxICONResourceHandler: public wxBitmapHandler diff --git a/src/common/mimecmn.cpp b/src/common/mimecmn.cpp index 2171a14328..f159809a84 100644 --- a/src/common/mimecmn.cpp +++ b/src/common/mimecmn.cpp @@ -48,7 +48,7 @@ // implementation classes: #if defined(__WXMSW__) #include "wx/msw/mimetype.h" -#elif ( defined(__WXMAC__) && wxOSX_USE_CARBON ) +#elif ( defined(__WXMAC__) ) #include "wx/osx/mimetype.h" #elif defined(__WXPM__) || defined (__EMX__) #include "wx/os2/mimetype.h" diff --git a/src/osx/carbon/icon.cpp b/src/osx/carbon/icon.cpp index fa46e15088..e156e55a18 100644 --- a/src/osx/carbon/icon.cpp +++ b/src/osx/carbon/icon.cpp @@ -167,178 +167,248 @@ void wxIcon::SetHeight( int WXUNUSED(height) ) { } +// Load an icon based on resource name or filel name +// Return true on success, false otherwise bool wxIcon::LoadFile( const wxString& filename, wxBitmapType type, int desiredWidth, int desiredHeight ) +{ + if( type == wxBITMAP_TYPE_ICON_RESOURCE ) + { + if( LoadIconFromSystemResource( filename, desiredWidth, desiredHeight ) ) + return true; + else + return LoadIconFromBundleResource( filename, desiredWidth, desiredHeight ); + } + else if( type == wxBITMAP_TYPE_ICON ) + { + return LoadIconFromFile( filename, desiredWidth, desiredHeight ); + } + else + { + return LoadIconAsBitmap( filename, type, desiredWidth, desiredHeight ); + } +} + +// Load a well known system icon by its wxWidgets identifier +// Returns true on success, false otherwise +bool wxIcon::LoadIconFromSystemResource(const wxString& resourceName, int desiredWidth, int desiredHeight) { UnRef(); - if ( type == wxBITMAP_TYPE_ICON_RESOURCE ) + OSType theId = 0 ; + + if ( resourceName == wxT("wxICON_INFORMATION") ) + { + theId = kAlertNoteIcon ; + } + else if ( resourceName == wxT("wxICON_QUESTION") ) + { + theId = kAlertCautionIcon ; + } + else if ( resourceName == wxT("wxICON_WARNING") ) + { + theId = kAlertCautionIcon ; + } + else if ( resourceName == wxT("wxICON_ERROR") ) { - OSType theId = 0 ; + theId = kAlertStopIcon ; + } + else if ( resourceName == wxT("wxICON_FOLDER") ) + { + theId = kGenericFolderIcon ; + } + else if ( resourceName == wxT("wxICON_FOLDER_OPEN") ) + { + theId = kOpenFolderIcon ; + } + else if ( resourceName == wxT("wxICON_NORMAL_FILE") ) + { + theId = kGenericDocumentIcon ; + } + else if ( resourceName == wxT("wxICON_EXECUTABLE_FILE") ) + { + theId = kGenericApplicationIcon ; + } + else if ( resourceName == wxT("wxICON_CDROM") ) + { + theId = kGenericCDROMIcon ; + } + else if ( resourceName == wxT("wxICON_FLOPPY") ) + { + theId = kGenericFloppyIcon ; + } + else if ( resourceName == wxT("wxICON_HARDDISK") ) + { + theId = kGenericHardDiskIcon ; + } + else if ( resourceName == wxT("wxICON_REMOVABLE") ) + { + theId = kGenericRemovableMediaIcon ; + } + else if ( resourceName == wxT("wxICON_DELETE") ) + { + theId = kToolbarDeleteIcon ; + } + else if ( resourceName == wxT("wxICON_GO_BACK") ) + { + theId = kBackwardArrowIcon ; + } + else if ( resourceName == wxT("wxICON_GO_FORWARD") ) + { + theId = kForwardArrowIcon ; + } + else if ( resourceName == wxT("wxICON_GO_HOME") ) + { + theId = kToolbarHomeIcon ; + } + else if ( resourceName == wxT("wxICON_HELP_SETTINGS") ) + { + theId = kGenericFontIcon ; + } + else if ( resourceName == wxT("wxICON_HELP_PAGE") ) + { + theId = kGenericDocumentIcon ; + } - if ( filename == wxT("wxICON_INFORMATION") ) - { - theId = kAlertNoteIcon ; - } - else if ( filename == wxT("wxICON_QUESTION") ) - { - theId = kAlertCautionIcon ; - } - else if ( filename == wxT("wxICON_WARNING") ) - { - theId = kAlertCautionIcon ; - } - else if ( filename == wxT("wxICON_ERROR") ) - { - theId = kAlertStopIcon ; - } - else if ( filename == wxT("wxICON_FOLDER") ) - { - theId = kGenericFolderIcon ; - } - else if ( filename == wxT("wxICON_FOLDER_OPEN") ) - { - theId = kOpenFolderIcon ; - } - else if ( filename == wxT("wxICON_NORMAL_FILE") ) - { - theId = kGenericDocumentIcon ; - } - else if ( filename == wxT("wxICON_EXECUTABLE_FILE") ) - { - theId = kGenericApplicationIcon ; - } - else if ( filename == wxT("wxICON_CDROM") ) - { - theId = kGenericCDROMIcon ; - } - else if ( filename == wxT("wxICON_FLOPPY") ) - { - theId = kGenericFloppyIcon ; - } - else if ( filename == wxT("wxICON_HARDDISK") ) - { - theId = kGenericHardDiskIcon ; - } - else if ( filename == wxT("wxICON_REMOVABLE") ) - { - theId = kGenericRemovableMediaIcon ; - } - else if ( filename == wxT("wxICON_DELETE") ) - { - theId = kToolbarDeleteIcon ; - } - else if ( filename == wxT("wxICON_GO_BACK") ) - { - theId = kBackwardArrowIcon ; - } - else if ( filename == wxT("wxICON_GO_FORWARD") ) - { - theId = kForwardArrowIcon ; - } - else if ( filename == wxT("wxICON_GO_HOME") ) - { - theId = kToolbarHomeIcon ; - } - else if ( filename == wxT("wxICON_HELP_SETTINGS") ) - { - theId = kGenericFontIcon ; - } - else if ( filename == wxT("wxICON_HELP_PAGE") ) + if ( theId != 0 ) + { + IconRef iconRef = NULL ; + verify_noerr( GetIconRef( kOnSystemDisk, kSystemIconsCreator, theId, &iconRef ) ) ; + if ( iconRef ) { - theId = kGenericDocumentIcon ; + m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ) ; + return true ; } - else - { - IconRef iconRef = NULL ; + } - // first look in the resource fork - if ( iconRef == NULL ) - { - Str255 theName ; - - wxMacStringToPascal( filename , theName ) ; - Handle resHandle = GetNamedResource( 'icns' , theName ) ; - if ( resHandle != 0L ) - { - IconFamilyHandle iconFamily = (IconFamilyHandle) resHandle ; - HLock((Handle) iconFamily); - OSStatus err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef ); - HUnlock((Handle) iconFamily); - if ( err != noErr ) - { - wxFAIL_MSG("Error when constructing icon ref"); - } - - ReleaseResource( resHandle ) ; - } - } - if ( iconRef == NULL ) - { - // TODO add other attempts to load it from files etc here - } - if ( iconRef ) - { - m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ) ; - return true ; - } - } + return false; +} - if ( theId != 0 ) +// Load an icon of type 'icns' by resource by name +// The resource must exist in one of the currently accessible bundles +// (usually this means the application bundle for the current application) +// Return true on success, false otherwise +bool wxIcon::LoadIconFromBundleResource(const wxString& resourceName, int desiredWidth, int desiredHeight) +{ + UnRef(); + + IconRef iconRef = NULL ; + + // first look in the resource fork + if ( iconRef == NULL ) + { + Str255 theName ; + + wxMacStringToPascal( resourceName , theName ) ; + Handle resHandle = GetNamedResource( 'icns' , theName ) ; + if ( resHandle != 0L ) { - IconRef iconRef = NULL ; - verify_noerr( GetIconRef( kOnSystemDisk, kSystemIconsCreator, theId, &iconRef ) ) ; - if ( iconRef ) - { - m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ) ; + IconFamilyHandle iconFamily = (IconFamilyHandle) resHandle ; + OSStatus err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef ); - return true ; + if ( err != noErr ) + { + wxFAIL_MSG("Error when constructing icon ref"); } + + ReleaseResource( resHandle ) ; } + } - return false ; + if ( iconRef ) + { + m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ); + return true; } - else + + return false; +} + +// Load an icon from an icon file using the underlying OS X API +// The icon file must be in a format understood by the OS +// Return true for success, false otherwise +bool wxIcon::LoadIconFromFile(const wxString& filename, int desiredWidth, int desiredHeight) +{ + UnRef(); + + OSStatus err; + bool result = false; + + // Get a file system reference + FSRef fsRef; + err = FSPathMakeRef( (const wxUint8*)filename.utf8_str().data(), &fsRef, NULL ); + + if( err != noErr ) + return false; + + // Get a handle on the icon family + IconFamilyHandle iconFamily; + err = ReadIconFromFSRef( &fsRef, &iconFamily ); + + if( err != noErr ) + return false; + + // Get the icon reference itself + IconRef iconRef; + err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef ); + + if( err == noErr ) { - wxBitmapHandler *handler = wxBitmap::FindHandler( type ); + // If everthing is OK, assign m_refData + m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ); + result = true; + } - if ( handler ) - { - wxBitmap bmp ; - if ( handler->LoadFile( &bmp , filename, type, desiredWidth, desiredHeight )) - { - CopyFromBitmap( bmp ) ; + // Release the iconFamily before returning + ReleaseResource( (Handle) iconFamily ); + return result; +} - return true ; - } +// Load an icon from a file using functionality from wxWidgets +// A suitable bitmap handler (or image handler) must be available +// Return true on success, false otherwise +bool wxIcon::LoadIconAsBitmap(const wxString& filename, wxBitmapType type, int desiredWidth, int desiredHeight) +{ + UnRef(); - return false ; - } - else + wxBitmapHandler *handler = wxBitmap::FindHandler( type ); + + if ( handler ) + { + wxBitmap bmp ; + if ( handler->LoadFile( &bmp , filename, type, desiredWidth, desiredHeight )) { + CopyFromBitmap( bmp ) ; + return true ; + } + } + #if wxUSE_IMAGE - wxImage loadimage( filename, type ); - if (loadimage.Ok()) - { - if ( desiredWidth == -1 ) - desiredWidth = loadimage.GetWidth() ; - if ( desiredHeight == -1 ) - desiredHeight = loadimage.GetHeight() ; - if ( desiredWidth != loadimage.GetWidth() || desiredHeight != loadimage.GetHeight() ) - loadimage.Rescale( desiredWidth , desiredHeight ) ; + else + { + wxImage loadimage( filename, type ); + if (loadimage.Ok()) + { + if ( desiredWidth == -1 ) + desiredWidth = loadimage.GetWidth() ; + if ( desiredHeight == -1 ) + desiredHeight = loadimage.GetHeight() ; + if ( desiredWidth != loadimage.GetWidth() || desiredHeight != loadimage.GetHeight() ) + loadimage.Rescale( desiredWidth , desiredHeight ) ; - wxBitmap bmp( loadimage ); - CopyFromBitmap( bmp ) ; + wxBitmap bmp( loadimage ); + CopyFromBitmap( bmp ) ; - return true; - } -#endif + return true; } } +#endif + return false; } + void wxIcon::CopyFromBitmap( const wxBitmap& bmp ) { UnRef() ; diff --git a/src/osx/core/mimetype.cpp b/src/osx/core/mimetype.cpp index 6bfd38a8b9..f51c856556 100644 --- a/src/osx/core/mimetype.cpp +++ b/src/osx/core/mimetype.cpp @@ -1,785 +1,662 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/osx/core/mimetype.cpp -// Purpose: classes and functions to manage MIME types -// Author: Vadim Zeitlin, Stefan Csomor +// Purpose: Mac OS X implementation for wx MIME-related classes +// Author: Neil Perkins // Modified by: -// Created: 23.09.98 +// Created: 2010-05-15 // RCS-ID: $Id: mimetype.cpp 54734 2008-07-21 01:33:51Z VZ $ -// Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows licence (part of wxExtra library) +// Copyright: (C) 2010 Neil Perkins +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -// for compilers that support precompilation, includes "wx.h". + #include "wx/wxprec.h" #ifdef __BORLANDC__ - #pragma hdrstop +#pragma hdrstop #endif -#if wxUSE_MIMETYPE && wxUSE_FILE - -#include "wx/unix/mimetype.h" - #ifndef WX_PRECOMP - #include "wx/dynarray.h" - #include "wx/string.h" - #include "wx/intl.h" - #include "wx/log.h" - #include "wx/utils.h" +#include "wx/defs.h" #endif -#include "wx/file.h" -#include "wx/confbase.h" +#if wxUSE_MIMETYPE -#include "wx/ffile.h" -#include "wx/dir.h" -#include "wx/tokenzr.h" -#include "wx/iconloc.h" -#include "wx/filename.h" -#include "wx/app.h" -#include "wx/apptrait.h" +#include "wx/osx/mimetype.h" +#include "wx/osx/private.h" -// other standard headers -#include +///////////////////////////////////////////////////////////////////////////// +// Helper functions +///////////////////////////////////////////////////////////////////////////// -// ---------------------------------------------------------------------------- -// constants -// ---------------------------------------------------------------------------- -// MIME code tracing mask -#define TRACE_MIME wxT("mime") +// Read a string or array of strings from a CFDictionary for a given key +// Return an empty list on error +wxArrayString ReadStringListFromCFDict( CFDictionaryRef dictionary, CFStringRef key ) +{ + // Create an empty list + wxArrayString results; -// ---------------------------------------------------------------------------- -// wxFileTypeImpl (Unix) -// ---------------------------------------------------------------------------- + // Look up the requested key + CFTypeRef valueData = CFDictionaryGetValue( dictionary, key ); -wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const -{ - wxString sTmp; - size_t i = 0; - while ( (i < m_index.GetCount() ) && sTmp.empty() ) + if( valueData ) { - sTmp = m_manager->GetCommand( verb, m_index[i] ); - i++; - } + // Value is an array + if( CFGetTypeID( valueData ) == CFArrayGetTypeID() ) + { + CFArrayRef valueList = reinterpret_cast< CFArrayRef >( valueData ); - return wxFileType::ExpandCommand(sTmp, params); -} + CFTypeRef itemData; + wxCFStringRef item; -bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const -{ - wxString sTmp; - size_t i = 0; - while ( (i < m_index.GetCount() ) && sTmp.empty() ) - { - sTmp = m_manager->m_aIcons[m_index[i]]; - i++; - } + // Look at each item in the array + for( CFIndex i = 0, n = CFArrayGetCount( valueList ); i < n; i++ ) + { + itemData = CFArrayGetValueAtIndex( valueList, i ); - if ( sTmp.empty() ) - return false; + // Make sure the item is a string + if( CFGetTypeID( itemData ) == CFStringGetTypeID() ) + { + // wxCFStringRef will automatically CFRelease, so an extra CFRetain is needed + item = reinterpret_cast< CFStringRef >( itemData ); + wxCFRetain( item.get() ); - if ( iconLoc ) - { - iconLoc->SetFileName(sTmp); + // Add the string to the list + results.Add( item.AsString() ); + } + } + } + + // Value is a single string - return a list of one item + else if( CFGetTypeID( valueData ) == CFStringGetTypeID() ) + { + // wxCFStringRef will automatically CFRelease, so an extra CFRetain is needed + wxCFStringRef value = reinterpret_cast< CFStringRef >( valueData ); + wxCFRetain( value.get() ); + + // Add the string to the list + results.Add( value.AsString() ); + } } - return true; + // Return the list. If the dictionary did not contain key, + // or contained the wrong data type, the list will be empty + return results; } -bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const + +// Given a single CFDictionary representing document type data, check whether +// it matches a particular file extension. Return true for a match, false otherwise +bool CheckDocTypeMatchesExt( CFDictionaryRef docType, CFStringRef requiredExt ) { - mimeTypes.Clear(); - size_t nCount = m_index.GetCount(); - for (size_t i = 0; i < nCount; i++) - mimeTypes.Add(m_manager->m_aTypes[m_index[i]]); + const static wxCFStringRef extKey( "CFBundleTypeExtensions" ); - return true; -} + CFTypeRef extData = CFDictionaryGetValue( docType, extKey ); -size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs, - wxArrayString *commands, - const wxFileType::MessageParameters& params) const -{ - wxString vrb, cmd, sTmp; - size_t count = 0; - wxMimeTypeCommands * sPairs; + if( !extData ) + return false; - // verbs and commands have been cleared already in mimecmn.cpp... - // if we find no entries in the exact match, try the inexact match - for (size_t n = 0; ((count == 0) && (n < m_index.GetCount())); n++) + if( CFGetTypeID( extData ) == CFArrayGetTypeID() ) { - // list of verb = command pairs for this mimetype - sPairs = m_manager->m_aEntries [m_index[n]]; - size_t i; - for ( i = 0; i < sPairs->GetCount(); i++ ) + CFArrayRef extList = reinterpret_cast< CFArrayRef >( extData ); + CFTypeRef extItem; + + for( CFIndex i = 0, n = CFArrayGetCount( extList ); i < n; i++ ) { - vrb = sPairs->GetVerb(i); - // some gnome entries have "." inside - vrb = vrb.AfterLast(wxT('.')); - cmd = sPairs->GetCmd(i); - if (! cmd.empty() ) + extItem = CFArrayGetValueAtIndex( extList, i ); + + if( CFGetTypeID( extItem ) == CFStringGetTypeID() ) { - cmd = wxFileType::ExpandCommand(cmd, params); - count++; - if ( vrb.IsSameAs(wxT("open"))) - { - if ( verbs ) - verbs->Insert(vrb, 0u); - if ( commands ) - commands ->Insert(cmd, 0u); - } - else - { - if ( verbs ) - verbs->Add(vrb); - if ( commands ) - commands->Add(cmd); - } - } + CFStringRef ext = reinterpret_cast< CFStringRef >( extItem ); + + if( CFStringCompare( ext, requiredExt, kCFCompareCaseInsensitive ) == kCFCompareEqualTo ) + return true; + } } } - return count; + if( CFGetTypeID( extData ) == CFStringGetTypeID() ) + { + CFStringRef ext = reinterpret_cast< CFStringRef >( extData ); + + if( CFStringCompare( ext, requiredExt, kCFCompareCaseInsensitive ) == kCFCompareEqualTo ) + return true; + } + + return false; } -bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) + +// Given a data structure representing document type data, or a list of such +// structures, find the one which matches a particular file extension +// The result will be a CFDictionary containining document type data +// if a match is found, or null otherwise +CFDictionaryRef GetDocTypeForExt( CFTypeRef docTypeData, CFStringRef requiredExt ) { - const wxString strExtensions = m_manager->GetExtension(m_index[0]); - extensions.Empty(); + CFDictionaryRef docType; + CFArrayRef docTypes; + CFTypeRef item; + + if( !docTypeData ) + return NULL; - // one extension in the space or comma-delimited list - wxString strExt; - wxString::const_iterator end = strExtensions.end(); - for ( wxString::const_iterator p = strExtensions.begin(); /* nothing */; ++p ) + if( CFGetTypeID( docTypeData ) == CFArrayGetTypeID() ) { - if ( p == end || *p == wxT(' ') || *p == wxT(',') ) - { - if ( !strExt.empty() ) - { - extensions.Add(strExt); - strExt.Empty(); - } - //else: repeated spaces - // (shouldn't happen, but it's not that important if it does happen) + docTypes = reinterpret_cast< CFArrayRef >( docTypeData ); - if ( p == end ) - break; - } - else if ( *p == wxT('.') ) + for( CFIndex i = 0, n = CFArrayGetCount( docTypes ); i < n; i++ ) { - // remove the dot from extension (but only if it's the first char) - if ( !strExt.empty() ) + item = CFArrayGetValueAtIndex( docTypes, i ); + + if( CFGetTypeID( item ) == CFDictionaryGetTypeID() ) { - strExt += wxT('.'); + docType = reinterpret_cast< CFDictionaryRef >( item ); + + if( CheckDocTypeMatchesExt( docType, requiredExt ) ) + return docType; } - //else: no, don't append it - } - else - { - strExt += *p; } } - return true; + if( CFGetTypeID( docTypeData ) == CFDictionaryGetTypeID() ) + { + CFDictionaryRef docType = reinterpret_cast< CFDictionaryRef >( docTypeData ); + + if( CheckDocTypeMatchesExt( docType, requiredExt ) ) + return docType; + } + + return NULL; } -// set an arbitrary command: -// could adjust the code to ask confirmation if it already exists and -// overwriteprompt is true, but this is currently ignored as *Associate* has -// no overwrite prompt -bool -wxFileTypeImpl::SetCommand(const wxString& cmd, - const wxString& verb, - bool WXUNUSED(overwriteprompt)) + +// Given an application bundle reference and the name of an icon file +// which is a resource in that bundle, look up the full (posix style) +// path to that icon. Returns the path, or an empty wxString on failure +wxString GetPathForIconFile( CFBundleRef bundle, CFStringRef iconFile ) { - wxArrayString strExtensions; - wxString strDesc, strIcon; + // If either parameter is NULL there is no hope of success + if( !bundle || !iconFile ) + return wxEmptyString; - wxArrayString strTypes; - GetMimeTypes(strTypes); - if ( strTypes.IsEmpty() ) - return false; + // Create a range object representing the whole string + CFRange wholeString; + wholeString.location = 0; + wholeString.length = CFStringGetLength( iconFile ); - wxMimeTypeCommands *entry = new wxMimeTypeCommands(); - entry->Add(verb + wxT("=") + cmd + wxT(" %s ")); + // Index of the period in the file name for iconFile + UniCharCount periodIndex; - bool ok = false; - size_t nCount = strTypes.GetCount(); - for ( size_t i = 0; i < nCount; i++ ) + // In order to locate the period delimiting the extension, + // iconFile must be represented as UniChar[] { - if ( m_manager->DoAssociation - ( - strTypes[i], - strIcon, - entry, - strExtensions, - strDesc - ) ) - { - // DoAssociation() took ownership of entry, don't delete it below - ok = true; - } - } + // Allocate a buffer and copy in the iconFile string + UniChar* buffer = new UniChar[ wholeString.length ]; + CFStringGetCharacters( iconFile, wholeString, buffer ); - if ( !ok ) - delete entry; + // Locate the period character + OSStatus status = LSGetExtensionInfo( wholeString.length, buffer, &periodIndex ); - return ok; -} + // Deallocate the buffer + delete buffer; -// ignore index on the grounds that we only have one icon in a Unix file -bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int WXUNUSED(index)) -{ - if (strIcon.empty()) - return false; + // If the period could not be located it will not be possible to get the URL + if( status != noErr || periodIndex == kLSInvalidExtensionIndex ) + return wxEmptyString; + } - wxArrayString strExtensions; - wxString strDesc; + // Range representing the name part of iconFile + CFRange iconNameRange; + iconNameRange.location = 0; + iconNameRange.length = periodIndex - 1; - wxArrayString strTypes; - GetMimeTypes(strTypes); - if ( strTypes.IsEmpty() ) - return false; + // Range representing the extension part of iconFile + CFRange iconExtRange; + iconExtRange.location = periodIndex; + iconExtRange.length = wholeString.length - periodIndex; - wxMimeTypeCommands *entry = new wxMimeTypeCommands(); - bool ok = false; - size_t nCount = strTypes.GetCount(); - for ( size_t i = 0; i < nCount; i++ ) - { - if ( m_manager->DoAssociation - ( - strTypes[i], - strIcon, - entry, - strExtensions, - strDesc - ) ) - { - // we don't need to free entry now, DoAssociation() took ownership - // of it - ok = true; - } - } + // Get the name and extension strings + wxCFStringRef iconName = CFStringCreateWithSubstring( kCFAllocatorDefault, iconFile, iconNameRange ); + wxCFStringRef iconExt = CFStringCreateWithSubstring( kCFAllocatorDefault, iconFile, iconExtRange ); + + // Now it is possible to query the URL for the icon as a resource + wxCFRef< CFURLRef > iconUrl = wxCFRef< CFURLRef >( CFBundleCopyResourceURL( bundle, iconName, iconExt, NULL ) ); - if ( !ok ) - delete entry; + if( !iconUrl.get() ) + return wxEmptyString; - return ok; + // All being well, return the icon path + return wxCFStringRef( CFURLCopyFileSystemPath( iconUrl, kCFURLPOSIXPathStyle ) ).AsString(); } -// ---------------------------------------------------------------------------- -// wxMimeTypesManagerImpl (Unix) -// ---------------------------------------------------------------------------- wxMimeTypesManagerImpl::wxMimeTypesManagerImpl() { - m_initialized = false; } -void wxMimeTypesManagerImpl::InitIfNeeded() +wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl() { - if ( !m_initialized ) - { - // set the flag first to prevent recursion - m_initialized = true; +} - wxString wm = wxTheApp->GetTraits()->GetDesktopEnvironment(); - if (wm == wxT("KDE")) - Initialize( wxMAILCAP_KDE ); - else if (wm == wxT("GNOME")) - Initialize( wxMAILCAP_GNOME ); - else - Initialize(); - } -} +///////////////////////////////////////////////////////////////////////////// +// Init / shutdown functions +// +// The Launch Services / UTI API provides no helpful way of getting a list +// of all registered types. Instead the API is focused arround looking up +// information for a particular file type once you already have some +// identifying piece of information. In order to get a list of registered +// types it would first be necessary to get a list of all bundles exporting +// type information (all application bundles may be sufficient) then look at +// the Info.plist file for those bundles and store the type information. As +// this would require trawling the hard disk when a wxWidgets program starts +// up it was decided instead to load the information lazily. +// +// If this behaviour really messes up your app, please feel free to implement +// the trawling approach (perhaps with a configure switch?). A good place to +// start would be CFBundleCreateBundlesFromDirectory( NULL, "/Applications", "app" ) +///////////////////////////////////////////////////////////////////////////// +void wxMimeTypesManagerImpl::Initialize(int WXUNUSED(mailcapStyles), const wxString& WXUNUSED(extraDir)) +{ + // NO-OP +} -// read system and user mailcaps and other files -void wxMimeTypesManagerImpl::Initialize(int WXUNUSED(mailcapStyles), - const wxString& WXUNUSED(sExtraDir)) +void wxMimeTypesManagerImpl::ClearData() { -#ifdef __VMS - // XDG tables are never installed on OpenVMS - return; -#endif -#if 0 - // Read MIME type - extension associations - LoadXDGGlobs( "/usr/share/mime/globs" ); - LoadXDGGlobs( "/usr/local/share/mime/globs" ); - - // Load desktop files for XDG, and then override them with the defaults. - // We will override them one desktop file at a time, rather - // than one mime type at a time, but it should be a reasonable - // heuristic. - { - wxString xdgDataHome = wxGetenv("XDG_DATA_HOME"); - if ( xdgDataHome.empty() ) - xdgDataHome = wxGetHomeDir() + "/.local/share"; - wxString xdgDataDirs = wxGetenv("XDG_DATA_DIRS"); - if ( xdgDataDirs.empty() ) - { - xdgDataDirs = "/usr/local/share:/usr/share"; - if (mailcapStyles & wxMAILCAP_GNOME) - xdgDataDirs += ":/usr/share/gnome:/opt/gnome/share"; - if (mailcapStyles & wxMAILCAP_KDE) - xdgDataDirs += ":/usr/share/kde3:/opt/kde3/share"; - } - if ( !sExtraDir.empty() ) - { - xdgDataDirs += ':'; - xdgDataDirs += sExtraDir; - } + // NO-OP +} - wxArrayString dirs; - wxStringTokenizer tokenizer(xdgDataDirs, ":"); - while ( tokenizer.HasMoreTokens() ) - { - wxString p = tokenizer.GetNextToken(); - dirs.Add(p); - } - dirs.insert(dirs.begin(), xdgDataHome); - wxString defaultsList; - size_t i; - for (i = 0; i < dirs.GetCount(); i++) - { - wxString f = dirs[i]; - if (f.Last() != '/') f += '/'; - f += "applications/defaults.list"; - if (wxFileExists(f)) - { - defaultsList = f; - break; - } - } +///////////////////////////////////////////////////////////////////////////// +// Lookup functions +// +// Apple uses a number of different systems for file type information. +// As of Spring 2010, these include: +// +// OS Types / OS Creators +// File Extensions +// Mime Types +// Uniform Type Identifiers (UTI) +// +// This implementation of the type manager for Mac supports all except OS +// Type / OS Creator codes, which have been deprecated for some time with +// less and less support in recent versions of OS X. +// +// The UTI system is the internal system used by OS X, as such it offers a +// one-to-one mapping with file types understood by Mac OS X and is the +// easiest way to convert between type systems. However, UTI meta-data is +// not stored with data files (as of OS X 10.6), instead the OS looks at +// the file extension and uses this to determine the UTI. Simillarly, most +// applications do not yet advertise the file types they can handle by UTI. +// +// The result is that no one typing system is suitable for all tasks. Further, +// as there is not a one-to-one mapping between type systems for the +// description of any given type, it follows that ambiguity cannot be precluded, +// whichever system is taken to be the "master". +// +// In the implementation below I have used UTI as the master key for looking +// up file types. Extensions and mime types are mapped to UTIs and the data +// for each UTI contains a list of all associated extensions and mime types. +// This has the advantage that unknown types will still be assigned a unique +// ID, while using any other system as the master could result in conflicts +// if there were no mime type assigned to an extension or vice versa. However +// there is still plenty of room for ambiguity if two or more applications +// are fighting over ownership of a particular type or group of types. +// +// If this proves to be serious issue it may be helpful to add some slightly +// more cleve logic to the code so that the key used to look up a file type is +// always first in the list in the resulting wxFileType object. I.e, if you +// look up .mpeg3 the list you get back could be .mpeg3, mp3, .mpg3, while +// looking up .mp3 would give .mp3, .mpg3, .mpeg3. The simplest way to do +// this would probably to keep two separate sets of data, one for lookup +// by extetnsion and one for lookup by mime type. +// +// One other point which may require consideration is handling of unrecognised +// types. Using UTI these will be assigned a unique ID of dyn.xxx. This will +// result in a wxFileType object being returned, although querying properties +// on that object will fail. If it would be more helpful to return NULL in this +// case a suitable check can be added. +///////////////////////////////////////////////////////////////////////////// - // Load application files and associate them to corresponding mime types. - size_t nDirs = dirs.GetCount(); - for (size_t nDir = 0; nDir < nDirs; nDir++) - { - wxString dirStr = dirs[nDir]; - if (dirStr.Last() != '/') dirStr += '/'; - dirStr += "applications"; - LoadXDGAppsFilesFromDir(dirStr); - } +// Look up a file type by extension +// The extensions if mapped to a UTI +// If the requested extension is not know the OS is querried and the results saved +wxFileType *wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext) +{ + wxString uti; - if (!defaultsList.IsEmpty()) - { - wxArrayString deskTopFilesSeen; + const TagMap::const_iterator extItr = m_extMap.find( ext ); - wxMimeTextFile textfile(defaultsList); - if ( textfile.Open() ) - { - int nIndex = textfile.pIndexOf( wxT("[Default Applications]") ); - if (nIndex != wxNOT_FOUND) - { - for (i = nIndex+1; i < textfile.GetLineCount(); i++) - { - if (textfile.GetLine(i).Find(wxT("=")) != wxNOT_FOUND) - { - wxString mimeType = textfile.GetVerb(i); - wxString desktopFile = textfile.GetCmd(i); - - if (deskTopFilesSeen.Index(desktopFile) == wxNOT_FOUND) - { - deskTopFilesSeen.Add(desktopFile); - size_t j; - for (j = 0; j < dirs.GetCount(); j++) - { - wxString desktopPath = dirs[j]; - if (desktopPath.Last() != '/') desktopPath += '/'; - desktopPath += "applications/"; - desktopPath += desktopFile; - - if (wxFileExists(desktopPath)) - LoadXDGApp(desktopPath); - } - } - } - } - } - } - } + if( extItr == m_extMap.end() ) + { + wxCFStringRef utiRef = UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, wxCFStringRef( ext ), NULL ); + m_extMap[ ext ] = uti = utiRef.AsString(); } -#endif + else + uti = extItr->second; + + return GetFileTypeFromUti( uti ); } -// clear data so you can read another group of WM files -void wxMimeTypesManagerImpl::ClearData() +// Look up a file type by mime type +// The mime type is mapped to a UTI +// If the requested extension is not know the OS is querried and the results saved +wxFileType *wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) { - m_aTypes.Clear(); - m_aIcons.Clear(); - m_aExtensions.Clear(); - m_aDescriptions.Clear(); + wxString uti; - WX_CLEAR_ARRAY(m_aEntries); - m_aEntries.Empty(); + const TagMap::const_iterator mimeItr = m_mimeMap.find( mimeType ); + + if( mimeItr == m_mimeMap.end() ) + { + wxCFStringRef utiRef = UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, wxCFStringRef( mimeType ), NULL ); + m_mimeMap[ mimeType ] = uti = utiRef.AsString(); + } + else + uti = mimeItr->second; + + return GetFileTypeFromUti( uti ); } -wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl() +// Look up a file type by UTI +// If the requested extension is not know the OS is querried and the results saved +wxFileType *wxMimeTypesManagerImpl::GetFileTypeFromUti(const wxString& uti) { - ClearData(); + UtiMap::const_iterator utiItr = m_utiMap.find( uti ); + + if( utiItr == m_utiMap.end() ) + { + LoadTypeDataForUti( uti ); + LoadDisplayDataForUti( uti ); + } + + wxFileType* const ft = new wxFileType; + ft->m_impl->m_uti = uti; + ft->m_impl->m_manager = this; + + return ft; } -wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) -{ - InitIfNeeded(); - wxString strType = ftInfo.GetMimeType(); - wxString strDesc = ftInfo.GetDescription(); - wxString strIcon = ftInfo.GetIconFile(); +///////////////////////////////////////////////////////////////////////////// +// Load functions +// +// These functions query the OS for information on a particular file type +///////////////////////////////////////////////////////////////////////////// - wxMimeTypeCommands *entry = new wxMimeTypeCommands(); - if ( ! ftInfo.GetOpenCommand().empty()) - entry->Add(wxT("open=") + ftInfo.GetOpenCommand() + wxT(" %s ")); - if ( ! ftInfo.GetPrintCommand().empty()) - entry->Add(wxT("print=") + ftInfo.GetPrintCommand() + wxT(" %s ")); +// Look up all extensions and mime types associated with a UTI +void wxMimeTypesManagerImpl::LoadTypeDataForUti(const wxString& uti) +{ + // Keys in to the UTI declaration plist + const static wxCFStringRef tagsKey( "UTTypeTagSpecification" ); + const static wxCFStringRef extKey( "public.filename-extension" ); + const static wxCFStringRef mimeKey( "public.mime-type" ); - // now find where these extensions are in the data store and remove them - wxArrayString sA_Exts = ftInfo.GetExtensions(); - wxString sExt, sExtStore; - size_t i, nIndex; - size_t nExtCount = sA_Exts.GetCount(); - for (i=0; i < nExtCount; i++) - { - sExt = sA_Exts.Item(i); + // Get the UTI as a CFString + wxCFStringRef utiRef( uti ); - // clean up to just a space before and after - sExt.Trim().Trim(false); - sExt = wxT(' ') + sExt + wxT(' '); - size_t nCount = m_aExtensions.GetCount(); - for (nIndex = 0; nIndex < nCount; nIndex++) - { - sExtStore = m_aExtensions.Item(nIndex); - if (sExtStore.Replace(sExt, wxT(" ") ) > 0) - m_aExtensions.Item(nIndex) = sExtStore; - } - } + // Get a copy of the UTI declaration + wxCFRef< CFDictionaryRef > utiDecl; + utiDecl = wxCFRef< CFDictionaryRef >( UTTypeCopyDeclaration( utiRef ) ); - if ( !DoAssociation(strType, strIcon, entry, sA_Exts, strDesc) ) - return NULL; + if( !utiDecl ) + return; - return GetFileTypeFromMimeType(strType); -} + // Get the tags spec (the section of a UTI declaration containing mappings to other type systems) + CFTypeRef tagsData = CFDictionaryGetValue( utiDecl, tagsKey ); -bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType, - const wxString& strIcon, - wxMimeTypeCommands *entry, - const wxArrayString& strExtensions, - const wxString& strDesc) -{ - int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true); + if( CFGetTypeID( tagsData ) != CFDictionaryGetTypeID() ) + return; - if ( nIndex == wxNOT_FOUND ) - return false; + CFDictionaryRef tags = reinterpret_cast< CFDictionaryRef >( tagsData ); - return true; + // Read tags for extensions and mime types + m_utiMap[ uti ].extensions = ReadStringListFromCFDict( tags, extKey ); + m_utiMap[ uti ].mimeTypes = ReadStringListFromCFDict( tags, mimeKey ); } -int wxMimeTypesManagerImpl::AddToMimeData(const wxString& strType, - const wxString& strIcon, - wxMimeTypeCommands *entry, - const wxArrayString& strExtensions, - const wxString& strDesc, - bool replaceExisting) + +// Look up the (locale) display name and icon file associated with a UTI +void wxMimeTypesManagerImpl::LoadDisplayDataForUti(const wxString& uti) { - InitIfNeeded(); + // Keys in to Info.plist + const static wxCFStringRef docTypesKey( "CFBundleDocumentTypes" ); + const static wxCFStringRef descKey( "CFBundleTypeName" ); + const static wxCFStringRef iconKey( "CFBundleTypeIconFile" ); - // ensure mimetype is always lower case - wxString mimeType = strType.Lower(); + // The call for finding the preferred application for a UTI is LSCopyDefaultRoleHandlerForContentType + // This returns an empty string on OS X 10.5 + // Instead it is necessary to get the primary extension and use LSGetApplicationForInfo + wxCFStringRef ext = UTTypeCopyPreferredTagWithClass( wxCFStringRef( uti ), kUTTagClassFilenameExtension ); - // is this a known MIME type? - int nIndex = m_aTypes.Index(mimeType); - if ( nIndex == wxNOT_FOUND ) - { - // new file type - m_aTypes.Add(mimeType); - m_aIcons.Add(strIcon); - m_aEntries.Add(entry ? entry : new wxMimeTypeCommands); + // Look up the preferred application + CFURLRef appUrl; + OSStatus status = LSGetApplicationForInfo( kLSUnknownType, kLSUnknownCreator, ext, kLSRolesAll, NULL, &appUrl ); - // change nIndex so we can use it below to add the extensions - m_aExtensions.Add(wxEmptyString); - nIndex = m_aExtensions.size() - 1; + if( status != noErr ) + return; - m_aDescriptions.Add(strDesc); - } - else // yes, we already have it - { - if ( replaceExisting ) - { - // if new description change it - if ( !strDesc.empty()) - m_aDescriptions[nIndex] = strDesc; + // Create a bundle object for that application + wxCFRef< CFBundleRef > bundle; + bundle = wxCFRef< CFBundleRef >( CFBundleCreate( kCFAllocatorDefault, appUrl ) ); - // if new icon change it - if ( !strIcon.empty()) - m_aIcons[nIndex] = strIcon; + if( !bundle ) + return; - if ( entry ) - { - delete m_aEntries[nIndex]; - m_aEntries[nIndex] = entry; - } - } - else // add data we don't already have ... - { - // if new description add only if none - if ( m_aDescriptions[nIndex].empty() ) - m_aDescriptions[nIndex] = strDesc; + // Get a all the document type data in this bundle + CFTypeRef docTypeData; + docTypeData = CFBundleGetValueForInfoDictionaryKey( bundle, docTypesKey ); - // if new icon and no existing icon - if ( m_aIcons[nIndex].empty() ) - m_aIcons[nIndex] = strIcon; + if( !docTypeData ) + return; - // add any new entries... - if ( entry ) - { - wxMimeTypeCommands *entryOld = m_aEntries[nIndex]; + // Find the document type entry that matches ext + CFDictionaryRef docType; + docType = GetDocTypeForExt( docTypeData, ext ); - size_t count = entry->GetCount(); - for ( size_t i = 0; i < count; i++ ) - { - const wxString& verb = entry->GetVerb(i); - if ( !entryOld->HasVerb(verb) ) - { - entryOld->AddOrReplaceVerb(verb, entry->GetCmd(i)); - } - } + if( !docType ) + return; - // as we don't store it anywhere, it won't be deleted later as - // usual -- do it immediately instead - delete entry; - } - } - } + // Get the display name for docType + wxCFStringRef description = reinterpret_cast< CFStringRef >( CFDictionaryGetValue( docType, descKey ) ); + wxCFRetain( description.get() ); + m_utiMap[ uti ].description = description.AsString(); - // always add the extensions to this mimetype - wxString& exts = m_aExtensions[nIndex]; + // Get the icon path for docType + CFStringRef iconFile = reinterpret_cast< CFStringRef > ( CFDictionaryGetValue( docType, iconKey ) ); + m_utiMap[ uti ].iconLoc.SetFileName( GetPathForIconFile( bundle, iconFile ) ); +} - // add all extensions we don't have yet - wxString ext; - size_t count = strExtensions.GetCount(); - for ( size_t i = 0; i < count; i++ ) - { - ext = strExtensions[i]; - ext += wxT(' '); - if ( exts.Find(ext) == wxNOT_FOUND ) - { - exts += ext; - } - } - // check data integrity - wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() && - m_aTypes.GetCount() == m_aExtensions.GetCount() && - m_aTypes.GetCount() == m_aIcons.GetCount() && - m_aTypes.GetCount() == m_aDescriptions.GetCount() ); +///////////////////////////////////////////////////////////////////////////// +// The remaining functionality from the public interface of +// wxMimeTypesManagerImpl is not implemented. +// +// Please see the note further up this file on Initialise/Clear to explain why +// EnumAllFileTypes is not available. +// +// Some thought will be needed before implementing Associate / Unassociate +// for OS X to ensure proper integration with the many file type and +// association mechanisms already used by the OS. Leaving these methods as +// NO-OP on OS X and asking client apps to put suitable entries in their +// Info.plist files when building their OS X bundle may well be the +// correct solution. +///////////////////////////////////////////////////////////////////////////// - return nIndex; + +size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& WXUNUSED(mimetypes)) +{ + return 0; } -wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext) +wxFileType *wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& WXUNUSED(ftInfo)) { - if (ext.empty() ) - return NULL; + return 0; +} - InitIfNeeded(); +bool wxMimeTypesManagerImpl::Unassociate(wxFileType *WXUNUSED(ft)) +{ + return false; +} - size_t count = m_aExtensions.GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - wxStringTokenizer tk(m_aExtensions[n], wxT(' ')); - while ( tk.HasMoreTokens() ) - { - // consider extensions as not being case-sensitive - if ( tk.GetNextToken().IsSameAs(ext, false /* no case */) ) - { - // found - wxFileType *fileType = new wxFileType; - fileType->m_impl->Init(this, n); +///////////////////////////////////////////////////////////////////////////// +// Getter methods +// +// These methods are private and should only ever be called by wxFileTypeImpl +// after the required information has been loaded. It should not be possible +// to get a wxFileTypeImpl for a UTI without information for that UTI being +// querried, however it is possible that some information may not have been +// found. +///////////////////////////////////////////////////////////////////////////// - return fileType; - } - } - } - return NULL; -} -wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) +bool wxMimeTypesManagerImpl::GetExtensions(const wxString& uti, wxArrayString& extensions) { - InitIfNeeded(); + const UtiMap::const_iterator itr = m_utiMap.find( uti ); - wxFileType * fileType = NULL; - // mime types are not case-sensitive - wxString mimetype(mimeType); - mimetype.MakeLower(); - - // first look for an exact match - int index = m_aTypes.Index(mimetype); - - if ( index != wxNOT_FOUND ) + if( itr == m_utiMap.end() || itr->second.extensions.GetCount() < 1 ) { - fileType = new wxFileType; - fileType->m_impl->Init(this, index); + extensions.Clear(); + return false; } - // then try to find "text/*" as match for "text/plain" (for example) - // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return - // the whole string - ok. + extensions = itr->second.extensions; + return true; +} - index = wxNOT_FOUND; - wxString strCategory = mimetype.BeforeFirst(wxT('/')); +bool wxMimeTypesManagerImpl::GetMimeType(const wxString& uti, wxString *mimeType) +{ + const UtiMap::const_iterator itr = m_utiMap.find( uti ); - size_t nCount = m_aTypes.GetCount(); - for ( size_t n = 0; n < nCount; n++ ) + if( itr == m_utiMap.end() || itr->second.mimeTypes.GetCount() < 1 ) { - if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) && - m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) - { - index = n; - break; - } + *mimeType = wxEmptyString; + return false; } - if ( index != wxNOT_FOUND ) + *mimeType = itr->second.mimeTypes[ 0 ]; + return true; +} + +bool wxMimeTypesManagerImpl::GetMimeTypes(const wxString& uti, wxArrayString& mimeTypes) +{ + const UtiMap::const_iterator itr = m_utiMap.find( uti ); + + if( itr == m_utiMap.end() || itr->second.mimeTypes.GetCount() < 1 ) { - // don't throw away fileType that was already found - if (!fileType) - fileType = new wxFileType; - fileType->m_impl->Init(this, index); + mimeTypes.Clear(); + return false; } - return fileType; + mimeTypes = itr->second.mimeTypes; + return true; } -wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const +bool wxMimeTypesManagerImpl::GetIcon(const wxString& uti, wxIconLocation *iconLoc) { - wxString command, testcmd, sV, sTmp; - sV = verb + wxT("="); - - // list of verb = command pairs for this mimetype - wxMimeTypeCommands * sPairs = m_aEntries [nIndex]; + const UtiMap::const_iterator itr = m_utiMap.find( uti ); - size_t i; - size_t nCount = sPairs->GetCount(); - for ( i = 0; i < nCount; i++ ) + if( itr == m_utiMap.end() || !itr->second.iconLoc.IsOk() ) { - sTmp = sPairs->GetVerbCmd (i); - if ( sTmp.Contains(sV) ) - command = sTmp.AfterFirst(wxT('=')); + *iconLoc = wxIconLocation(); + return false; } - return command; + *iconLoc = itr->second.iconLoc; + return true; } -void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype) +bool wxMimeTypesManagerImpl::GetDescription(const wxString& uti, wxString *desc) { - InitIfNeeded(); + const UtiMap::const_iterator itr = m_utiMap.find( uti ); - wxString extensions; - const wxArrayString& exts = filetype.GetExtensions(); - size_t nExts = exts.GetCount(); - for ( size_t nExt = 0; nExt < nExts; nExt++ ) + if( itr == m_utiMap.end() || itr->second.description.IsNull() ) { - if ( nExt > 0 ) - extensions += wxT(' '); - - extensions += exts[nExt]; + *desc = wxEmptyString; + return false; } - AddMimeTypeInfo(filetype.GetMimeType(), - extensions, - filetype.GetDescription()); + *desc = itr->second.description; + return true; } -void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType, - const wxString& strExtensions, - const wxString& strDesc) + +///////////////////////////////////////////////////////////////////////////// +// The remaining functionality has not yet been implemented for OS X +///////////////////////////////////////////////////////////////////////////// + +wxFileTypeImpl::wxFileTypeImpl() { - // reading mailcap may find image/* , while - // reading mime.types finds image/gif and no match is made - // this means all the get functions don't work fix this - wxString strIcon; - wxString sTmp = strExtensions; +} - wxArrayString sExts; - sTmp.Trim().Trim(false); +wxFileTypeImpl::~wxFileTypeImpl() +{ +} - while (!sTmp.empty()) - { - sExts.Add(sTmp.AfterLast(wxT(' '))); - sTmp = sTmp.BeforeLast(wxT(' ')); - } +// Query wxMimeTypesManagerImple to get real information for a file type +bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) const +{ + return m_manager->GetExtensions( m_uti, extensions ); +} - AddToMimeData(strMimeType, strIcon, NULL, sExts, strDesc, true); +bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const +{ + return m_manager->GetMimeType( m_uti, mimeType ); } -size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes) -{ - InitIfNeeded(); +bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const +{ + return m_manager->GetMimeTypes( m_uti, mimeTypes ); +} - mimetypes.Empty(); +bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const +{ + return m_manager->GetIcon( m_uti, iconLoc ); +} - size_t count = m_aTypes.GetCount(); - for ( size_t n = 0; n < count; n++ ) - { - // don't return template types from here (i.e. anything containg '*') - const wxString &type = m_aTypes[n]; - if ( type.Find(wxT('*')) == wxNOT_FOUND ) - { - mimetypes.Add(type); - } - } +bool wxFileTypeImpl::GetDescription(wxString *desc) const +{ + return m_manager->GetDescription( m_uti, desc ); +} - return mimetypes.GetCount(); +bool wxFileTypeImpl::GetOpenCommand(wxString *WXUNUSED(openCmd), const wxFileType::MessageParameters& WXUNUSED(params)) const +{ + return false; } -// ---------------------------------------------------------------------------- -// writing to MIME type files -// ---------------------------------------------------------------------------- +bool wxFileTypeImpl::GetPrintCommand(wxString *WXUNUSED(printCmd), const wxFileType::MessageParameters& WXUNUSED(params)) const +{ + return false; +} -bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft) +size_t wxFileTypeImpl::GetAllCommands(wxArrayString *WXUNUSED(verbs), wxArrayString *WXUNUSED(commands), const wxFileType::MessageParameters& WXUNUSED(params)) const { - InitIfNeeded(); + return false; +} - wxArrayString sMimeTypes; - ft->GetMimeTypes(sMimeTypes); +bool wxFileTypeImpl::SetCommand(const wxString& WXUNUSED(cmd), const wxString& WXUNUSED(verb), bool WXUNUSED(overwriteprompt)) +{ + return false; +} - size_t i; - size_t nCount = sMimeTypes.GetCount(); - for (i = 0; i < nCount; i ++) - { - const wxString &sMime = sMimeTypes.Item(i); - int nIndex = m_aTypes.Index(sMime); - if ( nIndex == wxNOT_FOUND) - { - // error if we get here ?? - return false; - } - else - { - m_aTypes.RemoveAt(nIndex); - m_aEntries.RemoveAt(nIndex); - m_aExtensions.RemoveAt(nIndex); - m_aDescriptions.RemoveAt(nIndex); - m_aIcons.RemoveAt(nIndex); - } - } - // check data integrity - wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() && - m_aTypes.GetCount() == m_aExtensions.GetCount() && - m_aTypes.GetCount() == m_aIcons.GetCount() && - m_aTypes.GetCount() == m_aDescriptions.GetCount() ); +bool wxFileTypeImpl::SetDefaultIcon(const wxString& WXUNUSED(strIcon), int WXUNUSED(index)) +{ + return false; +} - return true; +bool wxFileTypeImpl::Unassociate(wxFileType *WXUNUSED(ft)) +{ + return false; } -#endif - // wxUSE_MIMETYPE && wxUSE_FILE + +#endif // wxUSE_MIMETYPE + + -- 2.47.2