1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mac/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 /////////////////////////////////////////////////////////////////////////////
14 // TODO: Search Info[-macos](classic).plist dictionary in addition
15 // to Internet Config database.
17 // Maybe try a brainstorm a way to change the wxMimeTypesManager API
18 // to get info from a file instead/addition to current get all stuff
19 // API so that we can use Launch Services to get mime type info.
21 // Implement geticon from one of the finder info functions - or
22 // use launch services and search that app's plist for the icon.
24 // Put some special juice in for the print command.
29 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
30 #pragma implementation "mimetype.h"
33 // for compilers that support precompilation, includes "wx.h".
34 #include "wx/wxprec.h"
41 #include "wx/string.h"
53 #include "wx/dynarray.h"
54 #include "wx/confbase.h"
56 #include "wx/mac/mimetype.h"
57 #include "wx/mac/private.h" //wxMacMakeStringFromPascal
59 // other standard headers
61 #include <InternetConfig.h> //For mime types
64 /* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
66 /* IsRemoteVolume can be used to find out if the
67 volume referred to by vRefNum is a remote volume
68 located somewhere on a network. the volume's attribute
69 flags (copied from the GetVolParmsInfoBuffer structure)
70 are returned in the longword pointed to by vMAttrib. */
71 OSErr
IsRemoteVolume(short vRefNum
, Boolean
*isRemote
, long *vMAttrib
) {
73 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
);
81 *isRemote
= (volinfo
.vMServerAdr
!= 0);
82 *vMAttrib
= volinfo
.vMAttrib
;
88 /* BuildVolumeList fills the array pointed to by vols with
89 a list of the currently mounted volumes. If includeRemote
90 is true, then remote server volumes will be included in
91 the list. When remote server volumes are included in the
92 list, they will be added to the end of the list. On entry,
93 *count should contain the size of the array pointed to by
94 vols. On exit, *count will be set to the number of id numbers
95 placed in the array. If vMAttribMask is non-zero, then
96 only volumes with matching attributes are added to the
97 list of volumes. bits in the vMAttribMask should use the
98 same encoding as bits in the vMAttrib field of
99 the GetVolParmsInfoBuffer structure. */
100 OSErr
BuildVolumeList(Boolean includeRemote
, short *vols
,
101 long *count
, long vMAttribMask
) {
102 HParamBlockRec volPB
;
105 long nlocal
, nremote
;
108 /* set up and check parameters */
109 volPB
.volumeParam
.ioNamePtr
= NULL
;
110 nlocal
= nremote
= 0;
111 if (*count
== 0) return noErr
;
113 /* iterate through volumes */
114 for (volPB
.volumeParam
.ioVolIndex
= 1;
115 PBHGetVInfoSync(&volPB
) == noErr
;
116 volPB
.volumeParam
.ioVolIndex
++) {
118 /* skip remote volumes, if necessary */
119 err
= IsRemoteVolume(volPB
.volumeParam
.ioVRefNum
, &isRemote
, &vMAttrib
);
120 if (err
!= noErr
) goto bail
;
121 if ( ( includeRemote
|| ! isRemote
)
122 && (vMAttrib
& vMAttribMask
) == vMAttribMask
) {
124 /* add local volumes at the front, remote
125 volumes at the end */
127 vols
[nlocal
+ nremote
++] = volPB
.volumeParam
.ioVRefNum
;
130 BlockMoveData(vols
+nlocal
, vols
+nlocal
+1,
131 nremote
*sizeof(short));
132 vols
[nlocal
++] = volPB
.volumeParam
.ioVRefNum
;
136 if ((nlocal
+ nremote
) >= *count
) break;
140 *count
= (nlocal
+ nremote
);
145 /* FindApplication iterates through mounted volumes
146 searching for an application with the given creator
147 type. If includeRemote is true, then remote volumes
148 will be searched (after local ones) for an application
149 with the creator type. */
153 /* Hacked to output to appName */
155 OSErr
FindApplication(OSType appCreator
, Boolean includeRemote
, Str255 appName
, FSSpec
* appSpec
) {
156 short rRefNums
[kMaxVols
];
161 /* get a list of volumes - with desktop files */
163 err
= BuildVolumeList(includeRemote
, rRefNums
, &volCount
,
164 (1<<bHasDesktopMgr
) );
165 if (err
!= noErr
) return err
;
167 /* iterate through the list */
168 for (i
=0; i
<volCount
; i
++) {
170 /* has a desktop file? */
171 desktopPB
.ioCompletion
= NULL
;
172 desktopPB
.ioVRefNum
= rRefNums
[i
];
173 desktopPB
.ioNamePtr
= NULL
;
174 desktopPB
.ioIndex
= 0;
175 err
= PBDTGetPath(&desktopPB
);
176 if (err
!= noErr
) continue;
178 /* has the correct app?? */
179 desktopPB
.ioFileCreator
= appCreator
;
180 desktopPB
.ioNamePtr
= appName
;
181 err
= PBDTGetAPPLSync(&desktopPB
);
182 if (err
!= noErr
) continue;
184 /* make a file spec referring to it */
185 err
= FSMakeFSSpec(rRefNums
[i
],
186 desktopPB
.ioAPPLParID
, appName
,
188 if (err
!= noErr
) continue;
197 /* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
199 //yeah, duplicated code
200 pascal OSErr
FSpGetFullPath(const FSSpec
*spec
,
201 short *fullPathLength
,
213 /* Default to noErr */
214 realResult
= result
= noErr
;
216 /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */
218 if ( spec->name[0] == 0 )
220 result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec);
225 /* Make a copy of the input FSSpec that can be modified */
226 BlockMoveData(spec
, &tempSpec
, sizeof(FSSpec
));
229 if ( result
== noErr
)
231 if ( tempSpec
.parID
== fsRtParID
)
233 /* The object is a volume */
235 /* Add a colon to make it a full pathname */
237 tempSpec
.name
[tempSpec
.name
[0]] = ':';
240 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
244 /* The object isn't a volume */
246 /* Is the object a file or a directory? */
247 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
248 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
249 pb
.dirInfo
.ioDrDirID
= tempSpec
.parID
;
250 pb
.dirInfo
.ioFDirIndex
= 0;
251 result
= PBGetCatInfoSync(&pb
);
252 /* Allow file/directory name at end of path to not exist. */
254 if ( (result
== noErr
) || (result
== fnfErr
) )
256 /* if the object is a directory, append a colon so full pathname ends with colon */
257 if ( (result
== noErr
) && (pb
.hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0 )
260 tempSpec
.name
[tempSpec
.name
[0]] = ':';
263 /* Put the object name in first */
264 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
265 if ( result
== noErr
)
267 /* Get the ancestor directory names */
268 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
269 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
270 pb
.dirInfo
.ioDrParID
= tempSpec
.parID
;
271 do /* loop until we have an error or find the root directory */
273 pb
.dirInfo
.ioFDirIndex
= -1;
274 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
275 result
= PBGetCatInfoSync(&pb
);
276 if ( result
== noErr
)
278 /* Append colon to directory name */
280 tempSpec
.name
[tempSpec
.name
[0]] = ':';
282 /* Add directory name to beginning of fullPath */
283 (void) Munger(*fullPath
, 0, NULL
, 0, &tempSpec
.name
[1], tempSpec
.name
[0]);
286 } while ( (result
== noErr
) && (pb
.dirInfo
.ioDrDirID
!= fsRtDirID
) );
292 if ( result
== noErr
)
294 /* Return the length */
295 *fullPathLength
= GetHandleSize(*fullPath
);
296 result
= realResult
; /* return realResult in case it was fnfErr */
300 /* Dispose of the handle and return NULL and zero length */
301 if ( *fullPath
!= NULL
)
303 DisposeHandle(*fullPath
);
313 // On the mac there are two ways to open a file - one is through apple events and the
314 // finder, another is through mime types.
316 // So, really there are two ways to implement wxFileType...
318 // Mime types are only available on OS 8.1+ through the InternetConfig API
320 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
321 // Low - which means you have to iterate yourself through the mime database
322 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
323 // High - which requires access to the database every time
325 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
326 // and mid-level functions
328 // TODO: Should we call ICBegin/ICEnd? Then where?
332 inline void wxLogMimeDebug(const wxChar
* szMsg
, OSStatus status
)
334 wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg
, __LINE__
, (int)status
));
337 // in case we're compiling in non-GUI mode
338 class WXDLLEXPORT wxIcon
;
340 bool wxFileTypeImpl::SetCommand(const wxString
& cmd
, const wxString
& verb
, bool overwriteprompt
)
342 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
347 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int index
)
349 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
354 bool wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
355 const wxFileType::MessageParameters
& params
) const
357 wxString cmd
= GetCommand(wxT("open"));
359 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
361 return !openCmd
->empty();
365 wxFileTypeImpl::GetPrintCommand(wxString
*printCmd
,
366 const wxFileType::MessageParameters
& params
)
369 wxString cmd
= GetCommand(wxT("print"));
371 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
373 return !printCmd
->empty();
377 // Internet Config vs. Launch Services
379 // From OS 8 on there was internet config...
380 // However, OSX and its finder does not use info
381 // from Internet Config at all - the Internet Config
382 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
383 // OR REGISTERED THROUGH INTERNET CONFIG
385 // Therefore on OSX in order for the open command to be useful
386 // we need to go straight to launch services
389 #if defined(__DARWIN__)
391 //on darwin, use launch services
392 #include "LaunchServices.h"
394 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
396 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
398 if(verb
== wxT("open"))
401 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
402 (Handle
) m_manager
->m_hDatabase
,
405 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
406 sCurrentExtension
= sCurrentExtension
.Right(sCurrentExtension
.Length()-1 );
408 //type, creator, ext, roles, outapp (FSRef), outappurl
409 CFURLRef cfurlAppPath
;
410 OSStatus status
= LSGetApplicationForInfo (kLSUnknownType
,
412 wxMacCFStringHolder(sCurrentExtension
, wxLocale::GetSystemEncoding()),
419 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(cfurlAppPath
, kCFURLPOSIXPathStyle
);
420 CFRelease(cfurlAppPath
);
424 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
428 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
430 wxT("LSGetApplicationForInfo failed."),
435 return wxEmptyString
;
438 #else //carbon/classic implementation
440 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
442 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
444 if(verb
== wxT("open"))
447 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
448 (Handle
) m_manager
->m_hDatabase
,
451 //The entry in the mimetype database only contains the app
452 //that's registered - it may not exist... we need to remap the creator
453 //type and find the right application
455 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
459 if(FindApplication(entry
.fileCreator
, false, outName
, &outSpec
) != noErr
)
460 return wxEmptyString
;
462 Handle outPathHandle
;
464 OSErr err
= FSpGetFullPath(&outSpec
, &outPathSize
, &outPathHandle
);
468 char* szPath
= *outPathHandle
;
469 wxString
sClassicPath(szPath
, wxConvLocal
, outPathSize
);
470 #if defined(__DARWIN__)
471 //Classic Path --> Unix (OSX) Path
472 CFURLRef finalURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
473 wxMacCFStringHolder(sClassicPath
, wxLocale::GetSystemEncoding()),
475 false); //false == not a directory
477 //clean up memory from the classic path handle
478 DisposeHandle(outPathHandle
);
482 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(finalURL
, kCFURLPOSIXPathStyle
);
487 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
489 #else //classic HFS path acceptable
495 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus
)err
);
498 return wxEmptyString
;
502 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
504 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
507 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
508 (Handle
) m_manager
->m_hDatabase
,
511 *desc
= wxMacMakeStringFromPascal(entry
.entryName
);
515 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
517 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
520 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
521 (Handle
) m_manager
->m_hDatabase
,
524 //entry has period in it
525 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
526 extensions
.Add( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) );
530 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
532 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
535 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
536 (Handle
) m_manager
->m_hDatabase
,
539 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
543 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
557 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
559 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
561 // no such file type or no value or incorrect icon entry
565 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
, wxArrayString
* commands
,
566 const wxFileType::MessageParameters
& params
) const
568 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
573 if(GetOpenCommand(&sCommand
, params
))
575 verbs
->Add(wxString(wxT("open")));
576 commands
->Add(sCommand
);
583 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, const wxString
& extraDir
)
585 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
587 //some apps (non-wx) use the 'plst' resource instead
589 CFBundleRef cfbMain = CFBundleGetMainBundle();
590 wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
592 cfdInfo.PrintOut(sLog);
596 //start internet config - log if there's an error
597 //the second param is the signature of the application, also known
598 //as resource ID 0. However, as per some recent discussions, we may not
599 //have a signature for this app, so a generic 'APPL' which is the executable
600 //type will work for now
601 OSStatus status
= ICStart( (ICInstance
*) &m_hIC
, 'APPL');
605 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
612 m_hDatabase
= (void**) NewHandle(0);
613 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
615 //the database file can be corrupt (on OSX its
616 //~/Library/Preferences/com.apple.internetconfig.plist)
621 wxLogDebug(wxT("Corrupt Mime Database!"));
625 //obtain the number of entries in the map
626 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
627 wxASSERT( status
== noErr
);
633 for(long i = 1; i <= m_lCount; ++i)
635 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
639 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
640 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
641 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
644 impl.Init(this, pos);
646 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
649 impl.GetOpenCommand (&cmd,
650 wxFileType::MessageParameters (wxT("http://www.google.com"),
653 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
660 void wxMimeTypesManagerImpl::ClearData()
664 DisposeHandle((Handle
)m_hDatabase
);
665 //this can return an error, but we don't really care that much about it
666 ICStop( (ICInstance
) m_hIC
);
672 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
673 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
676 // extension -> file type
677 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
679 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
681 //low level functions - iterate through the database
685 for(long i
= 1; i
<= m_lCount
; ++i
)
687 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
691 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
692 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
694 wxFileType
* pFileType
= new wxFileType();
695 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
704 // MIME type -> extension -> file type
705 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
707 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
709 //low level functions - iterate through the database
713 for(long i
= 1; i
<= m_lCount
; ++i
)
715 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
716 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
720 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
722 wxFileType
* pFileType
= new wxFileType();
723 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
732 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
734 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
736 //low level functions - iterate through the database
740 long lStartCount
= (long) mimetypes
.GetCount();
742 for(long i
= 1; i
<= m_lCount
; ++i
)
744 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
745 if( status
== noErr
)
746 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
749 return mimetypes
.GetCount() - lStartCount
;
753 pascal OSStatus
MoreProcGetProcessTypeSignature(
754 const ProcessSerialNumberPtr pPSN
,
755 OSType
*pProcessType
,
758 OSStatus anErr
= noErr
;
759 ProcessInfoRec infoRec
;
760 ProcessSerialNumber localPSN
;
762 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
763 infoRec
.processName
= nil
;
764 infoRec
.processAppSpec
= nil
;
767 localPSN
.highLongOfPSN
= 0;
768 localPSN
.lowLongOfPSN
= kCurrentProcess
;
773 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
776 *pProcessType
= infoRec
.processType
;
777 *pCreator
= infoRec
.processSignature
;
781 }//end MoreProcGetProcessTypeSignature
785 // TODO: clean this up, its messy
790 #include "wx/mac/corefoundation/cfstring.h"
791 #include "wx/intl.h" //wxLocale for wxCFString
793 #define wxCF_RELEASE true
794 #define wxCF_RETAIN false
796 // ----------------------------------------------------------------------------
798 // ----------------------------------------------------------------------------
803 wxCFDictionary(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
805 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
806 if(bRetain
== wxCF_RETAIN
&& ref
)
810 wxCFDictionary(CFIndex cfiSize
= 0)
812 CFDictionaryKeyCallBacks kcbs
;
813 CFDictionaryValueCallBacks vcbs
;
814 BuildKeyCallbacks(&kcbs
);
815 BuildValueCallbacks(&vcbs
);
817 m_cfmdRef
= CFDictionaryCreateMutable(
818 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
826 {if(m_cfmdRef
) CFRelease(m_cfmdRef
);}
828 static const void* RetainProc(CFAllocatorRef
, const void* v
)
829 { return (const void*) CFRetain(v
); }
831 static void ReleaseProc(CFAllocatorRef
, const void* v
)
834 void MakeMutable(CFIndex cfiSize
= 0)
836 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
838 m_cfmdRef
= CFDictionaryCreateMutableCopy(
846 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
849 pCbs
->retain
= RetainProc
;
850 pCbs
->release
= ReleaseProc
;
851 pCbs
->copyDescription
= NULL
;
856 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
859 pCbs
->retain
= RetainProc
;
860 pCbs
->release
= ReleaseProc
;
861 pCbs
->copyDescription
= NULL
;
865 operator CFTypeRef () const
866 { return (CFTypeRef
)m_cfmdRef
; }
868 CFDictionaryRef
GetCFDictionary() const
869 { return (CFDictionaryRef
)m_cfmdRef
; }
871 CFMutableDictionaryRef
GetCFMutableDictionary()
872 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
874 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
877 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
880 CFIndex
GetCount() const
883 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
886 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
889 wxASSERT(Exists(cftKey
) == false);
890 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
893 void Remove(CFTypeRef cftKey
)
896 wxASSERT(Exists(cftKey
));
897 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
900 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
903 wxASSERT(Exists(cftKey
));
904 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
907 bool Exists(CFTypeRef cftKey
) const
910 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
) == true;
913 bool IsOk() const {return m_cfmdRef
!= NULL
; }
917 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID();
920 void PrintOut(wxString
& sMessage
)
922 PrintOutDictionary(sMessage
, m_cfmdRef
);
925 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
927 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
928 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
929 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
931 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
933 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
935 wxString sKey
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
936 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
939 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
942 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
945 wxString::Format(wxT("\n\t[Value : %s]"),
948 PrintOutType(sMessage
, sValue
, pValues
[i
]);
950 sMessage
<< wxT("\n");
957 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
960 for(CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
962 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
963 CFArrayGetValueAtIndex(cfaRef
, i
)
967 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
970 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
974 static void PrintOutType(wxString
& sMessage
, wxString sValue
, CFTypeRef cfRef
)
976 sMessage
<< wxT(" {");
978 if(sValue
== wxT("CFString"))
980 sMessage
<< wxMacCFStringHolder((CFStringRef
)cfRef
, false).AsString();
982 else if(sValue
== wxT("CFNumber"))
985 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
988 else if(sValue
== wxT("CFDictionary"))
990 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
992 else if(sValue
== wxT("CFArray"))
994 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
996 else if(sValue
== wxT("CFBoolean"))
998 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
1000 else if(sValue
== wxT("CFURL"))
1002 sMessage
<< wxMacCFStringHolder(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
1006 sMessage
<< wxT("*****UNKNOWN TYPE******");
1009 sMessage
<< wxT("} ");
1013 void MakeValidXML();
1016 CFTypeRef
WriteAsXML()
1018 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
1021 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
1024 CFStringRef cfsError
=NULL
;
1025 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
1026 kCFAllocatorDefault
,
1028 kCFPropertyListMutableContainersAndLeaves
,
1033 *pErrorMsg
= wxMacCFStringHolder(cfsError
).AsString();
1035 CFRelease(cfsError
);
1038 return m_cfmdRef
!= NULL
;
1041 CFMutableDictionaryRef m_cfmdRef
;
1044 // ----------------------------------------------------------------------------
1046 // ----------------------------------------------------------------------------
1051 wxCFArray(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
1053 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1054 if(bRetain
== wxCF_RETAIN
&& ref
)
1058 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1059 { Create(cfiSize
); }
1064 void MakeMutable(CFIndex cfiSize
= 0)
1066 wxASSERT(IsValid());
1068 CFMutableArrayRef oldref
= m_cfmaRef
;
1069 m_cfmaRef
= CFArrayCreateMutableCopy(
1070 kCFAllocatorDefault
,
1072 (CFArrayRef
)oldref
);
1076 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1079 pCbs
->retain
= RetainProc
;
1080 pCbs
->release
= ReleaseProc
;
1081 pCbs
->copyDescription
= NULL
;
1085 void Create(CFIndex cfiSize
= 0)
1088 CFArrayCallBacks cb
;
1089 BuildCallbacks(&cb
);
1091 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1095 {if(m_cfmaRef
) CFRelease(m_cfmaRef
);}
1097 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1098 { return (const void*) CFRetain(v
); }
1100 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1103 operator CFTypeRef () const
1104 { return (CFTypeRef
)m_cfmaRef
; }
1106 CFArrayRef
GetCFArray() const
1107 { return (CFArrayRef
)m_cfmaRef
; }
1109 CFMutableArrayRef
GetCFMutableArray()
1110 { return (CFMutableArrayRef
) m_cfmaRef
; }
1112 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1114 wxASSERT(IsValid());
1115 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1120 wxASSERT(IsValid());
1121 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1124 void Add(CFTypeRef cftValue
)
1126 wxASSERT(IsValid());
1127 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1130 void Remove(CFIndex cfiIndex
)
1132 wxASSERT(IsValid());
1133 wxASSERT(cfiIndex
< GetCount());
1134 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1137 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1139 wxASSERT(IsValid());
1140 wxASSERT(cfiIndex
< GetCount());
1141 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1144 bool IsOk() const {return m_cfmaRef
!= NULL
; }
1146 bool IsValid() const
1148 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1152 void MakeValidXML();
1156 CFMutableArrayRef m_cfmaRef
;
1159 // ----------------------------------------------------------------------------
1161 // ----------------------------------------------------------------------------
1166 wxCFString(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_Holder((CFStringRef
)ref
, bRetain
== wxCF_RELEASE
)
1169 wxCFString(const wxChar
* szString
) : m_Holder(wxString(szString
), wxLocale::GetSystemEncoding())
1172 wxCFString(const wxString
& sString
) : m_Holder(sString
, wxLocale::GetSystemEncoding())
1175 operator CFTypeRef() const
1176 {return (CFTypeRef
) ((CFStringRef
) m_Holder
); }
1178 bool IsOk() { return ((CFTypeRef
)(*this)) != NULL
; }
1180 wxString
BuildWXString() {return m_Holder
.AsString(); }
1183 wxMacCFStringHolder m_Holder
;
1186 // ----------------------------------------------------------------------------
1188 // ----------------------------------------------------------------------------
1193 wxCFNumber(int nValue
)
1195 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
,
1196 kCFNumberIntType
, &nValue
);
1199 wxCFNumber(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfnRef((CFNumberRef
)ref
)
1201 if(bRetain
== wxCF_RETAIN
&& ref
)
1206 { if(m_cfnRef
) CFRelease(m_cfnRef
); }
1209 operator CFTypeRef() const
1210 { return (CFTypeRef
) m_cfnRef
; }
1224 bool IsOk() { return m_cfnRef
!= NULL
; }
1227 CFNumberRef m_cfnRef
;
1230 // ----------------------------------------------------------------------------
1232 // ----------------------------------------------------------------------------
1237 wxCFURL(CFTypeRef ref
= NULL
, bool bRetain
= wxCF_RELEASE
) : m_cfurlRef((CFURLRef
)ref
)
1239 if(bRetain
== wxCF_RETAIN
&& ref
)
1242 wxCFURL(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1244 Create(URLString
, BaseURL
);
1247 void Create(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1249 m_cfurlRef
= CFURLCreateWithString(
1250 kCFAllocatorDefault
,
1251 (CFStringRef
)(CFTypeRef
)URLString
,
1252 (CFURLRef
) BaseURL
);
1255 ~wxCFURL() {if(m_cfurlRef
) CFRelease(m_cfurlRef
);}
1257 wxString
BuildWXString()
1259 return wxCFString(CFURLCopyPath(m_cfurlRef
)).BuildWXString();
1262 operator CFTypeRef() const
1263 { return (CFTypeRef
)m_cfurlRef
; }
1265 bool IsOk() { return m_cfurlRef
!= NULL
; }
1267 CFURLRef m_cfurlRef
;
1270 // ----------------------------------------------------------------------------
1272 // ----------------------------------------------------------------------------
1274 #define wxCFDATA_RELEASEBUFFER 1
1275 #define wxCFDATA_RETAINBUFFER 0
1280 wxCFData(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfdaRef((CFDataRef
)ref
)
1282 if(bRetain
== wxCF_RETAIN
&& ref
)
1285 wxCFData(const UInt8
* pBytes
, CFIndex len
, bool bKeep
= wxCFDATA_RELEASEBUFFER
)
1287 if(bKeep
== wxCFDATA_RELEASEBUFFER
)
1289 m_cfdaRef
= CFDataCreateWithBytesNoCopy
1290 (kCFAllocatorDefault
, pBytes
, len
, kCFAllocatorDefault
);
1294 m_cfdaRef
= CFDataCreate(kCFAllocatorDefault
, pBytes
, len
);
1297 ~wxCFData() {if(m_cfdaRef
) CFRelease(m_cfdaRef
);}
1299 const UInt8
* GetValue()
1301 return CFDataGetBytePtr(m_cfdaRef
);
1306 return CFDataGetLength(m_cfdaRef
);
1309 operator CFTypeRef() const
1310 { return (CFTypeRef
)m_cfdaRef
; }
1312 bool IsOk() { return m_cfdaRef
!= NULL
; }
1314 CFDataRef m_cfdaRef
;
1317 void wxCFDictionary::MakeValidXML()
1319 CFIndex cfiCount
= GetCount();
1320 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
1321 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
1323 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1325 //for plist xml format all dictionary keys must be cfstrings and no values in
1326 //the dictionary or subkeys/values can be NULL
1327 //Also, CFURLs are not allowed
1328 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
1330 //must be an array, dictionary, string, bool, or int and cannot be null
1331 //and dictionaries can only contain cfstring keys
1332 CFTypeRef cfRef
= pValues
[i
];
1334 CFGetTypeID(pKeys
[i
]) != CFStringGetTypeID() ||
1342 pKeys
= new CFTypeRef
[cfiCount
];
1343 pValues
= new CFTypeRef
[cfiCount
];
1344 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1346 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1349 wxCFArray
cfaCurrent(cfRef
);
1350 cfaCurrent
.MakeMutable();
1351 cfaCurrent
.MakeValidXML();
1352 Set(pKeys
[i
], cfaCurrent
);
1354 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1357 wxCFDictionary
cfdCurrent(cfRef
);
1358 cfdCurrent
.MakeMutable();
1359 cfdCurrent
.MakeValidXML();
1360 Set(pKeys
[i
], cfdCurrent
);
1362 else if( ( CFGetTypeID(cfRef
) == CFStringGetTypeID() ||
1363 CFGetTypeID(cfRef
) == CFNumberGetTypeID() ||
1364 CFGetTypeID(cfRef
) == CFBooleanGetTypeID() ) == false )
1371 pKeys
= new CFTypeRef
[cfiCount
];
1372 pValues
= new CFTypeRef
[cfiCount
];
1373 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1381 void wxCFArray::MakeValidXML()
1383 for(CFIndex i
= 0; i
< GetCount(); ++i
)
1385 //must be an array, dictionary, string, bool, or int and cannot be null
1386 //and dictionaries can only contain cfstring keys
1387 CFTypeRef cfRef
= (*this)[i
];
1393 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1396 wxCFArray
cfaCurrent(cfRef
);
1397 cfaCurrent
.MakeMutable();
1398 cfaCurrent
.MakeValidXML();
1401 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1404 wxCFDictionary
cfdCurrent(cfRef
);
1405 cfdCurrent
.MakeMutable();
1406 cfdCurrent
.MakeValidXML();
1409 else if( ( CFGetTypeID(cfRef
) == CFStringGetTypeID() ||
1410 CFGetTypeID(cfRef
) == CFNumberGetTypeID() ||
1411 CFGetTypeID(cfRef
) == CFBooleanGetTypeID() ) == false )
1427 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
1429 bool bInfoSuccess
= false;
1431 const wxArrayString
& asExtensions
= ftInfo
.GetExtensions();
1432 size_t dwFoundIndex
= 0;
1433 if(!asExtensions
.GetCount())
1435 wxLogDebug(wxT("Must have extension to associate with"));
1438 //Find and write to Info.plist in main bundle (note that some other
1439 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1440 //some apps (non-wx) use the 'plst' resource instead
1441 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1444 UInt32 dwBundleType
, dwBundleCreator
;
1445 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1447 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1448 //which will give us the incorrect info.plist path
1449 //otherwise it will be 'APPL', or in the case of a framework,
1451 if(dwBundleType
== 'APPL')
1454 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1455 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1457 // sInfoPath << wxT("file://");
1458 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1459 sInfoPath
<< wxT("Contents/Info.plist");
1461 // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1462 wxCFDictionary cfdInfo
;
1463 bool bInfoOpenSuccess
= false;
1465 if(indictfile
.Open(sInfoPath
, wxFile::read
));
1467 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1468 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1469 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1470 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1472 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1473 if(!bInfoOpenSuccess
)
1477 if(bInfoOpenSuccess
)
1479 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1481 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1483 bool bAddDocTypesArrayToDictionary
= cfaDocTypes
.IsOk() == false;
1484 if(bAddDocTypesArrayToDictionary
)
1485 cfaDocTypes
.Create();
1487 cfaDocTypes
.MakeMutable( cfaDocTypes
.GetCount() + 1 );
1489 bool bEntryFound
= false;
1491 //search for duplicate
1493 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1495 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1497 //A lot of apps dont do to mime types for some reason
1498 //so we go by extensions only
1499 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1502 if(cfaExtensions
.IsOk() == false)
1505 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1507 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1509 if(asExtensions
[iWXExt
] ==
1510 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1513 dwFoundIndex
= iWXExt
;
1516 } //end of wxstring array
1520 } //end for cf array
1526 wxCFDictionary cfdNewEntry
;
1528 if(!ftInfo
.GetDescription().empty())
1530 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeName")),
1531 wxCFString(ftInfo
.GetDescription()) );
1534 if(!ftInfo
.GetIconFile().empty())
1536 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeIconFile")),
1537 wxCFString(ftInfo
.GetIconFile()) );
1541 wxCFArray cfaOSTypes
;
1542 wxCFArray cfaExtensions
;
1543 wxCFArray cfaMimeTypes
;
1546 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1547 cfaOSTypes
.Add( wxCFString(wxT("****")) );
1548 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes
);
1550 if(ftInfo
.GetExtensionsCount() != 0) //'*' for unrestricted
1552 for(size_t iExtension
= 0; iExtension
< (size_t)ftInfo
.GetExtensionsCount(); ++iExtension
)
1554 cfaExtensions
.Add( wxCFString( asExtensions
[iExtension
] ) );
1557 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions
);
1560 if(!ftInfo
.GetMimeType().empty())
1562 cfaMimeTypes
.Add( wxCFString(ftInfo
.GetMimeType()) );
1563 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes
);
1566 // Editor - can perform all actions
1567 // Viewer - all actions except manipulation/saving
1568 // None - can perform no actions
1569 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
1571 // Is application bundled?
1572 cfdNewEntry
.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue
);
1575 cfaDocTypes
.Set(i
, cfdNewEntry
);
1577 cfaDocTypes
.Add(cfdNewEntry
);
1580 // set the doc types array in the muted dictionary
1583 if(bAddDocTypesArrayToDictionary
)
1584 cfdInfo
.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1586 cfdInfo
.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1588 cfdInfo
.MakeValidXML();
1591 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1593 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1596 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1597 (wxFileOffset
)cfdaInfo
.GetCount())
1599 wxLogDebug(wxT("error in writing to file"));
1603 bInfoSuccess
= true;
1604 //#if defined(__DARWIN__)
1605 // //force launch services to update its database for the finder
1606 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1607 // if(status != noErr)
1609 // wxLogDebug(wxT("LSRegisterURL Failed."));
1613 outdictfile
.Close();
1617 outdictfile
.Close();
1618 wxLogDebug(wxT("Could not read in new dictionary"));
1623 wxLogDebug(wxString(wxT("Could not open [")) +
1624 sInfoPath
+ wxT("] for writing."));
1629 wxLogDebug(wxT("No info dictionary in main bundle"));
1634 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1639 wxLogDebug(wxT("No main bundle"));
1642 #if defined(__DARWIN__)
1646 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1647 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1648 //the app's signature somehow...
1652 OSStatus status
= MoreProcGetProcessTypeSignature(NULL
,&processType
, &creator
);
1656 Str255 psCreatorName
;
1658 status
= FindApplication(creator
, false, psCreatorName
, &dummySpec
);
1663 //get the file type if it exists -
1664 //if it really does then modify the database then save it,
1665 //otherwise we need to create a whole new entry
1666 wxFileType
* pFileType
= GetFileTypeFromExtension(asExtensions
[dwFoundIndex
]);
1670 ICGetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1671 pFileType
->m_impl
->m_lIndex
, &entry
);
1673 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1674 entry
.fileCreator
= creator
;
1676 status
= ICSetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1677 pFileType
->m_impl
->m_lIndex
, &entry
);
1682 //kICAttrNoChange means we don't care about attributes such as
1683 //locking in the database
1684 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1685 // kICAttrNoChange, (Handle) m_hDatabase);
1686 // if(status == noErr)
1690 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1695 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICSetMapEntry failed.")));
1703 //TODO: Maybe force all 3 of these to be non-empty?
1706 Str255 psDescription
;
1708 wxMacStringToPascal(wxString(wxT(".")) + ftInfo
.GetExtensions()[0], psExtension
);
1709 wxMacStringToPascal(ftInfo
.GetMimeType(), psMimeType
);
1710 wxMacStringToPascal(ftInfo
.GetDescription(), psDescription
);
1712 Str255 psPostCreatorName
;
1713 wxMacStringToPascal(wxT(""), psPostCreatorName
);
1716 //add the entry to the database
1718 entry
.totalLength
= sizeof(ICMapEntry
);
1719 entry
.fixedLength
= kICMapFixedLength
;
1721 entry
.fileType
= 0; //TODO: File type?
1722 entry
.fileCreator
= creator
;
1723 entry
.postCreator
= 0;
1724 entry
.flags
= kICMapDataForkBit
; //TODO: Maybe resource is valid by default too?
1725 PLstrcpy( entry
.extension
, psExtension
) ;
1726 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1727 memcpy(entry
.postAppName
, psPostCreatorName
, sizeof(Str255
));
1728 memcpy(entry
.MIMEType
, psMimeType
, sizeof(Str255
));
1729 memcpy(entry
.entryName
, psDescription
, sizeof(Str255
));
1731 status
= ICAddMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &entry
);
1735 //kICAttrNoChange means we don't care about attributes such as
1736 //locking in the database
1737 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1738 // kICAttrNoChange, (Handle) m_hDatabase);
1740 //return the entry in the database if successful
1741 // if(status == noErr)
1742 return GetFileTypeFromExtension(ftInfo
.GetMimeType());
1745 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1750 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICAppMapEntry failed.")));
1753 } //end if FindApplcation was successful
1756 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("FindApplication failed.")));
1758 } //end if it could obtain app's signature
1761 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("GetProcessSignature failed.")));
1767 wxMimeTypesManagerImpl::Unassociate(wxFileType
*pFileType
)
1769 wxASSERT(pFileType
);
1770 bool bInfoSuccess
= false;
1772 wxArrayString asExtensions
;
1773 pFileType
->GetExtensions(asExtensions
);
1775 if(!asExtensions
.GetCount())
1777 wxLogDebug(wxT("Must have extension to disassociate"));
1781 //Find and write to Info.plist in main bundle (note that some other
1782 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1783 //some apps (non-wx) use the 'plst' resource instead
1784 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1787 UInt32 dwBundleType
, dwBundleCreator
;
1788 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1790 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1791 //which will give us the incorrect info.plist path
1792 //otherwise it will be 'APPL', or in the case of a framework,
1794 if(dwBundleType
== 'APPL')
1797 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1798 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1800 // sInfoPath << wxT("file://");
1801 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1802 sInfoPath
<< wxT("Contents/Info.plist");
1804 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1805 wxCFDictionary cfdInfo
;
1806 bool bInfoOpenSuccess
= false;
1808 if(indictfile
.Open(sInfoPath
, wxFile::read
));
1810 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1811 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1812 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1813 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1815 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1816 if(!bInfoOpenSuccess
)
1820 if(bInfoOpenSuccess
)
1822 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1824 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1826 if(cfaDocTypes
.IsOk())
1828 bool bEntryFound
= false;
1830 //search for duplicate
1832 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1834 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1836 //A lot of apps dont do to mime types for some reason
1837 //so we go by extensions only
1838 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1841 if(cfaExtensions
.IsOk() == false)
1844 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1846 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1848 if(asExtensions
[iWXExt
] ==
1849 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1852 cfaDocTypes
.Remove(i
);
1853 cfdInfo
.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes
);
1856 } //end of wxstring array
1860 } //end for cf array
1868 cfdInfo
.MakeValidXML();
1871 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1873 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1876 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1877 (wxFileOffset
)cfdaInfo
.GetCount())
1879 wxLogDebug(wxT("error in writing to file"));
1883 bInfoSuccess
= true;
1884 //#if defined(__DARWIN__)
1885 // //force launch services to update its database for the finder
1886 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1887 // if(status != noErr)
1889 // wxLogDebug(wxT("LSRegisterURL Failed."));
1893 outdictfile
.Close();
1897 outdictfile
.Close();
1898 wxLogDebug(wxT("Could not read in new dictionary"));
1903 wxLogDebug(wxString(wxT("Could not open [")) +
1904 sInfoPath
+ wxT("] for writing."));
1909 wxLogDebug(wxT("Entry not found to remove"));
1911 wxCFDictionary::PrintOutArray(sPrintOut
, (CFArrayRef
)(CFTypeRef
)cfaDocTypes
);
1912 wxLogDebug(sPrintOut
);
1913 for(size_t i
= 0; i
< asExtensions
.GetCount(); ++i
)
1914 wxLogDebug(asExtensions
[i
]);
1919 wxLogDebug(wxT("No doc types array found"));
1920 wxString sPrintOut
; cfdInfo
.PrintOut(sPrintOut
); wxLogDebug(sPrintOut
);
1925 wxLogDebug(wxT("No info dictionary in main bundle"));
1930 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1935 wxLogDebug(wxT("No main bundle"));
1938 #if defined(__DARWIN__)
1943 //this should be as easy as removing the entry from the database and then saving
1945 OSStatus status
= ICDeleteMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1946 pFileType
->m_impl
->m_lIndex
);
1950 //kICAttrNoChange means we don't care about attributes such as
1951 //locking in the database
1952 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1953 // kICAttrNoChange, (Handle) m_hDatabase);
1955 // if(status == noErr)
1959 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1965 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICDeleteMapEntry failed.")));
1971 CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile(
1972 kCFAllocatorDefault,
1973 (CFURLRef) (CFTypeRef)cfurlInfoLoc );
1978 Boolean bOpened = CFWriteStreamOpen(cfwsInfo);
1981 CFStringRef cfsError;
1982 CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo,
1984 kCFPropertyListXMLFormat_v1_0, //100
1986 if(cfsError && cfiWritten == 0)
1988 wxLogDebug(wxCFString(cfsError).BuildWXString());
1990 cfdInfo.PrintOut(sMessage);
1991 wxLogDebug(sMessage);
1995 bInfoSuccess = true;
1996 //#if defined(__DARWIN__)
1997 // //force launch services to update its database for the finder
1998 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1999 // if(status != noErr)
2001 // wxLogDebug(wxT("LSRegisterURL Failed."));
2006 CFWriteStreamClose(cfwsInfo);
2009 #endif //wxUSE_MIMETYPE