X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3d1a4878f36ba4b5f66c2ccfd2cb27a9dc528b6f..c9e227f4326f3456ae1e9f30911400fcf978484f:/src/mac/carbon/mimetmac.cpp diff --git a/src/mac/carbon/mimetmac.cpp b/src/mac/carbon/mimetmac.cpp index 29449a6b60..0c34efad85 100644 --- a/src/mac/carbon/mimetmac.cpp +++ b/src/mac/carbon/mimetmac.cpp @@ -1,12 +1,12 @@ ///////////////////////////////////////////////////////////////////////////// // Name: mac/mimetype.cpp -// Purpose: classes and functions to manage MIME types -// Author: Vadim Zeitlin +// Purpose: Mac Carbon implementation for wx mime-related classes +// Author: Ryan Norton // Modified by: -// Created: 23.09.98 +// Created: 04/16/2005 // RCS-ID: $Id$ -// Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows licence (part of wxExtra library) +// Copyright: (c) 2005 Ryan Norton () +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) @@ -28,6 +28,8 @@ #endif //WX_PRECOMP +#if wxUSE_MIMETYPE + #include "wx/log.h" #include "wx/file.h" #include "wx/intl.h" @@ -35,9 +37,30 @@ #include "wx/confbase.h" #include "wx/mac/mimetype.h" +#include "wx/mac/private.h" //wxMacMakeStringFromPascal // other standard headers #include +#include //For mime types + +// +// On the mac there are two ways to open a file - one is through apple events and the +// finder, another is through mime types. +// +// So, really there are two ways to implement wxFileType... +// +// Mime types are only available on OS 8.1+ through the InternetConfig API +// +// Much like the old-style file manager, it has 3 levels of flexibility for its methods - +// Low - which means you have to iterate yourself through the mime database +// Medium - which lets you sort of cache the database if you want to use lowlevel functions +// High - which requires access to the database every time +// +// We want to be efficient (i.e. professional :) ) about it, so we use a combo of low +// and mid-level functions +// +// TODO: Should we call ICBegin/ICEnd? Then where? +// // in case we're compiling in non-GUI mode class WXDLLEXPORT wxIcon; @@ -52,26 +75,224 @@ bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index) return FALSE; } -bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const +bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd, + const wxFileType::MessageParameters& params) const { - return FALSE; + wxString cmd = GetCommand(wxT("open")); + + *openCmd = wxFileType::ExpandCommand(cmd, params); + + return !openCmd->empty(); } -// @@ this function is half implemented -bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) +bool +wxFileTypeImpl::GetPrintCommand(wxString *printCmd, + const wxFileType::MessageParameters& params) + const { - return FALSE; + wxString cmd = GetCommand(wxT("print")); + + *printCmd = wxFileType::ExpandCommand(cmd, params); + + return !printCmd->empty(); } -bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const +/* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */ + + /* IsRemoteVolume can be used to find out if the + volume referred to by vRefNum is a remote volume + located somewhere on a network. the volume's attribute + flags (copied from the GetVolParmsInfoBuffer structure) + are returned in the longword pointed to by vMAttrib. */ +OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) { + HParamBlockRec volPB; + GetVolParmsInfoBuffer volinfo; + OSErr err; + volPB.ioParam.ioVRefNum = vRefNum; + volPB.ioParam.ioNamePtr = NULL; + volPB.ioParam.ioBuffer = (Ptr) &volinfo; + volPB.ioParam.ioReqCount = sizeof(volinfo); + err = PBHGetVolParmsSync(&volPB); + if (err == noErr) { + *isRemote = (volinfo.vMServerAdr != 0); + *vMAttrib = volinfo.vMAttrib; + } + return err; +} + + + /* BuildVolumeList fills the array pointed to by vols with + a list of the currently mounted volumes. If includeRemote + is true, then remote server volumes will be included in + the list. When remote server volumes are included in the + list, they will be added to the end of the list. On entry, + *count should contain the size of the array pointed to by + vols. On exit, *count will be set to the number of id numbers + placed in the array. If vMAttribMask is non-zero, then + only volumes with matching attributes are added to the + list of volumes. bits in the vMAttribMask should use the + same encoding as bits in the vMAttrib field of + the GetVolParmsInfoBuffer structure. */ +OSErr BuildVolumeList(Boolean includeRemote, short *vols, + long *count, long vMAttribMask) { + HParamBlockRec volPB; + Boolean isRemote; + OSErr err; + long nlocal, nremote; + long vMAttrib; + + /* set up and check parameters */ + volPB.volumeParam.ioNamePtr = NULL; + nlocal = nremote = 0; + if (*count == 0) return noErr; + + /* iterate through volumes */ + for (volPB.volumeParam.ioVolIndex = 1; + PBHGetVInfoSync(&volPB) == noErr; + volPB.volumeParam.ioVolIndex++) { + + /* skip remote volumes, if necessary */ + err = IsRemoteVolume(volPB.volumeParam.ioVRefNum, &isRemote, &vMAttrib); + if (err != noErr) goto bail; + if ( ( includeRemote || ! isRemote ) + && (vMAttrib & vMAttribMask) == vMAttribMask ) { + + /* add local volumes at the front, remote + volumes at the end */ + if (isRemote) + vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum; + else { + if (nremote > 0) + BlockMoveData(vols+nlocal, vols+nlocal+1, + nremote*sizeof(short)); + vols[nlocal++] = volPB.volumeParam.ioVRefNum; + } + + /* list full? */ + if ((nlocal + nremote) >= *count) break; + } + } +bail: + *count = (nlocal + nremote); + return err; +} + + + /* FindApplication iterates through mounted volumes + searching for an application with the given creator + type. If includeRemote is true, then remote volumes + will be searched (after local ones) for an application + with the creator type. */ + +#define kMaxVols 20 + +/* Hacked to output to appName */ + +OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName) { + short rRefNums[kMaxVols]; + long i, volCount; + DTPBRec desktopPB; + OSErr err; + FSSpec realappSpec; + FSSpec *appSpec = &realappSpec; + + /* get a list of volumes - with desktop files */ + volCount = kMaxVols; + err = BuildVolumeList(includeRemote, rRefNums, &volCount, + (1< 0 ) + if(!m_manager) + return wxEmptyString; + + if(verb == wxT("open")) { - *mimeType = m_strFileType ; - return TRUE ; + ICMapEntry entry; + OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, + m_lIndex, &entry); + wxASSERT( status == noErr ); + + //Technote 1002 is a godsend in launching apps :) + //The entry in the mimetype database only contains the app + //that's registered - it may not exist... we need to remap the creator + //type and find the right application + Str255 outName; + if(FindApplication(entry.fileCreator, false, outName) != noErr) + return wxEmptyString; + + //TODO: this is only partially correct - + //it should go to the os-specific application path folder (using wxStdPaths maybe?), + //then go to the bundled app and return that full path + return wxMacMakeStringFromPascal(outName); } - else - return FALSE; + return wxEmptyString; +} + +bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) +{ + if(!m_manager) + return false; + + ICMapEntry entry; + OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, + m_lIndex, &entry); + wxASSERT( status == noErr ); + + //entry has period in it + wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); + extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) ); + return true; +} + +bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const +{ + if(!m_manager) + return false; + + ICMapEntry entry; + OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, + m_lIndex, &entry); + wxASSERT( status == noErr ); + + *mimeType = wxMacMakeStringFromPascal(entry.MIMEType); + return true; } bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const @@ -96,122 +317,165 @@ bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const bool wxFileTypeImpl::GetDescription(wxString *desc) const { - return FALSE; + if(!m_manager) + return false; + + ICMapEntry entry; + OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, + m_lIndex, &entry); + wxASSERT( status == noErr ); + + *desc = wxString((char*)entry.entryName, wxConvLocal); + return true; } -size_t -wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands, +size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands, const wxFileType::MessageParameters& params) const { wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") ); return 0; } -void -wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir) +void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir) { - wxFAIL_MSG( _T("wxMimeTypesManagerImpl::Initialize() not yet implemented") ); -} - -void -wxMimeTypesManagerImpl::ClearData() -{ - wxFAIL_MSG( _T("wxMimeTypesManagerImpl::ClearData() not yet implemented") ); -} + wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!")); -// extension -> file type -wxFileType * -wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e) -{ - wxString ext = e ; - ext = ext.Lower() ; - if ( ext == wxT("txt") ) - { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("text/text")); - fileType->m_impl->SetExt(ext); - return fileType; - } - else if ( ext == wxT("htm") || ext == wxT("html") ) - { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("text/html")); - fileType->m_impl->SetExt(ext); - return fileType; - } - else if ( ext == wxT("gif") ) - { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/gif")); - fileType->m_impl->SetExt(ext); - return fileType; - } - else if ( ext == wxT("png" )) - { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/png")); - fileType->m_impl->SetExt(ext); - return fileType; - } - else if ( ext == wxT("jpg" )|| ext == wxT("jpeg") ) - { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/jpeg")); - fileType->m_impl->SetExt(ext); - return fileType; - } - else if ( ext == wxT("bmp") ) + //start internet config - log if there's an error + //the second param is the signature of the application, also known + //as resource ID 0. However, as per some recent discussions, we may not + //have a signature for this app, so a generic 'APPL' which is the executable + //type will work for now + OSStatus status = ICStart( (ICInstance*) &m_hIC, 'APPL'); + + if(status != noErr) { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/bmp")); - fileType->m_impl->SetExt(ext); - return fileType; + wxLogSysError(wxT("Could not initialize wxMimeTypesManager!")); + wxASSERT( false ); + return; } - else if ( ext == wxT("tif") || ext == wxT("tiff") ) + + ICAttr attr; + m_hDatabase = (void**) NewHandle(0); + status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase ); + + //the database file can be corrupt (on OSX its + //~/Library/Preferences/com.apple.internetconfig.plist) + //- bail if it is + if(status != noErr) { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/tiff")); - fileType->m_impl->SetExt(ext); - return fileType; + ClearData(); + wxLogSysError(wxT("Bad Mime Database!")); + return; } - else if ( ext == wxT("xpm") ) + + //obtain the number of entries in the map + status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount ); + wxASSERT( status == noErr ); +} + +void wxMimeTypesManagerImpl::ClearData() +{ + if(m_hIC != NULL) { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/xpm")); - fileType->m_impl->SetExt(ext); - return fileType; + DisposeHandle((Handle)m_hDatabase); + //this can return an error, but we don't really care that much about it + ICStop( (ICInstance) m_hIC ); + m_hIC = NULL; } - else if ( ext == wxT("xbm") ) +} + +// extension -> file type +wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e) +{ + wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); + +// ICMapEntry entry; +// OSStatus status = ICMapEntriesFilename( (ICInstance) m_hIC, (Handle) m_hDatabase, +// (unsigned char*) e.mb_str(wxConvLocal), &entry ); + +// if (status != noErr) +// return NULL; //err or ext not known + //low level functions - iterate through the database + ICMapEntry entry; + wxFileType* pFileType; + long pos; + + for(long i = 1; i <= m_lCount; ++i) { - wxFileType *fileType = new wxFileType; - fileType->m_impl->SetFileType(wxT("image/xbm")); - fileType->m_impl->SetExt(ext); - return fileType; + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); + + if(status == noErr) + { + wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); + if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it + { + pFileType = new wxFileType(); + pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); + break; + } + } } - - // unknown extension - return NULL; + + return pFileType; } // MIME type -> extension -> file type -wxFileType * -wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) +wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) { - return NULL; + wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); + + //low level functions - iterate through the database + ICMapEntry entry; + wxFileType* pFileType; + + long pos; + + for(long i = 1; i <= m_lCount; ++i) + { + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); + wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) ); + + if(status == noErr) + { + if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType) + { + pFileType = new wxFileType(); + pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); + break; + } + } + } + + return pFileType; } size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes) { - // VZ: don't know anything about this for Mac - wxFAIL_MSG( _T("wxMimeTypesManagerImpl::EnumAllFileTypes() not yet implemented") ); + wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); - return 0; + //low level functions - iterate through the database + ICMapEntry entry; + + long pos; + + for(long i = 1; i <= m_lCount; ++i) + { + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); + if( status == noErr ) + mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) ); + } + + return m_lCount; } wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) { wxFAIL_MSG( _T("wxMimeTypesManagerImpl::Associate() not yet implemented") ); + //on mac you have to embed it into the mac's file reference resource ('FREF' I believe) + //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get + //the app's signature somehow... return NULL; } @@ -219,6 +483,9 @@ wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft) { + //this should be as easy as removing the entry from the database and then saving + //the database return FALSE; } +#endif //wxUSE_MIMETYPE \ No newline at end of file