1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/mimetype.cpp
3 // Purpose: Mac Carbon implementation for wx MIME-related classes
8 // Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 // TODO: Search Info[-macos](classic).plist dictionary in addition
14 // to Internet Config database.
16 // Maybe try a brainstorm a way to change the wxMimeTypesManager API
17 // to get info from a file instead/addition to current get all stuff
18 // API so that we can use Launch Services to get MIME type info.
20 // Implement GetIcon from one of the FinderInfo functions - or
21 // use Launch Services and search that app's plist for the icon.
23 // Put some special juice in for the print command.
26 // for compilers that support precompilation, includes "wx.h".
27 #include "wx/wxprec.h"
35 #include "wx/mac/mimetype.h"
38 #include "wx/dynarray.h"
39 #include "wx/string.h"
49 #include "wx/confbase.h"
51 #include "wx/mac/private.h"
53 // other standard headers
57 #include <InternetConfig.h>
58 #include <CoreServices.h>
62 // START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html)
64 // IsRemoteVolume can be used to find out if the
65 // volume referred to by vRefNum is a remote volume
66 // located somewhere on a network. the volume's attribute
67 // flags (copied from the GetVolParmsInfoBuffer structure)
68 // are returned in the longword pointed to by vMAttrib.
69 OSErr
IsRemoteVolume(short vRefNum
, Boolean
*isRemote
, long *vMAttrib
)
72 GetVolParmsInfoBuffer volinfo
;
75 volPB
.ioParam
.ioVRefNum
= vRefNum
;
76 volPB
.ioParam
.ioNamePtr
= NULL
;
77 volPB
.ioParam
.ioBuffer
= (Ptr
)&volinfo
;
78 volPB
.ioParam
.ioReqCount
= sizeof(volinfo
);
79 err
= PBHGetVolParmsSync( &volPB
);
82 *isRemote
= (volinfo
.vMServerAdr
!= 0);
83 *vMAttrib
= volinfo
.vMAttrib
;
89 // BuildVolumeList fills the array pointed to by vols with
90 // a list of the currently mounted volumes. If includeRemote
91 // is true, then remote server volumes will be included in
92 // the list. When remote server volumes are included in the
93 // list, they will be added to the end of the list. On entry,
94 // *count should contain the size of the array pointed to by
95 // vols. On exit, *count will be set to the number of id numbers
96 // placed in the array. If vMAttribMask is non-zero, then
97 // only volumes with matching attributes are added to the
98 // list of volumes. bits in the vMAttribMask should use the
99 // same encoding as bits in the vMAttrib field of
100 // the GetVolParmsInfoBuffer structure.
101 OSErr
BuildVolumeList(Boolean includeRemote
, short *vols
,
102 long *count
, long vMAttribMask
)
104 HParamBlockRec volPB
;
107 long nlocal
, nremote
;
110 // set up and check parameters
111 volPB
.volumeParam
.ioNamePtr
= NULL
;
112 nlocal
= nremote
= 0;
116 // iterate through volumes
117 for (volPB
.volumeParam
.ioVolIndex
= 1;
118 PBHGetVInfoSync(&volPB
) == noErr
;
119 volPB
.volumeParam
.ioVolIndex
++)
121 // skip remote volumes, if necessary
122 err
= IsRemoteVolume(volPB
.volumeParam
.ioVRefNum
, &isRemote
, &vMAttrib
);
126 if ((includeRemote
|| !isRemote
) && ((vMAttrib
& vMAttribMask
) == vMAttribMask
))
128 // add local volumes at the front; remote volumes at the end
130 vols
[nlocal
+ nremote
++] = volPB
.volumeParam
.ioVRefNum
;
137 nremote
* sizeof(short) );
138 vols
[nlocal
++] = volPB
.volumeParam
.ioVRefNum
;
142 if ((nlocal
+ nremote
) >= *count
)
148 *count
= (nlocal
+ nremote
);
154 // FindApplication iterates through mounted volumes
155 // searching for an application with the given creator
156 // type. If includeRemote is true, then remote volumes
157 // will be searched (after local ones) for an application
158 // with the creator type.
160 // Hacked to output to appName
164 OSErr
FindApplication(OSType appCreator
, Boolean includeRemote
, Str255 appName
, FSSpec
* appSpec
)
166 short rRefNums
[kMaxVols
];
171 // get a list of volumes - with desktop files
173 err
= BuildVolumeList(includeRemote
, rRefNums
, &volCount
, (1 << bHasDesktopMgr
) );
177 // iterate through the list
178 for (i
=0; i
<volCount
; i
++)
180 // has a desktop file?
181 desktopPB
.ioCompletion
= NULL
;
182 desktopPB
.ioVRefNum
= rRefNums
[i
];
183 desktopPB
.ioNamePtr
= NULL
;
184 desktopPB
.ioIndex
= 0;
185 err
= PBDTGetPath( &desktopPB
);
189 // has the correct app??
190 desktopPB
.ioFileCreator
= appCreator
;
191 desktopPB
.ioNamePtr
= appName
;
192 err
= PBDTGetAPPLSync( &desktopPB
);
196 // make a file spec referring to it
197 err
= FSMakeFSSpec( rRefNums
[i
], desktopPB
.ioAPPLParID
, appName
, appSpec
);
208 // END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html)
210 // yeah, duplicated code
211 pascal OSErr
FSpGetFullPath( const FSSpec
*spec
,
212 short *fullPathLength
,
215 OSErr result
, realResult
;
223 realResult
= result
= noErr
;
225 // work around Nav Services "bug" (it returns invalid FSSpecs with empty names)
227 if ( spec
->name
[0] == 0 )
229 result
= FSMakeFSSpecCompat(spec
->vRefNum
, spec
->parID
, spec
->name
, &tempSpec
);
235 // Make a copy of the input FSSpec that can be modified
236 BlockMoveData( spec
, &tempSpec
, sizeof(FSSpec
) );
238 if ( result
== noErr
)
240 if ( tempSpec
.parID
== fsRtParID
)
242 // object is a volume
243 // Add a colon to make it a full pathname
245 tempSpec
.name
[tempSpec
.name
[0]] = ':';
248 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
252 // object isn't a volume
254 // Is the object a file or a directory?
255 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
256 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
257 pb
.dirInfo
.ioDrDirID
= tempSpec
.parID
;
258 pb
.dirInfo
.ioFDirIndex
= 0;
259 result
= PBGetCatInfoSync( &pb
);
261 // Allow file/directory name at end of path to not exist.
263 if ((result
== noErr
) || (result
== fnfErr
))
265 // if the object is a directory, append a colon so full pathname ends with colon
266 if ((result
== noErr
) && (pb
.hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0)
269 tempSpec
.name
[tempSpec
.name
[0]] = ':';
272 // Put the object name in first
273 result
= PtrToHand( &tempSpec
.name
[1], fullPath
, tempSpec
.name
[0] );
274 if ( result
== noErr
)
276 // Get the ancestor directory names
277 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
278 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
279 pb
.dirInfo
.ioDrParID
= tempSpec
.parID
;
281 // loop until we have an error or find the root directory
284 pb
.dirInfo
.ioFDirIndex
= -1;
285 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
286 result
= PBGetCatInfoSync(&pb
);
287 if ( result
== noErr
)
289 // Append colon to directory name
291 tempSpec
.name
[tempSpec
.name
[0]] = ':';
293 // Add directory name to beginning of fullPath
294 (void)Munger(*fullPath
, 0, NULL
, 0, &tempSpec
.name
[1], tempSpec
.name
[0]);
298 while ( (result
== noErr
) && (pb
.dirInfo
.ioDrDirID
!= fsRtDirID
) );
304 if ( result
== noErr
)
307 *fullPathLength
= GetHandleSize( *fullPath
);
308 result
= realResult
; // return realResult in case it was fnfErr
312 // Dispose of the handle and return NULL and zero length
313 if ( *fullPath
!= NULL
)
315 DisposeHandle( *fullPath
);
325 // On the mac there are two ways to open a file - one is through apple events and the
326 // finder, another is through mime types.
328 // So, really there are two ways to implement wxFileType...
330 // Mime types are only available on OS 8.1+ through the InternetConfig API
332 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
333 // Low - which means you have to iterate yourself through the mime database
334 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
335 // High - which requires access to the database every time
337 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
338 // and mid-level functions
340 // TODO: Should we call ICBegin/ICEnd? Then where?
344 inline void wxLogMimeDebug(const wxChar
* WXUNUSED_UNLESS_DEBUG(szMsg
), OSStatus
WXUNUSED_UNLESS_DEBUG(status
))
346 wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg
, __LINE__
, (int)status
));
349 // in case we're compiling in non-GUI mode
350 class WXDLLIMPEXP_FWD_CORE wxIcon
;
352 bool wxFileTypeImpl::SetCommand(const wxString
& WXUNUSED(cmd
), const wxString
& WXUNUSED(verb
), bool WXUNUSED(overwriteprompt
))
354 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
359 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& WXUNUSED(strIcon
), int WXUNUSED(index
))
361 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
366 bool wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
367 const wxFileType::MessageParameters
& params
) const
369 wxString cmd
= GetCommand(wxT("open"));
371 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
373 return !openCmd
->empty();
377 wxFileTypeImpl::GetPrintCommand(
379 const wxFileType::MessageParameters
& params
) const
381 wxString cmd
= GetCommand(wxT("print"));
383 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
385 return !printCmd
->empty();
389 // Internet Config vs. Launch Services
391 // From OS 8 on there was internet config...
392 // However, OSX and its finder does not use info
393 // from Internet Config at all - the Internet Config
394 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
395 // OR REGISTERED THROUGH INTERNET CONFIG
397 // Therefore on OSX in order for the open command to be useful
398 // we need to go straight to launch services
401 //on darwin, use launch services
403 #include <ApplicationServices/ApplicationServices.h>
405 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
407 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
409 if (verb
== wxT("open"))
412 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
413 (Handle
) m_manager
->m_hDatabase
,
416 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
417 sCurrentExtension
= sCurrentExtension
.Right(sCurrentExtension
.length()-1 );
419 //type, creator, ext, roles, outapp (FSRef), outappurl
420 CFURLRef cfurlAppPath
;
421 OSStatus status
= LSGetApplicationForInfo( kLSUnknownType
,
423 wxMacCFStringHolder(sCurrentExtension
, wxLocale::GetSystemEncoding()),
430 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(cfurlAppPath
, kCFURLPOSIXPathStyle
);
431 CFRelease(cfurlAppPath
);
434 // Since a filename might have spaces in it, so surround it with quotes
441 + wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding())
442 + wxString(wxT("'"));
449 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
451 wxT("LSGetApplicationForInfo failed."),
456 return wxEmptyString
;
459 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
461 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
464 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
465 (Handle
) m_manager
->m_hDatabase
, m_lIndex
, &entry
);
467 *desc
= wxMacMakeStringFromPascal( entry
.entryName
);
472 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
474 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
477 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
478 (Handle
) m_manager
->m_hDatabase
, m_lIndex
, &entry
);
480 //entry has period in it
481 wxString sCurrentExtension
= wxMacMakeStringFromPascal( entry
.extension
);
482 extensions
.Add( sCurrentExtension
.Right( sCurrentExtension
.length() - 1 ) );
487 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
489 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
492 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
493 (Handle
) m_manager
->m_hDatabase
, m_lIndex
, &entry
);
495 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
500 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
515 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
517 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
519 // no such file type or no value or incorrect icon entry
523 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
,
524 wxArrayString
* commands
,
525 const wxFileType::MessageParameters
& params
) const
527 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
532 if (GetOpenCommand(&sCommand
, params
))
534 verbs
->Add(wxString(wxT("open")));
535 commands
->Add(sCommand
);
542 void wxMimeTypesManagerImpl::Initialize(int WXUNUSED(mailcapStyles
), const wxString
& WXUNUSED(extraDir
))
544 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
546 // some apps (non-wx) use the 'plst' resource instead
548 CFBundleRef cfbMain
= CFBundleGetMainBundle();
549 wxCFDictionary
cfdInfo( CFBundleGetInfoDictionary(cfbMain
), wxCF_RETAIN
);
551 cfdInfo
.PrintOut(sLog
);
555 // start Internet Config - log if there's an error
556 // the second param is the signature of the application, also known
557 // as resource ID 0. However, as per some recent discussions, we may not
558 // have a signature for this app, so a generic 'APPL' which is the executable
559 // type will work for now.
560 OSStatus status
= ICStart( (ICInstance
*)&m_hIC
, 'APPL' );
564 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
572 m_hDatabase
= (void**) NewHandle(0);
573 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
575 //the database file can be corrupt (on OSX its
576 //~/Library/Preferences/com.apple.internetconfig.plist)
581 wxLogDebug(wxT("Corrupt MIME database!"));
585 //obtain the number of entries in the map
586 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
587 wxASSERT( status
== noErr
);
594 for (long i
= 1; i
<= m_lCount
; ++i
)
596 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
600 wxString sCreator
= wxMacMakeStringFromPascal(entry
.creatorAppName
);
601 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
602 wxString sMIMEType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
605 impl
.Init(this, pos
);
607 if (sMIMEType
== wxT("text/html") && sCurrentExtension
== wxT(".html"))
611 impl
.GetOpenCommand( &cmd
, wxFileType::MessageParameters (wxT("http://www.google.com")));
612 wxPrintf(wxT("APP: [%s]\n"), cmd
.c_str());
619 void wxMimeTypesManagerImpl::ClearData()
623 DisposeHandle( (Handle
)m_hDatabase
);
625 // this can return an error, but we don't really care that much about it
626 ICStop( (ICInstance
)m_hIC
);
632 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
633 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
636 // extension -> file type
637 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
639 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
641 //low level functions - iterate through the database
645 for (long i
= 1; i
<= m_lCount
; ++i
)
647 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
651 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
652 if ( sCurrentExtension
.Right(sCurrentExtension
.length() - 1) == e
) // entry has period in it
654 wxFileType
* pFileType
= new wxFileType();
655 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
665 // MIME type -> extension -> file type
666 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
668 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
673 // low level functions - iterate through the database
674 for (long i
= 1; i
<= m_lCount
; ++i
)
676 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
677 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
681 if ( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
683 wxFileType
* pFileType
= new wxFileType();
684 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
694 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
696 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
699 long pos
, lStartCount
;
701 // low level functions - iterate through the database
702 lStartCount
= (long) mimetypes
.GetCount();
703 for (long i
= 1; i
<= m_lCount
; ++i
)
705 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
706 if ( status
== noErr
)
707 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
710 return mimetypes
.GetCount() - lStartCount
;
713 pascal OSStatus
MoreProcGetProcessTypeSignature(
714 const ProcessSerialNumberPtr pPSN
,
715 OSType
*pProcessType
,
718 OSStatus anErr
= noErr
;
719 ProcessInfoRec infoRec
;
720 ProcessSerialNumber localPSN
;
722 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
723 infoRec
.processName
= NULL
;
725 infoRec
.processAppSpec
= NULL
;
730 localPSN
.highLongOfPSN
= 0;
731 localPSN
.lowLongOfPSN
= kCurrentProcess
;
738 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
741 *pProcessType
= infoRec
.processType
;
742 *pCreator
= infoRec
.processSignature
;
750 // TODO: clean this up, its messy
755 #include "wx/mac/corefoundation/cfstring.h"
757 #define wxCF_RELEASE true
758 #define wxCF_RETAIN false
760 // ----------------------------------------------------------------------------
762 // ----------------------------------------------------------------------------
767 wxCFDictionary(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
769 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
770 if (bRetain
== wxCF_RETAIN
&& ref
)
774 wxCFDictionary(CFIndex cfiSize
= 0)
776 CFDictionaryKeyCallBacks kcbs
;
777 CFDictionaryValueCallBacks vcbs
;
778 BuildKeyCallbacks(&kcbs
);
779 BuildValueCallbacks(&vcbs
);
781 m_cfmdRef
= CFDictionaryCreateMutable(
782 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
791 CFRelease(m_cfmdRef
);
794 static const void* RetainProc(CFAllocatorRef
, const void* v
)
795 { return (const void*) CFRetain(v
); }
797 static void ReleaseProc(CFAllocatorRef
, const void* v
)
800 void MakeMutable(CFIndex cfiSize
= 0)
802 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
804 m_cfmdRef
= CFDictionaryCreateMutableCopy(
805 kCFAllocatorDefault
, cfiSize
, oldref
);
810 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
813 pCbs
->retain
= RetainProc
;
814 pCbs
->release
= ReleaseProc
;
815 pCbs
->copyDescription
= NULL
;
820 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
823 pCbs
->retain
= RetainProc
;
824 pCbs
->release
= ReleaseProc
;
825 pCbs
->copyDescription
= NULL
;
829 operator CFTypeRef () const
830 { return (CFTypeRef
)m_cfmdRef
; }
832 CFDictionaryRef
GetCFDictionary() const
833 { return (CFDictionaryRef
)m_cfmdRef
; }
835 CFMutableDictionaryRef
GetCFMutableDictionary()
836 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
838 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
841 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
844 CFIndex
GetCount() const
847 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
850 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
853 wxASSERT(Exists(cftKey
) == false);
854 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
857 void Remove(CFTypeRef cftKey
)
860 wxASSERT(Exists(cftKey
));
861 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
864 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
867 wxASSERT(Exists(cftKey
));
868 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
871 bool Exists(CFTypeRef cftKey
) const
874 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
);
878 { return m_cfmdRef
!= NULL
; }
881 { return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID(); }
883 void PrintOut(wxString
& sMessage
)
885 PrintOutDictionary(sMessage
, m_cfmdRef
);
888 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
890 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
891 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
892 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
894 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
896 for (CFIndex i
= 0; i
< cfiCount
; ++i
)
898 wxString sKey
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
899 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
902 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
905 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
908 wxString::Format(wxT("\n\t[Value : %s]"),
911 PrintOutType(sMessage
, sValue
, pValues
[i
]);
913 sMessage
<< wxT("\n");
920 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
922 for (CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
924 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
925 CFArrayGetValueAtIndex(cfaRef
, i
)
929 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
932 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
936 static void PrintOutType(wxString
& sMessage
, const wxString
& sValue
, CFTypeRef cfRef
)
938 sMessage
<< wxT(" {");
940 if (sValue
== wxT("CFString"))
942 sMessage
<< wxMacCFStringHolder((CFStringRef
)cfRef
, false).AsString();
944 else if (sValue
== wxT("CFNumber"))
947 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
950 else if (sValue
== wxT("CFDictionary"))
952 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
954 else if (sValue
== wxT("CFArray"))
956 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
958 else if (sValue
== wxT("CFBoolean"))
960 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
962 else if (sValue
== wxT("CFURL"))
964 sMessage
<< wxMacCFStringHolder(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
968 sMessage
<< wxT("*****UNKNOWN TYPE******");
971 sMessage
<< wxT("} ");
978 CFTypeRef
WriteAsXML()
980 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
983 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
986 CFStringRef cfsError
=NULL
;
987 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
990 kCFPropertyListMutableContainersAndLeaves
,
995 *pErrorMsg
= wxMacCFStringHolder(cfsError
).AsString();
1000 return m_cfmdRef
!= NULL
;
1004 CFMutableDictionaryRef m_cfmdRef
;
1007 // ----------------------------------------------------------------------------
1009 // ----------------------------------------------------------------------------
1014 wxCFArray(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
1016 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1017 if (bRetain
== wxCF_RETAIN
&& ref
)
1021 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1022 { Create(cfiSize
); }
1027 void MakeMutable(CFIndex cfiSize
= 0)
1029 wxASSERT(IsValid());
1031 CFMutableArrayRef oldref
= m_cfmaRef
;
1032 m_cfmaRef
= CFArrayCreateMutableCopy(
1033 kCFAllocatorDefault
,
1035 (CFArrayRef
)oldref
);
1039 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1042 pCbs
->retain
= RetainProc
;
1043 pCbs
->release
= ReleaseProc
;
1044 pCbs
->copyDescription
= NULL
;
1048 void Create(CFIndex cfiSize
= 0)
1051 CFArrayCallBacks cb
;
1052 BuildCallbacks(&cb
);
1054 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1058 { if (m_cfmaRef
) CFRelease(m_cfmaRef
); }
1060 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1061 { return (const void*) CFRetain(v
); }
1063 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1066 operator CFTypeRef () const
1067 { return (CFTypeRef
)m_cfmaRef
; }
1069 CFArrayRef
GetCFArray() const
1070 { return (CFArrayRef
)m_cfmaRef
; }
1072 CFMutableArrayRef
GetCFMutableArray()
1073 { return (CFMutableArrayRef
) m_cfmaRef
; }
1075 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1077 wxASSERT(IsValid());
1078 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1083 wxASSERT(IsValid());
1084 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1087 void Add(CFTypeRef cftValue
)
1089 wxASSERT(IsValid());
1090 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1093 void Remove(CFIndex cfiIndex
)
1095 wxASSERT(IsValid());
1096 wxASSERT(cfiIndex
< GetCount());
1097 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1100 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1102 wxASSERT(IsValid());
1103 wxASSERT(cfiIndex
< GetCount());
1104 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1108 { return m_cfmaRef
!= NULL
; }
1110 bool IsValid() const
1112 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1116 void MakeValidXML();
1120 CFMutableArrayRef m_cfmaRef
;
1123 // ----------------------------------------------------------------------------
1125 // ----------------------------------------------------------------------------
1130 wxCFString(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_Holder((CFStringRef
)ref
, bRetain
== wxCF_RELEASE
)
1133 wxCFString(const wxChar
* szString
) : m_Holder(wxString(szString
), wxLocale::GetSystemEncoding())
1136 wxCFString(const wxString
& sString
) : m_Holder(sString
, wxLocale::GetSystemEncoding())
1139 virtual ~wxCFString() {}
1141 operator CFTypeRef() const
1142 { return (CFTypeRef
) ((CFStringRef
) m_Holder
); }
1145 { return ((CFTypeRef
)(*this)) != NULL
; }
1147 wxString
BuildWXString()
1148 { return m_Holder
.AsString(); }
1151 wxMacCFStringHolder m_Holder
;
1154 // ----------------------------------------------------------------------------
1156 // ----------------------------------------------------------------------------
1161 wxCFNumber(int nValue
)
1163 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &nValue
);
1166 wxCFNumber(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfnRef((CFNumberRef
)ref
)
1168 if (bRetain
== wxCF_RETAIN
&& ref
)
1172 virtual ~wxCFNumber()
1175 CFRelease(m_cfnRef
);
1178 operator CFTypeRef() const
1179 { return (CFTypeRef
) m_cfnRef
; }
1184 CFNumberGetValue( m_cfnRef
, kCFNumberIntType
, &nOut
);
1190 { return m_cfnRef
!= NULL
; }
1193 CFNumberRef m_cfnRef
;
1196 // ----------------------------------------------------------------------------
1198 // ----------------------------------------------------------------------------
1203 wxCFURL(CFTypeRef ref
= NULL
, bool bRetain
= wxCF_RELEASE
) : m_cfurlRef((CFURLRef
)ref
)
1205 if (bRetain
== wxCF_RETAIN
&& ref
)
1209 wxCFURL(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1211 Create(URLString
, BaseURL
);
1214 void Create(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1216 m_cfurlRef
= CFURLCreateWithString(
1217 kCFAllocatorDefault
,
1218 (CFStringRef
)(CFTypeRef
)URLString
,
1219 (CFURLRef
) BaseURL
);
1225 CFRelease(m_cfurlRef
);
1228 wxString
BuildWXString()
1230 return wxCFString(CFURLCopyPath(m_cfurlRef
)).BuildWXString();
1233 operator CFTypeRef() const
1234 { return (CFTypeRef
)m_cfurlRef
; }
1237 { return m_cfurlRef
!= NULL
; }
1240 CFURLRef m_cfurlRef
;
1243 // ----------------------------------------------------------------------------
1245 // ----------------------------------------------------------------------------
1247 #define wxCFDATA_RELEASEBUFFER 1
1252 wxCFData(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfdaRef((CFDataRef
)ref
)
1254 if (bRetain
== wxCF_RETAIN
&& ref
)
1258 wxCFData(const UInt8
* pBytes
, CFIndex len
, bool bKeep
= wxCFDATA_RELEASEBUFFER
)
1260 if (bKeep
== wxCFDATA_RELEASEBUFFER
)
1262 m_cfdaRef
= CFDataCreateWithBytesNoCopy
1263 (kCFAllocatorDefault
, pBytes
, len
, kCFAllocatorDefault
);
1267 m_cfdaRef
= CFDataCreate(kCFAllocatorDefault
, pBytes
, len
);
1274 CFRelease(m_cfdaRef
);
1277 const UInt8
* GetValue()
1278 { return CFDataGetBytePtr(m_cfdaRef
); }
1281 { return CFDataGetLength(m_cfdaRef
); }
1283 operator CFTypeRef() const
1284 { return (CFTypeRef
)m_cfdaRef
; }
1287 { return m_cfdaRef
!= NULL
; }
1290 CFDataRef m_cfdaRef
;
1293 void wxCFDictionary::MakeValidXML()
1295 CFIndex cfiCount
= GetCount();
1296 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
1297 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
1299 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1301 // for plist xml format, all dictionary keys must be cfstrings and
1302 // no values in the dictionary or subkeys/values can be NULL;
1303 // additionally, CFURLs are not allowed
1304 for (CFIndex i
= 0; i
< cfiCount
; ++i
)
1306 // must be an array, dictionary, string, bool, or int and cannot be null
1307 // and dictionaries can only contain cfstring keys
1308 CFTypeRef cfRef
= pValues
[i
];
1310 CFGetTypeID(pKeys
[i
]) != CFStringGetTypeID() ||
1318 pKeys
= new CFTypeRef
[cfiCount
];
1319 pValues
= new CFTypeRef
[cfiCount
];
1320 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1322 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1325 wxCFArray
cfaCurrent(cfRef
);
1326 cfaCurrent
.MakeMutable();
1327 cfaCurrent
.MakeValidXML();
1328 Set(pKeys
[i
], cfaCurrent
);
1330 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1333 wxCFDictionary
cfdCurrent(cfRef
);
1334 cfdCurrent
.MakeMutable();
1335 cfdCurrent
.MakeValidXML();
1336 Set(pKeys
[i
], cfdCurrent
);
1338 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1339 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1340 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1347 pKeys
= new CFTypeRef
[cfiCount
];
1348 pValues
= new CFTypeRef
[cfiCount
];
1349 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1357 void wxCFArray::MakeValidXML()
1359 for (CFIndex i
= 0; i
< GetCount(); ++i
)
1361 //must be an array, dictionary, string, bool, or int and cannot be null
1362 //and dictionaries can only contain cfstring keys
1363 CFTypeRef cfRef
= (*this)[i
];
1369 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1372 wxCFArray
cfaCurrent(cfRef
);
1373 cfaCurrent
.MakeMutable();
1374 cfaCurrent
.MakeValidXML();
1377 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1380 wxCFDictionary
cfdCurrent(cfRef
);
1381 cfdCurrent
.MakeMutable();
1382 cfdCurrent
.MakeValidXML();
1385 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1386 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1387 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1403 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
1405 bool bInfoSuccess
= false;
1407 const wxArrayString
& asExtensions
= ftInfo
.GetExtensions();
1408 size_t dwFoundIndex
= 0;
1409 if (!asExtensions
.GetCount())
1411 wxLogDebug(wxT("Must have extension to associate with"));
1414 // Find and write to Info.plist in main bundle (note that some other
1415 // apps have theirs named differently, i.e. IE's is named Info-macos.plist
1416 // some apps (non-wx) use the 'plst' resource instead
1417 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1420 UInt32 dwBundleType
, dwBundleCreator
;
1421 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1423 // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too),
1424 // which will give us the incorrect info.plist path
1425 // otherwise it will be 'APPL', or in the case of a framework, 'FMWK'
1426 if (dwBundleType
== 'APPL')
1428 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1429 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1431 // sInfoPath << wxT("file://");
1432 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1433 sInfoPath
<< wxT("Contents/Info.plist");
1435 // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1436 wxCFDictionary cfdInfo
;
1437 bool bInfoOpenSuccess
= false;
1439 if (indictfile
.Open(sInfoPath
, wxFile::read
))
1441 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1442 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1443 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1444 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1446 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1447 if (!bInfoOpenSuccess
)
1452 if (bInfoOpenSuccess
)
1454 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1456 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1458 bool bAddDocTypesArrayToDictionary
= !cfaDocTypes
.IsOk();
1459 if (bAddDocTypesArrayToDictionary
)
1460 cfaDocTypes
.Create();
1462 cfaDocTypes
.MakeMutable( cfaDocTypes
.GetCount() + 1 );
1464 bool bEntryFound
= false;
1466 // search for duplicates
1468 for (i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1470 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1472 // A lot of apps don't support MIME types for some reason
1473 // so we go by extensions only
1474 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1477 if (!cfaExtensions
.IsOk())
1480 for (CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1482 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1484 if (asExtensions
[iWXExt
] ==
1485 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1488 dwFoundIndex
= iWXExt
;
1492 } //end of wxstring array
1496 } //end for cf array
1500 } //end for doctypes
1502 wxCFDictionary cfdNewEntry
;
1504 if (!ftInfo
.GetDescription().empty())
1506 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeName")),
1507 wxCFString(ftInfo
.GetDescription()) );
1510 if (!ftInfo
.GetIconFile().empty())
1512 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeIconFile")),
1513 wxCFString(ftInfo
.GetIconFile()) );
1516 wxCFArray cfaOSTypes
;
1517 wxCFArray cfaExtensions
;
1518 wxCFArray cfaMimeTypes
;
1520 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1521 cfaOSTypes
.Add( wxCFString(wxT("****")) );
1522 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes
);
1524 //'*' for unrestricted
1525 if (ftInfo
.GetExtensionsCount() != 0)
1527 for (size_t iExtension
= 0; iExtension
< ftInfo
.GetExtensionsCount(); ++iExtension
)
1529 cfaExtensions
.Add( wxCFString( asExtensions
[iExtension
] ) );
1532 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions
);
1535 if (!ftInfo
.GetMimeType().empty())
1537 cfaMimeTypes
.Add( wxCFString(ftInfo
.GetMimeType()) );
1538 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes
);
1541 // Editor - can perform all actions
1542 // Viewer - all actions except manipulation/saving
1543 // None - can perform no actions
1544 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
1546 // Is application bundled?
1547 cfdNewEntry
.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue
);
1550 cfaDocTypes
.Set(i
, cfdNewEntry
);
1552 cfaDocTypes
.Add(cfdNewEntry
);
1554 // set the doc types array in the muted dictionary
1555 if (bAddDocTypesArrayToDictionary
)
1556 cfdInfo
.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1558 cfdInfo
.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1560 cfdInfo
.MakeValidXML();
1563 if (outdictfile
.Open(sInfoPath
, wxFile::write
))
1565 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1566 if (cfdaInfo
.IsOk())
1568 if (outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1569 (wxFileOffset
)cfdaInfo
.GetCount())
1571 wxLogDebug(wxT("error in writing to file"));
1575 bInfoSuccess
= true;
1577 //#if defined(__DARWIN__)
1578 // //force launch services to update its database for the finder
1579 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1580 // if (status != noErr)
1582 // wxLogDebug(wxT("LSRegisterURL Failed."));
1586 outdictfile
.Close();
1590 outdictfile
.Close();
1591 wxLogDebug(wxT("Could not read in new dictionary"));
1596 wxLogDebug(wxString(wxT("Could not open [")) +
1597 sInfoPath
+ wxT("] for writing."));
1602 wxLogDebug(wxT("No info dictionary in main bundle"));
1607 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1612 wxLogDebug(wxT("No main bundle"));
1618 // on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1619 // or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1620 // the app's signature somehow...
1622 OSType processType
, creator
;
1623 OSStatus status
= MoreProcGetProcessTypeSignature(NULL
, &processType
, &creator
);
1625 if (status
== noErr
)
1627 Str255 psCreatorName
;
1630 status
= FindApplication(creator
, false, psCreatorName
, &dummySpec
);
1633 status
= LSFindApplicationForInfo( creator
, NULL
, NULL
, &fsref
,NULL
);
1635 status
= FSGetCatalogInfo(&fsref
, kFSCatInfoNone
, NULL
, &name
, NULL
, NULL
);
1636 CFStringRef str
= FSCreateStringFromHFSUniStr( 0 , &name
);
1637 CFStringGetPascalString(str
, psCreatorName
, 256, CFStringGetSystemEncoding());
1641 if (status
== noErr
)
1643 //get the file type if it exists -
1644 //if it really does then modify the database then save it,
1645 //otherwise we need to create a whole new entry
1646 wxFileType
* pFileType
= GetFileTypeFromExtension(asExtensions
[dwFoundIndex
]);
1650 ICGetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1651 pFileType
->m_impl
->m_lIndex
, &entry
);
1653 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1654 entry
.fileCreator
= creator
;
1656 status
= ICSetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1657 pFileType
->m_impl
->m_lIndex
, &entry
);
1660 if (status
== noErr
)
1664 //kICAttrNoChange means we don't care about attributes such as
1665 //locking in the database
1666 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1667 // kICAttrNoChange, (Handle) m_hDatabase);
1668 // if (status == noErr)
1669 // return pFileType;
1672 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1677 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICSetMapEntry failed.")));
1680 // failure - cleanup
1685 // TODO: Maybe force all 3 of these to be non-empty?
1686 Str255 psExtension
, psMimeType
, psDescription
;
1688 wxMacStringToPascal(wxString(wxT(".")) + ftInfo
.GetExtensions()[0], psExtension
);
1689 wxMacStringToPascal(ftInfo
.GetMimeType(), psMimeType
);
1690 wxMacStringToPascal(ftInfo
.GetDescription(), psDescription
);
1692 Str255 psPostCreatorName
;
1693 wxMacStringToPascal(wxEmptyString
, psPostCreatorName
);
1695 //add the entry to the database
1697 entry
.totalLength
= sizeof(ICMapEntry
);
1698 entry
.fixedLength
= kICMapFixedLength
;
1700 entry
.fileType
= 0; //TODO: File type?
1701 entry
.fileCreator
= creator
;
1702 entry
.postCreator
= 0;
1703 entry
.flags
= kICMapDataForkBit
; //TODO: Maybe resource is valid by default too?
1704 PLstrcpy( entry
.extension
, psExtension
) ;
1705 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1706 memcpy(entry
.postAppName
, psPostCreatorName
, sizeof(Str255
));
1707 memcpy(entry
.MIMEType
, psMimeType
, sizeof(Str255
));
1708 memcpy(entry
.entryName
, psDescription
, sizeof(Str255
));
1710 status
= ICAddMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &entry
);
1711 if (status
== noErr
)
1713 return GetFileTypeFromExtension(ftInfo
.GetMimeType());
1715 // kICAttrNoChange means we don't care about attributes such as
1716 // locking in the database
1717 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1718 // kICAttrNoChange, (Handle) m_hDatabase);
1720 // return the entry in the database if successful
1721 // if (status == noErr)
1722 // return GetFileTypeFromExtension(ftInfo.GetMimeType());
1725 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1730 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICAppMapEntry failed.")));
1733 } // end if FindApplcation was successful
1736 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("FindApplication failed.")));
1738 } // end if it could obtain app's signature
1741 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("GetProcessSignature failed.")));
1748 wxMimeTypesManagerImpl::Unassociate(wxFileType
*pFileType
)
1750 wxASSERT(pFileType
);
1751 bool bInfoSuccess
= false;
1753 wxArrayString asExtensions
;
1754 pFileType
->GetExtensions(asExtensions
);
1756 if (!asExtensions
.GetCount())
1758 wxLogDebug(wxT("Must have extension to disassociate"));
1762 // Find and write to Info.plist in main bundle (note that some other
1763 // apps have theirs named differently, i.e. IE's is named Info-macos.plist
1764 // some apps (non-wx) use the 'plst' resource instead
1765 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1768 UInt32 dwBundleType
, dwBundleCreator
;
1769 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1771 // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too),
1772 // which will give us the incorrect info.plist path
1773 // otherwise it will be 'APPL', or in the case of a framework, 'FMWK'
1774 if (dwBundleType
== 'APPL')
1777 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1778 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1780 // sInfoPath << wxT("file://");
1781 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1782 sInfoPath
<< wxT("Contents/Info.plist");
1784 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1785 wxCFDictionary cfdInfo
;
1786 bool bInfoOpenSuccess
= false;
1788 if (indictfile
.Open(sInfoPath
, wxFile::read
))
1790 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1791 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1792 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1793 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1795 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1796 if (!bInfoOpenSuccess
)
1801 if (bInfoOpenSuccess
)
1803 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1805 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1807 if (cfaDocTypes
.IsOk())
1809 bool bEntryFound
= false;
1811 //search for duplicate
1813 for (i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1815 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1817 //A lot of apps dont do to mime types for some reason
1818 //so we go by extensions only
1819 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1822 if (!cfaExtensions
.IsOk())
1825 for (CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1827 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1829 if (asExtensions
[iWXExt
] ==
1830 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1833 cfaDocTypes
.Remove(i
);
1834 cfdInfo
.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes
);
1837 } //end of wxstring array
1841 } //end for cf array
1849 cfdInfo
.MakeValidXML();
1852 if (outdictfile
.Open(sInfoPath
, wxFile::write
))
1854 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1855 if (cfdaInfo
.IsOk())
1857 if (outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1858 (wxFileOffset
)cfdaInfo
.GetCount())
1860 wxLogDebug(wxT("error in writing to file"));
1864 bInfoSuccess
= true;
1866 //#if defined(__DARWIN__)
1867 // //force launch services to update its database for the finder
1868 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1869 // if (status != noErr)
1871 // wxLogDebug(wxT("LSRegisterURL Failed."));
1875 outdictfile
.Close();
1879 outdictfile
.Close();
1880 wxLogDebug(wxT("Could not read in new dictionary"));
1886 wxString(wxT("Could not open [")) +
1887 sInfoPath
+ wxT("] for writing."));
1892 wxLogDebug(wxT("Entry not found to remove"));
1895 wxCFDictionary::PrintOutArray(sPrintOut
, (CFArrayRef
)(CFTypeRef
)cfaDocTypes
);
1896 wxLogDebug(sPrintOut
);
1898 for (size_t i
= 0; i
< asExtensions
.GetCount(); ++i
)
1899 wxLogDebug(asExtensions
[i
]);
1904 wxLogDebug(wxT("No doc types array found"));
1905 wxString sPrintOut
; cfdInfo
.PrintOut(sPrintOut
); wxLogDebug(sPrintOut
);
1910 wxLogDebug(wxT("No info dictionary in main bundle"));
1915 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1920 wxLogDebug(wxT("No main bundle"));
1926 // this should be as easy as removing the entry from the database
1927 // and then saving the database
1928 OSStatus status
= ICDeleteMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1929 pFileType
->m_impl
->m_lIndex
);
1931 if (status
== noErr
)
1935 //kICAttrNoChange means we don't care about attributes such as
1936 //locking in the database
1937 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1938 // kICAttrNoChange, (Handle) m_hDatabase);
1940 // if (status == noErr)
1946 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1951 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICDeleteMapEntry failed.")));
1958 CFWriteStreamRef cfwsInfo
= CFWriteStreamCreateWithFile(
1959 kCFAllocatorDefault
,
1960 (CFURLRef
) (CFTypeRef
)cfurlInfoLoc
);
1965 Boolean bOpened
= CFWriteStreamOpen(cfwsInfo
);
1968 CFStringRef cfsError
;
1969 CFIndex cfiWritten
= CFPropertyListWriteToStream((CFPropertyListRef
)(CFTypeRef
)cfdInfo
,
1971 kCFPropertyListXMLFormat_v1_0
, //100
1973 if (cfsError
&& cfiWritten
== 0)
1975 wxLogDebug(wxCFString(cfsError
).BuildWXString());
1977 cfdInfo
.PrintOut(sMessage
);
1978 wxLogDebug(sMessage
);
1982 bInfoSuccess
= true;
1983 //#if defined(__DARWIN__)
1984 // //force launch services to update its database for the finder
1985 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1986 // if (status != noErr)
1988 // wxLogDebug(wxT("LSRegisterURL Failed."));
1993 CFWriteStreamClose(cfwsInfo
);
1996 #endif //wxUSE_MIMETYPE