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
63 #include <InternetConfig.h> //For mime types
66 /* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
68 /* IsRemoteVolume can be used to find out if the
69 volume referred to by vRefNum is a remote volume
70 located somewhere on a network. the volume's attribute
71 flags (copied from the GetVolParmsInfoBuffer structure)
72 are returned in the longword pointed to by vMAttrib. */
73 OSErr
IsRemoteVolume(short vRefNum
, Boolean
*isRemote
, long *vMAttrib
) {
75 GetVolParmsInfoBuffer volinfo
;
77 volPB
.ioParam
.ioVRefNum
= vRefNum
;
78 volPB
.ioParam
.ioNamePtr
= NULL
;
79 volPB
.ioParam
.ioBuffer
= (Ptr
) &volinfo
;
80 volPB
.ioParam
.ioReqCount
= sizeof(volinfo
);
81 err
= PBHGetVolParmsSync(&volPB
);
83 *isRemote
= (volinfo
.vMServerAdr
!= 0);
84 *vMAttrib
= volinfo
.vMAttrib
;
90 /* BuildVolumeList fills the array pointed to by vols with
91 a list of the currently mounted volumes. If includeRemote
92 is true, then remote server volumes will be included in
93 the list. When remote server volumes are included in the
94 list, they will be added to the end of the list. On entry,
95 *count should contain the size of the array pointed to by
96 vols. On exit, *count will be set to the number of id numbers
97 placed in the array. If vMAttribMask is non-zero, then
98 only volumes with matching attributes are added to the
99 list of volumes. bits in the vMAttribMask should use the
100 same encoding as bits in the vMAttrib field of
101 the GetVolParmsInfoBuffer structure. */
102 OSErr
BuildVolumeList(Boolean includeRemote
, short *vols
,
103 long *count
, long vMAttribMask
) {
104 HParamBlockRec volPB
;
107 long nlocal
, nremote
;
110 /* set up and check parameters */
111 volPB
.volumeParam
.ioNamePtr
= NULL
;
112 nlocal
= nremote
= 0;
113 if (*count
== 0) return noErr
;
115 /* iterate through volumes */
116 for (volPB
.volumeParam
.ioVolIndex
= 1;
117 PBHGetVInfoSync(&volPB
) == noErr
;
118 volPB
.volumeParam
.ioVolIndex
++) {
120 /* skip remote volumes, if necessary */
121 err
= IsRemoteVolume(volPB
.volumeParam
.ioVRefNum
, &isRemote
, &vMAttrib
);
122 if (err
!= noErr
) goto bail
;
123 if ( ( includeRemote
|| ! isRemote
)
124 && (vMAttrib
& vMAttribMask
) == vMAttribMask
) {
126 /* add local volumes at the front, remote
127 volumes at the end */
129 vols
[nlocal
+ nremote
++] = volPB
.volumeParam
.ioVRefNum
;
132 BlockMoveData(vols
+nlocal
, vols
+nlocal
+1,
133 nremote
*sizeof(short));
134 vols
[nlocal
++] = volPB
.volumeParam
.ioVRefNum
;
138 if ((nlocal
+ nremote
) >= *count
) break;
142 *count
= (nlocal
+ nremote
);
147 /* FindApplication iterates through mounted volumes
148 searching for an application with the given creator
149 type. If includeRemote is true, then remote volumes
150 will be searched (after local ones) for an application
151 with the creator type. */
155 /* Hacked to output to appName */
157 OSErr
FindApplication(OSType appCreator
, Boolean includeRemote
, Str255 appName
, FSSpec
* appSpec
) {
158 short rRefNums
[kMaxVols
];
163 /* get a list of volumes - with desktop files */
165 err
= BuildVolumeList(includeRemote
, rRefNums
, &volCount
,
166 (1<<bHasDesktopMgr
) );
167 if (err
!= noErr
) return err
;
169 /* iterate through the list */
170 for (i
=0; i
<volCount
; i
++) {
172 /* has a desktop file? */
173 desktopPB
.ioCompletion
= NULL
;
174 desktopPB
.ioVRefNum
= rRefNums
[i
];
175 desktopPB
.ioNamePtr
= NULL
;
176 desktopPB
.ioIndex
= 0;
177 err
= PBDTGetPath(&desktopPB
);
178 if (err
!= noErr
) continue;
180 /* has the correct app?? */
181 desktopPB
.ioFileCreator
= appCreator
;
182 desktopPB
.ioNamePtr
= appName
;
183 err
= PBDTGetAPPLSync(&desktopPB
);
184 if (err
!= noErr
) continue;
186 /* make a file spec referring to it */
187 err
= FSMakeFSSpec(rRefNums
[i
],
188 desktopPB
.ioAPPLParID
, appName
,
190 if (err
!= noErr
) continue;
199 /* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
201 //yeah, duplicated code
202 pascal OSErr
FSpGetFullPath(const FSSpec
*spec
,
203 short *fullPathLength
,
215 /* Default to noErr */
216 realResult
= result
= noErr
;
218 /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */
220 if ( spec->name[0] == 0 )
222 result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec);
227 /* Make a copy of the input FSSpec that can be modified */
228 BlockMoveData(spec
, &tempSpec
, sizeof(FSSpec
));
231 if ( result
== noErr
)
233 if ( tempSpec
.parID
== fsRtParID
)
235 /* The object is a volume */
237 /* Add a colon to make it a full pathname */
239 tempSpec
.name
[tempSpec
.name
[0]] = ':';
242 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
246 /* The object isn't a volume */
248 /* Is the object a file or a directory? */
249 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
250 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
251 pb
.dirInfo
.ioDrDirID
= tempSpec
.parID
;
252 pb
.dirInfo
.ioFDirIndex
= 0;
253 result
= PBGetCatInfoSync(&pb
);
254 /* Allow file/directory name at end of path to not exist. */
256 if ( (result
== noErr
) || (result
== fnfErr
) )
258 /* if the object is a directory, append a colon so full pathname ends with colon */
259 if ( (result
== noErr
) && (pb
.hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0 )
262 tempSpec
.name
[tempSpec
.name
[0]] = ':';
265 /* Put the object name in first */
266 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
267 if ( result
== noErr
)
269 /* Get the ancestor directory names */
270 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
271 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
272 pb
.dirInfo
.ioDrParID
= tempSpec
.parID
;
273 do /* loop until we have an error or find the root directory */
275 pb
.dirInfo
.ioFDirIndex
= -1;
276 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
277 result
= PBGetCatInfoSync(&pb
);
278 if ( result
== noErr
)
280 /* Append colon to directory name */
282 tempSpec
.name
[tempSpec
.name
[0]] = ':';
284 /* Add directory name to beginning of fullPath */
285 (void) Munger(*fullPath
, 0, NULL
, 0, &tempSpec
.name
[1], tempSpec
.name
[0]);
288 } while ( (result
== noErr
) && (pb
.dirInfo
.ioDrDirID
!= fsRtDirID
) );
294 if ( result
== noErr
)
296 /* Return the length */
297 *fullPathLength
= GetHandleSize(*fullPath
);
298 result
= realResult
; /* return realResult in case it was fnfErr */
302 /* Dispose of the handle and return NULL and zero length */
303 if ( *fullPath
!= NULL
)
305 DisposeHandle(*fullPath
);
315 // On the mac there are two ways to open a file - one is through apple events and the
316 // finder, another is through mime types.
318 // So, really there are two ways to implement wxFileType...
320 // Mime types are only available on OS 8.1+ through the InternetConfig API
322 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
323 // Low - which means you have to iterate yourself through the mime database
324 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
325 // High - which requires access to the database every time
327 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
328 // and mid-level functions
330 // TODO: Should we call ICBegin/ICEnd? Then where?
334 inline void wxLogMimeDebug(const wxChar
* szMsg
, OSStatus status
)
336 wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg
, __LINE__
, (int)status
));
339 // in case we're compiling in non-GUI mode
340 class WXDLLEXPORT wxIcon
;
342 bool wxFileTypeImpl::SetCommand(const wxString
& cmd
, const wxString
& verb
, bool overwriteprompt
)
344 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
349 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int index
)
351 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
356 bool wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
357 const wxFileType::MessageParameters
& params
) const
359 wxString cmd
= GetCommand(wxT("open"));
361 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
363 return !openCmd
->empty();
367 wxFileTypeImpl::GetPrintCommand(wxString
*printCmd
,
368 const wxFileType::MessageParameters
& params
)
371 wxString cmd
= GetCommand(wxT("print"));
373 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
375 return !printCmd
->empty();
379 // Internet Config vs. Launch Services
381 // From OS 8 on there was internet config...
382 // However, OSX and its finder does not use info
383 // from Internet Config at all - the Internet Config
384 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
385 // OR REGISTERED THROUGH INTERNET CONFIG
387 // Therefore on OSX in order for the open command to be useful
388 // we need to go straight to launch services
391 #if defined(__DARWIN__)
393 //on darwin, use launch services
394 #include <ApplicationServices/ApplicationServices.h>
396 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
398 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
400 if(verb
== wxT("open"))
403 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
404 (Handle
) m_manager
->m_hDatabase
,
407 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
408 sCurrentExtension
= sCurrentExtension
.Right(sCurrentExtension
.Length()-1 );
410 //type, creator, ext, roles, outapp (FSRef), outappurl
411 CFURLRef cfurlAppPath
;
412 OSStatus status
= LSGetApplicationForInfo (kLSUnknownType
,
414 wxMacCFStringHolder(sCurrentExtension
, wxLocale::GetSystemEncoding()),
421 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(cfurlAppPath
, kCFURLPOSIXPathStyle
);
422 CFRelease(cfurlAppPath
);
426 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
430 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
432 wxT("LSGetApplicationForInfo failed."),
437 return wxEmptyString
;
440 #else //carbon/classic implementation
442 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
444 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
446 if(verb
== wxT("open"))
449 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
450 (Handle
) m_manager
->m_hDatabase
,
453 //The entry in the mimetype database only contains the app
454 //that's registered - it may not exist... we need to remap the creator
455 //type and find the right application
457 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
461 if(FindApplication(entry
.fileCreator
, false, outName
, &outSpec
) != noErr
)
462 return wxEmptyString
;
464 Handle outPathHandle
;
466 OSErr err
= FSpGetFullPath(&outSpec
, &outPathSize
, &outPathHandle
);
470 char* szPath
= *outPathHandle
;
471 wxString
sClassicPath(szPath
, wxConvLocal
, outPathSize
);
472 #if defined(__DARWIN__)
473 //Classic Path --> Unix (OSX) Path
474 CFURLRef finalURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
475 wxMacCFStringHolder(sClassicPath
, wxLocale::GetSystemEncoding()),
477 false); //false == not a directory
479 //clean up memory from the classic path handle
480 DisposeHandle(outPathHandle
);
484 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(finalURL
, kCFURLPOSIXPathStyle
);
489 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
491 #else //classic HFS path acceptable
497 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus
)err
);
500 return wxEmptyString
;
504 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
506 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
509 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
510 (Handle
) m_manager
->m_hDatabase
,
513 *desc
= wxMacMakeStringFromPascal(entry
.entryName
);
517 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
519 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
522 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
523 (Handle
) m_manager
->m_hDatabase
,
526 //entry has period in it
527 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
528 extensions
.Add( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) );
532 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
534 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
537 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
538 (Handle
) m_manager
->m_hDatabase
,
541 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
545 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
559 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
561 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
563 // no such file type or no value or incorrect icon entry
567 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
, wxArrayString
* commands
,
568 const wxFileType::MessageParameters
& params
) const
570 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
575 if(GetOpenCommand(&sCommand
, params
))
577 verbs
->Add(wxString(wxT("open")));
578 commands
->Add(sCommand
);
585 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, const wxString
& extraDir
)
587 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
589 //some apps (non-wx) use the 'plst' resource instead
591 CFBundleRef cfbMain = CFBundleGetMainBundle();
592 wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
594 cfdInfo.PrintOut(sLog);
598 //start internet config - log if there's an error
599 //the second param is the signature of the application, also known
600 //as resource ID 0. However, as per some recent discussions, we may not
601 //have a signature for this app, so a generic 'APPL' which is the executable
602 //type will work for now
603 OSStatus status
= ICStart( (ICInstance
*) &m_hIC
, 'APPL');
607 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
614 m_hDatabase
= (void**) NewHandle(0);
615 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
617 //the database file can be corrupt (on OSX its
618 //~/Library/Preferences/com.apple.internetconfig.plist)
623 wxLogDebug(wxT("Corrupt Mime Database!"));
627 //obtain the number of entries in the map
628 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
629 wxASSERT( status
== noErr
);
635 for(long i = 1; i <= m_lCount; ++i)
637 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
641 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
642 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
643 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
646 impl.Init(this, pos);
648 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
651 impl.GetOpenCommand (&cmd,
652 wxFileType::MessageParameters (wxT("http://www.google.com")));
654 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
661 void wxMimeTypesManagerImpl::ClearData()
665 DisposeHandle((Handle
)m_hDatabase
);
666 //this can return an error, but we don't really care that much about it
667 ICStop( (ICInstance
) m_hIC
);
673 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
674 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
677 // extension -> file type
678 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
680 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
682 //low level functions - iterate through the database
686 for(long i
= 1; i
<= m_lCount
; ++i
)
688 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
692 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
693 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
695 wxFileType
* pFileType
= new wxFileType();
696 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
705 // MIME type -> extension -> file type
706 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
708 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
710 //low level functions - iterate through the database
714 for(long i
= 1; i
<= m_lCount
; ++i
)
716 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
717 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
721 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
723 wxFileType
* pFileType
= new wxFileType();
724 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
733 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
735 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
737 //low level functions - iterate through the database
741 long lStartCount
= (long) mimetypes
.GetCount();
743 for(long i
= 1; i
<= m_lCount
; ++i
)
745 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
746 if( status
== noErr
)
747 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
750 return mimetypes
.GetCount() - lStartCount
;
754 pascal OSStatus
MoreProcGetProcessTypeSignature(
755 const ProcessSerialNumberPtr pPSN
,
756 OSType
*pProcessType
,
759 OSStatus anErr
= noErr
;
760 ProcessInfoRec infoRec
;
761 ProcessSerialNumber localPSN
;
763 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
764 infoRec
.processName
= nil
;
765 infoRec
.processAppSpec
= nil
;
768 localPSN
.highLongOfPSN
= 0;
769 localPSN
.lowLongOfPSN
= kCurrentProcess
;
774 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
777 *pProcessType
= infoRec
.processType
;
778 *pCreator
= infoRec
.processSignature
;
782 }//end MoreProcGetProcessTypeSignature
786 // TODO: clean this up, its messy
791 #include "wx/mac/corefoundation/cfstring.h"
792 #include "wx/intl.h" //wxLocale for wxCFString
794 #define wxCF_RELEASE true
795 #define wxCF_RETAIN false
797 // ----------------------------------------------------------------------------
799 // ----------------------------------------------------------------------------
804 wxCFDictionary(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
806 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
807 if(bRetain
== wxCF_RETAIN
&& ref
)
811 wxCFDictionary(CFIndex cfiSize
= 0)
813 CFDictionaryKeyCallBacks kcbs
;
814 CFDictionaryValueCallBacks vcbs
;
815 BuildKeyCallbacks(&kcbs
);
816 BuildValueCallbacks(&vcbs
);
818 m_cfmdRef
= CFDictionaryCreateMutable(
819 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
827 {if(m_cfmdRef
) CFRelease(m_cfmdRef
);}
829 static const void* RetainProc(CFAllocatorRef
, const void* v
)
830 { return (const void*) CFRetain(v
); }
832 static void ReleaseProc(CFAllocatorRef
, const void* v
)
835 void MakeMutable(CFIndex cfiSize
= 0)
837 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
839 m_cfmdRef
= CFDictionaryCreateMutableCopy(
847 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
850 pCbs
->retain
= RetainProc
;
851 pCbs
->release
= ReleaseProc
;
852 pCbs
->copyDescription
= NULL
;
857 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
860 pCbs
->retain
= RetainProc
;
861 pCbs
->release
= ReleaseProc
;
862 pCbs
->copyDescription
= NULL
;
866 operator CFTypeRef () const
867 { return (CFTypeRef
)m_cfmdRef
; }
869 CFDictionaryRef
GetCFDictionary() const
870 { return (CFDictionaryRef
)m_cfmdRef
; }
872 CFMutableDictionaryRef
GetCFMutableDictionary()
873 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
875 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
878 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
881 CFIndex
GetCount() const
884 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
887 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
890 wxASSERT(Exists(cftKey
) == false);
891 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
894 void Remove(CFTypeRef cftKey
)
897 wxASSERT(Exists(cftKey
));
898 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
901 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
904 wxASSERT(Exists(cftKey
));
905 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
908 bool Exists(CFTypeRef cftKey
) const
911 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
);
914 bool IsOk() const {return m_cfmdRef
!= NULL
; }
918 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID();
921 void PrintOut(wxString
& sMessage
)
923 PrintOutDictionary(sMessage
, m_cfmdRef
);
926 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
928 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
929 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
930 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
932 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
934 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
936 wxString sKey
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
937 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
940 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
943 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
946 wxString::Format(wxT("\n\t[Value : %s]"),
949 PrintOutType(sMessage
, sValue
, pValues
[i
]);
951 sMessage
<< wxT("\n");
958 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
961 for(CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
963 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
964 CFArrayGetValueAtIndex(cfaRef
, i
)
968 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
971 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
975 static void PrintOutType(wxString
& sMessage
, wxString sValue
, CFTypeRef cfRef
)
977 sMessage
<< wxT(" {");
979 if(sValue
== wxT("CFString"))
981 sMessage
<< wxMacCFStringHolder((CFStringRef
)cfRef
, false).AsString();
983 else if(sValue
== wxT("CFNumber"))
986 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
989 else if(sValue
== wxT("CFDictionary"))
991 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
993 else if(sValue
== wxT("CFArray"))
995 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
997 else if(sValue
== wxT("CFBoolean"))
999 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
1001 else if(sValue
== wxT("CFURL"))
1003 sMessage
<< wxMacCFStringHolder(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
1007 sMessage
<< wxT("*****UNKNOWN TYPE******");
1010 sMessage
<< wxT("} ");
1014 void MakeValidXML();
1017 CFTypeRef
WriteAsXML()
1019 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
1022 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
1025 CFStringRef cfsError
=NULL
;
1026 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
1027 kCFAllocatorDefault
,
1029 kCFPropertyListMutableContainersAndLeaves
,
1034 *pErrorMsg
= wxMacCFStringHolder(cfsError
).AsString();
1036 CFRelease(cfsError
);
1039 return m_cfmdRef
!= NULL
;
1042 CFMutableDictionaryRef m_cfmdRef
;
1045 // ----------------------------------------------------------------------------
1047 // ----------------------------------------------------------------------------
1052 wxCFArray(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
1054 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1055 if(bRetain
== wxCF_RETAIN
&& ref
)
1059 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1060 { Create(cfiSize
); }
1065 void MakeMutable(CFIndex cfiSize
= 0)
1067 wxASSERT(IsValid());
1069 CFMutableArrayRef oldref
= m_cfmaRef
;
1070 m_cfmaRef
= CFArrayCreateMutableCopy(
1071 kCFAllocatorDefault
,
1073 (CFArrayRef
)oldref
);
1077 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1080 pCbs
->retain
= RetainProc
;
1081 pCbs
->release
= ReleaseProc
;
1082 pCbs
->copyDescription
= NULL
;
1086 void Create(CFIndex cfiSize
= 0)
1089 CFArrayCallBacks cb
;
1090 BuildCallbacks(&cb
);
1092 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1096 {if(m_cfmaRef
) CFRelease(m_cfmaRef
);}
1098 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1099 { return (const void*) CFRetain(v
); }
1101 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1104 operator CFTypeRef () const
1105 { return (CFTypeRef
)m_cfmaRef
; }
1107 CFArrayRef
GetCFArray() const
1108 { return (CFArrayRef
)m_cfmaRef
; }
1110 CFMutableArrayRef
GetCFMutableArray()
1111 { return (CFMutableArrayRef
) m_cfmaRef
; }
1113 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1115 wxASSERT(IsValid());
1116 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1121 wxASSERT(IsValid());
1122 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1125 void Add(CFTypeRef cftValue
)
1127 wxASSERT(IsValid());
1128 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1131 void Remove(CFIndex cfiIndex
)
1133 wxASSERT(IsValid());
1134 wxASSERT(cfiIndex
< GetCount());
1135 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1138 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1140 wxASSERT(IsValid());
1141 wxASSERT(cfiIndex
< GetCount());
1142 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1145 bool IsOk() const {return m_cfmaRef
!= NULL
; }
1147 bool IsValid() const
1149 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1153 void MakeValidXML();
1157 CFMutableArrayRef m_cfmaRef
;
1160 // ----------------------------------------------------------------------------
1162 // ----------------------------------------------------------------------------
1167 wxCFString(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_Holder((CFStringRef
)ref
, bRetain
== wxCF_RELEASE
)
1170 wxCFString(const wxChar
* szString
) : m_Holder(wxString(szString
), wxLocale::GetSystemEncoding())
1173 wxCFString(const wxString
& sString
) : m_Holder(sString
, wxLocale::GetSystemEncoding())
1176 operator CFTypeRef() const
1177 { return (CFTypeRef
) ((CFStringRef
) m_Holder
); }
1179 bool IsOk() { return ((CFTypeRef
)(*this)) != NULL
; }
1181 wxString
BuildWXString() {return m_Holder
.AsString(); }
1184 wxMacCFStringHolder m_Holder
;
1187 // ----------------------------------------------------------------------------
1189 // ----------------------------------------------------------------------------
1194 wxCFNumber(int nValue
)
1196 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &nValue
);
1199 wxCFNumber(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfnRef((CFNumberRef
)ref
)
1201 if(bRetain
== wxCF_RETAIN
&& ref
)
1208 CFRelease(m_cfnRef
);
1212 operator CFTypeRef() const
1213 { return (CFTypeRef
) m_cfnRef
; }
1218 CFNumberGetValue( m_cfnRef
,
1226 bool IsOk() { return m_cfnRef
!= NULL
; }
1229 CFNumberRef m_cfnRef
;
1232 // ----------------------------------------------------------------------------
1234 // ----------------------------------------------------------------------------
1239 wxCFURL(CFTypeRef ref
= NULL
, bool bRetain
= wxCF_RELEASE
) : m_cfurlRef((CFURLRef
)ref
)
1241 if(bRetain
== wxCF_RETAIN
&& ref
)
1244 wxCFURL(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1246 Create(URLString
, BaseURL
);
1249 void Create(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1251 m_cfurlRef
= CFURLCreateWithString(
1252 kCFAllocatorDefault
,
1253 (CFStringRef
)(CFTypeRef
)URLString
,
1254 (CFURLRef
) BaseURL
);
1257 ~wxCFURL() {if(m_cfurlRef
) CFRelease(m_cfurlRef
);}
1259 wxString
BuildWXString()
1261 return wxCFString(CFURLCopyPath(m_cfurlRef
)).BuildWXString();
1264 operator CFTypeRef() const
1265 { return (CFTypeRef
)m_cfurlRef
; }
1267 bool IsOk() { return m_cfurlRef
!= NULL
; }
1269 CFURLRef m_cfurlRef
;
1272 // ----------------------------------------------------------------------------
1274 // ----------------------------------------------------------------------------
1276 #define wxCFDATA_RELEASEBUFFER 1
1277 #define wxCFDATA_RETAINBUFFER 0
1282 wxCFData(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfdaRef((CFDataRef
)ref
)
1284 if(bRetain
== wxCF_RETAIN
&& ref
)
1287 wxCFData(const UInt8
* pBytes
, CFIndex len
, bool bKeep
= wxCFDATA_RELEASEBUFFER
)
1289 if(bKeep
== wxCFDATA_RELEASEBUFFER
)
1291 m_cfdaRef
= CFDataCreateWithBytesNoCopy
1292 (kCFAllocatorDefault
, pBytes
, len
, kCFAllocatorDefault
);
1296 m_cfdaRef
= CFDataCreate(kCFAllocatorDefault
, pBytes
, len
);
1299 ~wxCFData() {if(m_cfdaRef
) CFRelease(m_cfdaRef
);}
1301 const UInt8
* GetValue()
1303 return CFDataGetBytePtr(m_cfdaRef
);
1308 return CFDataGetLength(m_cfdaRef
);
1311 operator CFTypeRef() const
1312 { return (CFTypeRef
)m_cfdaRef
; }
1314 bool IsOk() { return m_cfdaRef
!= NULL
; }
1316 CFDataRef m_cfdaRef
;
1319 void wxCFDictionary::MakeValidXML()
1321 CFIndex cfiCount
= GetCount();
1322 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
1323 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
1325 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1327 //for plist xml format all dictionary keys must be cfstrings and no values in
1328 //the dictionary or subkeys/values can be NULL
1329 //Also, CFURLs are not allowed
1330 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
1332 //must be an array, dictionary, string, bool, or int and cannot be null
1333 //and dictionaries can only contain cfstring keys
1334 CFTypeRef cfRef
= pValues
[i
];
1336 CFGetTypeID(pKeys
[i
]) != CFStringGetTypeID() ||
1344 pKeys
= new CFTypeRef
[cfiCount
];
1345 pValues
= new CFTypeRef
[cfiCount
];
1346 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1348 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1351 wxCFArray
cfaCurrent(cfRef
);
1352 cfaCurrent
.MakeMutable();
1353 cfaCurrent
.MakeValidXML();
1354 Set(pKeys
[i
], cfaCurrent
);
1356 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1359 wxCFDictionary
cfdCurrent(cfRef
);
1360 cfdCurrent
.MakeMutable();
1361 cfdCurrent
.MakeValidXML();
1362 Set(pKeys
[i
], cfdCurrent
);
1364 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1365 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1366 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1373 pKeys
= new CFTypeRef
[cfiCount
];
1374 pValues
= new CFTypeRef
[cfiCount
];
1375 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1383 void wxCFArray::MakeValidXML()
1385 for(CFIndex i
= 0; i
< GetCount(); ++i
)
1387 //must be an array, dictionary, string, bool, or int and cannot be null
1388 //and dictionaries can only contain cfstring keys
1389 CFTypeRef cfRef
= (*this)[i
];
1395 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1398 wxCFArray
cfaCurrent(cfRef
);
1399 cfaCurrent
.MakeMutable();
1400 cfaCurrent
.MakeValidXML();
1403 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1406 wxCFDictionary
cfdCurrent(cfRef
);
1407 cfdCurrent
.MakeMutable();
1408 cfdCurrent
.MakeValidXML();
1411 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1412 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1413 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1429 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
1431 bool bInfoSuccess
= false;
1433 const wxArrayString
& asExtensions
= ftInfo
.GetExtensions();
1434 size_t dwFoundIndex
= 0;
1435 if(!asExtensions
.GetCount())
1437 wxLogDebug(wxT("Must have extension to associate with"));
1440 //Find and write to Info.plist in main bundle (note that some other
1441 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1442 //some apps (non-wx) use the 'plst' resource instead
1443 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1446 UInt32 dwBundleType
, dwBundleCreator
;
1447 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1449 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1450 //which will give us the incorrect info.plist path
1451 //otherwise it will be 'APPL', or in the case of a framework,
1453 if(dwBundleType
== 'APPL')
1456 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1457 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1459 // sInfoPath << wxT("file://");
1460 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1461 sInfoPath
<< wxT("Contents/Info.plist");
1463 // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1464 wxCFDictionary cfdInfo
;
1465 bool bInfoOpenSuccess
= false;
1467 if(indictfile
.Open(sInfoPath
, wxFile::read
))
1469 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1470 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1471 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1472 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1474 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1475 if(!bInfoOpenSuccess
)
1479 if(bInfoOpenSuccess
)
1481 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1483 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1485 bool bAddDocTypesArrayToDictionary
= cfaDocTypes
.IsOk() == false;
1486 if(bAddDocTypesArrayToDictionary
)
1487 cfaDocTypes
.Create();
1489 cfaDocTypes
.MakeMutable( cfaDocTypes
.GetCount() + 1 );
1491 bool bEntryFound
= false;
1493 //search for duplicate
1495 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1497 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1499 //A lot of apps dont do to mime types for some reason
1500 //so we go by extensions only
1501 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1504 if(cfaExtensions
.IsOk() == false)
1507 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1509 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1511 if(asExtensions
[iWXExt
] ==
1512 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1515 dwFoundIndex
= iWXExt
;
1518 } //end of wxstring array
1522 } //end for cf array
1528 wxCFDictionary cfdNewEntry
;
1530 if(!ftInfo
.GetDescription().empty())
1532 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeName")),
1533 wxCFString(ftInfo
.GetDescription()) );
1536 if(!ftInfo
.GetIconFile().empty())
1538 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeIconFile")),
1539 wxCFString(ftInfo
.GetIconFile()) );
1543 wxCFArray cfaOSTypes
;
1544 wxCFArray cfaExtensions
;
1545 wxCFArray cfaMimeTypes
;
1548 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1549 cfaOSTypes
.Add( wxCFString(wxT("****")) );
1550 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes
);
1552 if(ftInfo
.GetExtensionsCount() != 0) //'*' for unrestricted
1554 for(size_t iExtension
= 0; iExtension
< (size_t)ftInfo
.GetExtensionsCount(); ++iExtension
)
1556 cfaExtensions
.Add( wxCFString( asExtensions
[iExtension
] ) );
1559 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions
);
1562 if(!ftInfo
.GetMimeType().empty())
1564 cfaMimeTypes
.Add( wxCFString(ftInfo
.GetMimeType()) );
1565 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes
);
1568 // Editor - can perform all actions
1569 // Viewer - all actions except manipulation/saving
1570 // None - can perform no actions
1571 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
1573 // Is application bundled?
1574 cfdNewEntry
.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue
);
1577 cfaDocTypes
.Set(i
, cfdNewEntry
);
1579 cfaDocTypes
.Add(cfdNewEntry
);
1582 // set the doc types array in the muted dictionary
1585 if(bAddDocTypesArrayToDictionary
)
1586 cfdInfo
.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1588 cfdInfo
.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1590 cfdInfo
.MakeValidXML();
1593 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1595 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1598 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1599 (wxFileOffset
)cfdaInfo
.GetCount())
1601 wxLogDebug(wxT("error in writing to file"));
1605 bInfoSuccess
= true;
1606 //#if defined(__DARWIN__)
1607 // //force launch services to update its database for the finder
1608 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1609 // if(status != noErr)
1611 // wxLogDebug(wxT("LSRegisterURL Failed."));
1615 outdictfile
.Close();
1619 outdictfile
.Close();
1620 wxLogDebug(wxT("Could not read in new dictionary"));
1625 wxLogDebug(wxString(wxT("Could not open [")) +
1626 sInfoPath
+ wxT("] for writing."));
1631 wxLogDebug(wxT("No info dictionary in main bundle"));
1636 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1641 wxLogDebug(wxT("No main bundle"));
1644 #if defined(__DARWIN__)
1648 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1649 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1650 //the app's signature somehow...
1654 OSStatus status
= MoreProcGetProcessTypeSignature(NULL
,&processType
, &creator
);
1658 Str255 psCreatorName
;
1660 status
= FindApplication(creator
, false, psCreatorName
, &dummySpec
);
1665 //get the file type if it exists -
1666 //if it really does then modify the database then save it,
1667 //otherwise we need to create a whole new entry
1668 wxFileType
* pFileType
= GetFileTypeFromExtension(asExtensions
[dwFoundIndex
]);
1672 ICGetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1673 pFileType
->m_impl
->m_lIndex
, &entry
);
1675 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1676 entry
.fileCreator
= creator
;
1678 status
= ICSetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1679 pFileType
->m_impl
->m_lIndex
, &entry
);
1684 //kICAttrNoChange means we don't care about attributes such as
1685 //locking in the database
1686 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1687 // kICAttrNoChange, (Handle) m_hDatabase);
1688 // if(status == noErr)
1692 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1697 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICSetMapEntry failed.")));
1705 //TODO: Maybe force all 3 of these to be non-empty?
1708 Str255 psDescription
;
1710 wxMacStringToPascal(wxString(wxT(".")) + ftInfo
.GetExtensions()[0], psExtension
);
1711 wxMacStringToPascal(ftInfo
.GetMimeType(), psMimeType
);
1712 wxMacStringToPascal(ftInfo
.GetDescription(), psDescription
);
1714 Str255 psPostCreatorName
;
1715 wxMacStringToPascal(wxT(""), psPostCreatorName
);
1718 //add the entry to the database
1720 entry
.totalLength
= sizeof(ICMapEntry
);
1721 entry
.fixedLength
= kICMapFixedLength
;
1723 entry
.fileType
= 0; //TODO: File type?
1724 entry
.fileCreator
= creator
;
1725 entry
.postCreator
= 0;
1726 entry
.flags
= kICMapDataForkBit
; //TODO: Maybe resource is valid by default too?
1727 PLstrcpy( entry
.extension
, psExtension
) ;
1728 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1729 memcpy(entry
.postAppName
, psPostCreatorName
, sizeof(Str255
));
1730 memcpy(entry
.MIMEType
, psMimeType
, sizeof(Str255
));
1731 memcpy(entry
.entryName
, psDescription
, sizeof(Str255
));
1733 status
= ICAddMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &entry
);
1737 //kICAttrNoChange means we don't care about attributes such as
1738 //locking in the database
1739 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1740 // kICAttrNoChange, (Handle) m_hDatabase);
1742 //return the entry in the database if successful
1743 // if(status == noErr)
1744 return GetFileTypeFromExtension(ftInfo
.GetMimeType());
1747 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1752 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICAppMapEntry failed.")));
1755 } //end if FindApplcation was successful
1758 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("FindApplication failed.")));
1760 } //end if it could obtain app's signature
1763 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("GetProcessSignature failed.")));
1769 wxMimeTypesManagerImpl::Unassociate(wxFileType
*pFileType
)
1771 wxASSERT(pFileType
);
1772 bool bInfoSuccess
= false;
1774 wxArrayString asExtensions
;
1775 pFileType
->GetExtensions(asExtensions
);
1777 if(!asExtensions
.GetCount())
1779 wxLogDebug(wxT("Must have extension to disassociate"));
1783 //Find and write to Info.plist in main bundle (note that some other
1784 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1785 //some apps (non-wx) use the 'plst' resource instead
1786 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1789 UInt32 dwBundleType
, dwBundleCreator
;
1790 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1792 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1793 //which will give us the incorrect info.plist path
1794 //otherwise it will be 'APPL', or in the case of a framework,
1796 if(dwBundleType
== 'APPL')
1799 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1800 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1802 // sInfoPath << wxT("file://");
1803 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1804 sInfoPath
<< wxT("Contents/Info.plist");
1806 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1807 wxCFDictionary cfdInfo
;
1808 bool bInfoOpenSuccess
= false;
1810 if(indictfile
.Open(sInfoPath
, wxFile::read
))
1812 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1813 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1814 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1815 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1817 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1818 if(!bInfoOpenSuccess
)
1822 if(bInfoOpenSuccess
)
1824 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1826 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1828 if(cfaDocTypes
.IsOk())
1830 bool bEntryFound
= false;
1832 //search for duplicate
1834 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1836 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1838 //A lot of apps dont do to mime types for some reason
1839 //so we go by extensions only
1840 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1843 if(cfaExtensions
.IsOk() == false)
1846 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1848 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1850 if(asExtensions
[iWXExt
] ==
1851 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1854 cfaDocTypes
.Remove(i
);
1855 cfdInfo
.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes
);
1858 } //end of wxstring array
1862 } //end for cf array
1870 cfdInfo
.MakeValidXML();
1873 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1875 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1878 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1879 (wxFileOffset
)cfdaInfo
.GetCount())
1881 wxLogDebug(wxT("error in writing to file"));
1885 bInfoSuccess
= true;
1886 //#if defined(__DARWIN__)
1887 // //force launch services to update its database for the finder
1888 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1889 // if(status != noErr)
1891 // wxLogDebug(wxT("LSRegisterURL Failed."));
1895 outdictfile
.Close();
1899 outdictfile
.Close();
1900 wxLogDebug(wxT("Could not read in new dictionary"));
1905 wxLogDebug(wxString(wxT("Could not open [")) +
1906 sInfoPath
+ wxT("] for writing."));
1911 wxLogDebug(wxT("Entry not found to remove"));
1913 wxCFDictionary::PrintOutArray(sPrintOut
, (CFArrayRef
)(CFTypeRef
)cfaDocTypes
);
1914 wxLogDebug(sPrintOut
);
1915 for(size_t i
= 0; i
< asExtensions
.GetCount(); ++i
)
1916 wxLogDebug(asExtensions
[i
]);
1921 wxLogDebug(wxT("No doc types array found"));
1922 wxString sPrintOut
; cfdInfo
.PrintOut(sPrintOut
); wxLogDebug(sPrintOut
);
1927 wxLogDebug(wxT("No info dictionary in main bundle"));
1932 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1937 wxLogDebug(wxT("No main bundle"));
1940 #if defined(__DARWIN__)
1945 //this should be as easy as removing the entry from the database and then saving
1947 OSStatus status
= ICDeleteMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1948 pFileType
->m_impl
->m_lIndex
);
1952 //kICAttrNoChange means we don't care about attributes such as
1953 //locking in the database
1954 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1955 // kICAttrNoChange, (Handle) m_hDatabase);
1957 // if(status == noErr)
1961 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1967 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICDeleteMapEntry failed.")));
1973 CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile(
1974 kCFAllocatorDefault,
1975 (CFURLRef) (CFTypeRef)cfurlInfoLoc );
1980 Boolean bOpened = CFWriteStreamOpen(cfwsInfo);
1983 CFStringRef cfsError;
1984 CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo,
1986 kCFPropertyListXMLFormat_v1_0, //100
1988 if(cfsError && cfiWritten == 0)
1990 wxLogDebug(wxCFString(cfsError).BuildWXString());
1992 cfdInfo.PrintOut(sMessage);
1993 wxLogDebug(sMessage);
1997 bInfoSuccess = true;
1998 //#if defined(__DARWIN__)
1999 // //force launch services to update its database for the finder
2000 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
2001 // if(status != noErr)
2003 // wxLogDebug(wxT("LSRegisterURL Failed."));
2008 CFWriteStreamClose(cfwsInfo);
2011 #endif //wxUSE_MIMETYPE