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 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "mimetype.h"
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
24 #include "wx/string.h"
36 #include "wx/dynarray.h"
37 #include "wx/confbase.h"
39 #include "wx/mac/mimetype.h"
40 #include "wx/mac/private.h" //wxMacMakeStringFromPascal
42 // other standard headers
44 #include <InternetConfig.h> //For mime types
47 // On the mac there are two ways to open a file - one is through apple events and the
48 // finder, another is through mime types.
50 // So, really there are two ways to implement wxFileType...
52 // Mime types are only available on OS 8.1+ through the InternetConfig API
54 // Much like the old-style file manager, it has 3 levels of flexibility for its methods -
55 // Low - which means you have to iterate yourself through the mime database
56 // Medium - which lets you sort of cache the database if you want to use lowlevel functions
57 // High - which requires access to the database every time
59 // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
60 // and mid-level functions
62 // TODO: Should we call ICBegin/ICEnd? Then where?
65 // in case we're compiling in non-GUI mode
66 class WXDLLEXPORT wxIcon
;
68 bool wxFileTypeImpl::SetCommand(const wxString
& cmd
, const wxString
& verb
, bool overwriteprompt
)
73 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int index
)
78 bool wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
79 const wxFileType::MessageParameters
& params
) const
81 wxString cmd
= GetCommand(wxT("open"));
83 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
85 return !openCmd
->empty();
89 wxFileTypeImpl::GetPrintCommand(wxString
*printCmd
,
90 const wxFileType::MessageParameters
& params
)
93 wxString cmd
= GetCommand(wxT("print"));
95 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
97 return !printCmd
->empty();
100 /* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
102 /* IsRemoteVolume can be used to find out if the
103 volume referred to by vRefNum is a remote volume
104 located somewhere on a network. the volume's attribute
105 flags (copied from the GetVolParmsInfoBuffer structure)
106 are returned in the longword pointed to by vMAttrib. */
107 OSErr
IsRemoteVolume(short vRefNum
, Boolean
*isRemote
, long *vMAttrib
) {
108 HParamBlockRec volPB
;
109 GetVolParmsInfoBuffer volinfo
;
111 volPB
.ioParam
.ioVRefNum
= vRefNum
;
112 volPB
.ioParam
.ioNamePtr
= NULL
;
113 volPB
.ioParam
.ioBuffer
= (Ptr
) &volinfo
;
114 volPB
.ioParam
.ioReqCount
= sizeof(volinfo
);
115 err
= PBHGetVolParmsSync(&volPB
);
117 *isRemote
= (volinfo
.vMServerAdr
!= 0);
118 *vMAttrib
= volinfo
.vMAttrib
;
124 /* BuildVolumeList fills the array pointed to by vols with
125 a list of the currently mounted volumes. If includeRemote
126 is true, then remote server volumes will be included in
127 the list. When remote server volumes are included in the
128 list, they will be added to the end of the list. On entry,
129 *count should contain the size of the array pointed to by
130 vols. On exit, *count will be set to the number of id numbers
131 placed in the array. If vMAttribMask is non-zero, then
132 only volumes with matching attributes are added to the
133 list of volumes. bits in the vMAttribMask should use the
134 same encoding as bits in the vMAttrib field of
135 the GetVolParmsInfoBuffer structure. */
136 OSErr
BuildVolumeList(Boolean includeRemote
, short *vols
,
137 long *count
, long vMAttribMask
) {
138 HParamBlockRec volPB
;
141 long nlocal
, nremote
;
144 /* set up and check parameters */
145 volPB
.volumeParam
.ioNamePtr
= NULL
;
146 nlocal
= nremote
= 0;
147 if (*count
== 0) return noErr
;
149 /* iterate through volumes */
150 for (volPB
.volumeParam
.ioVolIndex
= 1;
151 PBHGetVInfoSync(&volPB
) == noErr
;
152 volPB
.volumeParam
.ioVolIndex
++) {
154 /* skip remote volumes, if necessary */
155 err
= IsRemoteVolume(volPB
.volumeParam
.ioVRefNum
, &isRemote
, &vMAttrib
);
156 if (err
!= noErr
) goto bail
;
157 if ( ( includeRemote
|| ! isRemote
)
158 && (vMAttrib
& vMAttribMask
) == vMAttribMask
) {
160 /* add local volumes at the front, remote
161 volumes at the end */
163 vols
[nlocal
+ nremote
++] = volPB
.volumeParam
.ioVRefNum
;
166 BlockMoveData(vols
+nlocal
, vols
+nlocal
+1,
167 nremote
*sizeof(short));
168 vols
[nlocal
++] = volPB
.volumeParam
.ioVRefNum
;
172 if ((nlocal
+ nremote
) >= *count
) break;
176 *count
= (nlocal
+ nremote
);
181 /* FindApplication iterates through mounted volumes
182 searching for an application with the given creator
183 type. If includeRemote is true, then remote volumes
184 will be searched (after local ones) for an application
185 with the creator type. */
189 /* Hacked to output to appName */
191 OSErr
FindApplication(OSType appCreator
, Boolean includeRemote
, Str255 appName
) {
192 short rRefNums
[kMaxVols
];
197 FSSpec
*appSpec
= &realappSpec
;
199 /* get a list of volumes - with desktop files */
201 err
= BuildVolumeList(includeRemote
, rRefNums
, &volCount
,
202 (1<<bHasDesktopMgr
) );
203 if (err
!= noErr
) return err
;
205 /* iterate through the list */
206 for (i
=0; i
<volCount
; i
++) {
208 /* has a desktop file? */
209 desktopPB
.ioCompletion
= NULL
;
210 desktopPB
.ioVRefNum
= rRefNums
[i
];
211 desktopPB
.ioNamePtr
= NULL
;
212 desktopPB
.ioIndex
= 0;
213 err
= PBDTGetPath(&desktopPB
);
214 if (err
!= noErr
) continue;
216 /* has the correct app?? */
217 desktopPB
.ioFileCreator
= appCreator
;
218 desktopPB
.ioNamePtr
= appName
;
219 err
= PBDTGetAPPLSync(&desktopPB
);
220 if (err
!= noErr
) continue;
222 /* make a file spec referring to it */
223 err
= FSMakeFSSpec(rRefNums
[i
],
224 desktopPB
.ioAPPLParID
, appName
,
226 if (err
!= noErr
) continue;
235 /* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
237 wxString
wxFileTypeImpl::GetCommand(const wxString
& verb
) const
240 return wxEmptyString
;
242 if(verb
== wxT("open"))
245 OSStatus status
= ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
246 (Handle
) m_manager
->m_hDatabase
,
248 wxASSERT( status
== noErr
);
250 //Technote 1002 is a godsend in launching apps :)
251 //The entry in the mimetype database only contains the app
252 //that's registered - it may not exist... we need to remap the creator
253 //type and find the right application
255 if(FindApplication(entry
.fileCreator
, false, outName
) != noErr
)
256 return wxEmptyString
;
258 //TODO: this is only partially correct -
259 //it should go to the os-specific application path folder (using wxStdPaths maybe?),
260 //then go to the bundled app and return that full path
261 return wxMacMakeStringFromPascal(outName
);
263 return wxEmptyString
;
266 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
272 OSStatus status
= ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
273 (Handle
) m_manager
->m_hDatabase
,
275 wxASSERT( status
== noErr
);
277 //entry has period in it
278 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
279 extensions
.Add( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) );
283 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
289 OSStatus status
= ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
290 (Handle
) m_manager
->m_hDatabase
,
292 wxASSERT( status
== noErr
);
294 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
298 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
312 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
314 // no such file type or no value or incorrect icon entry
318 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
324 OSStatus status
= ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
325 (Handle
) m_manager
->m_hDatabase
,
327 wxASSERT( status
== noErr
);
329 *desc
= wxString((char*)entry
.entryName
, wxConvLocal
);
333 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
, wxArrayString
* commands
,
334 const wxFileType::MessageParameters
& params
) const
336 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
340 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, const wxString
& extraDir
)
342 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
344 //start internet config - log if there's an error
345 //the second param is the signature of the application, also known
346 //as resource ID 0. However, as per some recent discussions, we may not
347 //have a signature for this app, so a generic 'APPL' which is the executable
348 //type will work for now
349 OSStatus status
= ICStart( (ICInstance
*) &m_hIC
, 'APPL');
353 wxLogSysError(wxT("Could not initialize wxMimeTypesManager!"));
359 m_hDatabase
= (void**) NewHandle(0);
360 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
362 //the database file can be corrupt (on OSX its
363 //~/Library/Preferences/com.apple.internetconfig.plist)
368 wxLogSysError(wxT("Bad Mime Database!"));
372 //obtain the number of entries in the map
373 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
374 wxASSERT( status
== noErr
);
377 void wxMimeTypesManagerImpl::ClearData()
381 DisposeHandle((Handle
)m_hDatabase
);
382 //this can return an error, but we don't really care that much about it
383 ICStop( (ICInstance
) m_hIC
);
388 // extension -> file type
389 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
391 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
394 // OSStatus status = ICMapEntriesFilename( (ICInstance) m_hIC, (Handle) m_hDatabase,
395 // (unsigned char*) e.mb_str(wxConvLocal), &entry );
397 // if (status != noErr)
398 // return NULL; //err or ext not known
399 //low level functions - iterate through the database
401 wxFileType
* pFileType
;
404 for(long i
= 1; i
<= m_lCount
; ++i
)
406 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
410 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
411 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
413 pFileType
= new wxFileType();
414 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
423 // MIME type -> extension -> file type
424 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
426 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
428 //low level functions - iterate through the database
430 wxFileType
* pFileType
;
434 for(long i
= 1; i
<= m_lCount
; ++i
)
436 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
437 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
441 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
443 pFileType
= new wxFileType();
444 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
453 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
455 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
457 //low level functions - iterate through the database
462 for(long i
= 1; i
<= m_lCount
; ++i
)
464 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
465 if( status
== noErr
)
466 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
473 wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
475 wxFAIL_MSG( _T("wxMimeTypesManagerImpl::Associate() not yet implemented") );
476 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
477 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
478 //the app's signature somehow...
484 wxMimeTypesManagerImpl::Unassociate(wxFileType
*ft
)
486 //this should be as easy as removing the entry from the database and then saving
491 #endif //wxUSE_MIMETYPE