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 // for compilers that support precompilation, includes "wx.h".
30 #include "wx/wxprec.h"
37 #include "wx/string.h"
49 #include "wx/dynarray.h"
50 #include "wx/confbase.h"
52 #include "wx/mac/mimetype.h"
53 #include "wx/mac/private.h" //wxMacMakeStringFromPascal
55 // other standard headers
59 #include <InternetConfig.h> //For mime types
62 /* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
64 /* IsRemoteVolume can be used to find out if the
65 volume referred to by vRefNum is a remote volume
66 located somewhere on a network. the volume's attribute
67 flags (copied from the GetVolParmsInfoBuffer structure)
68 are returned in the longword pointed to by vMAttrib. */
69 OSErr
IsRemoteVolume(short vRefNum
, Boolean
*isRemote
, long *vMAttrib
) {
71 GetVolParmsInfoBuffer volinfo
;
73 volPB
.ioParam
.ioVRefNum
= vRefNum
;
74 volPB
.ioParam
.ioNamePtr
= NULL
;
75 volPB
.ioParam
.ioBuffer
= (Ptr
) &volinfo
;
76 volPB
.ioParam
.ioReqCount
= sizeof(volinfo
);
77 err
= PBHGetVolParmsSync(&volPB
);
79 *isRemote
= (volinfo
.vMServerAdr
!= 0);
80 *vMAttrib
= volinfo
.vMAttrib
;
86 /* BuildVolumeList fills the array pointed to by vols with
87 a list of the currently mounted volumes. If includeRemote
88 is true, then remote server volumes will be included in
89 the list. When remote server volumes are included in the
90 list, they will be added to the end of the list. On entry,
91 *count should contain the size of the array pointed to by
92 vols. On exit, *count will be set to the number of id numbers
93 placed in the array. If vMAttribMask is non-zero, then
94 only volumes with matching attributes are added to the
95 list of volumes. bits in the vMAttribMask should use the
96 same encoding as bits in the vMAttrib field of
97 the GetVolParmsInfoBuffer structure. */
98 OSErr
BuildVolumeList(Boolean includeRemote
, short *vols
,
99 long *count
, long vMAttribMask
) {
100 HParamBlockRec volPB
;
103 long nlocal
, nremote
;
106 /* set up and check parameters */
107 volPB
.volumeParam
.ioNamePtr
= NULL
;
108 nlocal
= nremote
= 0;
109 if (*count
== 0) return noErr
;
111 /* iterate through volumes */
112 for (volPB
.volumeParam
.ioVolIndex
= 1;
113 PBHGetVInfoSync(&volPB
) == noErr
;
114 volPB
.volumeParam
.ioVolIndex
++) {
116 /* skip remote volumes, if necessary */
117 err
= IsRemoteVolume(volPB
.volumeParam
.ioVRefNum
, &isRemote
, &vMAttrib
);
118 if (err
!= noErr
) goto bail
;
119 if ( ( includeRemote
|| ! isRemote
)
120 && (vMAttrib
& vMAttribMask
) == vMAttribMask
) {
122 /* add local volumes at the front, remote
123 volumes at the end */
125 vols
[nlocal
+ nremote
++] = volPB
.volumeParam
.ioVRefNum
;
128 BlockMoveData(vols
+nlocal
, vols
+nlocal
+1,
129 nremote
*sizeof(short));
130 vols
[nlocal
++] = volPB
.volumeParam
.ioVRefNum
;
134 if ((nlocal
+ nremote
) >= *count
) break;
138 *count
= (nlocal
+ nremote
);
143 /* FindApplication iterates through mounted volumes
144 searching for an application with the given creator
145 type. If includeRemote is true, then remote volumes
146 will be searched (after local ones) for an application
147 with the creator type. */
151 /* Hacked to output to appName */
153 OSErr
FindApplication(OSType appCreator
, Boolean includeRemote
, Str255 appName
, FSSpec
* appSpec
) {
154 short rRefNums
[kMaxVols
];
159 /* get a list of volumes - with desktop files */
161 err
= BuildVolumeList(includeRemote
, rRefNums
, &volCount
,
162 (1<<bHasDesktopMgr
) );
163 if (err
!= noErr
) return err
;
165 /* iterate through the list */
166 for (i
=0; i
<volCount
; i
++) {
168 /* has a desktop file? */
169 desktopPB
.ioCompletion
= NULL
;
170 desktopPB
.ioVRefNum
= rRefNums
[i
];
171 desktopPB
.ioNamePtr
= NULL
;
172 desktopPB
.ioIndex
= 0;
173 err
= PBDTGetPath(&desktopPB
);
174 if (err
!= noErr
) continue;
176 /* has the correct app?? */
177 desktopPB
.ioFileCreator
= appCreator
;
178 desktopPB
.ioNamePtr
= appName
;
179 err
= PBDTGetAPPLSync(&desktopPB
);
180 if (err
!= noErr
) continue;
182 /* make a file spec referring to it */
183 err
= FSMakeFSSpec(rRefNums
[i
],
184 desktopPB
.ioAPPLParID
, appName
,
186 if (err
!= noErr
) continue;
195 /* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
197 //yeah, duplicated code
198 pascal OSErr
FSpGetFullPath(const FSSpec
*spec
,
199 short *fullPathLength
,
211 /* Default to noErr */
212 realResult
= result
= noErr
;
214 /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */
216 if ( spec->name[0] == 0 )
218 result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec);
223 /* Make a copy of the input FSSpec that can be modified */
224 BlockMoveData(spec
, &tempSpec
, sizeof(FSSpec
));
227 if ( result
== noErr
)
229 if ( tempSpec
.parID
== fsRtParID
)
231 /* The object is a volume */
233 /* Add a colon to make it a full pathname */
235 tempSpec
.name
[tempSpec
.name
[0]] = ':';
238 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
242 /* The object isn't a volume */
244 /* Is the object a file or a directory? */
245 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
246 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
247 pb
.dirInfo
.ioDrDirID
= tempSpec
.parID
;
248 pb
.dirInfo
.ioFDirIndex
= 0;
249 result
= PBGetCatInfoSync(&pb
);
250 /* Allow file/directory name at end of path to not exist. */
252 if ( (result
== noErr
) || (result
== fnfErr
) )
254 /* if the object is a directory, append a colon so full pathname ends with colon */
255 if ( (result
== noErr
) && (pb
.hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0 )
258 tempSpec
.name
[tempSpec
.name
[0]] = ':';
261 /* Put the object name in first */
262 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
263 if ( result
== noErr
)
265 /* Get the ancestor directory names */
266 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
267 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
268 pb
.dirInfo
.ioDrParID
= tempSpec
.parID
;
269 do /* loop until we have an error or find the root directory */
271 pb
.dirInfo
.ioFDirIndex
= -1;
272 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
273 result
= PBGetCatInfoSync(&pb
);
274 if ( result
== noErr
)
276 /* Append colon to directory name */
278 tempSpec
.name
[tempSpec
.name
[0]] = ':';
280 /* Add directory name to beginning of fullPath */
281 (void) Munger(*fullPath
, 0, NULL
, 0, &tempSpec
.name
[1], tempSpec
.name
[0]);
284 } while ( (result
== noErr
) && (pb
.dirInfo
.ioDrDirID
!= fsRtDirID
) );
290 if ( result
== noErr
)
292 /* Return the length */
293 *fullPathLength
= GetHandleSize(*fullPath
);
294 result
= realResult
; /* return realResult in case it was fnfErr */
298 /* Dispose of the handle and return NULL and zero length */
299 if ( *fullPath
!= NULL
)
301 DisposeHandle(*fullPath
);
311 // On the mac there are two ways to open a file - one is through apple events and the
312 // finder, another is through mime types.
314 // So, really there are two ways to implement wxFileType...
316 // Mime types are only available on OS 8.1+ through the InternetConfig API
318 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
319 // Low - which means you have to iterate yourself through the mime database
320 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
321 // High - which requires access to the database every time
323 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
324 // and mid-level functions
326 // TODO: Should we call ICBegin/ICEnd? Then where?
330 inline void wxLogMimeDebug(const wxChar
* szMsg
, OSStatus status
)
332 wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg
, __LINE__
, (int)status
));
335 // in case we're compiling in non-GUI mode
336 class WXDLLEXPORT wxIcon
;
338 bool wxFileTypeImpl::SetCommand(const wxString
& cmd
, const wxString
& verb
, bool overwriteprompt
)
340 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
345 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int index
)
347 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
352 bool wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
353 const wxFileType::MessageParameters
& params
) const
355 wxString cmd
= GetCommand(wxT("open"));
357 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
359 return !openCmd
->empty();
363 wxFileTypeImpl::GetPrintCommand(wxString
*printCmd
,
364 const wxFileType::MessageParameters
& params
)
367 wxString cmd
= GetCommand(wxT("print"));
369 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
371 return !printCmd
->empty();
375 // Internet Config vs. Launch Services
377 // From OS 8 on there was internet config...
378 // However, OSX and its finder does not use info
379 // from Internet Config at all - the Internet Config
380 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
381 // OR REGISTERED THROUGH INTERNET CONFIG
383 // Therefore on OSX in order for the open command to be useful
384 // we need to go straight to launch services
387 #if defined(__DARWIN__)
389 //on darwin, use launch services
390 #include <ApplicationServices/ApplicationServices.h>
392 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
394 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
396 if(verb
== wxT("open"))
399 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
400 (Handle
) m_manager
->m_hDatabase
,
403 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
404 sCurrentExtension
= sCurrentExtension
.Right(sCurrentExtension
.Length()-1 );
406 //type, creator, ext, roles, outapp (FSRef), outappurl
407 CFURLRef cfurlAppPath
;
408 OSStatus status
= LSGetApplicationForInfo (kLSUnknownType
,
410 wxMacCFStringHolder(sCurrentExtension
, wxLocale::GetSystemEncoding()),
417 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(cfurlAppPath
, kCFURLPOSIXPathStyle
);
418 CFRelease(cfurlAppPath
);
422 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
426 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
428 wxT("LSGetApplicationForInfo failed."),
433 return wxEmptyString
;
436 #else //carbon/classic implementation
438 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
440 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
442 if(verb
== wxT("open"))
445 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
446 (Handle
) m_manager
->m_hDatabase
,
449 //The entry in the mimetype database only contains the app
450 //that's registered - it may not exist... we need to remap the creator
451 //type and find the right application
453 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
457 if(FindApplication(entry
.fileCreator
, false, outName
, &outSpec
) != noErr
)
458 return wxEmptyString
;
460 Handle outPathHandle
;
462 OSErr err
= FSpGetFullPath(&outSpec
, &outPathSize
, &outPathHandle
);
466 char* szPath
= *outPathHandle
;
467 wxString
sClassicPath(szPath
, wxConvLocal
, outPathSize
);
468 #if defined(__DARWIN__)
469 //Classic Path --> Unix (OSX) Path
470 CFURLRef finalURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
471 wxMacCFStringHolder(sClassicPath
, wxLocale::GetSystemEncoding()),
473 false); //false == not a directory
475 //clean up memory from the classic path handle
476 DisposeHandle(outPathHandle
);
480 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(finalURL
, kCFURLPOSIXPathStyle
);
485 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
487 #else //classic HFS path acceptable
493 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus
)err
);
496 return wxEmptyString
;
500 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
502 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
505 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
506 (Handle
) m_manager
->m_hDatabase
,
509 *desc
= wxMacMakeStringFromPascal(entry
.entryName
);
513 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
515 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
518 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
519 (Handle
) m_manager
->m_hDatabase
,
522 //entry has period in it
523 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
524 extensions
.Add( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) );
528 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
530 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
533 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
534 (Handle
) m_manager
->m_hDatabase
,
537 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
541 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
555 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
557 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
559 // no such file type or no value or incorrect icon entry
563 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
, wxArrayString
* commands
,
564 const wxFileType::MessageParameters
& params
) const
566 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
571 if(GetOpenCommand(&sCommand
, params
))
573 verbs
->Add(wxString(wxT("open")));
574 commands
->Add(sCommand
);
581 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, const wxString
& extraDir
)
583 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
585 //some apps (non-wx) use the 'plst' resource instead
587 CFBundleRef cfbMain = CFBundleGetMainBundle();
588 wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
590 cfdInfo.PrintOut(sLog);
594 //start internet config - log if there's an error
595 //the second param is the signature of the application, also known
596 //as resource ID 0. However, as per some recent discussions, we may not
597 //have a signature for this app, so a generic 'APPL' which is the executable
598 //type will work for now
599 OSStatus status
= ICStart( (ICInstance
*) &m_hIC
, 'APPL');
603 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
610 m_hDatabase
= (void**) NewHandle(0);
611 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
613 //the database file can be corrupt (on OSX its
614 //~/Library/Preferences/com.apple.internetconfig.plist)
619 wxLogDebug(wxT("Corrupt Mime Database!"));
623 //obtain the number of entries in the map
624 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
625 wxASSERT( status
== noErr
);
631 for(long i = 1; i <= m_lCount; ++i)
633 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
637 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
638 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
639 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
642 impl.Init(this, pos);
644 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
647 impl.GetOpenCommand (&cmd,
648 wxFileType::MessageParameters (wxT("http://www.google.com")));
650 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
657 void wxMimeTypesManagerImpl::ClearData()
661 DisposeHandle((Handle
)m_hDatabase
);
662 //this can return an error, but we don't really care that much about it
663 ICStop( (ICInstance
) m_hIC
);
669 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
670 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
673 // extension -> file type
674 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
676 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
678 //low level functions - iterate through the database
682 for(long i
= 1; i
<= m_lCount
; ++i
)
684 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
688 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
689 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
691 wxFileType
* pFileType
= new wxFileType();
692 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
701 // MIME type -> extension -> file type
702 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
704 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
706 //low level functions - iterate through the database
710 for(long i
= 1; i
<= m_lCount
; ++i
)
712 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
713 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
717 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
719 wxFileType
* pFileType
= new wxFileType();
720 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
729 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
731 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
733 //low level functions - iterate through the database
737 long lStartCount
= (long) mimetypes
.GetCount();
739 for(long i
= 1; i
<= m_lCount
; ++i
)
741 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
742 if( status
== noErr
)
743 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
746 return mimetypes
.GetCount() - lStartCount
;
750 pascal OSStatus
MoreProcGetProcessTypeSignature(
751 const ProcessSerialNumberPtr pPSN
,
752 OSType
*pProcessType
,
755 OSStatus anErr
= noErr
;
756 ProcessInfoRec infoRec
;
757 ProcessSerialNumber localPSN
;
759 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
760 infoRec
.processName
= nil
;
761 infoRec
.processAppSpec
= nil
;
764 localPSN
.highLongOfPSN
= 0;
765 localPSN
.lowLongOfPSN
= kCurrentProcess
;
770 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
773 *pProcessType
= infoRec
.processType
;
774 *pCreator
= infoRec
.processSignature
;
778 }//end MoreProcGetProcessTypeSignature
782 // TODO: clean this up, its messy
787 #include "wx/mac/corefoundation/cfstring.h"
788 #include "wx/intl.h" //wxLocale for wxCFString
790 #define wxCF_RELEASE true
791 #define wxCF_RETAIN false
793 // ----------------------------------------------------------------------------
795 // ----------------------------------------------------------------------------
800 wxCFDictionary(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
802 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
803 if(bRetain
== wxCF_RETAIN
&& ref
)
807 wxCFDictionary(CFIndex cfiSize
= 0)
809 CFDictionaryKeyCallBacks kcbs
;
810 CFDictionaryValueCallBacks vcbs
;
811 BuildKeyCallbacks(&kcbs
);
812 BuildValueCallbacks(&vcbs
);
814 m_cfmdRef
= CFDictionaryCreateMutable(
815 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
823 {if(m_cfmdRef
) CFRelease(m_cfmdRef
);}
825 static const void* RetainProc(CFAllocatorRef
, const void* v
)
826 { return (const void*) CFRetain(v
); }
828 static void ReleaseProc(CFAllocatorRef
, const void* v
)
831 void MakeMutable(CFIndex cfiSize
= 0)
833 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
835 m_cfmdRef
= CFDictionaryCreateMutableCopy(
843 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
846 pCbs
->retain
= RetainProc
;
847 pCbs
->release
= ReleaseProc
;
848 pCbs
->copyDescription
= NULL
;
853 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
856 pCbs
->retain
= RetainProc
;
857 pCbs
->release
= ReleaseProc
;
858 pCbs
->copyDescription
= NULL
;
862 operator CFTypeRef () const
863 { return (CFTypeRef
)m_cfmdRef
; }
865 CFDictionaryRef
GetCFDictionary() const
866 { return (CFDictionaryRef
)m_cfmdRef
; }
868 CFMutableDictionaryRef
GetCFMutableDictionary()
869 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
871 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
874 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
877 CFIndex
GetCount() const
880 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
883 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
886 wxASSERT(Exists(cftKey
) == false);
887 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
890 void Remove(CFTypeRef cftKey
)
893 wxASSERT(Exists(cftKey
));
894 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
897 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
900 wxASSERT(Exists(cftKey
));
901 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
904 bool Exists(CFTypeRef cftKey
) const
907 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
);
910 bool IsOk() const {return m_cfmdRef
!= NULL
; }
914 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID();
917 void PrintOut(wxString
& sMessage
)
919 PrintOutDictionary(sMessage
, m_cfmdRef
);
922 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
924 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
925 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
926 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
928 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
930 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
932 wxString sKey
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
933 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
936 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
939 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
942 wxString::Format(wxT("\n\t[Value : %s]"),
945 PrintOutType(sMessage
, sValue
, pValues
[i
]);
947 sMessage
<< wxT("\n");
954 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
957 for(CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
959 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
960 CFArrayGetValueAtIndex(cfaRef
, i
)
964 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
967 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
971 static void PrintOutType(wxString
& sMessage
, const wxString
& sValue
, CFTypeRef cfRef
)
973 sMessage
<< wxT(" {");
975 if(sValue
== wxT("CFString"))
977 sMessage
<< wxMacCFStringHolder((CFStringRef
)cfRef
, false).AsString();
979 else if(sValue
== wxT("CFNumber"))
982 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
985 else if(sValue
== wxT("CFDictionary"))
987 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
989 else if(sValue
== wxT("CFArray"))
991 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
993 else if(sValue
== wxT("CFBoolean"))
995 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
997 else if(sValue
== wxT("CFURL"))
999 sMessage
<< wxMacCFStringHolder(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
1003 sMessage
<< wxT("*****UNKNOWN TYPE******");
1006 sMessage
<< wxT("} ");
1010 void MakeValidXML();
1013 CFTypeRef
WriteAsXML()
1015 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
1018 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
1021 CFStringRef cfsError
=NULL
;
1022 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
1023 kCFAllocatorDefault
,
1025 kCFPropertyListMutableContainersAndLeaves
,
1030 *pErrorMsg
= wxMacCFStringHolder(cfsError
).AsString();
1032 CFRelease(cfsError
);
1035 return m_cfmdRef
!= NULL
;
1038 CFMutableDictionaryRef m_cfmdRef
;
1041 // ----------------------------------------------------------------------------
1043 // ----------------------------------------------------------------------------
1048 wxCFArray(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
1050 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1051 if(bRetain
== wxCF_RETAIN
&& ref
)
1055 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1056 { Create(cfiSize
); }
1061 void MakeMutable(CFIndex cfiSize
= 0)
1063 wxASSERT(IsValid());
1065 CFMutableArrayRef oldref
= m_cfmaRef
;
1066 m_cfmaRef
= CFArrayCreateMutableCopy(
1067 kCFAllocatorDefault
,
1069 (CFArrayRef
)oldref
);
1073 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1076 pCbs
->retain
= RetainProc
;
1077 pCbs
->release
= ReleaseProc
;
1078 pCbs
->copyDescription
= NULL
;
1082 void Create(CFIndex cfiSize
= 0)
1085 CFArrayCallBacks cb
;
1086 BuildCallbacks(&cb
);
1088 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1092 {if(m_cfmaRef
) CFRelease(m_cfmaRef
);}
1094 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1095 { return (const void*) CFRetain(v
); }
1097 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1100 operator CFTypeRef () const
1101 { return (CFTypeRef
)m_cfmaRef
; }
1103 CFArrayRef
GetCFArray() const
1104 { return (CFArrayRef
)m_cfmaRef
; }
1106 CFMutableArrayRef
GetCFMutableArray()
1107 { return (CFMutableArrayRef
) m_cfmaRef
; }
1109 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1111 wxASSERT(IsValid());
1112 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1117 wxASSERT(IsValid());
1118 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1121 void Add(CFTypeRef cftValue
)
1123 wxASSERT(IsValid());
1124 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1127 void Remove(CFIndex cfiIndex
)
1129 wxASSERT(IsValid());
1130 wxASSERT(cfiIndex
< GetCount());
1131 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1134 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1136 wxASSERT(IsValid());
1137 wxASSERT(cfiIndex
< GetCount());
1138 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1141 bool IsOk() const {return m_cfmaRef
!= NULL
; }
1143 bool IsValid() const
1145 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1149 void MakeValidXML();
1153 CFMutableArrayRef m_cfmaRef
;
1156 // ----------------------------------------------------------------------------
1158 // ----------------------------------------------------------------------------
1163 wxCFString(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_Holder((CFStringRef
)ref
, bRetain
== wxCF_RELEASE
)
1166 wxCFString(const wxChar
* szString
) : m_Holder(wxString(szString
), wxLocale::GetSystemEncoding())
1169 wxCFString(const wxString
& sString
) : m_Holder(sString
, wxLocale::GetSystemEncoding())
1172 operator CFTypeRef() const
1173 { return (CFTypeRef
) ((CFStringRef
) m_Holder
); }
1175 bool IsOk() { return ((CFTypeRef
)(*this)) != NULL
; }
1177 wxString
BuildWXString() {return m_Holder
.AsString(); }
1180 wxMacCFStringHolder m_Holder
;
1183 // ----------------------------------------------------------------------------
1185 // ----------------------------------------------------------------------------
1190 wxCFNumber(int nValue
)
1192 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &nValue
);
1195 wxCFNumber(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfnRef((CFNumberRef
)ref
)
1197 if(bRetain
== wxCF_RETAIN
&& ref
)
1204 CFRelease(m_cfnRef
);
1208 operator CFTypeRef() const
1209 { return (CFTypeRef
) m_cfnRef
; }
1214 CFNumberGetValue( m_cfnRef
,
1222 bool IsOk() { return m_cfnRef
!= NULL
; }
1225 CFNumberRef m_cfnRef
;
1228 // ----------------------------------------------------------------------------
1230 // ----------------------------------------------------------------------------
1235 wxCFURL(CFTypeRef ref
= NULL
, bool bRetain
= wxCF_RELEASE
) : m_cfurlRef((CFURLRef
)ref
)
1237 if(bRetain
== wxCF_RETAIN
&& ref
)
1240 wxCFURL(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1242 Create(URLString
, BaseURL
);
1245 void Create(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1247 m_cfurlRef
= CFURLCreateWithString(
1248 kCFAllocatorDefault
,
1249 (CFStringRef
)(CFTypeRef
)URLString
,
1250 (CFURLRef
) BaseURL
);
1253 ~wxCFURL() {if(m_cfurlRef
) CFRelease(m_cfurlRef
);}
1255 wxString
BuildWXString()
1257 return wxCFString(CFURLCopyPath(m_cfurlRef
)).BuildWXString();
1260 operator CFTypeRef() const
1261 { return (CFTypeRef
)m_cfurlRef
; }
1263 bool IsOk() { return m_cfurlRef
!= NULL
; }
1265 CFURLRef m_cfurlRef
;
1268 // ----------------------------------------------------------------------------
1270 // ----------------------------------------------------------------------------
1272 #define wxCFDATA_RELEASEBUFFER 1
1273 #define wxCFDATA_RETAINBUFFER 0
1278 wxCFData(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfdaRef((CFDataRef
)ref
)
1280 if(bRetain
== wxCF_RETAIN
&& ref
)
1283 wxCFData(const UInt8
* pBytes
, CFIndex len
, bool bKeep
= wxCFDATA_RELEASEBUFFER
)
1285 if(bKeep
== wxCFDATA_RELEASEBUFFER
)
1287 m_cfdaRef
= CFDataCreateWithBytesNoCopy
1288 (kCFAllocatorDefault
, pBytes
, len
, kCFAllocatorDefault
);
1292 m_cfdaRef
= CFDataCreate(kCFAllocatorDefault
, pBytes
, len
);
1295 ~wxCFData() {if(m_cfdaRef
) CFRelease(m_cfdaRef
);}
1297 const UInt8
* GetValue()
1299 return CFDataGetBytePtr(m_cfdaRef
);
1304 return CFDataGetLength(m_cfdaRef
);
1307 operator CFTypeRef() const
1308 { return (CFTypeRef
)m_cfdaRef
; }
1310 bool IsOk() { return m_cfdaRef
!= NULL
; }
1312 CFDataRef m_cfdaRef
;
1315 void wxCFDictionary::MakeValidXML()
1317 CFIndex cfiCount
= GetCount();
1318 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
1319 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
1321 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1323 //for plist xml format all dictionary keys must be cfstrings and no values in
1324 //the dictionary or subkeys/values can be NULL
1325 //Also, CFURLs are not allowed
1326 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
1328 //must be an array, dictionary, string, bool, or int and cannot be null
1329 //and dictionaries can only contain cfstring keys
1330 CFTypeRef cfRef
= pValues
[i
];
1332 CFGetTypeID(pKeys
[i
]) != CFStringGetTypeID() ||
1340 pKeys
= new CFTypeRef
[cfiCount
];
1341 pValues
= new CFTypeRef
[cfiCount
];
1342 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1344 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1347 wxCFArray
cfaCurrent(cfRef
);
1348 cfaCurrent
.MakeMutable();
1349 cfaCurrent
.MakeValidXML();
1350 Set(pKeys
[i
], cfaCurrent
);
1352 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1355 wxCFDictionary
cfdCurrent(cfRef
);
1356 cfdCurrent
.MakeMutable();
1357 cfdCurrent
.MakeValidXML();
1358 Set(pKeys
[i
], cfdCurrent
);
1360 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1361 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1362 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1369 pKeys
= new CFTypeRef
[cfiCount
];
1370 pValues
= new CFTypeRef
[cfiCount
];
1371 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1379 void wxCFArray::MakeValidXML()
1381 for(CFIndex i
= 0; i
< GetCount(); ++i
)
1383 //must be an array, dictionary, string, bool, or int and cannot be null
1384 //and dictionaries can only contain cfstring keys
1385 CFTypeRef cfRef
= (*this)[i
];
1391 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1394 wxCFArray
cfaCurrent(cfRef
);
1395 cfaCurrent
.MakeMutable();
1396 cfaCurrent
.MakeValidXML();
1399 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1402 wxCFDictionary
cfdCurrent(cfRef
);
1403 cfdCurrent
.MakeMutable();
1404 cfdCurrent
.MakeValidXML();
1407 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1408 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1409 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1425 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
1427 bool bInfoSuccess
= false;
1429 const wxArrayString
& asExtensions
= ftInfo
.GetExtensions();
1430 size_t dwFoundIndex
= 0;
1431 if(!asExtensions
.GetCount())
1433 wxLogDebug(wxT("Must have extension to associate with"));
1436 //Find and write to Info.plist in main bundle (note that some other
1437 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1438 //some apps (non-wx) use the 'plst' resource instead
1439 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1442 UInt32 dwBundleType
, dwBundleCreator
;
1443 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1445 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1446 //which will give us the incorrect info.plist path
1447 //otherwise it will be 'APPL', or in the case of a framework,
1449 if(dwBundleType
== 'APPL')
1452 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1453 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1455 // sInfoPath << wxT("file://");
1456 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1457 sInfoPath
<< wxT("Contents/Info.plist");
1459 // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1460 wxCFDictionary cfdInfo
;
1461 bool bInfoOpenSuccess
= false;
1463 if(indictfile
.Open(sInfoPath
, wxFile::read
))
1465 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1466 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1467 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1468 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1470 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1471 if(!bInfoOpenSuccess
)
1475 if(bInfoOpenSuccess
)
1477 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1479 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1481 bool bAddDocTypesArrayToDictionary
= cfaDocTypes
.IsOk() == false;
1482 if(bAddDocTypesArrayToDictionary
)
1483 cfaDocTypes
.Create();
1485 cfaDocTypes
.MakeMutable( cfaDocTypes
.GetCount() + 1 );
1487 bool bEntryFound
= false;
1489 //search for duplicate
1491 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1493 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1495 //A lot of apps dont do to mime types for some reason
1496 //so we go by extensions only
1497 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1500 if(cfaExtensions
.IsOk() == false)
1503 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1505 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1507 if(asExtensions
[iWXExt
] ==
1508 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1511 dwFoundIndex
= iWXExt
;
1514 } //end of wxstring array
1518 } //end for cf array
1524 wxCFDictionary cfdNewEntry
;
1526 if(!ftInfo
.GetDescription().empty())
1528 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeName")),
1529 wxCFString(ftInfo
.GetDescription()) );
1532 if(!ftInfo
.GetIconFile().empty())
1534 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeIconFile")),
1535 wxCFString(ftInfo
.GetIconFile()) );
1539 wxCFArray cfaOSTypes
;
1540 wxCFArray cfaExtensions
;
1541 wxCFArray cfaMimeTypes
;
1544 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1545 cfaOSTypes
.Add( wxCFString(wxT("****")) );
1546 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes
);
1548 if(ftInfo
.GetExtensionsCount() != 0) //'*' for unrestricted
1550 for(size_t iExtension
= 0; iExtension
< (size_t)ftInfo
.GetExtensionsCount(); ++iExtension
)
1552 cfaExtensions
.Add( wxCFString( asExtensions
[iExtension
] ) );
1555 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions
);
1558 if(!ftInfo
.GetMimeType().empty())
1560 cfaMimeTypes
.Add( wxCFString(ftInfo
.GetMimeType()) );
1561 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes
);
1564 // Editor - can perform all actions
1565 // Viewer - all actions except manipulation/saving
1566 // None - can perform no actions
1567 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
1569 // Is application bundled?
1570 cfdNewEntry
.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue
);
1573 cfaDocTypes
.Set(i
, cfdNewEntry
);
1575 cfaDocTypes
.Add(cfdNewEntry
);
1578 // set the doc types array in the muted dictionary
1581 if(bAddDocTypesArrayToDictionary
)
1582 cfdInfo
.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1584 cfdInfo
.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1586 cfdInfo
.MakeValidXML();
1589 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1591 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1594 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1595 (wxFileOffset
)cfdaInfo
.GetCount())
1597 wxLogDebug(wxT("error in writing to file"));
1601 bInfoSuccess
= true;
1602 //#if defined(__DARWIN__)
1603 // //force launch services to update its database for the finder
1604 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1605 // if(status != noErr)
1607 // wxLogDebug(wxT("LSRegisterURL Failed."));
1611 outdictfile
.Close();
1615 outdictfile
.Close();
1616 wxLogDebug(wxT("Could not read in new dictionary"));
1621 wxLogDebug(wxString(wxT("Could not open [")) +
1622 sInfoPath
+ wxT("] for writing."));
1627 wxLogDebug(wxT("No info dictionary in main bundle"));
1632 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1637 wxLogDebug(wxT("No main bundle"));
1640 #if defined(__DARWIN__)
1644 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1645 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1646 //the app's signature somehow...
1650 OSStatus status
= MoreProcGetProcessTypeSignature(NULL
,&processType
, &creator
);
1654 Str255 psCreatorName
;
1656 status
= FindApplication(creator
, false, psCreatorName
, &dummySpec
);
1661 //get the file type if it exists -
1662 //if it really does then modify the database then save it,
1663 //otherwise we need to create a whole new entry
1664 wxFileType
* pFileType
= GetFileTypeFromExtension(asExtensions
[dwFoundIndex
]);
1668 ICGetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1669 pFileType
->m_impl
->m_lIndex
, &entry
);
1671 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1672 entry
.fileCreator
= creator
;
1674 status
= ICSetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1675 pFileType
->m_impl
->m_lIndex
, &entry
);
1680 //kICAttrNoChange means we don't care about attributes such as
1681 //locking in the database
1682 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1683 // kICAttrNoChange, (Handle) m_hDatabase);
1684 // if(status == noErr)
1688 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1693 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICSetMapEntry failed.")));
1701 //TODO: Maybe force all 3 of these to be non-empty?
1704 Str255 psDescription
;
1706 wxMacStringToPascal(wxString(wxT(".")) + ftInfo
.GetExtensions()[0], psExtension
);
1707 wxMacStringToPascal(ftInfo
.GetMimeType(), psMimeType
);
1708 wxMacStringToPascal(ftInfo
.GetDescription(), psDescription
);
1710 Str255 psPostCreatorName
;
1711 wxMacStringToPascal(wxT(""), psPostCreatorName
);
1714 //add the entry to the database
1716 entry
.totalLength
= sizeof(ICMapEntry
);
1717 entry
.fixedLength
= kICMapFixedLength
;
1719 entry
.fileType
= 0; //TODO: File type?
1720 entry
.fileCreator
= creator
;
1721 entry
.postCreator
= 0;
1722 entry
.flags
= kICMapDataForkBit
; //TODO: Maybe resource is valid by default too?
1723 PLstrcpy( entry
.extension
, psExtension
) ;
1724 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1725 memcpy(entry
.postAppName
, psPostCreatorName
, sizeof(Str255
));
1726 memcpy(entry
.MIMEType
, psMimeType
, sizeof(Str255
));
1727 memcpy(entry
.entryName
, psDescription
, sizeof(Str255
));
1729 status
= ICAddMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &entry
);
1733 //kICAttrNoChange means we don't care about attributes such as
1734 //locking in the database
1735 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1736 // kICAttrNoChange, (Handle) m_hDatabase);
1738 //return the entry in the database if successful
1739 // if(status == noErr)
1740 return GetFileTypeFromExtension(ftInfo
.GetMimeType());
1743 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1748 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICAppMapEntry failed.")));
1751 } //end if FindApplcation was successful
1754 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("FindApplication failed.")));
1756 } //end if it could obtain app's signature
1759 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("GetProcessSignature failed.")));
1765 wxMimeTypesManagerImpl::Unassociate(wxFileType
*pFileType
)
1767 wxASSERT(pFileType
);
1768 bool bInfoSuccess
= false;
1770 wxArrayString asExtensions
;
1771 pFileType
->GetExtensions(asExtensions
);
1773 if(!asExtensions
.GetCount())
1775 wxLogDebug(wxT("Must have extension to disassociate"));
1779 //Find and write to Info.plist in main bundle (note that some other
1780 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1781 //some apps (non-wx) use the 'plst' resource instead
1782 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1785 UInt32 dwBundleType
, dwBundleCreator
;
1786 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1788 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1789 //which will give us the incorrect info.plist path
1790 //otherwise it will be 'APPL', or in the case of a framework,
1792 if(dwBundleType
== 'APPL')
1795 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1796 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1798 // sInfoPath << wxT("file://");
1799 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1800 sInfoPath
<< wxT("Contents/Info.plist");
1802 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1803 wxCFDictionary cfdInfo
;
1804 bool bInfoOpenSuccess
= false;
1806 if(indictfile
.Open(sInfoPath
, wxFile::read
))
1808 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1809 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1810 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1811 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1813 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1814 if(!bInfoOpenSuccess
)
1818 if(bInfoOpenSuccess
)
1820 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1822 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1824 if(cfaDocTypes
.IsOk())
1826 bool bEntryFound
= false;
1828 //search for duplicate
1830 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1832 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1834 //A lot of apps dont do to mime types for some reason
1835 //so we go by extensions only
1836 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1839 if(cfaExtensions
.IsOk() == false)
1842 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1844 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1846 if(asExtensions
[iWXExt
] ==
1847 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1850 cfaDocTypes
.Remove(i
);
1851 cfdInfo
.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes
);
1854 } //end of wxstring array
1858 } //end for cf array
1866 cfdInfo
.MakeValidXML();
1869 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1871 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1874 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1875 (wxFileOffset
)cfdaInfo
.GetCount())
1877 wxLogDebug(wxT("error in writing to file"));
1881 bInfoSuccess
= true;
1882 //#if defined(__DARWIN__)
1883 // //force launch services to update its database for the finder
1884 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1885 // if(status != noErr)
1887 // wxLogDebug(wxT("LSRegisterURL Failed."));
1891 outdictfile
.Close();
1895 outdictfile
.Close();
1896 wxLogDebug(wxT("Could not read in new dictionary"));
1901 wxLogDebug(wxString(wxT("Could not open [")) +
1902 sInfoPath
+ wxT("] for writing."));
1907 wxLogDebug(wxT("Entry not found to remove"));
1909 wxCFDictionary::PrintOutArray(sPrintOut
, (CFArrayRef
)(CFTypeRef
)cfaDocTypes
);
1910 wxLogDebug(sPrintOut
);
1911 for(size_t i
= 0; i
< asExtensions
.GetCount(); ++i
)
1912 wxLogDebug(asExtensions
[i
]);
1917 wxLogDebug(wxT("No doc types array found"));
1918 wxString sPrintOut
; cfdInfo
.PrintOut(sPrintOut
); wxLogDebug(sPrintOut
);
1923 wxLogDebug(wxT("No info dictionary in main bundle"));
1928 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1933 wxLogDebug(wxT("No main bundle"));
1936 #if defined(__DARWIN__)
1941 //this should be as easy as removing the entry from the database and then saving
1943 OSStatus status
= ICDeleteMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1944 pFileType
->m_impl
->m_lIndex
);
1948 //kICAttrNoChange means we don't care about attributes such as
1949 //locking in the database
1950 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1951 // kICAttrNoChange, (Handle) m_hDatabase);
1953 // if(status == noErr)
1957 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1963 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICDeleteMapEntry failed.")));
1969 CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile(
1970 kCFAllocatorDefault,
1971 (CFURLRef) (CFTypeRef)cfurlInfoLoc );
1976 Boolean bOpened = CFWriteStreamOpen(cfwsInfo);
1979 CFStringRef cfsError;
1980 CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo,
1982 kCFPropertyListXMLFormat_v1_0, //100
1984 if(cfsError && cfiWritten == 0)
1986 wxLogDebug(wxCFString(cfsError).BuildWXString());
1988 cfdInfo.PrintOut(sMessage);
1989 wxLogDebug(sMessage);
1993 bInfoSuccess = true;
1994 //#if defined(__DARWIN__)
1995 // //force launch services to update its database for the finder
1996 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1997 // if(status != noErr)
1999 // wxLogDebug(wxT("LSRegisterURL Failed."));
2004 CFWriteStreamClose(cfwsInfo);
2007 #endif //wxUSE_MIMETYPE