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 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
246 (Handle
) m_manager
->m_hDatabase
,
249 //Technote 1002 is a godsend in launching apps :)
250 //The entry in the mimetype database only contains the app
251 //that's registered - it may not exist... we need to remap the creator
252 //type and find the right application
254 if(FindApplication(entry
.fileCreator
, false, outName
) != noErr
)
255 return wxEmptyString
;
257 //TODO: this is only partially correct -
258 //it should go to the os-specific application path folder (using wxStdPaths maybe?),
259 //then go to the bundled app and return that full path
260 return wxMacMakeStringFromPascal(outName
);
262 return wxEmptyString
;
265 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
271 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
272 (Handle
) m_manager
->m_hDatabase
,
275 //entry has period in it
276 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
277 extensions
.Add( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) );
281 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
287 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
288 (Handle
) m_manager
->m_hDatabase
,
291 *mimeType
= wxMacMakeStringFromPascal(entry
.MIMEType
);
295 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
309 bool wxFileTypeImpl::GetIcon(wxIconLocation
*WXUNUSED(icon
)) const
311 // no such file type or no value or incorrect icon entry
315 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
321 ICGetMapEntry( (ICInstance
) m_manager
->m_hIC
,
322 (Handle
) m_manager
->m_hDatabase
,
325 *desc
= wxString((char*)entry
.entryName
, wxConvLocal
);
329 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
* verbs
, wxArrayString
* commands
,
330 const wxFileType::MessageParameters
& params
) const
332 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
336 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, const wxString
& extraDir
)
338 wxASSERT_MSG(m_hIC
== NULL
, wxT("Already initialized wxMimeTypesManager!"));
340 //start internet config - log if there's an error
341 //the second param is the signature of the application, also known
342 //as resource ID 0. However, as per some recent discussions, we may not
343 //have a signature for this app, so a generic 'APPL' which is the executable
344 //type will work for now
345 OSStatus status
= ICStart( (ICInstance
*) &m_hIC
, 'APPL');
349 wxLogSysError(wxT("Could not initialize wxMimeTypesManager!"));
355 m_hDatabase
= (void**) NewHandle(0);
356 status
= ICFindPrefHandle( (ICInstance
) m_hIC
, kICMapping
, &attr
, (Handle
) m_hDatabase
);
358 //the database file can be corrupt (on OSX its
359 //~/Library/Preferences/com.apple.internetconfig.plist)
364 wxLogSysError(wxT("Bad Mime Database!"));
368 //obtain the number of entries in the map
369 status
= ICCountMapEntries( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, &m_lCount
);
370 wxASSERT( status
== noErr
);
373 void wxMimeTypesManagerImpl::ClearData()
377 DisposeHandle((Handle
)m_hDatabase
);
378 //this can return an error, but we don't really care that much about it
379 ICStop( (ICInstance
) m_hIC
);
384 // extension -> file type
385 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
387 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
390 // OSStatus status = ICMapEntriesFilename( (ICInstance) m_hIC, (Handle) m_hDatabase,
391 // (unsigned char*) e.mb_str(wxConvLocal), &entry );
393 // if (status != noErr)
394 // return NULL; //err or ext not known
395 //low level functions - iterate through the database
397 wxFileType
* pFileType
;
400 for(long i
= 1; i
<= m_lCount
; ++i
)
402 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
406 wxString sCurrentExtension
= wxMacMakeStringFromPascal(entry
.extension
);
407 if( sCurrentExtension
.Right(sCurrentExtension
.Length()-1) == e
) //entry has period in it
409 pFileType
= new wxFileType();
410 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
419 // MIME type -> extension -> file type
420 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
422 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
424 //low level functions - iterate through the database
426 wxFileType
* pFileType
;
430 for(long i
= 1; i
<= m_lCount
; ++i
)
432 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
433 wxASSERT_MSG( status
== noErr
, wxString::Format(wxT("Error: %d"), (int)status
) );
437 if( wxMacMakeStringFromPascal(entry
.MIMEType
) == mimeType
)
439 pFileType
= new wxFileType();
440 pFileType
->m_impl
->Init((wxMimeTypesManagerImpl
*)this, pos
);
449 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
451 wxASSERT_MSG( m_hIC
!= NULL
, wxT("wxMimeTypesManager not Initialized!") );
453 //low level functions - iterate through the database
458 for(long i
= 1; i
<= m_lCount
; ++i
)
460 OSStatus status
= ICGetIndMapEntry( (ICInstance
) m_hIC
, (Handle
) m_hDatabase
, i
, &pos
, &entry
);
461 if( status
== noErr
)
462 mimetypes
.Add( wxMacMakeStringFromPascal(entry
.MIMEType
) );
469 wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
471 wxFAIL_MSG( _T("wxMimeTypesManagerImpl::Associate() not yet implemented") );
472 //on mac you have to embed it into the mac's file reference resource ('FREF' I believe)
473 //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get
474 //the app's signature somehow...
480 wxMimeTypesManagerImpl::Unassociate(wxFileType
*ft
)
482 //this should be as easy as removing the entry from the database and then saving
487 #endif //wxUSE_MIMETYPE