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")));
652 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
659 void wxMimeTypesManagerImpl::ClearData()
663 DisposeHandle((Handle
)m_hDatabase
);
664 //this can return an error, but we don't really care that much about it
665 ICStop( (ICInstance
) m_hIC
);
671 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
672 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
675 // extension -> file type
676 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
678 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
680 //low level functions - iterate through the database
684 for(long i
= 1; i
<= m_lCount
; ++i
)
686 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
690 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
691 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
693 wxFileType
* pFileType
= new wxFileType();
694 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
703 // MIME type -> extension -> file type
704 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
706 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
708 //low level functions - iterate through the database
712 for(long i
= 1; i
<= m_lCount
; ++i
)
714 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
715 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
719 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
721 wxFileType
* pFileType
= new wxFileType();
722 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
731 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
733 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
735 //low level functions - iterate through the database
739 long lStartCount
= (long) mimetypes
.GetCount();
741 for(long i
= 1; i
<= m_lCount
; ++i
)
743 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
744 if( status
== noErr
)
745 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
748 return mimetypes
.GetCount() - lStartCount
;
752 pascal OSStatus
MoreProcGetProcessTypeSignature(
753 const ProcessSerialNumberPtr pPSN
,
754 OSType
*pProcessType
,
757 OSStatus anErr
= noErr
;
758 ProcessInfoRec infoRec
;
759 ProcessSerialNumber localPSN
;
761 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
762 infoRec
.processName
= nil
;
763 infoRec
.processAppSpec
= nil
;
766 localPSN
.highLongOfPSN
= 0;
767 localPSN
.lowLongOfPSN
= kCurrentProcess
;
772 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
775 *pProcessType
= infoRec
.processType
;
776 *pCreator
= infoRec
.processSignature
;
780 }//end MoreProcGetProcessTypeSignature
784 // TODO: clean this up, its messy
789 #include "wx/mac/corefoundation/cfstring.h"
790 #include "wx/intl.h" //wxLocale for wxCFString
792 #define wxCF_RELEASE true
793 #define wxCF_RETAIN false
795 // ----------------------------------------------------------------------------
797 // ----------------------------------------------------------------------------
802 wxCFDictionary(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
804 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
805 if(bRetain
== wxCF_RETAIN
&& ref
)
809 wxCFDictionary(CFIndex cfiSize
= 0)
811 CFDictionaryKeyCallBacks kcbs
;
812 CFDictionaryValueCallBacks vcbs
;
813 BuildKeyCallbacks(&kcbs
);
814 BuildValueCallbacks(&vcbs
);
816 m_cfmdRef
= CFDictionaryCreateMutable(
817 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
825 {if(m_cfmdRef
) CFRelease(m_cfmdRef
);}
827 static const void* RetainProc(CFAllocatorRef
, const void* v
)
828 { return (const void*) CFRetain(v
); }
830 static void ReleaseProc(CFAllocatorRef
, const void* v
)
833 void MakeMutable(CFIndex cfiSize
= 0)
835 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
837 m_cfmdRef
= CFDictionaryCreateMutableCopy(
845 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
848 pCbs
->retain
= RetainProc
;
849 pCbs
->release
= ReleaseProc
;
850 pCbs
->copyDescription
= NULL
;
855 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
858 pCbs
->retain
= RetainProc
;
859 pCbs
->release
= ReleaseProc
;
860 pCbs
->copyDescription
= NULL
;
864 operator CFTypeRef () const
865 { return (CFTypeRef
)m_cfmdRef
; }
867 CFDictionaryRef
GetCFDictionary() const
868 { return (CFDictionaryRef
)m_cfmdRef
; }
870 CFMutableDictionaryRef
GetCFMutableDictionary()
871 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
873 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
876 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
879 CFIndex
GetCount() const
882 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
885 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
888 wxASSERT(Exists(cftKey
) == false);
889 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
892 void Remove(CFTypeRef cftKey
)
895 wxASSERT(Exists(cftKey
));
896 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
899 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
902 wxASSERT(Exists(cftKey
));
903 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
906 bool Exists(CFTypeRef cftKey
) const
909 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
) == true;
912 bool IsOk() const {return m_cfmdRef
!= NULL
; }
916 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID();
919 void PrintOut(wxString
& sMessage
)
921 PrintOutDictionary(sMessage
, m_cfmdRef
);
924 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
926 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
927 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
928 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
930 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
932 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
934 wxString sKey
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
935 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
938 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
941 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
944 wxString::Format(wxT("\n\t[Value : %s]"),
947 PrintOutType(sMessage
, sValue
, pValues
[i
]);
949 sMessage
<< wxT("\n");
956 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
959 for(CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
961 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
962 CFArrayGetValueAtIndex(cfaRef
, i
)
966 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
969 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
973 static void PrintOutType(wxString
& sMessage
, wxString sValue
, CFTypeRef cfRef
)
975 sMessage
<< wxT(" {");
977 if(sValue
== wxT("CFString"))
979 sMessage
<< wxMacCFStringHolder((CFStringRef
)cfRef
, false).AsString();
981 else if(sValue
== wxT("CFNumber"))
984 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
987 else if(sValue
== wxT("CFDictionary"))
989 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
991 else if(sValue
== wxT("CFArray"))
993 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
995 else if(sValue
== wxT("CFBoolean"))
997 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
999 else if(sValue
== wxT("CFURL"))
1001 sMessage
<< wxMacCFStringHolder(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
1005 sMessage
<< wxT("*****UNKNOWN TYPE******");
1008 sMessage
<< wxT("} ");
1012 void MakeValidXML();
1015 CFTypeRef
WriteAsXML()
1017 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
1020 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
1023 CFStringRef cfsError
=NULL
;
1024 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
1025 kCFAllocatorDefault
,
1027 kCFPropertyListMutableContainersAndLeaves
,
1032 *pErrorMsg
= wxMacCFStringHolder(cfsError
).AsString();
1034 CFRelease(cfsError
);
1037 return m_cfmdRef
!= NULL
;
1040 CFMutableDictionaryRef m_cfmdRef
;
1043 // ----------------------------------------------------------------------------
1045 // ----------------------------------------------------------------------------
1050 wxCFArray(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
1052 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1053 if(bRetain
== wxCF_RETAIN
&& ref
)
1057 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1058 { Create(cfiSize
); }
1063 void MakeMutable(CFIndex cfiSize
= 0)
1065 wxASSERT(IsValid());
1067 CFMutableArrayRef oldref
= m_cfmaRef
;
1068 m_cfmaRef
= CFArrayCreateMutableCopy(
1069 kCFAllocatorDefault
,
1071 (CFArrayRef
)oldref
);
1075 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1078 pCbs
->retain
= RetainProc
;
1079 pCbs
->release
= ReleaseProc
;
1080 pCbs
->copyDescription
= NULL
;
1084 void Create(CFIndex cfiSize
= 0)
1087 CFArrayCallBacks cb
;
1088 BuildCallbacks(&cb
);
1090 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1094 {if(m_cfmaRef
) CFRelease(m_cfmaRef
);}
1096 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1097 { return (const void*) CFRetain(v
); }
1099 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1102 operator CFTypeRef () const
1103 { return (CFTypeRef
)m_cfmaRef
; }
1105 CFArrayRef
GetCFArray() const
1106 { return (CFArrayRef
)m_cfmaRef
; }
1108 CFMutableArrayRef
GetCFMutableArray()
1109 { return (CFMutableArrayRef
) m_cfmaRef
; }
1111 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1113 wxASSERT(IsValid());
1114 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1119 wxASSERT(IsValid());
1120 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1123 void Add(CFTypeRef cftValue
)
1125 wxASSERT(IsValid());
1126 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1129 void Remove(CFIndex cfiIndex
)
1131 wxASSERT(IsValid());
1132 wxASSERT(cfiIndex
< GetCount());
1133 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1136 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1138 wxASSERT(IsValid());
1139 wxASSERT(cfiIndex
< GetCount());
1140 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1143 bool IsOk() const {return m_cfmaRef
!= NULL
; }
1145 bool IsValid() const
1147 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1151 void MakeValidXML();
1155 CFMutableArrayRef m_cfmaRef
;
1158 // ----------------------------------------------------------------------------
1160 // ----------------------------------------------------------------------------
1165 wxCFString(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_Holder((CFStringRef
)ref
, bRetain
== wxCF_RELEASE
)
1168 wxCFString(const wxChar
* szString
) : m_Holder(wxString(szString
), wxLocale::GetSystemEncoding())
1171 wxCFString(const wxString
& sString
) : m_Holder(sString
, wxLocale::GetSystemEncoding())
1174 operator CFTypeRef() const
1175 { return (CFTypeRef
) ((CFStringRef
) m_Holder
); }
1177 bool IsOk() { return ((CFTypeRef
)(*this)) != NULL
; }
1179 wxString
BuildWXString() {return m_Holder
.AsString(); }
1182 wxMacCFStringHolder m_Holder
;
1185 // ----------------------------------------------------------------------------
1187 // ----------------------------------------------------------------------------
1192 wxCFNumber(int nValue
)
1194 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &nValue
);
1197 wxCFNumber(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfnRef((CFNumberRef
)ref
)
1199 if(bRetain
== wxCF_RETAIN
&& ref
)
1206 CFRelease(m_cfnRef
);
1210 operator CFTypeRef() const
1211 { return (CFTypeRef
) m_cfnRef
; }
1216 CFNumberGetValue( 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