]>
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 | ||
7dc3cc31 | 46 | |
c9e227f4 RN |
47 | /* START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */ |
48 | ||
49 | /* IsRemoteVolume can be used to find out if the | |
50 | volume referred to by vRefNum is a remote volume | |
51 | located somewhere on a network. the volume's attribute | |
52 | flags (copied from the GetVolParmsInfoBuffer structure) | |
53 | are returned in the longword pointed to by vMAttrib. */ | |
54 | OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) { | |
55 | HParamBlockRec volPB; | |
56 | GetVolParmsInfoBuffer volinfo; | |
57 | OSErr err; | |
58 | volPB.ioParam.ioVRefNum = vRefNum; | |
59 | volPB.ioParam.ioNamePtr = NULL; | |
60 | volPB.ioParam.ioBuffer = (Ptr) &volinfo; | |
61 | volPB.ioParam.ioReqCount = sizeof(volinfo); | |
62 | err = PBHGetVolParmsSync(&volPB); | |
63 | if (err == noErr) { | |
64 | *isRemote = (volinfo.vMServerAdr != 0); | |
65 | *vMAttrib = volinfo.vMAttrib; | |
66 | } | |
67 | return err; | |
68 | } | |
69 | ||
70 | ||
71 | /* BuildVolumeList fills the array pointed to by vols with | |
72 | a list of the currently mounted volumes. If includeRemote | |
73 | is true, then remote server volumes will be included in | |
74 | the list. When remote server volumes are included in the | |
75 | list, they will be added to the end of the list. On entry, | |
76 | *count should contain the size of the array pointed to by | |
77 | vols. On exit, *count will be set to the number of id numbers | |
78 | placed in the array. If vMAttribMask is non-zero, then | |
79 | only volumes with matching attributes are added to the | |
80 | list of volumes. bits in the vMAttribMask should use the | |
81 | same encoding as bits in the vMAttrib field of | |
82 | the GetVolParmsInfoBuffer structure. */ | |
83 | OSErr BuildVolumeList(Boolean includeRemote, short *vols, | |
84 | long *count, long vMAttribMask) { | |
85 | HParamBlockRec volPB; | |
86 | Boolean isRemote; | |
87 | OSErr err; | |
88 | long nlocal, nremote; | |
89 | long vMAttrib; | |
90 | ||
91 | /* set up and check parameters */ | |
92 | volPB.volumeParam.ioNamePtr = NULL; | |
93 | nlocal = nremote = 0; | |
94 | if (*count == 0) return noErr; | |
95 | ||
96 | /* iterate through volumes */ | |
97 | for (volPB.volumeParam.ioVolIndex = 1; | |
98 | PBHGetVInfoSync(&volPB) == noErr; | |
99 | volPB.volumeParam.ioVolIndex++) { | |
100 | ||
101 | /* skip remote volumes, if necessary */ | |
102 | err = IsRemoteVolume(volPB.volumeParam.ioVRefNum, &isRemote, &vMAttrib); | |
103 | if (err != noErr) goto bail; | |
104 | if ( ( includeRemote || ! isRemote ) | |
105 | && (vMAttrib & vMAttribMask) == vMAttribMask ) { | |
106 | ||
107 | /* add local volumes at the front, remote | |
108 | volumes at the end */ | |
109 | if (isRemote) | |
110 | vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum; | |
111 | else { | |
112 | if (nremote > 0) | |
113 | BlockMoveData(vols+nlocal, vols+nlocal+1, | |
114 | nremote*sizeof(short)); | |
115 | vols[nlocal++] = volPB.volumeParam.ioVRefNum; | |
116 | } | |
117 | ||
118 | /* list full? */ | |
119 | if ((nlocal + nremote) >= *count) break; | |
120 | } | |
121 | } | |
122 | bail: | |
123 | *count = (nlocal + nremote); | |
124 | return err; | |
125 | } | |
126 | ||
127 | ||
128 | /* FindApplication iterates through mounted volumes | |
129 | searching for an application with the given creator | |
130 | type. If includeRemote is true, then remote volumes | |
131 | will be searched (after local ones) for an application | |
132 | with the creator type. */ | |
133 | ||
134 | #define kMaxVols 20 | |
135 | ||
136 | /* Hacked to output to appName */ | |
137 | ||
7baa887c | 138 | OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName, FSSpec* appSpec) { |
c9e227f4 RN |
139 | short rRefNums[kMaxVols]; |
140 | long i, volCount; | |
141 | DTPBRec desktopPB; | |
142 | OSErr err; | |
c9e227f4 RN |
143 | |
144 | /* get a list of volumes - with desktop files */ | |
145 | volCount = kMaxVols; | |
146 | err = BuildVolumeList(includeRemote, rRefNums, &volCount, | |
147 | (1<<bHasDesktopMgr) ); | |
148 | if (err != noErr) return err; | |
149 | ||
150 | /* iterate through the list */ | |
151 | for (i=0; i<volCount; i++) { | |
152 | ||
153 | /* has a desktop file? */ | |
154 | desktopPB.ioCompletion = NULL; | |
155 | desktopPB.ioVRefNum = rRefNums[i]; | |
156 | desktopPB.ioNamePtr = NULL; | |
157 | desktopPB.ioIndex = 0; | |
158 | err = PBDTGetPath(&desktopPB); | |
159 | if (err != noErr) continue; | |
160 | ||
161 | /* has the correct app?? */ | |
162 | desktopPB.ioFileCreator = appCreator; | |
163 | desktopPB.ioNamePtr = appName; | |
164 | err = PBDTGetAPPLSync(&desktopPB); | |
165 | if (err != noErr) continue; | |
166 | ||
167 | /* make a file spec referring to it */ | |
168 | err = FSMakeFSSpec(rRefNums[i], | |
169 | desktopPB.ioAPPLParID, appName, | |
170 | appSpec); | |
171 | if (err != noErr) continue; | |
172 | ||
173 | /* found it! */ | |
174 | return noErr; | |
175 | ||
176 | } | |
177 | return fnfErr; | |
178 | } | |
179 | ||
180 | /* END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) */ | |
181 | ||
7baa887c RN |
182 | //yeah, duplicated code |
183 | pascal OSErr FSpGetFullPath(const FSSpec *spec, | |
184 | short *fullPathLength, | |
185 | Handle *fullPath) | |
186 | { | |
187 | OSErr result; | |
188 | OSErr realResult; | |
189 | FSSpec tempSpec; | |
190 | CInfoPBRec pb; | |
191 | ||
192 | *fullPathLength = 0; | |
193 | *fullPath = NULL; | |
194 | ||
195 | ||
196 | /* Default to noErr */ | |
197 | realResult = result = noErr; | |
198 | ||
199 | /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */ | |
200 | /* | |
201 | if ( spec->name[0] == 0 ) | |
202 | { | |
203 | result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec); | |
204 | } | |
205 | else | |
206 | { | |
207 | */ | |
208 | /* Make a copy of the input FSSpec that can be modified */ | |
209 | BlockMoveData(spec, &tempSpec, sizeof(FSSpec)); | |
210 | /* }*/ | |
211 | ||
212 | if ( result == noErr ) | |
213 | { | |
214 | if ( tempSpec.parID == fsRtParID ) | |
215 | { | |
216 | /* The object is a volume */ | |
217 | ||
218 | /* Add a colon to make it a full pathname */ | |
219 | ++tempSpec.name[0]; | |
220 | tempSpec.name[tempSpec.name[0]] = ':'; | |
221 | ||
222 | /* We're done */ | |
223 | result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); | |
224 | } | |
225 | else | |
226 | { | |
227 | /* The object isn't a volume */ | |
228 | ||
229 | /* Is the object a file or a directory? */ | |
230 | pb.dirInfo.ioNamePtr = tempSpec.name; | |
231 | pb.dirInfo.ioVRefNum = tempSpec.vRefNum; | |
232 | pb.dirInfo.ioDrDirID = tempSpec.parID; | |
233 | pb.dirInfo.ioFDirIndex = 0; | |
234 | result = PBGetCatInfoSync(&pb); | |
235 | /* Allow file/directory name at end of path to not exist. */ | |
236 | realResult = result; | |
237 | if ( (result == noErr) || (result == fnfErr) ) | |
238 | { | |
239 | /* if the object is a directory, append a colon so full pathname ends with colon */ | |
240 | if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) | |
241 | { | |
242 | ++tempSpec.name[0]; | |
243 | tempSpec.name[tempSpec.name[0]] = ':'; | |
244 | } | |
245 | ||
246 | /* Put the object name in first */ | |
247 | result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); | |
248 | if ( result == noErr ) | |
249 | { | |
250 | /* Get the ancestor directory names */ | |
251 | pb.dirInfo.ioNamePtr = tempSpec.name; | |
252 | pb.dirInfo.ioVRefNum = tempSpec.vRefNum; | |
253 | pb.dirInfo.ioDrParID = tempSpec.parID; | |
254 | do /* loop until we have an error or find the root directory */ | |
255 | { | |
256 | pb.dirInfo.ioFDirIndex = -1; | |
257 | pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; | |
258 | result = PBGetCatInfoSync(&pb); | |
259 | if ( result == noErr ) | |
260 | { | |
261 | /* Append colon to directory name */ | |
262 | ++tempSpec.name[0]; | |
263 | tempSpec.name[tempSpec.name[0]] = ':'; | |
264 | ||
265 | /* Add directory name to beginning of fullPath */ | |
266 | (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]); | |
267 | result = MemError(); | |
268 | } | |
269 | } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) ); | |
270 | } | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | if ( result == noErr ) | |
276 | { | |
277 | /* Return the length */ | |
278 | *fullPathLength = GetHandleSize(*fullPath); | |
279 | result = realResult; /* return realResult in case it was fnfErr */ | |
280 | } | |
281 | else | |
282 | { | |
283 | /* Dispose of the handle and return NULL and zero length */ | |
284 | if ( *fullPath != NULL ) | |
285 | { | |
286 | DisposeHandle(*fullPath); | |
287 | } | |
288 | *fullPath = NULL; | |
289 | *fullPathLength = 0; | |
290 | } | |
291 | ||
292 | return ( result ); | |
293 | } | |
294 | ||
295 | // | |
296 | // On the mac there are two ways to open a file - one is through apple events and the | |
297 | // finder, another is through mime types. | |
298 | // | |
299 | // So, really there are two ways to implement wxFileType... | |
300 | // | |
301 | // Mime types are only available on OS 8.1+ through the InternetConfig API | |
302 | // | |
303 | // Much like the old-style file manager, it has 3 levels of flexibility for its methods - | |
304 | // Low - which means you have to iterate yourself through the mime database | |
305 | // Medium - which lets you sort of cache the database if you want to use lowlevel functions | |
306 | // High - which requires access to the database every time | |
307 | // | |
308 | // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low | |
309 | // and mid-level functions | |
310 | // | |
311 | // TODO: Should we call ICBegin/ICEnd? Then where? | |
312 | // | |
313 | ||
314 | // debug helper | |
315 | inline void wxLogMimeDebug(const wxChar* szMsg, OSStatus status) | |
316 | { | |
317 | wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg, __LINE__, (int)status)); | |
318 | } | |
319 | ||
320 | // in case we're compiling in non-GUI mode | |
321 | class WXDLLEXPORT wxIcon; | |
322 | ||
323 | bool wxFileTypeImpl::SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt) | |
324 | { | |
325 | return FALSE; | |
326 | } | |
327 | ||
328 | bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int index) | |
329 | { | |
330 | return FALSE; | |
331 | } | |
332 | ||
333 | bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd, | |
334 | const wxFileType::MessageParameters& params) const | |
335 | { | |
336 | wxString cmd = GetCommand(wxT("open")); | |
337 | ||
338 | *openCmd = wxFileType::ExpandCommand(cmd, params); | |
339 | ||
340 | return !openCmd->empty(); | |
341 | } | |
342 | ||
343 | bool | |
344 | wxFileTypeImpl::GetPrintCommand(wxString *printCmd, | |
345 | const wxFileType::MessageParameters& params) | |
346 | const | |
347 | { | |
348 | wxString cmd = GetCommand(wxT("print")); | |
349 | ||
350 | *printCmd = wxFileType::ExpandCommand(cmd, params); | |
351 | ||
352 | return !printCmd->empty(); | |
353 | } | |
354 | ||
355 | // | |
356 | // Internet Config vs. Launch Services | |
357 | // | |
358 | // From OS 8 on there was internet config... | |
359 | // However, OSX and its finder does not use info | |
360 | // from Internet Config at all - the Internet Config | |
361 | // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS | |
362 | // OR REGISTERED THROUGH INTERNET CONFIG | |
363 | // | |
364 | // Therefore on OSX in order for the open command to be useful | |
365 | // we need to go straight to launch services | |
366 | // | |
367 | ||
368 | #if defined(__DARWIN__) | |
369 | ||
370 | //on darwin, use launch services | |
371 | #include "LaunchServices.h" | |
372 | ||
c9e227f4 | 373 | wxString wxFileTypeImpl::GetCommand(const wxString& verb) const |
7dc3cc31 | 374 | { |
7baa887c RN |
375 | wxASSERT(m_manager); |
376 | ||
377 | if(!m_manager) | |
378 | return wxEmptyString; | |
379 | ||
380 | if(verb == wxT("open")) | |
381 | { | |
382 | ICMapEntry entry; | |
383 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, | |
384 | (Handle) m_manager->m_hDatabase, | |
385 | m_lIndex, &entry); | |
386 | ||
387 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
388 | sCurrentExtension = sCurrentExtension.Right(sCurrentExtension.Length()-1 ); | |
389 | ||
390 | //type, creator, ext, roles, outapp (FSRef), outappurl | |
391 | CFURLRef cfurlAppPath; | |
392 | OSStatus status = LSGetApplicationForInfo (kLSUnknownType, | |
393 | kLSUnknownCreator, | |
394 | wxMacCFStringHolder(sCurrentExtension, wxLocale::GetSystemEncoding()), | |
395 | kLSRolesAll, | |
396 | NULL, | |
397 | &cfurlAppPath); | |
398 | ||
399 | if(status == noErr) | |
400 | { | |
401 | CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(cfurlAppPath, kCFURLPOSIXPathStyle); | |
402 | CFRelease(cfurlAppPath); | |
403 | ||
404 | //PHEW! Success! | |
405 | if(cfsUnixPath) | |
406 | return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding()); | |
407 | } | |
408 | else | |
409 | { | |
410 | wxLogDebug(wxString::Format(wxT("%i - %s - %i"), | |
411 | __LINE__, | |
412 | wxT("LSGetApplicationForInfo failed."), | |
413 | (int)status)); | |
414 | } | |
415 | } | |
416 | ||
417 | return wxEmptyString; | |
418 | } | |
419 | ||
420 | #else //carbon/classic implementation | |
421 | ||
422 | wxString wxFileTypeImpl::GetCommand(const wxString& verb) const | |
423 | { | |
424 | wxASSERT(m_manager); | |
425 | ||
c9e227f4 RN |
426 | if(!m_manager) |
427 | return wxEmptyString; | |
428 | ||
429 | if(verb == wxT("open")) | |
7dc3cc31 | 430 | { |
c9e227f4 | 431 | ICMapEntry entry; |
c02af653 RN |
432 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, |
433 | (Handle) m_manager->m_hDatabase, | |
434 | m_lIndex, &entry); | |
c9e227f4 | 435 | |
c9e227f4 RN |
436 | //The entry in the mimetype database only contains the app |
437 | //that's registered - it may not exist... we need to remap the creator | |
438 | //type and find the right application | |
7baa887c RN |
439 | |
440 | // THIS IS REALLY COMPLICATED :\. There are a lot of conversions going | |
441 | // on here. | |
c9e227f4 | 442 | Str255 outName; |
7baa887c RN |
443 | FSSpec outSpec; |
444 | if(FindApplication(entry.fileCreator, false, outName, &outSpec) != noErr) | |
c9e227f4 RN |
445 | return wxEmptyString; |
446 | ||
7baa887c RN |
447 | Handle outPathHandle; |
448 | short outPathSize; | |
449 | OSErr err = FSpGetFullPath(&outSpec, &outPathSize, &outPathHandle); | |
450 | ||
451 | if(err == noErr) | |
452 | { | |
453 | char* szPath = *outPathHandle; | |
454 | wxString sClassicPath(szPath, wxConvLocal, outPathSize); | |
455 | #if defined(__DARWIN__) | |
456 | //Classic Path --> Unix (OSX) Path | |
457 | CFURLRef finalURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, | |
458 | wxMacCFStringHolder(sClassicPath, wxLocale::GetSystemEncoding()), | |
459 | kCFURLHFSPathStyle, | |
460 | false); //false == not a directory | |
461 | ||
462 | //clean up memory from the classic path handle | |
463 | DisposeHandle(outPathHandle); | |
464 | ||
465 | if(finalURL) | |
466 | { | |
467 | CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(finalURL, kCFURLPOSIXPathStyle); | |
468 | CFRelease(finalURL); | |
469 | ||
470 | //PHEW! Success! | |
471 | if(cfsUnixPath) | |
472 | return wxMacCFStringHolder(cfsUnixPath).AsString(wxLocale::GetSystemEncoding()); | |
473 | } | |
474 | #else //classic HFS path acceptable | |
475 | return sClassicPath; | |
476 | #endif | |
477 | } | |
478 | else | |
479 | { | |
480 | wxLogMimeDebug(wxT("FSpGetFullPath failed."), (OSStatus)err); | |
481 | } | |
7dc3cc31 | 482 | } |
c9e227f4 RN |
483 | return wxEmptyString; |
484 | } | |
7baa887c | 485 | #endif //!DARWIN |
c9e227f4 RN |
486 | |
487 | bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) | |
488 | { | |
489 | if(!m_manager) | |
490 | return false; | |
491 | ||
492 | ICMapEntry entry; | |
c02af653 RN |
493 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, |
494 | (Handle) m_manager->m_hDatabase, | |
495 | m_lIndex, &entry); | |
c9e227f4 RN |
496 | |
497 | //entry has period in it | |
498 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
499 | extensions.Add( sCurrentExtension.Right(sCurrentExtension.Length()-1) ); | |
500 | return true; | |
501 | } | |
502 | ||
503 | bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const | |
504 | { | |
505 | if(!m_manager) | |
506 | return false; | |
507 | ||
508 | ICMapEntry entry; | |
c02af653 RN |
509 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, |
510 | (Handle) m_manager->m_hDatabase, | |
511 | m_lIndex, &entry); | |
c9e227f4 RN |
512 | |
513 | *mimeType = wxMacMakeStringFromPascal(entry.MIMEType); | |
514 | return true; | |
7dc3cc31 VS |
515 | } |
516 | ||
4d2976ad VS |
517 | bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const |
518 | { | |
519 | wxString s; | |
520 | ||
521 | if (GetMimeType(&s)) | |
522 | { | |
523 | mimeTypes.Clear(); | |
524 | mimeTypes.Add(s); | |
525 | return TRUE; | |
526 | } | |
527 | else | |
528 | return FALSE; | |
529 | } | |
530 | ||
da0766ab | 531 | bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const |
7dc3cc31 VS |
532 | { |
533 | // no such file type or no value or incorrect icon entry | |
534 | return FALSE; | |
535 | } | |
536 | ||
537 | bool wxFileTypeImpl::GetDescription(wxString *desc) const | |
538 | { | |
c9e227f4 RN |
539 | if(!m_manager) |
540 | return false; | |
541 | ||
542 | ICMapEntry entry; | |
c02af653 RN |
543 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, |
544 | (Handle) m_manager->m_hDatabase, | |
545 | m_lIndex, &entry); | |
c9e227f4 RN |
546 | |
547 | *desc = wxString((char*)entry.entryName, wxConvLocal); | |
548 | return true; | |
7dc3cc31 VS |
549 | } |
550 | ||
c9e227f4 | 551 | size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, wxArrayString * commands, |
e40298d5 | 552 | const wxFileType::MessageParameters& params) const |
007ae8d0 | 553 | { |
0fe3b231 | 554 | wxFAIL_MSG( _T("wxFileTypeImpl::GetAllCommands() not yet implemented") ); |
f040060e GD |
555 | return 0; |
556 | } | |
557 | ||
c9e227f4 | 558 | void wxMimeTypesManagerImpl::Initialize(int mailcapStyles, const wxString& extraDir) |
f040060e | 559 | { |
c9e227f4 | 560 | wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!")); |
007ae8d0 | 561 | |
c9e227f4 RN |
562 | //start internet config - log if there's an error |
563 | //the second param is the signature of the application, also known | |
564 | //as resource ID 0. However, as per some recent discussions, we may not | |
565 | //have a signature for this app, so a generic 'APPL' which is the executable | |
566 | //type will work for now | |
567 | OSStatus status = ICStart( (ICInstance*) &m_hIC, 'APPL'); | |
568 | ||
569 | if(status != noErr) | |
7dc3cc31 | 570 | { |
7baa887c | 571 | wxLogDebug(wxT("Could not initialize wxMimeTypesManager!")); |
c9e227f4 RN |
572 | wxASSERT( false ); |
573 | return; | |
7dc3cc31 | 574 | } |
c9e227f4 RN |
575 | |
576 | ICAttr attr; | |
577 | m_hDatabase = (void**) NewHandle(0); | |
578 | status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase ); | |
579 | ||
580 | //the database file can be corrupt (on OSX its | |
581 | //~/Library/Preferences/com.apple.internetconfig.plist) | |
582 | //- bail if it is | |
583 | if(status != noErr) | |
7dc3cc31 | 584 | { |
c9e227f4 | 585 | ClearData(); |
7baa887c | 586 | wxLogDebug(wxT("Corrupt Mime Database!")); |
c9e227f4 | 587 | return; |
7dc3cc31 | 588 | } |
c9e227f4 RN |
589 | |
590 | //obtain the number of entries in the map | |
591 | status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount ); | |
592 | wxASSERT( status == noErr ); | |
7baa887c RN |
593 | /* |
594 | //debug stuff | |
595 | ICMapEntry entry; | |
596 | long pos; | |
597 | ||
598 | for(long i = 1; i <= m_lCount; ++i) | |
599 | { | |
600 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); | |
601 | ||
602 | if(status == noErr) | |
603 | { | |
604 | wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName); | |
605 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
606 | wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType); | |
607 | ||
608 | wxFileTypeImpl impl; | |
609 | impl.Init(this, pos); | |
610 | ||
611 | if(sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html")) | |
612 | { | |
613 | wxString cmd; | |
614 | impl.GetOpenCommand (&cmd, | |
615 | wxFileType::MessageParameters (wxT("http://www.google.com"), | |
616 | _T(""))); | |
617 | ||
618 | wxPrintf(wxT("APP: [%s]\n"), cmd.c_str()); | |
619 | } | |
620 | } | |
621 | } | |
622 | */ | |
c9e227f4 RN |
623 | } |
624 | ||
625 | void wxMimeTypesManagerImpl::ClearData() | |
626 | { | |
627 | if(m_hIC != NULL) | |
7dc3cc31 | 628 | { |
c9e227f4 RN |
629 | DisposeHandle((Handle)m_hDatabase); |
630 | //this can return an error, but we don't really care that much about it | |
631 | ICStop( (ICInstance) m_hIC ); | |
632 | m_hIC = NULL; | |
7dc3cc31 | 633 | } |
c9e227f4 RN |
634 | } |
635 | ||
636 | // extension -> file type | |
637 | wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e) | |
638 | { | |
639 | wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); | |
640 | ||
c9e227f4 RN |
641 | //low level functions - iterate through the database |
642 | ICMapEntry entry; | |
643 | wxFileType* pFileType; | |
644 | long pos; | |
645 | ||
646 | for(long i = 1; i <= m_lCount; ++i) | |
7dc3cc31 | 647 | { |
c9e227f4 RN |
648 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); |
649 | ||
650 | if(status == noErr) | |
651 | { | |
652 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
653 | if( sCurrentExtension.Right(sCurrentExtension.Length()-1) == e ) //entry has period in it | |
654 | { | |
655 | pFileType = new wxFileType(); | |
656 | pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); | |
657 | break; | |
658 | } | |
659 | } | |
7dc3cc31 | 660 | } |
c9e227f4 RN |
661 | |
662 | return pFileType; | |
7dc3cc31 VS |
663 | } |
664 | ||
665 | // MIME type -> extension -> file type | |
c9e227f4 | 666 | wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) |
7dc3cc31 | 667 | { |
c9e227f4 RN |
668 | wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); |
669 | ||
670 | //low level functions - iterate through the database | |
671 | ICMapEntry entry; | |
672 | wxFileType* pFileType; | |
673 | ||
674 | long pos; | |
675 | ||
676 | for(long i = 1; i <= m_lCount; ++i) | |
677 | { | |
678 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); | |
679 | wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) ); | |
680 | ||
681 | if(status == noErr) | |
682 | { | |
683 | if( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType) | |
684 | { | |
685 | pFileType = new wxFileType(); | |
686 | pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); | |
687 | break; | |
688 | } | |
689 | } | |
690 | } | |
691 | ||
692 | return pFileType; | |
7dc3cc31 VS |
693 | } |
694 | ||
695 | size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes) | |
696 | { | |
c9e227f4 | 697 | wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); |
7dc3cc31 | 698 | |
c9e227f4 RN |
699 | //low level functions - iterate through the database |
700 | ICMapEntry entry; | |
701 | ||
702 | long pos; | |
7baa887c | 703 | long lStartCount = (long) mimetypes.GetCount(); |
c9e227f4 RN |
704 | |
705 | for(long i = 1; i <= m_lCount; ++i) | |
706 | { | |
707 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry); | |
708 | if( status == noErr ) | |
709 | mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) ); | |
710 | } | |
711 | ||
7baa887c | 712 | return mimetypes.GetCount() - lStartCount; |
7dc3cc31 VS |
713 | } |
714 | ||
7baa887c RN |
715 | |
716 | pascal OSStatus MoreProcGetProcessTypeSignature( | |
717 | const ProcessSerialNumberPtr pPSN, | |
718 | OSType *pProcessType, | |
719 | OSType *pCreator) | |
720 | { | |
721 | OSStatus anErr = noErr; | |
722 | ProcessInfoRec infoRec; | |
723 | ProcessSerialNumber localPSN; | |
724 | ||
725 | infoRec.processInfoLength = sizeof(ProcessInfoRec); | |
726 | infoRec.processName = nil; | |
727 | infoRec.processAppSpec = nil; | |
728 | ||
729 | if ( pPSN == nil ) { | |
730 | localPSN.highLongOfPSN = 0; | |
731 | localPSN.lowLongOfPSN = kCurrentProcess; | |
732 | } else { | |
733 | localPSN = *pPSN; | |
734 | } | |
735 | ||
736 | anErr = GetProcessInformation(&localPSN, &infoRec); | |
737 | if (anErr == noErr) | |
738 | { | |
739 | *pProcessType = infoRec.processType; | |
740 | *pCreator = infoRec.processSignature; | |
741 | } | |
742 | ||
743 | return anErr; | |
744 | }//end MoreProcGetProcessTypeSignature | |
745 | ||
007ae8d0 GD |
746 | wxFileType * |
747 | wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) | |
748 | { | |
7baa887c RN |
749 | #if 1 |
750 | wxFAIL_MSG(wxT("Associate not ready for production use")); | |
751 | return NULL; | |
752 | #else | |
c9e227f4 RN |
753 | //on mac you have to embed it into the mac's file reference resource ('FREF' I believe) |
754 | //or, alternately, you could just add an entry to m_hDatabase, but you'd need to get | |
755 | //the app's signature somehow... | |
007ae8d0 | 756 | |
7baa887c RN |
757 | OSType processType, |
758 | creator; | |
759 | OSStatus status = MoreProcGetProcessTypeSignature(NULL,&processType, &creator); | |
760 | ||
761 | if(status == noErr) | |
762 | { | |
763 | Str255 psCreatorName; | |
764 | FSSpec dummySpec; | |
765 | status = FindApplication(creator, false, psCreatorName, &dummySpec); | |
766 | ||
767 | if(status == noErr) | |
768 | { | |
769 | ||
770 | //get the file type if it exists - | |
771 | //if it really does then modify the database then save it, | |
772 | //otherwise we need to create a whole new entry | |
773 | wxFileType* pFileType = GetFileTypeFromMimeType(ftInfo.GetMimeType()); | |
774 | if(pFileType) | |
775 | { | |
776 | ICMapEntry entry; | |
777 | ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, | |
778 | pFileType->m_impl->m_lIndex, &entry); | |
779 | ||
780 | entry.creatorAppName = psCreatorName; | |
781 | entry.fileCreator = creator; | |
782 | ||
783 | status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, | |
784 | pFileType->m_impl->m_lIndex, &entry); | |
785 | ||
786 | //success | |
787 | if(status == noErr) | |
788 | return pFileType; | |
789 | else | |
790 | { | |
791 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed."))); | |
792 | } | |
793 | ||
794 | //failure - cleanup | |
795 | delete pFileType; | |
796 | } | |
797 | else | |
798 | { | |
799 | //TODO: Maybe force all 3 of these to be non-empty? | |
800 | Str255 psExtension; | |
801 | Str255 psMimeType; | |
802 | Str255 psDescription; | |
803 | ||
804 | wxMacStringToPascal(ftInfo.GetExtensions()[0], psExtension); | |
805 | wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType); | |
806 | wxMacStringToPascal(ftInfo.GetDescription(), psDescription); | |
807 | ||
808 | Str255 psPostCreatorName; | |
809 | wxMacStringToPascal(wxT(""), psPostCreatorName); | |
810 | ||
811 | ||
812 | //add the entry to the database | |
813 | //TODO: Does this work? | |
814 | ICMapEntry entry; | |
815 | entry.totalLength = sizeof(ICMapEntry); | |
816 | entry.fixedLength = kICMapFixedLength; | |
817 | entry.version = 0; | |
818 | entry.fileType = 0; //TODO: File type? | |
819 | entry.fileCreator = creator; | |
820 | entry.postCreator = 0; | |
821 | entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too? | |
822 | entry.extension = psExtension; | |
823 | entry.creatorAppName = psCreatorName; | |
824 | entry.postAppName = psPostCreatorName; | |
825 | entry.MIMEType = psMimeType; | |
826 | entry.entryName = psDescription; | |
827 | ||
828 | status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry); | |
829 | ||
830 | if(status == noErr) | |
831 | { | |
832 | //kICAttrNoChange means we don't care about attributes such as | |
833 | //locking in the database | |
834 | status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, | |
835 | kICAttrNoChange, (Handle) m_hDatabase); | |
836 | ||
837 | //return the entry in the database if successful | |
838 | if(status == noErr) | |
839 | return GetFileTypeFromMimeType(ftInfo.GetMimeType()); | |
840 | else | |
841 | { | |
842 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed."))); | |
843 | } | |
844 | } | |
845 | else | |
846 | { | |
847 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed."))); | |
848 | } | |
849 | } | |
850 | } //end if FindApplcation was successful | |
851 | else | |
852 | { | |
853 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed."))); | |
854 | } | |
855 | } //end if it could obtain app's signature | |
856 | else | |
857 | { | |
858 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed."))); | |
859 | } | |
007ae8d0 | 860 | return NULL; |
7baa887c | 861 | #endif |
007ae8d0 | 862 | } |
f040060e GD |
863 | |
864 | bool | |
7baa887c | 865 | wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType) |
f040060e | 866 | { |
7baa887c RN |
867 | #if 1 |
868 | wxFAIL_MSG(wxT("Unassociate not ready for production use")); | |
869 | return FALSE; | |
870 | #else | |
c9e227f4 RN |
871 | //this should be as easy as removing the entry from the database and then saving |
872 | //the database | |
7baa887c RN |
873 | OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, |
874 | pFileType->m_impl->m_lIndex); | |
875 | ||
876 | if(status == noErr) | |
877 | { | |
878 | //kICAttrNoChange means we don't care about attributes such as | |
879 | //locking in the database | |
880 | status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, | |
881 | kICAttrNoChange, (Handle) m_hDatabase); | |
882 | ||
883 | if(status == noErr) | |
884 | return TRUE; | |
885 | else | |
886 | { | |
887 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed."))); | |
888 | } | |
889 | ||
890 | } | |
891 | else | |
892 | { | |
893 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed."))); | |
894 | } | |
895 | ||
f040060e | 896 | return FALSE; |
7baa887c | 897 | #endif |
f040060e GD |
898 | } |
899 | ||
4b8f9d46 | 900 | #endif //wxUSE_MIMETYPE |