]>
Commit | Line | Data |
---|---|---|
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 | |
66 | class WXDLLEXPORT wxIcon; | |
67 | ||
f040060e GD |
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 | } | |
7dc3cc31 | 77 | |
c9e227f4 RN |
78 | bool 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 |
88 | bool |
89 | wxFileTypeImpl::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. */ | |
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 | |
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 | ||
265 | bool 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 | ||
281 | bool 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 |
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 | ||
da0766ab | 309 | bool 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 | ||
315 | bool 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 | 329 | size_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 | 336 | void 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 | ||
373 | void 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 | |
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) | |
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 | 420 | wxFileType* 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 | ||
449 | size_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 |
468 | wxFileType * |
469 | wxMimeTypesManagerImpl::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 | |
479 | bool | |
480 | wxMimeTypesManagerImpl::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 |