]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/mimetmac.cpp
Implement wxMimeTypesManager on mac
[wxWidgets.git] / src / mac / carbon / mimetmac.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: mac/mimetype.cpp
3// Purpose: Mac Carbon implementation for wx mime-related classes
4// Author: Ryan Norton
5// Modified by:
6// Created: 04/16/2005
7// RCS-ID: $Id$
8// Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>)
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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
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
31#if wxUSE_MIMETYPE
32
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"
40#include "wx/mac/private.h" //wxMacMakeStringFromPascal
41
42// other standard headers
43#include <ctype.h>
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//
64
65// in case we're compiling in non-GUI mode
66class WXDLLEXPORT wxIcon;
67
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}
77
78bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
79 const wxFileType::MessageParameters& params) const
80{
81 wxString cmd = GetCommand(wxT("open"));
82
83 *openCmd = wxFileType::ExpandCommand(cmd, params);
84
85 return !openCmd->empty();
86}
87
88bool
89wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
90 const wxFileType::MessageParameters& params)
91 const
92{
93 wxString cmd = GetCommand(wxT("print"));
94
95 *printCmd = wxFileType::ExpandCommand(cmd, params);
96
97 return !printCmd->empty();
98}
99
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
238{
239 if(!m_manager)
240 return wxEmptyString;
241
242 if(verb == wxT("open"))
243 {
244 ICMapEntry entry;
245 OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC,
246 (Handle) m_manager->m_hDatabase,
247 m_lIndex, &entry);
248 wxASSERT( status == noErr );
249
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
254 Str255 outName;
255 if(FindApplication(entry.fileCreator, false, outName) != noErr)
256 return wxEmptyString;
257
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);
262 }
263 return wxEmptyString;
264}
265
266bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
267{
268 if(!m_manager)
269 return false;
270
271 ICMapEntry entry;
272 OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC,
273 (Handle) m_manager->m_hDatabase,
274 m_lIndex, &entry);
275 wxASSERT( status == noErr );
276
277 //entry has period in it
278 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
279 extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) );
280 return true;
281}
282
283bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
284{
285 if(!m_manager)
286 return false;
287
288 ICMapEntry entry;
289 OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC,
290 (Handle) m_manager->m_hDatabase,
291 m_lIndex, &entry);
292 wxASSERT( status == noErr );
293
294 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
295 return true;
296}
297
298bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
299{
300 wxString s;
301
302 if (GetMimeType(&s))
303 {
304 mimeTypes.Clear();
305 mimeTypes.Add(s);
306 return TRUE;
307 }
308 else
309 return FALSE;
310}
311
312bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
313{
314 // no such file type or no value or incorrect icon entry
315 return FALSE;
316}
317
318bool wxFileTypeImpl::GetDescription(wxString *desc) const
319{
320 if(!m_manager)
321 return false;
322
323 ICMapEntry entry;
324 OSStatus status = ICGetMapEntry( (ICInstance) m_manager->m_hIC,
325 (Handle) m_manager->m_hDatabase,
326 m_lIndex, &entry);
327 wxASSERT( status == noErr );
328
329 *desc = wxString((char*)entry.entryName, wxConvLocal);
330 return true;
331}
332
333size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
334 const wxFileType::MessageParameters& params) const
335{
336 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
337 return 0;
338}
339
340void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
341{
342 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
343
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');
350
351 if(status != noErr)
352 {
353 wxLogSysError(wxT("Could not initialize wxMimeTypesManager!"));
354 wxASSERT( false );
355 return;
356 }
357
358 ICAttr attr;
359 m_hDatabase = (void**) NewHandle(0);
360 status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase );
361
362 //the database file can be corrupt (on OSX its
363 //~/Library/Preferences/com.apple.internetconfig.plist)
364 //- bail if it is
365 if(status != noErr)
366 {
367 ClearData();
368 wxLogSysError(wxT("Bad Mime Database!"));
369 return;
370 }
371
372 //obtain the number of entries in the map
373 status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount );
374 wxASSERT( status == noErr );
375}
376
377void wxMimeTypesManagerImpl::ClearData()
378{
379 if(m_hIC != NULL)
380 {
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 );
384 m_hIC = NULL;
385 }
386}
387
388// extension -> file type
389wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
390{
391 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
392
393// ICMapEntry entry;
394// OSStatus status = ICMapEntriesFilename( (ICInstance) m_hIC, (Handle) m_hDatabase,
395// (unsigned char*) e.mb_str(wxConvLocal), &entry );
396
397// if (status != noErr)
398// return NULL; //err or ext not known
399 //low level functions - iterate through the database
400 ICMapEntry entry;
401 wxFileType* pFileType;
402 long pos;
403
404 for(long i = 1; i <= m_lCount; ++i)
405 {
406 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
407
408 if(status == noErr)
409 {
410 wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension);
411 if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it
412 {
413 pFileType = new wxFileType();
414 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
415 break;
416 }
417 }
418 }
419
420 return pFileType;
421}
422
423// MIME type -> extension -> file type
424wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
425{
426 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
427
428 //low level functions - iterate through the database
429 ICMapEntry entry;
430 wxFileType* pFileType;
431
432 long pos;
433
434 for(long i = 1; i <= m_lCount; ++i)
435 {
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) );
438
439 if(status == noErr)
440 {
441 if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType)
442 {
443 pFileType = new wxFileType();
444 pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos);
445 break;
446 }
447 }
448 }
449
450 return pFileType;
451}
452
453size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
454{
455 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
456
457 //low level functions - iterate through the database
458 ICMapEntry entry;
459
460 long pos;
461
462 for(long i = 1; i <= m_lCount; ++i)
463 {
464 OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry);
465 if( status == noErr )
466 mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) );
467 }
468
469 return m_lCount;
470}
471
472wxFileType *
473wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
474{
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...
479
480 return NULL;
481}
482
483bool
484wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
485{
486 //this should be as easy as removing the entry from the database and then saving
487 //the database
488 return FALSE;
489}
490
491#endif //wxUSE_MIMETYPE