]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mimetmac.cpp
Improved prop sheet dialog layout
[wxWidgets.git] / src / mac / carbon / mimetmac.cpp
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
66 class WXDLLEXPORT wxIcon;
67
68 bool wxFileTypeImpl::SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt)
69 {
70 return FALSE;
71 }
72
73 bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index)
74 {
75 return FALSE;
76 }
77
78 bool 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
88 bool
89 wxFileTypeImpl::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. */
107 OSErr 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. */
136 OSErr 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 }
175 bail:
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
191 OSErr 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
237 wxString 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 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
246 (Handle) m_manager->m_hDatabase,
247 m_lIndex, &entry);
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);
261 }
262 return wxEmptyString;
263 }
264
265 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
266 {
267 if(!m_manager)
268 return false;
269
270 ICMapEntry entry;
271 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
272 (Handle) m_manager->m_hDatabase,
273 m_lIndex, &entry);
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
281 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
282 {
283 if(!m_manager)
284 return false;
285
286 ICMapEntry entry;
287 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
288 (Handle) m_manager->m_hDatabase,
289 m_lIndex, &entry);
290
291 *mimeType = wxMacMakeStringFromPascal(entry.MIMEType);
292 return true;
293 }
294
295 bool 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
309 bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const
310 {
311 // no such file type or no value or incorrect icon entry
312 return FALSE;
313 }
314
315 bool wxFileTypeImpl::GetDescription(wxString *desc) const
316 {
317 if(!m_manager)
318 return false;
319
320 ICMapEntry entry;
321 ICGetMapEntry( (ICInstance) m_manager->m_hIC,
322 (Handle) m_manager->m_hDatabase,
323 m_lIndex, &entry);
324
325 *desc = wxString((char*)entry.entryName, wxConvLocal);
326 return true;
327 }
328
329 size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands,
330 const wxFileType::MessageParameters& params) const
331 {
332 wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") );
333 return 0;
334 }
335
336 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir)
337 {
338 wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!"));
339
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)
348 {
349 wxLogSysError(wxT("Could not initialize wxMimeTypesManager!"));
350 wxASSERT( false );
351 return;
352 }
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)
362 {
363 ClearData();
364 wxLogSysError(wxT("Bad Mime Database!"));
365 return;
366 }
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
373 void wxMimeTypesManagerImpl::ClearData()
374 {
375 if(m_hIC != NULL)
376 {
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;
381 }
382 }
383
384 // extension -> file type
385 wxFileType* 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)
401 {
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 }
414 }
415
416 return pFileType;
417 }
418
419 // MIME type -> extension -> file type
420 wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
421 {
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;
447 }
448
449 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
450 {
451 wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") );
452
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;
466 }
467
468 wxFileType *
469 wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
470 {
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...
475
476 return NULL;
477 }
478
479 bool
480 wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
481 {
482 //this should be as easy as removing the entry from the database and then saving
483 //the database
484 return FALSE;
485 }
486
487 #endif //wxUSE_MIMETYPE