]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/mimetmac.cpp
Improved prop sheet dialog layout
[wxWidgets.git] / src / mac / carbon / mimetmac.cpp
CommitLineData
7dc3cc31
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: mac/mimetype.cpp
c9e227f4
RN
3// Purpose: Mac Carbon implementation for wx mime-related classes
4// Author: Ryan Norton
7dc3cc31 5// Modified by:
c9e227f4 6// Created: 04/16/2005
7dc3cc31 7// RCS-ID: $Id$
c9e227f4
RN
8// Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>)
9// Licence: wxWindows licence
7dc3cc31
VS
10/////////////////////////////////////////////////////////////////////////////
11
3d1a4878 12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
7dc3cc31
VS
13#pragma implementation "mimetype.h"
14#endif
15
16// for compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20 #pragma hdrstop
21#endif
22
7dc3cc31
VS
23#ifndef WX_PRECOMP
24 #include "wx/string.h"
25 #if wxUSE_GUI
26 #include "wx/icon.h"
27 #endif
28#endif //WX_PRECOMP
29
30
4b8f9d46
RN
31#if wxUSE_MIMETYPE
32
7dc3cc31
VS
33#include "wx/log.h"
34#include "wx/file.h"
35#include "wx/intl.h"
36#include "wx/dynarray.h"
37#include "wx/confbase.h"
38
39#include "wx/mac/mimetype.h"
c9e227f4 40#include "wx/mac/private.h" //wxMacMakeStringFromPascal
7dc3cc31
VS
41
42// other standard headers
43#include <ctype.h>
c9e227f4
RN
44#include <InternetConfig.h> //For mime types
45
46//
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.
49//
50// So, really there are two ways to implement wxFileType...
51//
52// Mime types are only available on OS 8.1+ through the InternetConfig API
53//
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
58//
59// We want to be efficient (i.e. professional :) ) about it, so we use a combo of low
60// and mid-level functions
61//
62// TODO: Should we call ICBegin/ICEnd? Then where?
63//
7dc3cc31
VS
64
65// in case we're compiling in non-GUI mode
66class WXDLLEXPORT wxIcon;
67
f040060e
GD
68bool wxFileTypeImpl::SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt)
69{
70 return FALSE;
71}
72
73bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index)
74{
75 return FALSE;
76}
7dc3cc31 77
c9e227f4
RN
78bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
79 const wxFileType::MessageParameters& params) const
7dc3cc31 80{
c9e227f4
RN
81 wxString cmd = GetCommand(wxT("open"));
82
83 *openCmd = wxFileType::ExpandCommand(cmd, params);
84
85 return !openCmd->empty();
7dc3cc31
VS
86}
87
c9e227f4
RN
88bool
89wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
90 const wxFileType::MessageParameters& params)
91 const
7dc3cc31 92{
c9e227f4
RN
93 wxString cmd = GetCommand(wxT("print"));
94
95 *printCmd = wxFileType::ExpandCommand(cmd, params);
96
97 return !printCmd->empty();
7dc3cc31
VS
98}
99
c9e227f4
RN
100/* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
101
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. */
107OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) {
108 HParamBlockRec volPB;
109 GetVolParmsInfoBuffer volinfo;
110 OSErr err;
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);
116 if (err == noErr) {
117 *isRemote = (volinfo.vMServerAdr != 0);
118 *vMAttrib = volinfo.vMAttrib;
119 }
120 return err;
121}
122
123
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. */
136OSErr BuildVolumeList(Boolean includeRemote, short *vols,
137 long *count, long vMAttribMask) {
138 HParamBlockRec volPB;
139 Boolean isRemote;
140 OSErr err;
141 long nlocal, nremote;
142 long vMAttrib;
143
144 /* set up and check parameters */
145 volPB.volumeParam.ioNamePtr = NULL;
146 nlocal = nremote = 0;
147 if (*count == 0) return noErr;
148
149 /* iterate through volumes */
150 for (volPB.volumeParam.ioVolIndex = 1;
151 PBHGetVInfoSync(&volPB) == noErr;
152 volPB.volumeParam.ioVolIndex++) {
153
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 ) {
159
160 /* add local volumes at the front, remote
161 volumes at the end */
162 if (isRemote)
163 vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum;
164 else {
165 if (nremote > 0)
166 BlockMoveData(vols+nlocal, vols+nlocal+1,
167 nremote*sizeof(short));
168 vols[nlocal++] = volPB.volumeParam.ioVRefNum;
169 }
170
171 /* list full? */
172 if ((nlocal + nremote) >= *count) break;
173 }
174 }
175bail:
176 *count = (nlocal + nremote);
177 return err;
178}
179
180
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. */
186
187#define kMaxVols 20
188
189/* Hacked to output to appName */
190
191OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName) {
192 short rRefNums[kMaxVols];
193 long i, volCount;
194 DTPBRec desktopPB;
195 OSErr err;
196 FSSpec realappSpec;
197 FSSpec *appSpec = &realappSpec;
198
199 /* get a list of volumes - with desktop files */
200 volCount = kMaxVols;
201 err = BuildVolumeList(includeRemote, rRefNums, &volCount,
202 (1<<bHasDesktopMgr) );
203 if (err != noErr) return err;
204
205 /* iterate through the list */
206 for (i=0; i<volCount; i++) {
207
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;
215
216 /* has the correct app?? */
217 desktopPB.ioFileCreator = appCreator;
218 desktopPB.ioNamePtr = appName;
219 err = PBDTGetAPPLSync(&desktopPB);
220 if (err != noErr) continue;
221
222 /* make a file spec referring to it */
223 err = FSMakeFSSpec(rRefNums[i],
224 desktopPB.ioAPPLParID, appName,
225 appSpec);
226 if (err != noErr) continue;
227
228 /* found it! */
229 return noErr;
230
231 }
232 return fnfErr;
233}
234
235/* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */
236
237wxString wxFileTypeImpl::GetCommand(const wxString& verb) const
7dc3cc31 238{
c9e227f4
RN
239 if(!m_manager)
240 return wxEmptyString;
241
242 if(verb == wxT("open"))
7dc3cc31 243 {
c9e227f4 244 ICMapEntry entry;
c02af653
RN
245 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
246 (Handle) m_manager->m_hDatabase,
247 m_lIndex, &entry);
c9e227f4
RN
248
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
253 Str255 outName;
254 if(FindApplication(entry.fileCreator, false, outName) != noErr)
255 return wxEmptyString;
256
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);
7dc3cc31 261 }
c9e227f4
RN
262 return wxEmptyString;
263}
264
265bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
266{
267 if(!m_manager)
268 return false;
269
270 ICMapEntry entry;
c02af653
RN
271 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
272 (Handle) m_manager->m_hDatabase,
273 m_lIndex, &entry);
c9e227f4
RN
274
275 //entry has period in it
276 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
277 extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) );
278 return true;
279}
280
281bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
282{
283 if(!m_manager)
284 return false;
285
286 ICMapEntry entry;
c02af653
RN
287 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
288 (Handle) m_manager->m_hDatabase,
289 m_lIndex, &entry);
c9e227f4
RN
290
291 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
292 return true;
7dc3cc31
VS
293}
294
4d2976ad
VS
295bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
296{
297 wxString s;
298
299 if (GetMimeType(&s))
300 {
301 mimeTypes.Clear();
302 mimeTypes.Add(s);
303 return TRUE;
304 }
305 else
306 return FALSE;
307}
308
da0766ab 309bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
7dc3cc31
VS
310{
311 // no such file type or no value or incorrect icon entry
312 return FALSE;
313}
314
315bool wxFileTypeImpl::GetDescription(wxString *desc) const
316{
c9e227f4
RN
317 if(!m_manager)
318 return false;
319
320 ICMapEntry entry;
c02af653
RN
321 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
322 (Handle) m_manager->m_hDatabase,
323 m_lIndex, &entry);
c9e227f4
RN
324
325 *desc = wxString((char*)entry.entryName, wxConvLocal);
326 return true;
7dc3cc31
VS
327}
328
c9e227f4 329size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
e40298d5 330 const wxFileType::MessageParameters& params) const
007ae8d0 331{
0fe3b231 332 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
f040060e
GD
333 return 0;
334}
335
c9e227f4 336void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
f040060e 337{
c9e227f4 338 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
007ae8d0 339
c9e227f4
RN
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');
346
347 if(status != noErr)
7dc3cc31 348 {
c9e227f4
RN
349 wxLogSysError(wxT("Could not initialize wxMimeTypesManager!"));
350 wxASSERT( false );
351 return;
7dc3cc31 352 }
c9e227f4
RN
353
354 ICAttr attr;
355 m_hDatabase = (void**) NewHandle(0);
356 status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase );
357
358 //the database file can be corrupt (on OSX its
359 //~/Library/Preferences/com.apple.internetconfig.plist)
360 //- bail if it is
361 if(status != noErr)
7dc3cc31 362 {
c9e227f4
RN
363 ClearData();
364 wxLogSysError(wxT("Bad Mime Database!"));
365 return;
7dc3cc31 366 }
c9e227f4
RN
367
368 //obtain the number of entries in the map
369 status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount );
370 wxASSERT( status == noErr );
371}
372
373void wxMimeTypesManagerImpl::ClearData()
374{
375 if(m_hIC != NULL)
7dc3cc31 376 {
c9e227f4
RN
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 );
380 m_hIC = NULL;
7dc3cc31 381 }
c9e227f4
RN
382}
383
384// extension -> file type
385wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
386{
387 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
388
389// ICMapEntry entry;
390// OSStatus status = ICMapEntriesFilename( (ICInstance) m_hIC, (Handle) m_hDatabase,
391// (unsigned char*) e.mb_str(wxConvLocal), &entry );
392
393// if (status != noErr)
394// return NULL; //err or ext not known
395 //low level functions - iterate through the database
396 ICMapEntry entry;
397 wxFileType* pFileType;
398 long pos;
399
400 for(long i = 1; i <= m_lCount; ++i)
7dc3cc31 401 {
c9e227f4
RN
402 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
403
404 if(status == noErr)
405 {
406 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
407 if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it
408 {
409 pFileType = new wxFileType();
410 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
411 break;
412 }
413 }
7dc3cc31 414 }
c9e227f4
RN
415
416 return pFileType;
7dc3cc31
VS
417}
418
419// MIME type -> extension -> file type
c9e227f4 420wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
7dc3cc31 421{
c9e227f4
RN
422 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
423
424 //low level functions - iterate through the database
425 ICMapEntry entry;
426 wxFileType* pFileType;
427
428 long pos;
429
430 for(long i = 1; i <= m_lCount; ++i)
431 {
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) );
434
435 if(status == noErr)
436 {
437 if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType)
438 {
439 pFileType = new wxFileType();
440 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
441 break;
442 }
443 }
444 }
445
446 return pFileType;
7dc3cc31
VS
447}
448
449size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
450{
c9e227f4 451 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
7dc3cc31 452
c9e227f4
RN
453 //low level functions - iterate through the database
454 ICMapEntry entry;
455
456 long pos;
457
458 for(long i = 1; i <= m_lCount; ++i)
459 {
460 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
461 if( status == noErr )
462 mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) );
463 }
464
465 return m_lCount;
7dc3cc31
VS
466}
467
007ae8d0
GD
468wxFileType *
469wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
470{
0fe3b231 471 wxFAIL_MSG( _T("wxMimeTypesManagerImpl::Associate() not yet implemented") );
c9e227f4
RN
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...
007ae8d0
GD
475
476 return NULL;
477}
f040060e
GD
478
479bool
480wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
481{
c9e227f4
RN
482 //this should be as easy as removing the entry from the database and then saving
483 //the database
f040060e
GD
484 return FALSE;
485}
486
4b8f9d46 487#endif //wxUSE_MIMETYPE