1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/mimetmac.cpp
3 // Purpose: Mac Carbon implementation for wx MIME-related classes
7 // Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>)
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
12 // TODO: Search Info[-macos](classic).plist dictionary in addition
13 // to Internet Config database.
15 // Maybe try a brainstorm a way to change the wxMimeTypesManager API
16 // to get info from a file instead/addition to current get all stuff
17 // API so that we can use Launch Services to get MIME type info.
19 // Implement GetIcon from one of the FinderInfo functions - or
20 // use Launch Services and search that app's plist for the icon.
22 // Put some special juice in for the print command.
25 // for compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
34 #include "wx/osx/mimetype.h"
37 #include "wx/dynarray.h"
38 #include "wx/string.h"
48 #include "wx/confbase.h"
50 #include "wx/osx/private.h"
52 // other standard headers
56 #include <InternetConfig.h>
57 #include <CoreServices.h>
61 // START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html)
63 // IsRemoteVolume can be used to find out if the
64 // volume referred to by vRefNum is a remote volume
65 // located somewhere on a network. the volume's attribute
66 // flags (copied from the GetVolParmsInfoBuffer structure)
67 // are returned in the longword pointed to by vMAttrib.
68 OSErr
IsRemoteVolume(short vRefNum
, Boolean
*isRemote
, long *vMAttrib
)
71 GetVolParmsInfoBuffer volinfo
;
74 volPB
.ioParam
.ioVRefNum
= vRefNum
;
75 volPB
.ioParam
.ioNamePtr
= NULL
;
76 volPB
.ioParam
.ioBuffer
= (Ptr
)&volinfo
;
77 volPB
.ioParam
.ioReqCount
= sizeof(volinfo
);
78 err
= PBHGetVolParmsSync( &volPB
);
81 *isRemote
= (volinfo
.vMServerAdr
!= 0);
82 *vMAttrib
= volinfo
.vMAttrib
;
88 // BuildVolumeList fills the array pointed to by vols with
89 // a list of the currently mounted volumes. If includeRemote
90 // is true, then remote server volumes will be included in
91 // the list. When remote server volumes are included in the
92 // list, they will be added to the end of the list. On entry,
93 // *count should contain the size of the array pointed to by
94 // vols. On exit, *count will be set to the number of id numbers
95 // placed in the array. If vMAttribMask is non-zero, then
96 // only volumes with matching attributes are added to the
97 // list of volumes. bits in the vMAttribMask should use the
98 // same encoding as bits in the vMAttrib field of
99 // the GetVolParmsInfoBuffer structure.
100 OSErr
BuildVolumeList(Boolean includeRemote
, short *vols
,
101 long *count
, long vMAttribMask
)
103 HParamBlockRec volPB
;
106 long nlocal
, nremote
;
109 // set up and check parameters
110 volPB
.volumeParam
.ioNamePtr
= NULL
;
111 nlocal
= nremote
= 0;
115 // iterate through volumes
116 for (volPB
.volumeParam
.ioVolIndex
= 1;
117 PBHGetVInfoSync(&volPB
) == noErr
;
118 volPB
.volumeParam
.ioVolIndex
++)
120 // skip remote volumes, if necessary
121 err
= IsRemoteVolume(volPB
.volumeParam
.ioVRefNum
, &isRemote
, &vMAttrib
);
125 if ((includeRemote
|| !isRemote
) && ((vMAttrib
& vMAttribMask
) == vMAttribMask
))
127 // add local volumes at the front; remote volumes at the end
129 vols
[nlocal
+ nremote
++] = volPB
.volumeParam
.ioVRefNum
;
136 nremote
* sizeof(short) );
137 vols
[nlocal
++] = volPB
.volumeParam
.ioVRefNum
;
141 if ((nlocal
+ nremote
) >= *count
)
147 *count
= (nlocal
+ nremote
);
153 // FindApplication iterates through mounted volumes
154 // searching for an application with the given creator
155 // type. If includeRemote is true, then remote volumes
156 // will be searched (after local ones) for an application
157 // with the creator type.
159 // Hacked to output to appName
163 OSErr
FindApplication(OSType appCreator
, Boolean includeRemote
, Str255 appName
, FSSpec
* appSpec
)
165 short rRefNums
[kMaxVols
];
170 // get a list of volumes - with desktop files
172 err
= BuildVolumeList(includeRemote
, rRefNums
, &volCount
, (1 << bHasDesktopMgr
) );
176 // iterate through the list
177 for (i
=0; i
<volCount
; i
++)
179 // has a desktop file?
180 desktopPB
.ioCompletion
= NULL
;
181 desktopPB
.ioVRefNum
= rRefNums
[i
];
182 desktopPB
.ioNamePtr
= NULL
;
183 desktopPB
.ioIndex
= 0;
184 err
= PBDTGetPath( &desktopPB
);
188 // has the correct app??
189 desktopPB
.ioFileCreator
= appCreator
;
190 desktopPB
.ioNamePtr
= appName
;
191 err
= PBDTGetAPPLSync( &desktopPB
);
195 // make a file spec referring to it
196 err
= FSMakeFSSpec( rRefNums
[i
], desktopPB
.ioAPPLParID
, appName
, appSpec
);
207 // END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html)
209 // yeah, duplicated code
210 pascal OSErr
FSpGetFullPath( const FSSpec
*spec
,
211 short *fullPathLength
,
214 OSErr result
, realResult
;
222 realResult
= result
= noErr
;
224 // work around Nav Services "bug" (it returns invalid FSSpecs with empty names)
226 if ( spec
->name
[0] == 0 )
228 result
= FSMakeFSSpecCompat(spec
->vRefNum
, spec
->parID
, spec
->name
, &tempSpec
);
234 // Make a copy of the input FSSpec that can be modified
235 BlockMoveData( spec
, &tempSpec
, sizeof(FSSpec
) );
237 if ( result
== noErr
)
239 if ( tempSpec
.parID
== fsRtParID
)
241 // object is a volume
242 // Add a colon to make it a full pathname
244 tempSpec
.name
[tempSpec
.name
[0]] = ':';
247 result
= PtrToHand(&tempSpec
.name
[1], fullPath
, tempSpec
.name
[0]);
251 // object isn't a volume
253 // Is the object a file or a directory?
254 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
255 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
256 pb
.dirInfo
.ioDrDirID
= tempSpec
.parID
;
257 pb
.dirInfo
.ioFDirIndex
= 0;
258 result
= PBGetCatInfoSync( &pb
);
260 // Allow file/directory name at end of path to not exist.
262 if ((result
== noErr
) || (result
== fnfErr
))
264 // if the object is a directory, append a colon so full pathname ends with colon
265 if ((result
== noErr
) && (pb
.hFileInfo
.ioFlAttrib
& kioFlAttribDirMask
) != 0)
268 tempSpec
.name
[tempSpec
.name
[0]] = ':';
271 // Put the object name in first
272 result
= PtrToHand( &tempSpec
.name
[1], fullPath
, tempSpec
.name
[0] );
273 if ( result
== noErr
)
275 // Get the ancestor directory names
276 pb
.dirInfo
.ioNamePtr
= tempSpec
.name
;
277 pb
.dirInfo
.ioVRefNum
= tempSpec
.vRefNum
;
278 pb
.dirInfo
.ioDrParID
= tempSpec
.parID
;
280 // loop until we have an error or find the root directory
283 pb
.dirInfo
.ioFDirIndex
= -1;
284 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
285 result
= PBGetCatInfoSync(&pb
);
286 if ( result
== noErr
)
288 // Append colon to directory name
290 tempSpec
.name
[tempSpec
.name
[0]] = ':';
292 // Add directory name to beginning of fullPath
293 (void)Munger(*fullPath
, 0, NULL
, 0, &tempSpec
.name
[1], tempSpec
.name
[0]);
297 while ( (result
== noErr
) && (pb
.dirInfo
.ioDrDirID
!= fsRtDirID
) );
303 if ( result
== noErr
)
306 *fullPathLength
= GetHandleSize( *fullPath
);
307 result
= realResult
; // return realResult in case it was fnfErr
311 // Dispose of the handle and return NULL and zero length
312 if ( *fullPath
!= NULL
)
314 DisposeHandle( *fullPath
);
324 // On the mac there are two ways to open a file - one is through apple events and the
325 // finder, another is through mime types.
327 // So, really there are two ways to implement wxFileType...
329 // Mime types are only available on OS 8.1+ through the InternetConfig API
331 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
332 // Low - which means you have to iterate yourself through the mime database
333 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
334 // High - which requires access to the database every time
336 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
337 // and mid-level functions
339 // TODO: Should we call ICBegin/ICEnd? Then where?
343 inline void wxLogMimeDebug(const wxChar
* WXUNUSED_UNLESS_DEBUG(szMsg
), OSStatus
WXUNUSED_UNLESS_DEBUG(status
))
345 wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg
, __LINE__
, (int)status
));
348 // in case we're compiling in non-GUI mode
349 class WXDLLIMPEXP_FWD_CORE wxIcon
;
351 bool wxFileTypeImpl::SetCommand(const wxString
& WXUNUSED(cmd
), const wxString
& WXUNUSED(verb
), bool WXUNUSED(overwriteprompt
))
353 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
358 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& WXUNUSED(strIcon
), int WXUNUSED(index
))
360 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
365 bool wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
366 const wxFileType::MessageParameters
& params
) const
368 wxString cmd
= GetCommand(wxT("open"));
370 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
372 return !openCmd
->empty();
376 wxFileTypeImpl::GetPrintCommand(
378 const wxFileType::MessageParameters
& params
) const
380 wxString cmd
= GetCommand(wxT("print"));
382 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
384 return !printCmd
->empty();
388 // Internet Config vs. Launch Services
390 // From OS 8 on there was internet config...
391 // However, OSX and its finder does not use info
392 // from Internet Config at all - the Internet Config
393 // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS
394 // OR REGISTERED THROUGH INTERNET CONFIG
396 // Therefore on OSX in order for the open command to be useful
397 // we need to go straight to launch services
400 //on darwin, use launch services
402 #include <ApplicationServices/ApplicationServices.h>
404 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
406 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
408 if (verb
== wxT("open"))
411 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
412 (Handle
) m_manager
->m_hDatabase
,
415 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
416 sCurrentExtension
= sCurrentExtension
.Right(sCurrentExtension
.length()-1 );
418 //type, creator, ext, roles, outapp (FSRef), outappurl
419 CFURLRef cfurlAppPath
;
420 OSStatus status
= LSGetApplicationForInfo( kLSUnknownType
,
422 wxCFStringRef(sCurrentExtension
, wxLocale::GetSystemEncoding()),
429 CFStringRef cfsUnixPath
= CFURLCopyFileSystemPath(cfurlAppPath
, kCFURLPOSIXPathStyle
);
430 CFRelease(cfurlAppPath
);
433 // Since a filename might have spaces in it, so surround it with quotes
440 + wxCFStringRef(cfsUnixPath
).AsString(wxLocale::GetSystemEncoding())
441 + wxString(wxT("'"));
448 wxLogDebug(wxString::Format(wxT("%i - %s - %i"),
450 wxT("LSGetApplicationForInfo failed."),
455 return wxEmptyString
;
458 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
460 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
463 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
464 (Handle
) m_manager
->m_hDatabase
, m_lIndex
, &entry
);
466 *desc
= wxMacMakeStringFromPascal( entry
.entryName
);
471 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
473 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
476 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
477 (Handle
) m_manager
->m_hDatabase
, m_lIndex
, &entry
);
479 //entry has period in it
480 wxString sCurrentExtension
= wxMacMakeStringFromPascal( entry
.extension
);
481 extensions
.Add( sCurrentExtension
.Right( sCurrentExtension
.length() - 1 ) );
486 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
488 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
491 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
492 (Handle
) m_manager
->m_hDatabase
, m_lIndex
, &entry
);
494 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
499 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
514 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
516 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
518 // no such file type or no value or incorrect icon entry
522 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
,
523 wxArrayString
* commands
,
524 const wxFileType::MessageParameters
& params
) const
526 wxASSERT_MSG( m_manager
!= NULL
, wxT("Bad wxFileType") );
531 if (GetOpenCommand(&sCommand
, params
))
533 verbs
->Add(wxString(wxT("open")));
534 commands
->Add(sCommand
);
541 void wxMimeTypesManagerImpl::Initialize(int WXUNUSED(mailcapStyles
), const wxString
& WXUNUSED(extraDir
))
543 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
545 // some apps (non-wx) use the 'plst' resource instead
547 CFBundleRef cfbMain
= CFBundleGetMainBundle();
548 wxCFDictionary
cfdInfo( CFBundleGetInfoDictionary(cfbMain
), wxCF_RETAIN
);
550 cfdInfo
.PrintOut(sLog
);
554 // start Internet Config - log if there's an error
555 // the second param is the signature of the application, also known
556 // as resource ID 0. However, as per some recent discussions, we may not
557 // have a signature for this app, so a generic 'APPL' which is the executable
558 // type will work for now.
559 OSStatus status
= ICStart( (ICInstance
*)&m_hIC
, 'APPL' );
563 wxLogDebug(wxT("Could not initialize wxMimeTypesManager!"));
571 m_hDatabase
= (void**) NewHandle(0);
572 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
574 //the database file can be corrupt (on OSX its
575 //~/Library/Preferences/com.apple.internetconfig.plist)
580 wxLogDebug(wxT("Corrupt MIME database!"));
584 //obtain the number of entries in the map
585 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
586 wxASSERT( status
== noErr
);
593 for (long i
= 1; i
<= m_lCount
; ++i
)
595 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
599 wxString sCreator
= wxMacMakeStringFromPascal(entry
.creatorAppName
);
600 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
601 wxString sMIMEType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
604 impl
.Init(this, pos
);
606 if (sMIMEType
== wxT("text/html") && sCurrentExtension
== wxT(".html"))
610 impl
.GetOpenCommand( &cmd
, wxFileType::MessageParameters (wxT("http://www.google.com")));
611 wxPrintf(wxT("APP: [%s]\n"), cmd
.c_str());
618 void wxMimeTypesManagerImpl::ClearData()
622 DisposeHandle( (Handle
)m_hDatabase
);
624 // this can return an error, but we don't really care that much about it
625 ICStop( (ICInstance
)m_hIC
);
631 // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting?
632 // A) Some intermediate indexes are bad while subsequent ones may be good. Its weird, I know.
635 // extension -> file type
636 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
638 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
640 //low level functions - iterate through the database
644 for (long i
= 1; i
<= m_lCount
; ++i
)
646 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
650 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
651 if ( sCurrentExtension
.Right(sCurrentExtension
.length() - 1) == e
) // entry has period in it
653 wxFileType
* pFileType
= new wxFileType();
654 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
664 // MIME type -> extension -> file type
665 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
667 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
672 // low level functions - iterate through the database
673 for (long i
= 1; i
<= m_lCount
; ++i
)
675 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
676 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
680 if ( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
682 wxFileType
* pFileType
= new wxFileType();
683 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
693 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
695 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
698 long pos
, lStartCount
;
700 // low level functions - iterate through the database
701 lStartCount
= (long) mimetypes
.GetCount();
702 for (long i
= 1; i
<= m_lCount
; ++i
)
704 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
705 if ( status
== noErr
)
706 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
709 return mimetypes
.GetCount() - lStartCount
;
712 pascal OSStatus
MoreProcGetProcessTypeSignature(
713 const ProcessSerialNumberPtr pPSN
,
714 OSType
*pProcessType
,
717 OSStatus anErr
= noErr
;
718 ProcessInfoRec infoRec
;
719 ProcessSerialNumber localPSN
;
721 infoRec
.processInfoLength
= sizeof(ProcessInfoRec
);
722 infoRec
.processName
= NULL
;
724 infoRec
.processAppSpec
= NULL
;
729 localPSN
.highLongOfPSN
= 0;
730 localPSN
.lowLongOfPSN
= kCurrentProcess
;
737 anErr
= GetProcessInformation(&localPSN
, &infoRec
);
740 *pProcessType
= infoRec
.processType
;
741 *pCreator
= infoRec
.processSignature
;
749 // TODO: clean this up, its messy
754 #include "wx/osx/core/cfstring.h"
756 #define wxCF_RELEASE true
757 #define wxCF_RETAIN false
759 // ----------------------------------------------------------------------------
761 // ----------------------------------------------------------------------------
766 wxCFDictionary(CFTypeRef ref
)
768 m_cfmdRef
= (CFMutableDictionaryRef
) ref
;
771 wxCFDictionary(CFIndex cfiSize
= 0)
773 CFDictionaryKeyCallBacks kcbs
;
774 CFDictionaryValueCallBacks vcbs
;
775 BuildKeyCallbacks(&kcbs
);
776 BuildValueCallbacks(&vcbs
);
778 m_cfmdRef
= CFDictionaryCreateMutable(
779 kCFAllocatorDefault
, cfiSize
, &kcbs
, &vcbs
);
788 CFRelease(m_cfmdRef
);
791 static const void* RetainProc(CFAllocatorRef
, const void* v
)
792 { return (const void*) CFRetain(v
); }
794 static void ReleaseProc(CFAllocatorRef
, const void* v
)
797 void MakeMutable(CFIndex cfiSize
= 0)
799 CFDictionaryRef oldref
= (CFDictionaryRef
) m_cfmdRef
;
801 m_cfmdRef
= CFDictionaryCreateMutableCopy(
802 kCFAllocatorDefault
, cfiSize
, oldref
);
807 void BuildKeyCallbacks(CFDictionaryKeyCallBacks
* pCbs
)
810 pCbs
->retain
= RetainProc
;
811 pCbs
->release
= ReleaseProc
;
812 pCbs
->copyDescription
= NULL
;
817 void BuildValueCallbacks(CFDictionaryValueCallBacks
* pCbs
)
820 pCbs
->retain
= RetainProc
;
821 pCbs
->release
= ReleaseProc
;
822 pCbs
->copyDescription
= NULL
;
826 operator CFTypeRef () const
827 { return (CFTypeRef
)m_cfmdRef
; }
829 CFDictionaryRef
GetCFDictionary() const
830 { return (CFDictionaryRef
)m_cfmdRef
; }
832 CFMutableDictionaryRef
GetCFMutableDictionary()
833 { return (CFMutableDictionaryRef
) m_cfmdRef
; }
835 CFTypeRef
operator [] (CFTypeRef cftEntry
) const
838 return (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
)m_cfmdRef
, cftEntry
);
841 CFIndex
GetCount() const
844 return CFDictionaryGetCount((CFDictionaryRef
)m_cfmdRef
);
847 void Add(CFTypeRef cftKey
, CFTypeRef cftValue
)
850 wxASSERT(Exists(cftKey
) == false);
851 CFDictionaryAddValue(m_cfmdRef
, cftKey
, cftValue
);
854 void Remove(CFTypeRef cftKey
)
857 wxASSERT(Exists(cftKey
));
858 CFDictionaryRemoveValue(m_cfmdRef
, cftKey
);
861 void Set(CFTypeRef cftKey
, CFTypeRef cftValue
)
864 wxASSERT(Exists(cftKey
));
865 CFDictionarySetValue(m_cfmdRef
, cftKey
, cftValue
);
868 bool Exists(CFTypeRef cftKey
) const
871 return CFDictionaryContainsKey((CFDictionaryRef
)m_cfmdRef
, cftKey
);
875 { return m_cfmdRef
!= NULL
; }
878 { return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmdRef
) == CFDictionaryGetTypeID(); }
880 void PrintOut(wxString
& sMessage
)
882 PrintOutDictionary(sMessage
, m_cfmdRef
);
885 static void PrintOutDictionary(wxString
& sMessage
, CFDictionaryRef cfdRef
)
887 CFIndex cfiCount
= CFDictionaryGetCount(cfdRef
);
888 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
889 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
891 CFDictionaryGetKeysAndValues(cfdRef
, pKeys
, pValues
);
893 for (CFIndex i
= 0; i
< cfiCount
; ++i
)
895 wxString sKey
= wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(pKeys
[i
]))).AsString();
896 wxString sValue
= wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(pValues
[i
]))).AsString();
899 wxString::Format(wxT("[{#%d} Key : %s]"), (int) i
,
902 PrintOutType(sMessage
, sKey
, pKeys
[i
]);
905 wxString::Format(wxT("\n\t[Value : %s]"),
908 PrintOutType(sMessage
, sValue
, pValues
[i
]);
910 sMessage
<< wxT("\n");
917 static void PrintOutArray(wxString
& sMessage
, CFArrayRef cfaRef
)
919 for (CFIndex i
= 0; i
< CFArrayGetCount(cfaRef
); ++i
)
921 wxString sValue
= wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(
922 CFArrayGetValueAtIndex(cfaRef
, i
)
926 wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i
,
929 PrintOutType(sMessage
, sValue
, CFArrayGetValueAtIndex(cfaRef
, i
));
933 static void PrintOutType(wxString
& sMessage
, const wxString
& sValue
, CFTypeRef cfRef
)
935 sMessage
<< wxT(" {");
937 if (sValue
== wxT("CFString"))
939 sMessage
<< wxCFStringRef(wxCFRetain((CFStringRef
)cfRef
)).AsString();
941 else if (sValue
== wxT("CFNumber"))
944 CFNumberGetValue((CFNumberRef
)cfRef
, kCFNumberIntType
, &nOut
);
947 else if (sValue
== wxT("CFDictionary"))
949 PrintOutDictionary(sMessage
, (CFDictionaryRef
)cfRef
);
951 else if (sValue
== wxT("CFArray"))
953 PrintOutArray(sMessage
, (CFArrayRef
)cfRef
);
955 else if (sValue
== wxT("CFBoolean"))
957 sMessage
<< (cfRef
== kCFBooleanTrue
? wxT("true") : wxT("false"));
959 else if (sValue
== wxT("CFURL"))
961 sMessage
<< wxCFStringRef(CFURLCopyPath((CFURLRef
) cfRef
)).AsString();
965 sMessage
<< wxT("*****UNKNOWN TYPE******");
968 sMessage
<< wxT("} ");
975 CFTypeRef
WriteAsXML()
977 return CFPropertyListCreateXMLData(kCFAllocatorDefault
, m_cfmdRef
);
980 bool ReadAsXML(CFTypeRef cfData
, wxString
* pErrorMsg
= NULL
)
983 CFStringRef cfsError
=NULL
;
984 m_cfmdRef
= (CFMutableDictionaryRef
) CFPropertyListCreateFromXMLData(
987 kCFPropertyListMutableContainersAndLeaves
,
992 *pErrorMsg
= wxCFStringRef(cfsError
).AsString();
997 return m_cfmdRef
!= NULL
;
1001 CFMutableDictionaryRef m_cfmdRef
;
1004 // ----------------------------------------------------------------------------
1006 // ----------------------------------------------------------------------------
1011 wxCFArray(CFTypeRef ref
)
1013 m_cfmaRef
= (CFMutableArrayRef
)ref
;
1016 wxCFArray(CFIndex cfiSize
= 0) : m_cfmaRef(NULL
)
1017 { Create(cfiSize
); }
1022 void MakeMutable(CFIndex cfiSize
= 0)
1024 wxASSERT(IsValid());
1026 CFMutableArrayRef oldref
= m_cfmaRef
;
1027 m_cfmaRef
= CFArrayCreateMutableCopy(
1028 kCFAllocatorDefault
,
1030 (CFArrayRef
)oldref
);
1034 void BuildCallbacks(CFArrayCallBacks
* pCbs
)
1037 pCbs
->retain
= RetainProc
;
1038 pCbs
->release
= ReleaseProc
;
1039 pCbs
->copyDescription
= NULL
;
1043 void Create(CFIndex cfiSize
= 0)
1046 CFArrayCallBacks cb
;
1047 BuildCallbacks(&cb
);
1049 m_cfmaRef
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiSize
, &cb
);
1053 { if (m_cfmaRef
) CFRelease(m_cfmaRef
); }
1055 static const void* RetainProc(CFAllocatorRef
, const void* v
)
1056 { return (const void*) CFRetain(v
); }
1058 static void ReleaseProc(CFAllocatorRef
, const void* v
)
1061 operator CFTypeRef () const
1062 { return (CFTypeRef
)m_cfmaRef
; }
1064 CFArrayRef
GetCFArray() const
1065 { return (CFArrayRef
)m_cfmaRef
; }
1067 CFMutableArrayRef
GetCFMutableArray()
1068 { return (CFMutableArrayRef
) m_cfmaRef
; }
1070 CFTypeRef
operator [] (CFIndex cfiIndex
) const
1072 wxASSERT(IsValid());
1073 return (CFTypeRef
) CFArrayGetValueAtIndex((CFArrayRef
)m_cfmaRef
, cfiIndex
);
1078 wxASSERT(IsValid());
1079 return CFArrayGetCount((CFArrayRef
)m_cfmaRef
);
1082 void Add(CFTypeRef cftValue
)
1084 wxASSERT(IsValid());
1085 CFArrayAppendValue(m_cfmaRef
, cftValue
);
1088 void Remove(CFIndex cfiIndex
)
1090 wxASSERT(IsValid());
1091 wxASSERT(cfiIndex
< GetCount());
1092 CFArrayRemoveValueAtIndex(m_cfmaRef
, cfiIndex
);
1095 void Set(CFIndex cfiIndex
, CFTypeRef cftValue
)
1097 wxASSERT(IsValid());
1098 wxASSERT(cfiIndex
< GetCount());
1099 CFArraySetValueAtIndex(m_cfmaRef
, cfiIndex
, cftValue
);
1103 { return m_cfmaRef
!= NULL
; }
1105 bool IsValid() const
1107 return IsOk() && CFGetTypeID((CFTypeRef
)m_cfmaRef
) == CFArrayGetTypeID();
1111 void MakeValidXML();
1115 CFMutableArrayRef m_cfmaRef
;
1118 // ----------------------------------------------------------------------------
1120 // ----------------------------------------------------------------------------
1125 wxCFNumber(int nValue
)
1127 m_cfnRef
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &nValue
);
1130 wxCFNumber(CFTypeRef ref
) : m_cfnRef((CFNumberRef
)ref
)
1134 virtual ~wxCFNumber()
1137 CFRelease(m_cfnRef
);
1140 operator CFTypeRef() const
1141 { return (CFTypeRef
) m_cfnRef
; }
1146 CFNumberGetValue( m_cfnRef
, kCFNumberIntType
, &nOut
);
1152 { return m_cfnRef
!= NULL
; }
1155 CFNumberRef m_cfnRef
;
1158 // ----------------------------------------------------------------------------
1160 // ----------------------------------------------------------------------------
1165 wxCFURL(CFTypeRef ref
= NULL
) : m_cfurlRef((CFURLRef
)ref
)
1169 wxCFURL(const wxCFStringRef
& URLString
, CFTypeRef BaseURL
= NULL
)
1171 Create(URLString
, BaseURL
);
1174 void Create(const wxCFStringRef
& URLString
, CFTypeRef BaseURL
= NULL
)
1176 m_cfurlRef
= CFURLCreateWithString(
1177 kCFAllocatorDefault
,
1178 (CFStringRef
)(CFTypeRef
)URLString
,
1179 (CFURLRef
) BaseURL
);
1185 CFRelease(m_cfurlRef
);
1188 wxString
BuildWXString()
1190 return wxCFStringRef(CFURLCopyPath(m_cfurlRef
)).AsString();
1193 operator CFTypeRef() const
1194 { return (CFTypeRef
)m_cfurlRef
; }
1197 { return m_cfurlRef
!= NULL
; }
1200 CFURLRef m_cfurlRef
;
1203 // ----------------------------------------------------------------------------
1205 // ----------------------------------------------------------------------------
1207 #define wxCFDATA_RELEASEBUFFER 1
1212 wxCFData(CFTypeRef ref
) : m_cfdaRef((CFDataRef
)ref
)
1216 wxCFData(const UInt8
* pBytes
, CFIndex len
, bool bKeep
= wxCFDATA_RELEASEBUFFER
)
1218 if (bKeep
== wxCFDATA_RELEASEBUFFER
)
1220 m_cfdaRef
= CFDataCreateWithBytesNoCopy
1221 (kCFAllocatorDefault
, pBytes
, len
, kCFAllocatorDefault
);
1225 m_cfdaRef
= CFDataCreate(kCFAllocatorDefault
, pBytes
, len
);
1232 CFRelease(m_cfdaRef
);
1235 const UInt8
* GetValue()
1236 { return CFDataGetBytePtr(m_cfdaRef
); }
1239 { return CFDataGetLength(m_cfdaRef
); }
1241 operator CFTypeRef() const
1242 { return (CFTypeRef
)m_cfdaRef
; }
1245 { return m_cfdaRef
!= NULL
; }
1248 CFDataRef m_cfdaRef
;
1251 void wxCFDictionary::MakeValidXML()
1253 CFIndex cfiCount
= GetCount();
1254 CFTypeRef
* pKeys
= new CFTypeRef
[cfiCount
];
1255 CFTypeRef
* pValues
= new CFTypeRef
[cfiCount
];
1257 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1259 // for plist xml format, all dictionary keys must be cfstrings and
1260 // no values in the dictionary or subkeys/values can be NULL;
1261 // additionally, CFURLs are not allowed
1262 for (CFIndex i
= 0; i
< cfiCount
; ++i
)
1264 // must be an array, dictionary, string, bool, or int and cannot be null
1265 // and dictionaries can only contain cfstring keys
1266 CFTypeRef cfRef
= pValues
[i
];
1268 CFGetTypeID(pKeys
[i
]) != CFStringGetTypeID() ||
1276 pKeys
= new CFTypeRef
[cfiCount
];
1277 pValues
= new CFTypeRef
[cfiCount
];
1278 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1280 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1283 wxCFArray
cfaCurrent(cfRef
);
1284 cfaCurrent
.MakeMutable();
1285 cfaCurrent
.MakeValidXML();
1286 Set(pKeys
[i
], cfaCurrent
);
1288 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1291 wxCFDictionary
cfdCurrent(cfRef
);
1292 cfdCurrent
.MakeMutable();
1293 cfdCurrent
.MakeValidXML();
1294 Set(pKeys
[i
], cfdCurrent
);
1296 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1297 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1298 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1305 pKeys
= new CFTypeRef
[cfiCount
];
1306 pValues
= new CFTypeRef
[cfiCount
];
1307 CFDictionaryGetKeysAndValues(m_cfmdRef
, pKeys
, pValues
);
1315 void wxCFArray::MakeValidXML()
1317 for (CFIndex i
= 0; i
< GetCount(); ++i
)
1319 //must be an array, dictionary, string, bool, or int and cannot be null
1320 //and dictionaries can only contain cfstring keys
1321 CFTypeRef cfRef
= (*this)[i
];
1327 else if (CFGetTypeID(cfRef
) == CFArrayGetTypeID())
1330 wxCFArray
cfaCurrent(cfRef
);
1331 cfaCurrent
.MakeMutable();
1332 cfaCurrent
.MakeValidXML();
1335 else if (CFGetTypeID(cfRef
) == CFDictionaryGetTypeID())
1338 wxCFDictionary
cfdCurrent(cfRef
);
1339 cfdCurrent
.MakeMutable();
1340 cfdCurrent
.MakeValidXML();
1343 else if ( CFGetTypeID(cfRef
) != CFStringGetTypeID() &&
1344 CFGetTypeID(cfRef
) != CFNumberGetTypeID() &&
1345 CFGetTypeID(cfRef
) != CFBooleanGetTypeID() )
1361 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
1363 bool bInfoSuccess
= false;
1365 const wxArrayString
& asExtensions
= ftInfo
.GetExtensions();
1366 size_t dwFoundIndex
= 0;
1367 if (!asExtensions
.GetCount())
1369 wxLogDebug(wxT("Must have extension to associate with"));
1372 // Find and write to Info.plist in main bundle (note that some other
1373 // apps have theirs named differently, i.e. IE's is named Info-macos.plist
1374 // some apps (non-wx) use the 'plst' resource instead
1375 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1378 UInt32 dwBundleType
, dwBundleCreator
;
1379 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1381 // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too),
1382 // which will give us the incorrect info.plist path
1383 // otherwise it will be 'APPL', or in the case of a framework, 'FMWK'
1384 if (dwBundleType
== 'APPL')
1386 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1387 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1389 // sInfoPath << wxT("file://");
1390 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1391 sInfoPath
<< wxT("Contents/Info.plist");
1393 // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1394 wxCFDictionary cfdInfo
;
1395 bool bInfoOpenSuccess
= false;
1397 if (indictfile
.Open(sInfoPath
, wxFile::read
))
1399 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1400 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1401 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1402 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1404 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1405 if (!bInfoOpenSuccess
)
1412 if (bInfoOpenSuccess
)
1414 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1416 wxCFArray
cfaDocTypes( wxCFRetain( cfdInfo
[ wxCFStringRef(wxT("CFBundleDocumentTypes")) ] ) );
1418 bool bAddDocTypesArrayToDictionary
= !cfaDocTypes
.IsOk();
1419 if (bAddDocTypesArrayToDictionary
)
1420 cfaDocTypes
.Create();
1422 cfaDocTypes
.MakeMutable( cfaDocTypes
.GetCount() + 1 );
1424 bool bEntryFound
= false;
1426 // search for duplicates
1428 for (i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1430 wxCFDictionary
cfdDocTypeEntry( wxCFRetain( cfaDocTypes
[i
] ) );
1432 // A lot of apps don't support MIME types for some reason
1433 // so we go by extensions only
1434 wxCFArray
cfaExtensions( wxCFRetain( cfdDocTypeEntry
[ wxCFStringRef(wxT("CFBundleTypeExtensions")) ] ) );
1436 if (!cfaExtensions
.IsOk())
1439 for (CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1441 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1443 if (asExtensions
[iWXExt
] ==
1444 wxCFStringRef( wxCFRetain( (CFStringRef
) cfaExtensions
[iExt
] ) ).AsString())
1447 dwFoundIndex
= iWXExt
;
1451 } //end of wxstring array
1455 } //end for cf array
1459 } //end for doctypes
1461 wxCFDictionary cfdNewEntry
;
1463 if (!ftInfo
.GetDescription().empty())
1465 cfdNewEntry
.Add( wxCFStringRef(wxT("CFBundleTypeName")),
1466 wxCFStringRef(ftInfo
.GetDescription()) );
1469 if (!ftInfo
.GetIconFile().empty())
1471 cfdNewEntry
.Add( wxCFStringRef(wxT("CFBundleTypeIconFile")),
1472 wxCFStringRef(ftInfo
.GetIconFile()) );
1475 wxCFArray cfaOSTypes
;
1476 wxCFArray cfaExtensions
;
1477 wxCFArray cfaMimeTypes
;
1479 //OSTypes is a cfarray of four-char-codes - '****' for unrestricted
1480 cfaOSTypes
.Add( wxCFStringRef(wxT("****")) );
1481 cfdNewEntry
.Add( wxCFStringRef(wxT("CFBundleTypeOSTypes")), cfaOSTypes
);
1483 //'*' for unrestricted
1484 if (ftInfo
.GetExtensionsCount() != 0)
1486 for (size_t iExtension
= 0; iExtension
< ftInfo
.GetExtensionsCount(); ++iExtension
)
1488 cfaExtensions
.Add( wxCFStringRef( asExtensions
[iExtension
] ) );
1491 cfdNewEntry
.Add( wxCFStringRef(wxT("CFBundleTypeExtensions")), cfaExtensions
);
1494 if (!ftInfo
.GetMimeType().empty())
1496 cfaMimeTypes
.Add( wxCFStringRef(ftInfo
.GetMimeType()) );
1497 cfdNewEntry
.Add( wxCFStringRef(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes
);
1500 // Editor - can perform all actions
1501 // Viewer - all actions except manipulation/saving
1502 // None - can perform no actions
1503 cfdNewEntry
.Add( wxCFStringRef(wxT("CFBundleTypeRole")), wxCFStringRef(wxT("Editor")) );
1505 // Is application bundled?
1506 cfdNewEntry
.Add( wxCFStringRef(wxT("LSTypeIsPackage")), kCFBooleanTrue
);
1509 cfaDocTypes
.Set(i
, cfdNewEntry
);
1511 cfaDocTypes
.Add(cfdNewEntry
);
1513 // set the doc types array in the muted dictionary
1514 if (bAddDocTypesArrayToDictionary
)
1515 cfdInfo
.Add(wxCFStringRef(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1517 cfdInfo
.Set(wxCFStringRef(wxT("CFBundleDocumentTypes")), cfaDocTypes
);
1519 cfdInfo
.MakeValidXML();
1522 if (outdictfile
.Open(sInfoPath
, wxFile::write
))
1524 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1525 if (cfdaInfo
.IsOk())
1527 if (outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1528 (wxFileOffset
)cfdaInfo
.GetCount())
1530 wxLogDebug(wxT("error in writing to file"));
1534 bInfoSuccess
= true;
1536 //#if defined(__DARWIN__)
1537 // //force launch services to update its database for the finder
1538 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1539 // if (status != noErr)
1541 // wxLogDebug(wxT("LSRegisterURL Failed."));
1545 outdictfile
.Close();
1549 outdictfile
.Close();
1550 wxLogDebug(wxT("Could not read in new dictionary"));
1555 wxLogDebug(wxString(wxT("Could not open [")) +
1556 sInfoPath
+ wxT("] for writing."));
1561 wxLogDebug(wxT("No info dictionary in main bundle"));
1566 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1571 wxLogDebug(wxT("No main bundle"));
1577 // on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
1578 // or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
1579 // the app's signature somehow...
1581 OSType processType
, creator
;
1582 OSStatus status
= MoreProcGetProcessTypeSignature(NULL
, &processType
, &creator
);
1584 if (status
== noErr
)
1586 Str255 psCreatorName
;
1589 status
= FindApplication(creator
, false, psCreatorName
, &dummySpec
);
1592 status
= LSFindApplicationForInfo( creator
, NULL
, NULL
, &fsref
,NULL
);
1594 status
= FSGetCatalogInfo(&fsref
, kFSCatInfoNone
, NULL
, &name
, NULL
, NULL
);
1595 CFStringRef str
= FSCreateStringFromHFSUniStr( 0 , &name
);
1596 CFStringGetPascalString(str
, psCreatorName
, 256, CFStringGetSystemEncoding());
1600 if (status
== noErr
)
1602 //get the file type if it exists -
1603 //if it really does then modify the database then save it,
1604 //otherwise we need to create a whole new entry
1605 wxFileType
* pFileType
= GetFileTypeFromExtension(asExtensions
[dwFoundIndex
]);
1609 ICGetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1610 pFileType
->m_impl
->m_lIndex
, &entry
);
1612 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1613 entry
.fileCreator
= creator
;
1615 status
= ICSetMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1616 pFileType
->m_impl
->m_lIndex
, &entry
);
1619 if (status
== noErr
)
1623 //kICAttrNoChange means we don't care about attributes such as
1624 //locking in the database
1625 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1626 // kICAttrNoChange, (Handle) m_hDatabase);
1627 // if (status == noErr)
1628 // return pFileType;
1631 // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed.")));
1636 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICSetMapEntry failed.")));
1639 // failure - cleanup
1644 // TODO: Maybe force all 3 of these to be non-empty?
1645 Str255 psExtension
, psMimeType
, psDescription
;
1647 wxMacStringToPascal(wxString(wxT(".")) + ftInfo
.GetExtensions()[0], psExtension
);
1648 wxMacStringToPascal(ftInfo
.GetMimeType(), psMimeType
);
1649 wxMacStringToPascal(ftInfo
.GetDescription(), psDescription
);
1651 Str255 psPostCreatorName
;
1652 wxMacStringToPascal(wxEmptyString
, psPostCreatorName
);
1654 //add the entry to the database
1656 entry
.totalLength
= sizeof(ICMapEntry
);
1657 entry
.fixedLength
= kICMapFixedLength
;
1659 entry
.fileType
= 0; //TODO: File type?
1660 entry
.fileCreator
= creator
;
1661 entry
.postCreator
= 0;
1662 entry
.flags
= kICMapDataForkBit
; //TODO: Maybe resource is valid by default too?
1663 PLstrcpy( entry
.extension
, psExtension
) ;
1664 memcpy(entry
.creatorAppName
, psCreatorName
, sizeof(Str255
));
1665 memcpy(entry
.postAppName
, psPostCreatorName
, sizeof(Str255
));
1666 memcpy(entry
.MIMEType
, psMimeType
, sizeof(Str255
));
1667 memcpy(entry
.entryName
, psDescription
, sizeof(Str255
));
1669 status
= ICAddMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &entry
);
1670 if (status
== noErr
)
1672 return GetFileTypeFromExtension(ftInfo
.GetMimeType());
1674 // kICAttrNoChange means we don't care about attributes such as
1675 // locking in the database
1676 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1677 // kICAttrNoChange, (Handle) m_hDatabase);
1679 // return the entry in the database if successful
1680 // if (status == noErr)
1681 // return GetFileTypeFromExtension(ftInfo.GetMimeType());
1684 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1689 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICAppMapEntry failed.")));
1692 } // end if FindApplcation was successful
1695 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("FindApplication failed.")));
1697 } // end if it could obtain app's signature
1700 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("GetProcessSignature failed.")));
1707 wxMimeTypesManagerImpl::Unassociate(wxFileType
*pFileType
)
1709 wxASSERT(pFileType
);
1710 bool bInfoSuccess
= false;
1712 wxArrayString asExtensions
;
1713 pFileType
->GetExtensions(asExtensions
);
1715 if (!asExtensions
.GetCount())
1717 wxLogDebug(wxT("Must have extension to disassociate"));
1721 // Find and write to Info.plist in main bundle (note that some other
1722 // apps have theirs named differently, i.e. IE's is named Info-macos.plist
1723 // some apps (non-wx) use the 'plst' resource instead
1724 CFBundleRef cfbMain
= CFBundleGetMainBundle();
1727 UInt32 dwBundleType
, dwBundleCreator
;
1728 CFBundleGetPackageInfo(cfbMain
, &dwBundleType
, &dwBundleCreator
);
1730 // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too),
1731 // which will give us the incorrect info.plist path
1732 // otherwise it will be 'APPL', or in the case of a framework, 'FMWK'
1733 if (dwBundleType
== 'APPL')
1736 wxCFURL
cfurlBundleLoc((CFTypeRef
)CFBundleCopyBundleURL(cfbMain
));
1737 // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain));
1739 // sInfoPath << wxT("file://");
1740 sInfoPath
<< cfurlBundleLoc
.BuildWXString();
1741 sInfoPath
<< wxT("Contents/Info.plist");
1743 // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN );
1744 wxCFDictionary cfdInfo
;
1745 bool bInfoOpenSuccess
= false;
1747 if (indictfile
.Open(sInfoPath
, wxFile::read
))
1749 CFIndex cfiBufLen
= (CFIndex
) indictfile
.Length();
1750 const UInt8
* pBuffer
= new UInt8
[cfiBufLen
];
1751 indictfile
.Read((void*)pBuffer
, cfiBufLen
);
1752 wxCFData
cfdaInDict(pBuffer
, cfiBufLen
);
1754 bInfoOpenSuccess
= cfdInfo
.ReadAsXML(cfdaInDict
, &sError
);
1755 if (!bInfoOpenSuccess
)
1762 if (bInfoOpenSuccess
)
1764 cfdInfo
.MakeMutable( cfdInfo
.GetCount() + 1 );
1766 wxCFArray
cfaDocTypes( wxCFRetain( cfdInfo
[ wxCFStringRef(wxT("CFBundleDocumentTypes")) ] ) );
1768 if (cfaDocTypes
.IsOk())
1770 bool bEntryFound
= false;
1772 //search for duplicate
1774 for (i
= 0; i
< cfaDocTypes
.GetCount(); ++i
)
1776 wxCFDictionary
cfdDocTypeEntry( wxCFRetain( cfaDocTypes
[i
] ) );
1778 //A lot of apps dont do to mime types for some reason
1779 //so we go by extensions only
1780 wxCFArray
cfaExtensions( wxCFRetain( cfdDocTypeEntry
[ wxCFStringRef(wxT("CFBundleTypeExtensions")) ]) );
1782 if (!cfaExtensions
.IsOk())
1785 for (CFIndex iExt
= 0; iExt
< cfaExtensions
.GetCount(); ++iExt
)
1787 for (size_t iWXExt
= 0; iWXExt
< asExtensions
.GetCount(); ++iWXExt
)
1789 if (asExtensions
[iWXExt
] ==
1790 wxCFStringRef( wxCFRetain( (CFStringRef
) cfaExtensions
[iExt
] ) ).AsString())
1793 cfaDocTypes
.Remove(i
);
1794 cfdInfo
.Set( wxCFStringRef(wxT("CFBundleDocumentTypes")) , cfaDocTypes
);
1797 } //end of wxstring array
1801 } //end for cf array
1809 cfdInfo
.MakeValidXML();
1812 if (outdictfile
.Open(sInfoPath
, wxFile::write
))
1814 wxCFData
cfdaInfo(cfdInfo
.WriteAsXML());
1815 if (cfdaInfo
.IsOk())
1817 if (outdictfile
.Write(cfdaInfo
.GetValue(), cfdaInfo
.GetCount()) !=
1818 (wxFileOffset
)cfdaInfo
.GetCount())
1820 wxLogDebug(wxT("error in writing to file"));
1824 bInfoSuccess
= true;
1826 //#if defined(__DARWIN__)
1827 // //force launch services to update its database for the finder
1828 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1829 // if (status != noErr)
1831 // wxLogDebug(wxT("LSRegisterURL Failed."));
1835 outdictfile
.Close();
1839 outdictfile
.Close();
1840 wxLogDebug(wxT("Could not read in new dictionary"));
1846 wxString(wxT("Could not open [")) +
1847 sInfoPath
+ wxT("] for writing."));
1852 wxLogDebug(wxT("Entry not found to remove"));
1855 wxCFDictionary::PrintOutArray(sPrintOut
, (CFArrayRef
)(CFTypeRef
)cfaDocTypes
);
1856 wxLogDebug(sPrintOut
);
1858 for (size_t i
= 0; i
< asExtensions
.GetCount(); ++i
)
1860 wxLogDebug(asExtensions
[i
]);
1866 wxLogDebug(wxT("No doc types array found"));
1867 wxString sPrintOut
; cfdInfo
.PrintOut(sPrintOut
); wxLogDebug(sPrintOut
);
1872 wxLogDebug(wxT("No info dictionary in main bundle"));
1877 wxLogDebug(wxT("Can only call associate from bundled app within XXX.app"));
1882 wxLogDebug(wxT("No main bundle"));
1888 // this should be as easy as removing the entry from the database
1889 // and then saving the database
1890 OSStatus status
= ICDeleteMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
,
1891 pFileType
->m_impl
->m_lIndex
);
1893 if (status
== noErr
)
1897 //kICAttrNoChange means we don't care about attributes such as
1898 //locking in the database
1899 // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping,
1900 // kICAttrNoChange, (Handle) m_hDatabase);
1902 // if (status == noErr)
1908 // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed.")));
1913 wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__
, wxT("ICDeleteMapEntry failed.")));
1920 CFWriteStreamRef cfwsInfo
= CFWriteStreamCreateWithFile(
1921 kCFAllocatorDefault
,
1922 (CFURLRef
) (CFTypeRef
)cfurlInfoLoc
);
1927 Boolean bOpened
= CFWriteStreamOpen(cfwsInfo
);
1930 CFStringRef cfsError
;
1931 CFIndex cfiWritten
= CFPropertyListWriteToStream((CFPropertyListRef
)(CFTypeRef
)cfdInfo
,
1933 kCFPropertyListXMLFormat_v1_0
, //100
1935 if (cfsError
&& cfiWritten
== 0)
1937 wxLogDebug(wxCFStringRef(cfsError
).BuildWXString());
1939 cfdInfo
.PrintOut(sMessage
);
1940 wxLogDebug(sMessage
);
1944 bInfoSuccess
= true;
1945 //#if defined(__DARWIN__)
1946 // //force launch services to update its database for the finder
1947 // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true);
1948 // if (status != noErr)
1950 // wxLogDebug(wxT("LSRegisterURL Failed."));
1955 CFWriteStreamClose(cfwsInfo
);
1958 #endif //wxUSE_MIMETYPE