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
);
421 //Since a filename might have spaces in it, so surround it with quotes
423 return wxString(wxT("'")) + wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding()) + wxString(wxT("'"));
427 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
429 wxT("LSGetApplicationForInfo failed."),
434 return wxEmptyString
;
437 #else //carbon/classic implementation
439 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
441 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
443 if(verb
== wxT("open"))
446 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
447 (Handle
) m_manager
->m_hDatabase
,
450 //The entry in the mimetype database only contains the app
451 //that's registered - it may not exist... we need to remap the creator
452 //type and find the right application
454 // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going
458 if(FindApplication(entry
.fileCreator
, false, outName
, &outSpec
) != noErr
)
459 return wxEmptyString
;
461 Handle outPathHandle
;
463 OSErr err
= FSpGetFullPath(&outSpec
, &outPathSize
, &outPathHandle
);
467 char* szPath
= *outPathHandle
;
468 wxString
sClassicPath(szPath
, wxConvLocal
, outPathSize
);
469 #if defined(__DARWIN__)
470 //Classic Path --> Unix (OSX) Path
471 CFURLRef finalURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
472 wxMacCFStringHolder(sClassicPath
, wxLocale::GetSystemEncoding()),
474 false); //false == not a directory
476 //clean up memory from the classic path handle
477 DisposeHandle(outPathHandle
);
481 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(finalURL
, kCFURLPOSIXPathStyle
);
486 return wxMacCFStringHolder(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding());
488 #else //classic HFS path acceptable
494 wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus
)err
);
497 return wxEmptyString
;
501 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
503 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
506 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
507 (Handle
) m_manager
->m_hDatabase
,
510 *desc
= wxMacMakeStringFromPascal(entry
.entryName
);
514 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
516 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
519 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
520 (Handle
) m_manager
->m_hDatabase
,
523 //entry has period in it
524 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
525 extensions
.Add( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) );
529 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
531 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
534 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
535 (Handle
) m_manager
->m_hDatabase
,
538 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
542 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
556 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
558 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
560 // no such file type or no value or incorrect icon entry
564 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
, wxArrayString
* commands
,
565 const wxFileType::MessageParameters
& params
) const
567 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
572 if(GetOpenCommand(&sCommand
, params
))
574 verbs
->Add(wxString(wxT("open")));
575 commands
->Add(sCommand
);
582 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, const wxString
& extraDir
)
584 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
586 //some apps (non-wx) use the 'plst' resource instead
588 CFBundleRef cfbMain = CFBundleGetMainBundle();
589 wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
591 cfdInfo.PrintOut(sLog);
595 //start internet config - log if there's an error
596 //the second param is the signature of the application, also known
597 //as resource ID 0. However, as per some recent discussions, we may not
598 //have a signature for this app, so a generic 'APPL' which is the executable
599 //type will work for now
600 OSStatus status
= ICStart( (ICInstance
*) &m_hIC
, 'APPL');
604 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
611 m_hDatabase
= (void**) NewHandle(0);
612 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
614 //the database file can be corrupt (on OSX its
615 //~/Library/Preferences/com.apple.internetconfig.plist)
620 wxLogDebug(wxT("Corrupt Mime Database!"));
624 //obtain the number of entries in the map
625 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
626 wxASSERT( status
== noErr
);
632 for(long i = 1; i <= m_lCount; ++i)
634 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
638 wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName);
639 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
640 wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType);
643 impl.Init(this, pos);
645 if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html"))
648 impl.GetOpenCommand (&cmd,
649 wxFileType::MessageParameters (wxT("http://www.google.com")));
651 wxPrintf(wxT("APP: [%s]\n"), cmd.c_str());
658 void wxMimeTypesManagerImpl::ClearData()
662 DisposeHandle((Handle
)m_hDatabase
);
663 //this can return an error, but we don't really care that much about it
664 ICStop( (ICInstance
) m_hIC
);
670 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
671 // A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know.
674 // extension -> file type
675 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
677 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
679 //low level functions - iterate through the database
683 for(long i
= 1; i
<= m_lCount
; ++i
)
685 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
689 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
690 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
692 wxFileType
* pFileType
= new wxFileType();
693 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
702 // MIME type -> extension -> file type
703 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
705 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
707 //low level functions - iterate through the database
711 for(long i
= 1; i
<= m_lCount
; ++i
)
713 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
714 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
718 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
720 wxFileType
* pFileType
= new wxFileType();
721 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
730 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
732 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
734 //low level functions - iterate through the database
738 long lStartCount
= (long) mimetypes
.GetCount();
740 for(long i
= 1; i
<= m_lCount
; ++i
)
742 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
743 if( status
== noErr
)
744 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
747 return mimetypes
.GetCount() - lStartCount
;
751 pascal OSStatus
MoreProcGetProcessTypeSignature(
752 const ProcessSerialNumberPtr pPSN
,
753 OSType
*pProcessType
,
756 OSStatus anErr
= noErr
;
757 ProcessInfoRec infoRec
;
758 ProcessSerialNumber localPSN
;
760 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
761 infoRec
.processName
= nil
;
762 infoRec
.processAppSpec
= nil
;
765 localPSN
.highLongOfPSN
= 0;
766 localPSN
.lowLongOfPSN
= kCurrentProcess
;
771 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
774 *pProcessType
= infoRec
.processType
;
775 *pCreator
= infoRec
.processSignature
;
779 }//end MoreProcGetProcessTypeSignature
783 // TODO: clean this up, its messy
788 #include "wx/mac/corefoundation/cfstring.h"
789 #include "wx/intl.h" //wxLocale for wxCFString
791 #define wxCF_RELEASE true
792 #define wxCF_RETAIN false
794 // ----------------------------------------------------------------------------
796 // ----------------------------------------------------------------------------
801 wxCFDictionary(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
803 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
804 if(bRetain
== wxCF_RETAIN
&& ref
)
808 wxCFDictionary(CFIndex cfiSize
= 0)
810 CFDictionaryKeyCallBacks kcbs
;
811 CFDictionaryValueCallBacks vcbs
;
812 BuildKeyCallbacks(&kcbs
);
813 BuildValueCallbacks(&vcbs
);
815 m_cfmdRef
= CFDictionaryCreateMutable(
816 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
824 {if(m_cfmdRef
) CFRelease(m_cfmdRef
);}
826 static const void* RetainProc(CFAllocatorRef
, const void* v
)
827 { return (const void*) CFRetain(v
); }
829 static void ReleaseProc(CFAllocatorRef
, const void* v
)
832 void MakeMutable(CFIndex cfiSize
= 0)
834 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
836 m_cfmdRef
= CFDictionaryCreateMutableCopy(
844 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
847 pCbs
->retain
= RetainProc
;
848 pCbs
->release
= ReleaseProc
;
849 pCbs
->copyDescription
= NULL
;
854 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
857 pCbs
->retain
= RetainProc
;
858 pCbs
->release
= ReleaseProc
;
859 pCbs
->copyDescription
= NULL
;
863 operator CFTypeRef () const
864 { return (CFTypeRef
)m_cfmdRef
; }
866 CFDictionaryRef
GetCFDictionary() const
867 { return (CFDictionaryRef
)m_cfmdRef
; }
869 CFMutableDictionaryRef
GetCFMutableDictionary()
870 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
872 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
875 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
878 CFIndex
GetCount() const
881 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
884 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
887 wxASSERT(Exists(cftKey
) == false);
888 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
891 void Remove(CFTypeRef cftKey
)
894 wxASSERT(Exists(cftKey
));
895 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
898 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
901 wxASSERT(Exists(cftKey
));
902 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
905 bool Exists(CFTypeRef cftKey
) const
908 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
);
911 bool IsOk() const {return m_cfmdRef
!= NULL
; }
915 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID();
918 void PrintOut(wxString
& sMessage
)
920 PrintOutDictionary(sMessage
, m_cfmdRef
);
923 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
925 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
926 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
927 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
929 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
931 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
933 wxString sKey
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
934 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
937 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
940 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
943 wxString::Format(wxT("\n\t[Value : %s]"),
946 PrintOutType(sMessage
, sValue
, pValues
[i
]);
948 sMessage
<< wxT("\n");
955 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
958 for(CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
960 wxString sValue
= wxMacCFStringHolder(CFCopyTypeIDDescription(CFGetTypeID(
961 CFArrayGetValueAtIndex(cfaRef
, i
)
965 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
968 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
972 static void PrintOutType(wxString
& sMessage
, const wxString
& sValue
, CFTypeRef cfRef
)
974 sMessage
<< wxT(" {");
976 if(sValue
== wxT("CFString"))
978 sMessage
<< wxMacCFStringHolder((CFStringRef
)cfRef
, false).AsString();
980 else if(sValue
== wxT("CFNumber"))
983 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
986 else if(sValue
== wxT("CFDictionary"))
988 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
990 else if(sValue
== wxT("CFArray"))
992 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
994 else if(sValue
== wxT("CFBoolean"))
996 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
998 else if(sValue
== wxT("CFURL"))
1000 sMessage
<< wxMacCFStringHolder(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
1004 sMessage
<< wxT("*****UNKNOWN TYPE******");
1007 sMessage
<< wxT("} ");
1011 void MakeValidXML();
1014 CFTypeRef
WriteAsXML()
1016 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
1019 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
1022 CFStringRef cfsError
=NULL
;
1023 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
1024 kCFAllocatorDefault
,
1026 kCFPropertyListMutableContainersAndLeaves
,
1031 *pErrorMsg
= wxMacCFStringHolder(cfsError
).AsString();
1033 CFRelease(cfsError
);
1036 return m_cfmdRef
!= NULL
;
1039 CFMutableDictionaryRef m_cfmdRef
;
1042 // ----------------------------------------------------------------------------
1044 // ----------------------------------------------------------------------------
1049 wxCFArray(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
)
1051 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1052 if(bRetain
== wxCF_RETAIN
&& ref
)
1056 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1057 { Create(cfiSize
); }
1062 void MakeMutable(CFIndex cfiSize
= 0)
1064 wxASSERT(IsValid());
1066 CFMutableArrayRef oldref
= m_cfmaRef
;
1067 m_cfmaRef
= CFArrayCreateMutableCopy(
1068 kCFAllocatorDefault
,
1070 (CFArrayRef
)oldref
);
1074 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1077 pCbs
->retain
= RetainProc
;
1078 pCbs
->release
= ReleaseProc
;
1079 pCbs
->copyDescription
= NULL
;
1083 void Create(CFIndex cfiSize
= 0)
1086 CFArrayCallBacks cb
;
1087 BuildCallbacks(&cb
);
1089 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1093 {if(m_cfmaRef
) CFRelease(m_cfmaRef
);}
1095 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1096 { return (const void*) CFRetain(v
); }
1098 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1101 operator CFTypeRef () const
1102 { return (CFTypeRef
)m_cfmaRef
; }
1104 CFArrayRef
GetCFArray() const
1105 { return (CFArrayRef
)m_cfmaRef
; }
1107 CFMutableArrayRef
GetCFMutableArray()
1108 { return (CFMutableArrayRef
) m_cfmaRef
; }
1110 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1112 wxASSERT(IsValid());
1113 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1118 wxASSERT(IsValid());
1119 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1122 void Add(CFTypeRef cftValue
)
1124 wxASSERT(IsValid());
1125 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1128 void Remove(CFIndex cfiIndex
)
1130 wxASSERT(IsValid());
1131 wxASSERT(cfiIndex
< GetCount());
1132 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1135 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1137 wxASSERT(IsValid());
1138 wxASSERT(cfiIndex
< GetCount());
1139 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1142 bool IsOk() const {return m_cfmaRef
!= NULL
; }
1144 bool IsValid() const
1146 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1150 void MakeValidXML();
1154 CFMutableArrayRef m_cfmaRef
;
1157 // ----------------------------------------------------------------------------
1159 // ----------------------------------------------------------------------------
1164 wxCFString(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_Holder((CFStringRef
)ref
, bRetain
== wxCF_RELEASE
)
1167 wxCFString(const wxChar
* szString
) : m_Holder(wxString(szString
), wxLocale::GetSystemEncoding())
1170 wxCFString(const wxString
& sString
) : m_Holder(sString
, wxLocale::GetSystemEncoding())
1173 operator CFTypeRef() const
1174 { return (CFTypeRef
) ((CFStringRef
) m_Holder
); }
1176 bool IsOk() { return ((CFTypeRef
)(*this)) != NULL
; }
1178 wxString
BuildWXString() {return m_Holder
.AsString(); }
1181 wxMacCFStringHolder m_Holder
;
1184 // ----------------------------------------------------------------------------
1186 // ----------------------------------------------------------------------------
1191 wxCFNumber(int nValue
)
1193 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &nValue
);
1196 wxCFNumber(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfnRef((CFNumberRef
)ref
)
1198 if(bRetain
== wxCF_RETAIN
&& ref
)
1205 CFRelease(m_cfnRef
);
1209 operator CFTypeRef() const
1210 { return (CFTypeRef
) m_cfnRef
; }
1215 CFNumberGetValue( m_cfnRef
,
1223 bool IsOk() { return m_cfnRef
!= NULL
; }
1226 CFNumberRef m_cfnRef
;
1229 // ----------------------------------------------------------------------------
1231 // ----------------------------------------------------------------------------
1236 wxCFURL(CFTypeRef ref
= NULL
, bool bRetain
= wxCF_RELEASE
) : m_cfurlRef((CFURLRef
)ref
)
1238 if(bRetain
== wxCF_RETAIN
&& ref
)
1241 wxCFURL(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1243 Create(URLString
, BaseURL
);
1246 void Create(const wxCFString
& URLString
, CFTypeRef BaseURL
= NULL
)
1248 m_cfurlRef
= CFURLCreateWithString(
1249 kCFAllocatorDefault
,
1250 (CFStringRef
)(CFTypeRef
)URLString
,
1251 (CFURLRef
) BaseURL
);
1254 ~wxCFURL() {if(m_cfurlRef
) CFRelease(m_cfurlRef
);}
1256 wxString
BuildWXString()
1258 return wxCFString(CFURLCopyPath(m_cfurlRef
)).BuildWXString();
1261 operator CFTypeRef() const
1262 { return (CFTypeRef
)m_cfurlRef
; }
1264 bool IsOk() { return m_cfurlRef
!= NULL
; }
1266 CFURLRef m_cfurlRef
;
1269 // ----------------------------------------------------------------------------
1271 // ----------------------------------------------------------------------------
1273 #define wxCFDATA_RELEASEBUFFER 1
1274 #define wxCFDATA_RETAINBUFFER 0
1279 wxCFData(CFTypeRef ref
, bool bRetain
= wxCF_RELEASE
) : m_cfdaRef((CFDataRef
)ref
)
1281 if(bRetain
== wxCF_RETAIN
&& ref
)
1284 wxCFData(const UInt8
* pBytes
, CFIndex len
, bool bKeep
= wxCFDATA_RELEASEBUFFER
)
1286 if(bKeep
== wxCFDATA_RELEASEBUFFER
)
1288 m_cfdaRef
= CFDataCreateWithBytesNoCopy
1289 (kCFAllocatorDefault
, pBytes
, len
, kCFAllocatorDefault
);
1293 m_cfdaRef
= CFDataCreate(kCFAllocatorDefault
, pBytes
, len
);
1296 ~wxCFData() {if(m_cfdaRef
) CFRelease(m_cfdaRef
);}
1298 const UInt8
* GetValue()
1300 return CFDataGetBytePtr(m_cfdaRef
);
1305 return CFDataGetLength(m_cfdaRef
);
1308 operator CFTypeRef() const
1309 { return (CFTypeRef
)m_cfdaRef
; }
1311 bool IsOk() { return m_cfdaRef
!= NULL
; }
1313 CFDataRef m_cfdaRef
;
1316 void wxCFDictionary::MakeValidXML()
1318 CFIndex cfiCount
= GetCount();
1319 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
1320 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
1322 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1324 //for plist xml format all dictionary keys must be cfstrings and no values in
1325 //the dictionary or subkeys/values can be NULL
1326 //Also, CFURLs are not allowed
1327 for(CFIndex i
= 0; i
< cfiCount
; ++i
)
1329 //must be an array, dictionary, string, bool, or int and cannot be null
1330 //and dictionaries can only contain cfstring keys
1331 CFTypeRef cfRef
= pValues
[i
];
1333 CFGetTypeID(pKeys
[i
]) != CFStringGetTypeID() ||
1341 pKeys
= new CFTypeRef
[cfiCount
];
1342 pValues
= new CFTypeRef
[cfiCount
];
1343 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1345 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1348 wxCFArray
cfaCurrent(cfRef
);
1349 cfaCurrent
.MakeMutable();
1350 cfaCurrent
.MakeValidXML();
1351 Set(pKeys
[i
], cfaCurrent
);
1353 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1356 wxCFDictionary
cfdCurrent(cfRef
);
1357 cfdCurrent
.MakeMutable();
1358 cfdCurrent
.MakeValidXML();
1359 Set(pKeys
[i
], cfdCurrent
);
1361 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1362 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1363 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1370 pKeys
= new CFTypeRef
[cfiCount
];
1371 pValues
= new CFTypeRef
[cfiCount
];
1372 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1380 void wxCFArray::MakeValidXML()
1382 for(CFIndex i
= 0; i
< GetCount(); ++i
)
1384 //must be an array, dictionary, string, bool, or int and cannot be null
1385 //and dictionaries can only contain cfstring keys
1386 CFTypeRef cfRef
= (*this)[i
];
1392 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1395 wxCFArray
cfaCurrent(cfRef
);
1396 cfaCurrent
.MakeMutable();
1397 cfaCurrent
.MakeValidXML();
1400 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1403 wxCFDictionary
cfdCurrent(cfRef
);
1404 cfdCurrent
.MakeMutable();
1405 cfdCurrent
.MakeValidXML();
1408 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1409 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1410 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1426 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
1428 bool bInfoSuccess
= false;
1430 const wxArrayString
& asExtensions
= ftInfo
.GetExtensions();
1431 size_t dwFoundIndex
= 0;
1432 if(!asExtensions
.GetCount())
1434 wxLogDebug(wxT("Must have extension to associate with"));
1437 //Find and write to Info.plist in main bundle (note that some other
1438 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1439 //some apps (non-wx) use the 'plst' resource instead
1440 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1443 UInt32 dwBundleType
, dwBundleCreator
;
1444 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1446 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1447 //which will give us the incorrect info.plist path
1448 //otherwise it will be 'APPL', or in the case of a framework,
1450 if(dwBundleType
== 'APPL')
1453 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1454 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1456 // sInfoPath << wxT("file://");
1457 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1458 sInfoPath
<< wxT("Contents/Info.plist");
1460 // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1461 wxCFDictionary cfdInfo
;
1462 bool bInfoOpenSuccess
= false;
1464 if(indictfile
.Open(sInfoPath
, wxFile::read
))
1466 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1467 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1468 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1469 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1471 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1472 if(!bInfoOpenSuccess
)
1476 if(bInfoOpenSuccess
)
1478 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1480 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1482 bool bAddDocTypesArrayToDictionary
= cfaDocTypes
.IsOk() == false;
1483 if(bAddDocTypesArrayToDictionary
)
1484 cfaDocTypes
.Create();
1486 cfaDocTypes
.MakeMutable( cfaDocTypes
.GetCount() + 1 );
1488 bool bEntryFound
= false;
1490 //search for duplicate
1492 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1494 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1496 //A lot of apps dont do to mime types for some reason
1497 //so we go by extensions only
1498 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1501 if(cfaExtensions
.IsOk() == false)
1504 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1506 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1508 if(asExtensions
[iWXExt
] ==
1509 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1512 dwFoundIndex
= iWXExt
;
1515 } //end of wxstring array
1519 } //end for cf array
1525 wxCFDictionary cfdNewEntry
;
1527 if(!ftInfo
.GetDescription().empty())
1529 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeName")),
1530 wxCFString(ftInfo
.GetDescription()) );
1533 if(!ftInfo
.GetIconFile().empty())
1535 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeIconFile")),
1536 wxCFString(ftInfo
.GetIconFile()) );
1540 wxCFArray cfaOSTypes
;
1541 wxCFArray cfaExtensions
;
1542 wxCFArray cfaMimeTypes
;
1545 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1546 cfaOSTypes
.Add( wxCFString(wxT("****")) );
1547 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeOSTypes")), cfaOSTypes
);
1549 if(ftInfo
.GetExtensionsCount() != 0) //'*' for unrestricted
1551 for(size_t iExtension
= 0; iExtension
< (size_t)ftInfo
.GetExtensionsCount(); ++iExtension
)
1553 cfaExtensions
.Add( wxCFString( asExtensions
[iExtension
] ) );
1556 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeExtensions")), cfaExtensions
);
1559 if(!ftInfo
.GetMimeType().empty())
1561 cfaMimeTypes
.Add( wxCFString(ftInfo
.GetMimeType()) );
1562 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes
);
1565 // Editor - can perform all actions
1566 // Viewer - all actions except manipulation/saving
1567 // None - can perform no actions
1568 cfdNewEntry
.Add( wxCFString(wxT("CFBundleTypeRole")), wxCFString(wxT("Editor")) );
1570 // Is application bundled?
1571 cfdNewEntry
.Add( wxCFString(wxT("LSTypeIsPackage")), kCFBooleanTrue
);
1574 cfaDocTypes
.Set(i
, cfdNewEntry
);
1576 cfaDocTypes
.Add(cfdNewEntry
);
1579 // set the doc types array in the muted dictionary
1582 if(bAddDocTypesArrayToDictionary
)
1583 cfdInfo
.Add(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1585 cfdInfo
.Set(wxCFString(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1587 cfdInfo
.MakeValidXML();
1590 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1592 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1595 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1596 (wxFileOffset
)cfdaInfo
.GetCount())
1598 wxLogDebug(wxT("error in writing to file"));
1602 bInfoSuccess
= true;
1603 //#if defined(__DARWIN__)
1604 // //force launch services to update its database for the finder
1605 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1606 // if(status != noErr)
1608 // wxLogDebug(wxT("LSRegisterURL Failed."));
1612 outdictfile
.Close();
1616 outdictfile
.Close();
1617 wxLogDebug(wxT("Could not read in new dictionary"));
1622 wxLogDebug(wxString(wxT("Could not open [")) +
1623 sInfoPath
+ wxT("] for writing."));
1628 wxLogDebug(wxT("No info dictionary in main bundle"));
1633 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1638 wxLogDebug(wxT("No main bundle"));
1641 #if defined(__DARWIN__)
1645 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1646 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1647 //the app's signature somehow...
1651 OSStatus status
= MoreProcGetProcessTypeSignature(NULL
,&processType
, &creator
);
1655 Str255 psCreatorName
;
1657 status
= FindApplication(creator
, false, psCreatorName
, &dummySpec
);
1662 //get the file type if it exists -
1663 //if it really does then modify the database then save it,
1664 //otherwise we need to create a whole new entry
1665 wxFileType
* pFileType
= GetFileTypeFromExtension(asExtensions
[dwFoundIndex
]);
1669 ICGetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1670 pFileType
->m_impl
->m_lIndex
, &entry
);
1672 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1673 entry
.fileCreator
= creator
;
1675 status
= ICSetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1676 pFileType
->m_impl
->m_lIndex
, &entry
);
1681 //kICAttrNoChange means we don't care about attributes such as
1682 //locking in the database
1683 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1684 // kICAttrNoChange, (Handle) m_hDatabase);
1685 // if(status == noErr)
1689 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1694 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICSetMapEntry failed.")));
1702 //TODO: Maybe force all 3 of these to be non-empty?
1705 Str255 psDescription
;
1707 wxMacStringToPascal(wxString(wxT(".")) + ftInfo
.GetExtensions()[0], psExtension
);
1708 wxMacStringToPascal(ftInfo
.GetMimeType(), psMimeType
);
1709 wxMacStringToPascal(ftInfo
.GetDescription(), psDescription
);
1711 Str255 psPostCreatorName
;
1712 wxMacStringToPascal(wxT(""), psPostCreatorName
);
1715 //add the entry to the database
1717 entry
.totalLength
= sizeof(ICMapEntry
);
1718 entry
.fixedLength
= kICMapFixedLength
;
1720 entry
.fileType
= 0; //TODO: File type?
1721 entry
.fileCreator
= creator
;
1722 entry
.postCreator
= 0;
1723 entry
.flags
= kICMapDataForkBit
; //TODO: Maybe resource is valid by default too?
1724 PLstrcpy( entry
.extension
, psExtension
) ;
1725 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1726 memcpy(entry
.postAppName
, psPostCreatorName
, sizeof(Str255
));
1727 memcpy(entry
.MIMEType
, psMimeType
, sizeof(Str255
));
1728 memcpy(entry
.entryName
, psDescription
, sizeof(Str255
));
1730 status
= ICAddMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &entry
);
1734 //kICAttrNoChange means we don't care about attributes such as
1735 //locking in the database
1736 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1737 // kICAttrNoChange, (Handle) m_hDatabase);
1739 //return the entry in the database if successful
1740 // if(status == noErr)
1741 return GetFileTypeFromExtension(ftInfo
.GetMimeType());
1744 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1749 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICAppMapEntry failed.")));
1752 } //end if FindApplcation was successful
1755 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("FindApplication failed.")));
1757 } //end if it could obtain app's signature
1760 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("GetProcessSignature failed.")));
1766 wxMimeTypesManagerImpl::Unassociate(wxFileType
*pFileType
)
1768 wxASSERT(pFileType
);
1769 bool bInfoSuccess
= false;
1771 wxArrayString asExtensions
;
1772 pFileType
->GetExtensions(asExtensions
);
1774 if(!asExtensions
.GetCount())
1776 wxLogDebug(wxT("Must have extension to disassociate"));
1780 //Find and write to Info.plist in main bundle (note that some other
1781 //apps have theirs named differently, i.e. IE's is named Info-macos.plist
1782 //some apps (non-wx) use the 'plst' resource instead
1783 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1786 UInt32 dwBundleType
, dwBundleCreator
;
1787 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1789 //if launching terminal non-app version will be 'BNDL' (generic bundle, maybe in other cases too),
1790 //which will give us the incorrect info.plist path
1791 //otherwise it will be 'APPL', or in the case of a framework,
1793 if(dwBundleType
== 'APPL')
1796 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1797 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1799 // sInfoPath << wxT("file://");
1800 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1801 sInfoPath
<< wxT("Contents/Info.plist");
1803 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1804 wxCFDictionary cfdInfo
;
1805 bool bInfoOpenSuccess
= false;
1807 if(indictfile
.Open(sInfoPath
, wxFile::read
))
1809 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1810 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1811 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1812 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1814 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1815 if(!bInfoOpenSuccess
)
1819 if(bInfoOpenSuccess
)
1821 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1823 wxCFArray
cfaDocTypes( cfdInfo
[ wxCFString(wxT("CFBundleDocumentTypes")) ], wxCF_RETAIN
);
1825 if(cfaDocTypes
.IsOk())
1827 bool bEntryFound
= false;
1829 //search for duplicate
1831 for(i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1833 wxCFDictionary
cfdDocTypeEntry( cfaDocTypes
[i
], wxCF_RETAIN
);
1835 //A lot of apps dont do to mime types for some reason
1836 //so we go by extensions only
1837 wxCFArray
cfaExtensions( cfdDocTypeEntry
[ wxCFString(wxT("CFBundleTypeExtensions")) ],
1840 if(cfaExtensions
.IsOk() == false)
1843 for(CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1845 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1847 if(asExtensions
[iWXExt
] ==
1848 wxCFString(cfaExtensions
[iExt
], wxCF_RETAIN
).BuildWXString())
1851 cfaDocTypes
.Remove(i
);
1852 cfdInfo
.Set( wxCFString(wxT("CFBundleDocumentTypes")) , cfaDocTypes
);
1855 } //end of wxstring array
1859 } //end for cf array
1867 cfdInfo
.MakeValidXML();
1870 if(outdictfile
.Open(sInfoPath
, wxFile::write
))
1872 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1875 if(outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1876 (wxFileOffset
)cfdaInfo
.GetCount())
1878 wxLogDebug(wxT("error in writing to file"));
1882 bInfoSuccess
= true;
1883 //#if defined(__DARWIN__)
1884 // //force launch services to update its database for the finder
1885 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1886 // if(status != noErr)
1888 // wxLogDebug(wxT("LSRegisterURL Failed."));
1892 outdictfile
.Close();
1896 outdictfile
.Close();
1897 wxLogDebug(wxT("Could not read in new dictionary"));
1902 wxLogDebug(wxString(wxT("Could not open [")) +
1903 sInfoPath
+ wxT("] for writing."));
1908 wxLogDebug(wxT("Entry not found to remove"));
1910 wxCFDictionary::PrintOutArray(sPrintOut
, (CFArrayRef
)(CFTypeRef
)cfaDocTypes
);
1911 wxLogDebug(sPrintOut
);
1912 for(size_t i
= 0; i
< asExtensions
.GetCount(); ++i
)
1913 wxLogDebug(asExtensions
[i
]);
1918 wxLogDebug(wxT("No doc types array found"));
1919 wxString sPrintOut
; cfdInfo
.PrintOut(sPrintOut
); wxLogDebug(sPrintOut
);
1924 wxLogDebug(wxT("No info dictionary in main bundle"));
1929 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1934 wxLogDebug(wxT("No main bundle"));
1937 #if defined(__DARWIN__)
1942 //this should be as easy as removing the entry from the database and then saving
1944 OSStatus status
= ICDeleteMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1945 pFileType
->m_impl
->m_lIndex
);
1949 //kICAttrNoChange means we don't care about attributes such as
1950 //locking in the database
1951 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1952 // kICAttrNoChange, (Handle) m_hDatabase);
1954 // if(status == noErr)
1958 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1964 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICDeleteMapEntry failed.")));
1970 CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile(
1971 kCFAllocatorDefault,
1972 (CFURLRef) (CFTypeRef)cfurlInfoLoc );
1977 Boolean bOpened = CFWriteStreamOpen(cfwsInfo);
1980 CFStringRef cfsError;
1981 CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo,
1983 kCFPropertyListXMLFormat_v1_0, //100
1985 if(cfsError && cfiWritten == 0)
1987 wxLogDebug(wxCFString(cfsError).BuildWXString());
1989 cfdInfo.PrintOut(sMessage);
1990 wxLogDebug(sMessage);
1994 bInfoSuccess = true;
1995 //#if defined(__DARWIN__)
1996 // //force launch services to update its database for the finder
1997 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1998 // if(status != noErr)
2000 // wxLogDebug(wxT("LSRegisterURL Failed."));
2005 CFWriteStreamClose(cfwsInfo);
2008 #endif //wxUSE_MIMETYPE