]>
Commit | Line | Data |
---|---|---|
489468fe | 1 | ///////////////////////////////////////////////////////////////////////////// |
80fdcdb9 | 2 | // Name: src/osx/carbon/mimetmac.cpp |
489468fe SC |
3 | // Purpose: Mac Carbon implementation for wx MIME-related classes |
4 | // Author: Ryan Norton | |
5 | // Modified by: | |
6 | // Created: 04/16/2005 | |
489468fe SC |
7 | // Copyright: (c) 2005 Ryan Norton (<wxprojects@comcast.net>) |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | // | |
12 | // TODO: Search Info[-macos](classic).plist dictionary in addition | |
13 | // to Internet Config database. | |
14 | // | |
15 | // Maybe try a brainstorm a way to change the wxMimeTypesManager API | |
16 | // to get info from a file instead/addition to current get all stuff | |
17 | // API so that we can use Launch Services to get MIME type info. | |
18 | // | |
19 | // Implement GetIcon from one of the FinderInfo functions - or | |
20 | // use Launch Services and search that app's plist for the icon. | |
21 | // | |
22 | // Put some special juice in for the print command. | |
23 | // | |
24 | ||
25 | // for compilers that support precompilation, includes "wx.h". | |
26 | #include "wx/wxprec.h" | |
27 | ||
28 | #ifdef __BORLANDC__ | |
29 | #pragma hdrstop | |
30 | #endif | |
31 | ||
32 | #if wxUSE_MIMETYPE | |
33 | ||
1f0c8f31 | 34 | #include "wx/osx/mimetype.h" |
489468fe SC |
35 | |
36 | #ifndef WX_PRECOMP | |
37 | #include "wx/dynarray.h" | |
38 | #include "wx/string.h" | |
39 | #include "wx/intl.h" | |
40 | #include "wx/log.h" | |
41 | ||
42 | #if wxUSE_GUI | |
43 | #include "wx/icon.h" | |
44 | #endif | |
45 | #endif | |
46 | ||
47 | #include "wx/file.h" | |
48 | #include "wx/confbase.h" | |
49 | ||
1f0c8f31 | 50 | #include "wx/osx/private.h" |
489468fe SC |
51 | |
52 | // other standard headers | |
53 | #include <ctype.h> | |
54 | ||
55 | #ifndef __DARWIN__ | |
56 | #include <InternetConfig.h> | |
57 | #include <CoreServices.h> | |
58 | #endif | |
59 | ||
60 | #ifndef __LP64__ | |
61 | // START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) | |
62 | ||
63 | // IsRemoteVolume can be used to find out if the | |
64 | // volume referred to by vRefNum is a remote volume | |
65 | // located somewhere on a network. the volume's attribute | |
66 | // flags (copied from the GetVolParmsInfoBuffer structure) | |
67 | // are returned in the longword pointed to by vMAttrib. | |
68 | OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) | |
69 | { | |
70 | HParamBlockRec volPB; | |
71 | GetVolParmsInfoBuffer volinfo; | |
72 | OSErr err; | |
73 | ||
74 | volPB.ioParam.ioVRefNum = vRefNum; | |
75 | volPB.ioParam.ioNamePtr = NULL; | |
76 | volPB.ioParam.ioBuffer = (Ptr)&volinfo; | |
77 | volPB.ioParam.ioReqCount = sizeof(volinfo); | |
78 | err = PBHGetVolParmsSync( &volPB ); | |
79 | if (err == noErr) | |
80 | { | |
81 | *isRemote = (volinfo.vMServerAdr != 0); | |
82 | *vMAttrib = volinfo.vMAttrib; | |
83 | } | |
84 | ||
85 | return err; | |
86 | } | |
87 | ||
88 | // BuildVolumeList fills the array pointed to by vols with | |
89 | // a list of the currently mounted volumes. If includeRemote | |
90 | // is true, then remote server volumes will be included in | |
91 | // the list. When remote server volumes are included in the | |
92 | // list, they will be added to the end of the list. On entry, | |
93 | // *count should contain the size of the array pointed to by | |
94 | // vols. On exit, *count will be set to the number of id numbers | |
95 | // placed in the array. If vMAttribMask is non-zero, then | |
96 | // only volumes with matching attributes are added to the | |
97 | // list of volumes. bits in the vMAttribMask should use the | |
98 | // same encoding as bits in the vMAttrib field of | |
99 | // the GetVolParmsInfoBuffer structure. | |
100 | OSErr BuildVolumeList(Boolean includeRemote, short *vols, | |
101 | long *count, long vMAttribMask) | |
102 | { | |
103 | HParamBlockRec volPB; | |
104 | Boolean isRemote; | |
105 | OSErr err = noErr; | |
106 | long nlocal, nremote; | |
107 | long vMAttrib; | |
108 | ||
109 | // set up and check parameters | |
110 | volPB.volumeParam.ioNamePtr = NULL; | |
111 | nlocal = nremote = 0; | |
112 | if (*count == 0) | |
113 | return noErr; | |
114 | ||
115 | // iterate through volumes | |
116 | for (volPB.volumeParam.ioVolIndex = 1; | |
117 | PBHGetVInfoSync(&volPB) == noErr; | |
118 | volPB.volumeParam.ioVolIndex++) | |
119 | { | |
120 | // skip remote volumes, if necessary | |
121 | err = IsRemoteVolume(volPB.volumeParam.ioVRefNum, &isRemote, &vMAttrib); | |
122 | if (err != noErr) | |
123 | goto bail; | |
124 | ||
125 | if ((includeRemote || !isRemote) && ((vMAttrib & vMAttribMask) == vMAttribMask)) | |
126 | { | |
127 | // add local volumes at the front; remote volumes at the end | |
128 | if (isRemote) | |
129 | vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum; | |
130 | else | |
131 | { | |
132 | if (nremote > 0) | |
133 | BlockMoveData( | |
134 | vols + nlocal, | |
135 | vols + nlocal + 1, | |
136 | nremote * sizeof(short) ); | |
137 | vols[nlocal++] = volPB.volumeParam.ioVRefNum; | |
138 | } | |
139 | ||
140 | // list full? | |
141 | if ((nlocal + nremote) >= *count) | |
142 | break; | |
143 | } | |
144 | } | |
145 | ||
146 | bail: | |
147 | *count = (nlocal + nremote); | |
148 | ||
149 | return err; | |
150 | } | |
151 | ||
152 | ||
153 | // FindApplication iterates through mounted volumes | |
154 | // searching for an application with the given creator | |
155 | // type. If includeRemote is true, then remote volumes | |
156 | // will be searched (after local ones) for an application | |
157 | // with the creator type. | |
158 | // | |
159 | // Hacked to output to appName | |
160 | // | |
161 | #define kMaxVols 20 | |
162 | ||
163 | OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName, FSSpec* appSpec) | |
164 | { | |
165 | short rRefNums[kMaxVols]; | |
166 | long i, volCount; | |
167 | DTPBRec desktopPB; | |
168 | OSErr err; | |
169 | ||
170 | // get a list of volumes - with desktop files | |
171 | volCount = kMaxVols; | |
172 | err = BuildVolumeList(includeRemote, rRefNums, &volCount, (1 << bHasDesktopMgr) ); | |
173 | if (err != noErr) | |
174 | return err; | |
175 | ||
176 | // iterate through the list | |
177 | for (i=0; i<volCount; i++) | |
178 | { | |
179 | // has a desktop file? | |
180 | desktopPB.ioCompletion = NULL; | |
181 | desktopPB.ioVRefNum = rRefNums[i]; | |
182 | desktopPB.ioNamePtr = NULL; | |
183 | desktopPB.ioIndex = 0; | |
184 | err = PBDTGetPath( &desktopPB ); | |
185 | if (err != noErr) | |
186 | continue; | |
187 | ||
188 | // has the correct app?? | |
189 | desktopPB.ioFileCreator = appCreator; | |
190 | desktopPB.ioNamePtr = appName; | |
191 | err = PBDTGetAPPLSync( &desktopPB ); | |
192 | if (err != noErr) | |
193 | continue; | |
194 | ||
195 | // make a file spec referring to it | |
196 | err = FSMakeFSSpec( rRefNums[i], desktopPB.ioAPPLParID, appName, appSpec ); | |
197 | if (err != noErr) | |
198 | continue; | |
199 | ||
200 | // found it! | |
201 | return noErr; | |
202 | } | |
203 | ||
204 | return fnfErr; | |
205 | } | |
206 | ||
207 | // END CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) | |
208 | ||
209 | // yeah, duplicated code | |
210 | pascal OSErr FSpGetFullPath( const FSSpec *spec, | |
211 | short *fullPathLength, | |
212 | Handle *fullPath ) | |
213 | { | |
214 | OSErr result, realResult; | |
215 | FSSpec tempSpec; | |
216 | CInfoPBRec pb; | |
217 | ||
218 | *fullPathLength = 0; | |
219 | *fullPath = NULL; | |
220 | ||
221 | // default to noErr | |
222 | realResult = result = noErr; | |
223 | ||
224 | // work around Nav Services "bug" (it returns invalid FSSpecs with empty names) | |
225 | #if 0 | |
226 | if ( spec->name[0] == 0 ) | |
227 | { | |
228 | result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec); | |
229 | } | |
230 | else | |
231 | { | |
232 | #endif | |
233 | ||
234 | // Make a copy of the input FSSpec that can be modified | |
235 | BlockMoveData( spec, &tempSpec, sizeof(FSSpec) ); | |
236 | ||
237 | if ( result == noErr ) | |
238 | { | |
239 | if ( tempSpec.parID == fsRtParID ) | |
240 | { | |
241 | // object is a volume | |
242 | // Add a colon to make it a full pathname | |
243 | ++tempSpec.name[0]; | |
244 | tempSpec.name[tempSpec.name[0]] = ':'; | |
245 | ||
246 | // We're done | |
247 | result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); | |
248 | } | |
249 | else | |
250 | { | |
251 | // object isn't a volume | |
252 | ||
253 | // Is the object a file or a directory? | |
254 | pb.dirInfo.ioNamePtr = tempSpec.name; | |
255 | pb.dirInfo.ioVRefNum = tempSpec.vRefNum; | |
256 | pb.dirInfo.ioDrDirID = tempSpec.parID; | |
257 | pb.dirInfo.ioFDirIndex = 0; | |
258 | result = PBGetCatInfoSync( &pb ); | |
259 | ||
260 | // Allow file/directory name at end of path to not exist. | |
261 | realResult = result; | |
262 | if ((result == noErr) || (result == fnfErr)) | |
263 | { | |
264 | // if the object is a directory, append a colon so full pathname ends with colon | |
265 | if ((result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0) | |
266 | { | |
267 | ++tempSpec.name[0]; | |
268 | tempSpec.name[tempSpec.name[0]] = ':'; | |
269 | } | |
270 | ||
271 | // Put the object name in first | |
272 | result = PtrToHand( &tempSpec.name[1], fullPath, tempSpec.name[0] ); | |
273 | if ( result == noErr ) | |
274 | { | |
275 | // Get the ancestor directory names | |
276 | pb.dirInfo.ioNamePtr = tempSpec.name; | |
277 | pb.dirInfo.ioVRefNum = tempSpec.vRefNum; | |
278 | pb.dirInfo.ioDrParID = tempSpec.parID; | |
279 | ||
280 | // loop until we have an error or find the root directory | |
281 | do | |
282 | { | |
283 | pb.dirInfo.ioFDirIndex = -1; | |
284 | pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; | |
285 | result = PBGetCatInfoSync(&pb); | |
286 | if ( result == noErr ) | |
287 | { | |
288 | // Append colon to directory name | |
289 | ++tempSpec.name[0]; | |
290 | tempSpec.name[tempSpec.name[0]] = ':'; | |
291 | ||
292 | // Add directory name to beginning of fullPath | |
293 | (void)Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]); | |
294 | result = MemError(); | |
295 | } | |
296 | } | |
297 | while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) ); | |
298 | } | |
299 | } | |
300 | } | |
301 | } | |
302 | ||
303 | if ( result == noErr ) | |
304 | { | |
305 | // Return the length | |
306 | *fullPathLength = GetHandleSize( *fullPath ); | |
307 | result = realResult; // return realResult in case it was fnfErr | |
308 | } | |
309 | else | |
310 | { | |
311 | // Dispose of the handle and return NULL and zero length | |
312 | if ( *fullPath != NULL ) | |
313 | { | |
314 | DisposeHandle( *fullPath ); | |
315 | *fullPath = NULL; | |
316 | } | |
317 | *fullPathLength = 0; | |
318 | } | |
319 | ||
320 | return result; | |
321 | } | |
322 | #endif // LP64 | |
323 | // | |
324 | // On the mac there are two ways to open a file - one is through apple events and the | |
325 | // finder, another is through mime types. | |
326 | // | |
327 | // So, really there are two ways to implement wxFileType... | |
328 | // | |
329 | // Mime types are only available on OS 8.1+ through the InternetConfig API | |
330 | // | |
331 | // Much like the old-style file manager, it has 3 levels of flexibility for its methods - | |
332 | // Low - which means you have to iterate yourself through the mime database | |
333 | // Medium - which lets you sort of cache the database if you want to use lowlevel functions | |
334 | // High - which requires access to the database every time | |
335 | // | |
336 | // We want to be efficient (i.e. professional :) ) about it, so we use a combo of low | |
337 | // and mid-level functions | |
338 | // | |
339 | // TODO: Should we call ICBegin/ICEnd? Then where? | |
340 | // | |
341 | ||
342 | // debug helper | |
343 | inline void wxLogMimeDebug(const wxChar* WXUNUSED_UNLESS_DEBUG(szMsg), OSStatus WXUNUSED_UNLESS_DEBUG(status)) | |
344 | { | |
345 | wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg, __LINE__, (int)status)); | |
346 | } | |
347 | ||
348 | // in case we're compiling in non-GUI mode | |
349 | class WXDLLIMPEXP_FWD_CORE wxIcon; | |
350 | ||
351 | bool wxFileTypeImpl::SetCommand(const wxString& WXUNUSED(cmd), const wxString& WXUNUSED(verb), bool WXUNUSED(overwriteprompt)) | |
352 | { | |
353 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
354 | ||
355 | return false; | |
356 | } | |
357 | ||
358 | bool wxFileTypeImpl::SetDefaultIcon(const wxString& WXUNUSED(strIcon), int WXUNUSED(index)) | |
359 | { | |
360 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
361 | ||
362 | return false; | |
363 | } | |
364 | ||
365 | bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd, | |
366 | const wxFileType::MessageParameters& params) const | |
367 | { | |
368 | wxString cmd = GetCommand(wxT("open")); | |
369 | ||
370 | *openCmd = wxFileType::ExpandCommand(cmd, params); | |
371 | ||
372 | return !openCmd->empty(); | |
373 | } | |
374 | ||
375 | bool | |
376 | wxFileTypeImpl::GetPrintCommand( | |
377 | wxString *printCmd, | |
378 | const wxFileType::MessageParameters& params) const | |
379 | { | |
380 | wxString cmd = GetCommand(wxT("print")); | |
381 | ||
382 | *printCmd = wxFileType::ExpandCommand(cmd, params); | |
383 | ||
384 | return !printCmd->empty(); | |
385 | } | |
386 | ||
387 | // | |
388 | // Internet Config vs. Launch Services | |
389 | // | |
390 | // From OS 8 on there was internet config... | |
391 | // However, OSX and its finder does not use info | |
392 | // from Internet Config at all - the Internet Config | |
393 | // database ONLY CONTAINS APPS THAT ARE CLASSIC APPS | |
394 | // OR REGISTERED THROUGH INTERNET CONFIG | |
395 | // | |
396 | // Therefore on OSX in order for the open command to be useful | |
397 | // we need to go straight to launch services | |
398 | // | |
399 | ||
400 | //on darwin, use launch services | |
401 | ||
402 | #include <ApplicationServices/ApplicationServices.h> | |
403 | ||
404 | wxString wxFileTypeImpl::GetCommand(const wxString& verb) const | |
405 | { | |
406 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
407 | ||
408 | if (verb == wxT("open")) | |
409 | { | |
410 | ICMapEntry entry; | |
411 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, | |
412 | (Handle) m_manager->m_hDatabase, | |
413 | m_lIndex, &entry); | |
414 | ||
415 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
416 | sCurrentExtension = sCurrentExtension.Right(sCurrentExtension.length()-1 ); | |
417 | ||
418 | //type, creator, ext, roles, outapp (FSRef), outappurl | |
419 | CFURLRef cfurlAppPath; | |
420 | OSStatus status = LSGetApplicationForInfo( kLSUnknownType, | |
421 | kLSUnknownCreator, | |
422 | wxCFStringRef(sCurrentExtension, wxLocale::GetSystemEncoding()), | |
423 | kLSRolesAll, | |
424 | NULL, | |
425 | &cfurlAppPath ); | |
426 | ||
427 | if (status == noErr) | |
428 | { | |
429 | CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(cfurlAppPath, kCFURLPOSIXPathStyle); | |
430 | CFRelease(cfurlAppPath); | |
431 | ||
432 | // PHEW! Success! | |
433 | // Since a filename might have spaces in it, so surround it with quotes | |
434 | if (cfsUnixPath) | |
435 | { | |
436 | wxString resultStr; | |
437 | ||
438 | resultStr = | |
439 | wxString(wxT("'")) | |
440 | + wxCFStringRef(cfsUnixPath).AsString(wxLocale::GetSystemEncoding()) | |
441 | + wxString(wxT("'")); | |
442 | ||
443 | return resultStr; | |
444 | } | |
445 | } | |
446 | else | |
447 | { | |
448 | wxLogDebug(wxString::Format(wxT("%i - %s - %i"), | |
449 | __LINE__, | |
450 | wxT("LSGetApplicationForInfo failed."), | |
451 | (int)status)); | |
452 | } | |
453 | } | |
454 | ||
455 | return wxEmptyString; | |
456 | } | |
457 | ||
458 | bool wxFileTypeImpl::GetDescription(wxString *desc) const | |
459 | { | |
460 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
461 | ||
462 | ICMapEntry entry; | |
463 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, | |
464 | (Handle) m_manager->m_hDatabase, m_lIndex, &entry ); | |
465 | ||
466 | *desc = wxMacMakeStringFromPascal( entry.entryName ); | |
467 | ||
468 | return true; | |
469 | } | |
470 | ||
471 | bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) | |
472 | { | |
473 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
474 | ||
475 | ICMapEntry entry; | |
476 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, | |
477 | (Handle) m_manager->m_hDatabase, m_lIndex, &entry ); | |
478 | ||
479 | //entry has period in it | |
480 | wxString sCurrentExtension = wxMacMakeStringFromPascal( entry.extension ); | |
481 | extensions.Add( sCurrentExtension.Right( sCurrentExtension.length() - 1 ) ); | |
482 | ||
483 | return true; | |
484 | } | |
485 | ||
486 | bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const | |
487 | { | |
488 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
489 | ||
490 | ICMapEntry entry; | |
491 | ICGetMapEntry( (ICInstance) m_manager->m_hIC, | |
492 | (Handle) m_manager->m_hDatabase, m_lIndex, &entry ); | |
493 | ||
494 | *mimeType = wxMacMakeStringFromPascal(entry.MIMEType); | |
495 | ||
496 | return true; | |
497 | } | |
498 | ||
499 | bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const | |
500 | { | |
501 | wxString s; | |
502 | ||
503 | if (GetMimeType(&s)) | |
504 | { | |
505 | mimeTypes.Clear(); | |
506 | mimeTypes.Add(s); | |
507 | ||
508 | return true; | |
509 | } | |
510 | ||
511 | return false; | |
512 | } | |
513 | ||
514 | bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const | |
515 | { | |
516 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
517 | ||
518 | // no such file type or no value or incorrect icon entry | |
519 | return false; | |
520 | } | |
521 | ||
522 | size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, | |
523 | wxArrayString * commands, | |
524 | const wxFileType::MessageParameters& params) const | |
525 | { | |
526 | wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); | |
527 | ||
528 | wxString sCommand; | |
529 | size_t ulCount = 0; | |
530 | ||
531 | if (GetOpenCommand(&sCommand, params)) | |
532 | { | |
533 | verbs->Add(wxString(wxT("open"))); | |
534 | commands->Add(sCommand); | |
535 | ++ulCount; | |
536 | } | |
537 | ||
538 | return ulCount; | |
539 | } | |
540 | ||
541 | void wxMimeTypesManagerImpl::Initialize(int WXUNUSED(mailcapStyles), const wxString& WXUNUSED(extraDir)) | |
542 | { | |
543 | wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!")); | |
544 | ||
545 | // some apps (non-wx) use the 'plst' resource instead | |
546 | #if 0 | |
547 | CFBundleRef cfbMain = CFBundleGetMainBundle(); | |
548 | wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN ); | |
549 | wxString sLog; | |
550 | cfdInfo.PrintOut(sLog); | |
551 | wxLogDebug(sLog); | |
552 | #endif | |
553 | ||
554 | // start Internet Config - log if there's an error | |
555 | // the second param is the signature of the application, also known | |
556 | // as resource ID 0. However, as per some recent discussions, we may not | |
557 | // have a signature for this app, so a generic 'APPL' which is the executable | |
558 | // type will work for now. | |
559 | OSStatus status = ICStart( (ICInstance*)&m_hIC, 'APPL' ); | |
560 | ||
561 | if (status != noErr) | |
562 | { | |
563 | wxLogDebug(wxT("Could not initialize wxMimeTypesManager!")); | |
564 | wxFAIL; | |
565 | m_hIC = NULL; | |
566 | ||
567 | return; | |
568 | } | |
569 | ||
570 | ICAttr attr; | |
571 | m_hDatabase = (void**) NewHandle(0); | |
572 | status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase ); | |
573 | ||
574 | //the database file can be corrupt (on OSX its | |
575 | //~/Library/Preferences/com.apple.internetconfig.plist) | |
576 | //- bail if it is | |
577 | if (status != noErr) | |
578 | { | |
579 | ClearData(); | |
580 | wxLogDebug(wxT("Corrupt MIME database!")); | |
581 | return; | |
582 | } | |
583 | ||
584 | //obtain the number of entries in the map | |
585 | status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount ); | |
586 | wxASSERT( status == noErr ); | |
587 | ||
588 | #if 0 | |
589 | //debug stuff | |
590 | ICMapEntry entry; | |
591 | long pos; | |
592 | ||
593 | for (long i = 1; i <= m_lCount; ++i) | |
594 | { | |
595 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); | |
596 | ||
597 | if (status == noErr) | |
598 | { | |
599 | wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName); | |
600 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
601 | wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType); | |
602 | ||
603 | wxFileTypeImpl impl; | |
604 | impl.Init(this, pos); | |
605 | ||
606 | if (sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html")) | |
607 | { | |
608 | wxString cmd; | |
609 | ||
610 | impl.GetOpenCommand( &cmd, wxFileType::MessageParameters (wxT("http://www.google.com"))); | |
611 | wxPrintf(wxT("APP: [%s]\n"), cmd.c_str()); | |
612 | } | |
613 | } | |
614 | } | |
615 | #endif | |
616 | } | |
617 | ||
618 | void wxMimeTypesManagerImpl::ClearData() | |
619 | { | |
620 | if (m_hIC != NULL) | |
621 | { | |
622 | DisposeHandle( (Handle)m_hDatabase ); | |
623 | ||
624 | // this can return an error, but we don't really care that much about it | |
625 | ICStop( (ICInstance)m_hIC ); | |
626 | m_hIC = NULL; | |
627 | } | |
628 | } | |
629 | ||
630 | // | |
631 | // Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting? | |
3d167787 | 632 | // A) Some intermediate indexes are bad while subsequent ones may be good. Its weird, I know. |
489468fe SC |
633 | // |
634 | ||
635 | // extension -> file type | |
636 | wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e) | |
637 | { | |
638 | wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); | |
639 | ||
640 | //low level functions - iterate through the database | |
641 | ICMapEntry entry; | |
642 | long pos; | |
643 | ||
644 | for (long i = 1; i <= m_lCount; ++i) | |
645 | { | |
646 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); | |
647 | ||
648 | if (status == noErr) | |
649 | { | |
650 | wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); | |
651 | if ( sCurrentExtension.Right(sCurrentExtension.length() - 1) == e ) // entry has period in it | |
652 | { | |
653 | wxFileType* pFileType = new wxFileType(); | |
654 | pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); | |
655 | ||
656 | return pFileType; | |
657 | } | |
658 | } | |
659 | } | |
660 | ||
661 | return NULL; | |
662 | } | |
663 | ||
664 | // MIME type -> extension -> file type | |
665 | wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) | |
666 | { | |
667 | wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); | |
668 | ||
669 | ICMapEntry entry; | |
670 | long pos; | |
671 | ||
672 | // low level functions - iterate through the database | |
673 | for (long i = 1; i <= m_lCount; ++i) | |
674 | { | |
675 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); | |
676 | wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) ); | |
677 | ||
678 | if (status == noErr) | |
679 | { | |
680 | if ( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType) | |
681 | { | |
682 | wxFileType* pFileType = new wxFileType(); | |
683 | pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); | |
684 | ||
685 | return pFileType; | |
686 | } | |
687 | } | |
688 | } | |
689 | ||
690 | return NULL; | |
691 | } | |
692 | ||
693 | size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes) | |
694 | { | |
695 | wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); | |
696 | ||
697 | ICMapEntry entry; | |
698 | long pos, lStartCount; | |
699 | ||
700 | // low level functions - iterate through the database | |
701 | lStartCount = (long) mimetypes.GetCount(); | |
702 | for (long i = 1; i <= m_lCount; ++i) | |
703 | { | |
704 | OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); | |
705 | if ( status == noErr ) | |
706 | mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) ); | |
707 | } | |
708 | ||
709 | return mimetypes.GetCount() - lStartCount; | |
710 | } | |
711 | ||
712 | pascal OSStatus MoreProcGetProcessTypeSignature( | |
713 | const ProcessSerialNumberPtr pPSN, | |
714 | OSType *pProcessType, | |
715 | OSType *pCreator) | |
716 | { | |
717 | OSStatus anErr = noErr; | |
718 | ProcessInfoRec infoRec; | |
719 | ProcessSerialNumber localPSN; | |
720 | ||
721 | infoRec.processInfoLength = sizeof(ProcessInfoRec); | |
722 | infoRec.processName = NULL; | |
723 | #ifndef __LP64__ | |
724 | infoRec.processAppSpec = NULL; | |
725 | #endif | |
726 | ||
727 | if ( pPSN == NULL ) | |
728 | { | |
729 | localPSN.highLongOfPSN = 0; | |
730 | localPSN.lowLongOfPSN = kCurrentProcess; | |
731 | } | |
732 | else | |
733 | { | |
734 | localPSN = *pPSN; | |
735 | } | |
736 | ||
737 | anErr = GetProcessInformation(&localPSN, &infoRec); | |
738 | if (anErr == noErr) | |
739 | { | |
740 | *pProcessType = infoRec.processType; | |
741 | *pCreator = infoRec.processSignature; | |
742 | } | |
743 | ||
744 | return anErr; | |
745 | } | |
746 | ||
747 | // | |
748 | // | |
749 | // TODO: clean this up, its messy | |
750 | // | |
751 | // | |
752 | // | |
753 | ||
1f0c8f31 | 754 | #include "wx/osx/core/cfstring.h" |
489468fe SC |
755 | |
756 | #define wxCF_RELEASE true | |
757 | #define wxCF_RETAIN false | |
758 | ||
759 | // ---------------------------------------------------------------------------- | |
760 | // wxCFDictionary | |
761 | // ---------------------------------------------------------------------------- | |
762 | ||
763 | class wxCFDictionary | |
764 | { | |
765 | public: | |
766 | wxCFDictionary(CFTypeRef ref) | |
767 | { | |
768 | m_cfmdRef = (CFMutableDictionaryRef) ref; | |
769 | } | |
770 | ||
771 | wxCFDictionary(CFIndex cfiSize = 0) | |
772 | { | |
773 | CFDictionaryKeyCallBacks kcbs; | |
774 | CFDictionaryValueCallBacks vcbs; | |
775 | BuildKeyCallbacks(&kcbs); | |
776 | BuildValueCallbacks(&vcbs); | |
777 | ||
778 | m_cfmdRef = CFDictionaryCreateMutable( | |
779 | kCFAllocatorDefault, cfiSize, &kcbs, &vcbs ); | |
780 | } | |
781 | ||
782 | ~wxCFDictionary() | |
783 | { Clear(); } | |
784 | ||
785 | void Clear() | |
786 | { | |
787 | if (m_cfmdRef) | |
788 | CFRelease(m_cfmdRef); | |
789 | } | |
790 | ||
791 | static const void* RetainProc(CFAllocatorRef, const void* v) | |
792 | { return (const void*) CFRetain(v); } | |
793 | ||
794 | static void ReleaseProc(CFAllocatorRef, const void* v) | |
795 | { CFRelease(v); } | |
796 | ||
797 | void MakeMutable(CFIndex cfiSize = 0) | |
798 | { | |
799 | CFDictionaryRef oldref = (CFDictionaryRef) m_cfmdRef; | |
800 | ||
801 | m_cfmdRef = CFDictionaryCreateMutableCopy( | |
802 | kCFAllocatorDefault, cfiSize, oldref ); | |
803 | ||
804 | CFRelease( oldref ); | |
805 | } | |
806 | ||
807 | void BuildKeyCallbacks(CFDictionaryKeyCallBacks* pCbs) | |
808 | { | |
809 | pCbs->version = 0; | |
810 | pCbs->retain = RetainProc; | |
811 | pCbs->release = ReleaseProc; | |
812 | pCbs->copyDescription = NULL; | |
813 | pCbs->equal = NULL; | |
814 | pCbs->hash = NULL; | |
815 | } | |
816 | ||
817 | void BuildValueCallbacks(CFDictionaryValueCallBacks* pCbs) | |
818 | { | |
819 | pCbs->version = 0; | |
820 | pCbs->retain = RetainProc; | |
821 | pCbs->release = ReleaseProc; | |
822 | pCbs->copyDescription = NULL; | |
823 | pCbs->equal = NULL; | |
824 | } | |
825 | ||
826 | operator CFTypeRef () const | |
827 | { return (CFTypeRef)m_cfmdRef; } | |
828 | ||
829 | CFDictionaryRef GetCFDictionary() const | |
830 | { return (CFDictionaryRef)m_cfmdRef; } | |
831 | ||
832 | CFMutableDictionaryRef GetCFMutableDictionary() | |
833 | { return (CFMutableDictionaryRef) m_cfmdRef; } | |
834 | ||
835 | CFTypeRef operator [] (CFTypeRef cftEntry) const | |
836 | { | |
837 | wxASSERT(IsValid()); | |
838 | return (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef)m_cfmdRef, cftEntry); | |
839 | } | |
840 | ||
841 | CFIndex GetCount() const | |
842 | { | |
843 | wxASSERT(IsValid()); | |
844 | return CFDictionaryGetCount((CFDictionaryRef)m_cfmdRef); | |
845 | } | |
846 | ||
847 | void Add(CFTypeRef cftKey, CFTypeRef cftValue) | |
848 | { | |
849 | wxASSERT(IsValid()); | |
850 | wxASSERT(Exists(cftKey) == false); | |
851 | CFDictionaryAddValue(m_cfmdRef, cftKey, cftValue); | |
852 | } | |
853 | ||
854 | void Remove(CFTypeRef cftKey) | |
855 | { | |
856 | wxASSERT(IsValid()); | |
857 | wxASSERT(Exists(cftKey)); | |
858 | CFDictionaryRemoveValue(m_cfmdRef, cftKey); | |
859 | } | |
860 | ||
861 | void Set(CFTypeRef cftKey, CFTypeRef cftValue) | |
862 | { | |
863 | wxASSERT(IsValid()); | |
864 | wxASSERT(Exists(cftKey)); | |
865 | CFDictionarySetValue(m_cfmdRef, cftKey, cftValue); | |
866 | } | |
867 | ||
868 | bool Exists(CFTypeRef cftKey) const | |
869 | { | |
870 | wxASSERT(IsValid()); | |
871 | return CFDictionaryContainsKey((CFDictionaryRef)m_cfmdRef, cftKey); | |
872 | } | |
873 | ||
874 | bool IsOk() const | |
875 | { return m_cfmdRef != NULL; } | |
876 | ||
877 | bool IsValid() const | |
878 | { return IsOk() && CFGetTypeID((CFTypeRef)m_cfmdRef) == CFDictionaryGetTypeID(); } | |
879 | ||
880 | void PrintOut(wxString& sMessage) | |
881 | { | |
882 | PrintOutDictionary(sMessage, m_cfmdRef); | |
883 | } | |
884 | ||
885 | static void PrintOutDictionary(wxString& sMessage, CFDictionaryRef cfdRef) | |
886 | { | |
887 | CFIndex cfiCount = CFDictionaryGetCount(cfdRef); | |
888 | CFTypeRef* pKeys = new CFTypeRef[cfiCount]; | |
889 | CFTypeRef* pValues = new CFTypeRef[cfiCount]; | |
890 | ||
891 | CFDictionaryGetKeysAndValues(cfdRef, pKeys, pValues); | |
892 | ||
893 | for (CFIndex i = 0; i < cfiCount; ++i) | |
894 | { | |
895 | wxString sKey = wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(pKeys[i]))).AsString(); | |
896 | wxString sValue = wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(pValues[i]))).AsString(); | |
897 | ||
898 | sMessage << | |
899 | wxString::Format(wxT("[{#%d} Key : %s]"), (int) i, | |
900 | sKey.c_str()); | |
901 | ||
902 | PrintOutType(sMessage, sKey, pKeys[i]); | |
903 | ||
904 | sMessage << | |
905 | wxString::Format(wxT("\n\t[Value : %s]"), | |
906 | sValue.c_str()); | |
907 | ||
908 | PrintOutType(sMessage, sValue, pValues[i]); | |
909 | ||
910 | sMessage << wxT("\n"); | |
911 | } | |
912 | ||
913 | delete [] pKeys; | |
914 | delete [] pValues; | |
915 | } | |
916 | ||
917 | static void PrintOutArray(wxString& sMessage, CFArrayRef cfaRef) | |
918 | { | |
919 | for (CFIndex i = 0; i < CFArrayGetCount(cfaRef); ++i) | |
920 | { | |
921 | wxString sValue = wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID( | |
922 | CFArrayGetValueAtIndex(cfaRef, i) | |
923 | ))).AsString(); | |
924 | ||
925 | sMessage << | |
926 | wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i, | |
927 | sValue.c_str()); | |
928 | ||
929 | PrintOutType(sMessage, sValue, CFArrayGetValueAtIndex(cfaRef, i)); | |
930 | } | |
931 | } | |
932 | ||
933 | static void PrintOutType(wxString& sMessage, const wxString& sValue, CFTypeRef cfRef) | |
934 | { | |
935 | sMessage << wxT(" {"); | |
936 | ||
937 | if (sValue == wxT("CFString")) | |
938 | { | |
939 | sMessage << wxCFStringRef(wxCFRetain((CFStringRef)cfRef)).AsString(); | |
940 | } | |
941 | else if (sValue == wxT("CFNumber")) | |
942 | { | |
943 | int nOut; | |
944 | CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &nOut); | |
945 | sMessage << nOut; | |
946 | } | |
947 | else if (sValue == wxT("CFDictionary")) | |
948 | { | |
949 | PrintOutDictionary(sMessage, (CFDictionaryRef)cfRef); | |
950 | } | |
951 | else if (sValue == wxT("CFArray")) | |
952 | { | |
953 | PrintOutArray(sMessage, (CFArrayRef)cfRef); | |
954 | } | |
955 | else if (sValue == wxT("CFBoolean")) | |
956 | { | |
957 | sMessage << (cfRef == kCFBooleanTrue ? wxT("true") : wxT("false")); | |
958 | } | |
959 | else if (sValue == wxT("CFURL")) | |
960 | { | |
961 | sMessage << wxCFStringRef(CFURLCopyPath((CFURLRef) cfRef)).AsString(); | |
962 | } | |
963 | else | |
964 | { | |
965 | sMessage << wxT("*****UNKNOWN TYPE******"); | |
966 | } | |
967 | ||
968 | sMessage << wxT("} "); | |
969 | } | |
970 | ||
971 | #if wxUSE_MIMETYPE | |
972 | void MakeValidXML(); | |
973 | #endif | |
974 | ||
975 | CFTypeRef WriteAsXML() | |
976 | { | |
977 | return CFPropertyListCreateXMLData(kCFAllocatorDefault, m_cfmdRef); | |
978 | } | |
979 | ||
980 | bool ReadAsXML(CFTypeRef cfData, wxString* pErrorMsg = NULL) | |
981 | { | |
982 | Clear(); | |
983 | CFStringRef cfsError=NULL; | |
984 | m_cfmdRef = (CFMutableDictionaryRef) CFPropertyListCreateFromXMLData( | |
985 | kCFAllocatorDefault, | |
986 | (CFDataRef)cfData, | |
987 | kCFPropertyListMutableContainersAndLeaves, | |
988 | &cfsError ); | |
989 | if (cfsError) | |
990 | { | |
991 | if (pErrorMsg) | |
992 | *pErrorMsg = wxCFStringRef(cfsError).AsString(); | |
993 | else | |
994 | CFRelease(cfsError); | |
995 | } | |
996 | ||
997 | return m_cfmdRef != NULL; | |
998 | } | |
999 | ||
1000 | private: | |
1001 | CFMutableDictionaryRef m_cfmdRef; | |
1002 | }; | |
1003 | ||
1004 | // ---------------------------------------------------------------------------- | |
1005 | // wxCFArray | |
1006 | // ---------------------------------------------------------------------------- | |
1007 | ||
1008 | class wxCFArray | |
1009 | { | |
1010 | public: | |
1011 | wxCFArray(CFTypeRef ref) | |
1012 | { | |
1013 | m_cfmaRef = (CFMutableArrayRef)ref; | |
1014 | } | |
1015 | ||
1016 | wxCFArray(CFIndex cfiSize = 0) : m_cfmaRef(NULL) | |
1017 | { Create(cfiSize); } | |
1018 | ||
1019 | ~wxCFArray() | |
1020 | { Clear(); } | |
1021 | ||
1022 | void MakeMutable(CFIndex cfiSize = 0) | |
1023 | { | |
1024 | wxASSERT(IsValid()); | |
1025 | ||
1026 | CFMutableArrayRef oldref = m_cfmaRef; | |
1027 | m_cfmaRef = CFArrayCreateMutableCopy( | |
1028 | kCFAllocatorDefault, | |
1029 | cfiSize, | |
1030 | (CFArrayRef)oldref); | |
1031 | CFRelease(oldref); | |
1032 | } | |
1033 | ||
1034 | void BuildCallbacks(CFArrayCallBacks* pCbs) | |
1035 | { | |
1036 | pCbs->version = 0; | |
1037 | pCbs->retain = RetainProc; | |
1038 | pCbs->release = ReleaseProc; | |
1039 | pCbs->copyDescription = NULL; | |
1040 | pCbs->equal = NULL; | |
1041 | } | |
1042 | ||
1043 | void Create(CFIndex cfiSize = 0) | |
1044 | { | |
1045 | Clear(); | |
1046 | CFArrayCallBacks cb; | |
1047 | BuildCallbacks(&cb); | |
1048 | ||
1049 | m_cfmaRef = CFArrayCreateMutable(kCFAllocatorDefault, cfiSize, &cb); | |
1050 | } | |
1051 | ||
1052 | void Clear() | |
1053 | { if (m_cfmaRef) CFRelease(m_cfmaRef); } | |
1054 | ||
1055 | static const void* RetainProc(CFAllocatorRef, const void* v) | |
1056 | { return (const void*) CFRetain(v); } | |
1057 | ||
1058 | static void ReleaseProc(CFAllocatorRef, const void* v) | |
1059 | { CFRelease(v); } | |
1060 | ||
1061 | operator CFTypeRef () const | |
1062 | { return (CFTypeRef)m_cfmaRef; } | |
1063 | ||
1064 | CFArrayRef GetCFArray() const | |
1065 | { return (CFArrayRef)m_cfmaRef; } | |
1066 | ||
1067 | CFMutableArrayRef GetCFMutableArray() | |
1068 | { return (CFMutableArrayRef) m_cfmaRef; } | |
1069 | ||
1070 | CFTypeRef operator [] (CFIndex cfiIndex) const | |
1071 | { | |
1072 | wxASSERT(IsValid()); | |
1073 | return (CFTypeRef) CFArrayGetValueAtIndex((CFArrayRef)m_cfmaRef, cfiIndex); | |
1074 | } | |
1075 | ||
1076 | CFIndex GetCount() | |
1077 | { | |
1078 | wxASSERT(IsValid()); | |
1079 | return CFArrayGetCount((CFArrayRef)m_cfmaRef); | |
1080 | } | |
1081 | ||
1082 | void Add(CFTypeRef cftValue) | |
1083 | { | |
1084 | wxASSERT(IsValid()); | |
1085 | CFArrayAppendValue(m_cfmaRef, cftValue); | |
1086 | } | |
1087 | ||
1088 | void Remove(CFIndex cfiIndex) | |
1089 | { | |
1090 | wxASSERT(IsValid()); | |
1091 | wxASSERT(cfiIndex < GetCount()); | |
1092 | CFArrayRemoveValueAtIndex(m_cfmaRef, cfiIndex); | |
1093 | } | |
1094 | ||
1095 | void Set(CFIndex cfiIndex, CFTypeRef cftValue) | |
1096 | { | |
1097 | wxASSERT(IsValid()); | |
1098 | wxASSERT(cfiIndex < GetCount()); | |
1099 | CFArraySetValueAtIndex(m_cfmaRef, cfiIndex, cftValue); | |
1100 | } | |
1101 | ||
1102 | bool IsOk() const | |
1103 | { return m_cfmaRef != NULL; } | |
1104 | ||
1105 | bool IsValid() const | |
1106 | { | |
1107 | return IsOk() && CFGetTypeID((CFTypeRef)m_cfmaRef) == CFArrayGetTypeID(); | |
1108 | } | |
1109 | ||
1110 | #if wxUSE_MIMETYPE | |
1111 | void MakeValidXML(); | |
1112 | #endif | |
1113 | ||
1114 | private: | |
1115 | CFMutableArrayRef m_cfmaRef; | |
1116 | }; | |
1117 | ||
1118 | // ---------------------------------------------------------------------------- | |
1119 | // wxCFNumber | |
1120 | // ---------------------------------------------------------------------------- | |
1121 | ||
1122 | class wxCFNumber | |
1123 | { | |
1124 | public: | |
1125 | wxCFNumber(int nValue) | |
1126 | { | |
1127 | m_cfnRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &nValue); | |
1128 | } | |
1129 | ||
1130 | wxCFNumber(CFTypeRef ref) : m_cfnRef((CFNumberRef)ref) | |
1131 | { | |
1132 | } | |
1133 | ||
1134 | virtual ~wxCFNumber() | |
1135 | { | |
1136 | if (m_cfnRef) | |
1137 | CFRelease(m_cfnRef); | |
1138 | } | |
1139 | ||
1140 | operator CFTypeRef() const | |
1141 | { return (CFTypeRef) m_cfnRef; } | |
1142 | ||
1143 | int GetValue() | |
1144 | { | |
1145 | int nOut; | |
1146 | CFNumberGetValue( m_cfnRef, kCFNumberIntType, &nOut ); | |
1147 | ||
1148 | return nOut; | |
1149 | } | |
1150 | ||
1151 | bool IsOk() | |
1152 | { return m_cfnRef != NULL; } | |
1153 | ||
1154 | private: | |
1155 | CFNumberRef m_cfnRef; | |
1156 | }; | |
1157 | ||
1158 | // ---------------------------------------------------------------------------- | |
1159 | // wxCFURL | |
1160 | // ---------------------------------------------------------------------------- | |
1161 | ||
1162 | class wxCFURL | |
1163 | { | |
1164 | public: | |
1165 | wxCFURL(CFTypeRef ref = NULL) : m_cfurlRef((CFURLRef)ref) | |
1166 | { | |
1167 | } | |
1168 | ||
1169 | wxCFURL(const wxCFStringRef& URLString, CFTypeRef BaseURL = NULL) | |
1170 | { | |
1171 | Create(URLString, BaseURL); | |
1172 | } | |
1173 | ||
1174 | void Create(const wxCFStringRef& URLString, CFTypeRef BaseURL = NULL) | |
1175 | { | |
1176 | m_cfurlRef = CFURLCreateWithString( | |
1177 | kCFAllocatorDefault, | |
1178 | (CFStringRef)(CFTypeRef)URLString, | |
1179 | (CFURLRef) BaseURL); | |
1180 | } | |
1181 | ||
1182 | virtual ~wxCFURL() | |
1183 | { | |
1184 | if (m_cfurlRef) | |
1185 | CFRelease(m_cfurlRef); | |
1186 | } | |
1187 | ||
1188 | wxString BuildWXString() | |
1189 | { | |
1190 | return wxCFStringRef(CFURLCopyPath(m_cfurlRef)).AsString(); | |
1191 | } | |
1192 | ||
1193 | operator CFTypeRef() const | |
1194 | { return (CFTypeRef)m_cfurlRef; } | |
1195 | ||
1196 | bool IsOk() | |
1197 | { return m_cfurlRef != NULL; } | |
1198 | ||
1199 | private: | |
1200 | CFURLRef m_cfurlRef; | |
1201 | }; | |
1202 | ||
1203 | // ---------------------------------------------------------------------------- | |
1204 | // wxCFData | |
1205 | // ---------------------------------------------------------------------------- | |
1206 | ||
1207 | #define wxCFDATA_RELEASEBUFFER 1 | |
1208 | ||
1209 | class wxCFData | |
1210 | { | |
1211 | public: | |
1212 | wxCFData(CFTypeRef ref) : m_cfdaRef((CFDataRef)ref) | |
1213 | { | |
1214 | } | |
1215 | ||
1216 | wxCFData(const UInt8* pBytes, CFIndex len, bool bKeep = wxCFDATA_RELEASEBUFFER) | |
1217 | { | |
1218 | if (bKeep == wxCFDATA_RELEASEBUFFER) | |
1219 | { | |
1220 | m_cfdaRef = CFDataCreateWithBytesNoCopy | |
1221 | (kCFAllocatorDefault, pBytes, len, kCFAllocatorDefault); | |
1222 | } | |
1223 | else | |
1224 | { | |
1225 | m_cfdaRef = CFDataCreate(kCFAllocatorDefault, pBytes, len); | |
1226 | } | |
1227 | } | |
1228 | ||
1229 | virtual ~wxCFData() | |
1230 | { | |
1231 | if (m_cfdaRef) | |
1232 | CFRelease(m_cfdaRef); | |
1233 | } | |
1234 | ||
1235 | const UInt8* GetValue() | |
1236 | { return CFDataGetBytePtr(m_cfdaRef); } | |
1237 | ||
1238 | CFIndex GetCount() | |
1239 | { return CFDataGetLength(m_cfdaRef); } | |
1240 | ||
1241 | operator CFTypeRef() const | |
1242 | { return (CFTypeRef)m_cfdaRef; } | |
1243 | ||
1244 | bool IsOk() | |
1245 | { return m_cfdaRef != NULL; } | |
1246 | ||
1247 | private: | |
1248 | CFDataRef m_cfdaRef; | |
1249 | }; | |
1250 | ||
1251 | void wxCFDictionary::MakeValidXML() | |
1252 | { | |
1253 | CFIndex cfiCount = GetCount(); | |
1254 | CFTypeRef* pKeys = new CFTypeRef[cfiCount]; | |
1255 | CFTypeRef* pValues = new CFTypeRef[cfiCount]; | |
1256 | ||
1257 | CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues); | |
1258 | ||
1259 | // for plist xml format, all dictionary keys must be cfstrings and | |
1260 | // no values in the dictionary or subkeys/values can be NULL; | |
1261 | // additionally, CFURLs are not allowed | |
1262 | for (CFIndex i = 0; i < cfiCount; ++i) | |
1263 | { | |
1264 | // must be an array, dictionary, string, bool, or int and cannot be null | |
1265 | // and dictionaries can only contain cfstring keys | |
1266 | CFTypeRef cfRef = pValues[i]; | |
1267 | if (!pKeys[i] || | |
1268 | CFGetTypeID(pKeys[i]) != CFStringGetTypeID() || | |
1269 | !cfRef) | |
1270 | { | |
1271 | Remove(pKeys[i]); | |
1272 | --i; | |
1273 | --cfiCount; | |
1274 | delete [] pKeys; | |
1275 | delete [] pValues; | |
1276 | pKeys = new CFTypeRef[cfiCount]; | |
1277 | pValues = new CFTypeRef[cfiCount]; | |
1278 | CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues); | |
1279 | } | |
1280 | else if (CFGetTypeID(cfRef) == CFArrayGetTypeID()) | |
1281 | { | |
1282 | CFRetain(cfRef); | |
1283 | wxCFArray cfaCurrent(cfRef); | |
1284 | cfaCurrent.MakeMutable(); | |
1285 | cfaCurrent.MakeValidXML(); | |
1286 | Set(pKeys[i], cfaCurrent); | |
1287 | } | |
1288 | else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID()) | |
1289 | { | |
1290 | CFRetain(cfRef); | |
1291 | wxCFDictionary cfdCurrent(cfRef); | |
1292 | cfdCurrent.MakeMutable(); | |
1293 | cfdCurrent.MakeValidXML(); | |
1294 | Set(pKeys[i], cfdCurrent); | |
1295 | } | |
1296 | else if ( CFGetTypeID(cfRef) != CFStringGetTypeID() && | |
1297 | CFGetTypeID(cfRef) != CFNumberGetTypeID() && | |
1298 | CFGetTypeID(cfRef) != CFBooleanGetTypeID() ) | |
1299 | { | |
1300 | Remove(pKeys[i]); | |
1301 | --i; | |
1302 | --cfiCount; | |
1303 | delete [] pKeys; | |
1304 | delete [] pValues; | |
1305 | pKeys = new CFTypeRef[cfiCount]; | |
1306 | pValues = new CFTypeRef[cfiCount]; | |
1307 | CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues); | |
1308 | } | |
1309 | } | |
1310 | ||
1311 | delete [] pValues; | |
1312 | delete [] pKeys; | |
1313 | } | |
1314 | ||
1315 | void wxCFArray::MakeValidXML() | |
1316 | { | |
1317 | for (CFIndex i = 0; i < GetCount(); ++i) | |
1318 | { | |
1319 | //must be an array, dictionary, string, bool, or int and cannot be null | |
1320 | //and dictionaries can only contain cfstring keys | |
1321 | CFTypeRef cfRef = (*this)[i]; | |
1322 | if (!cfRef) | |
1323 | { | |
1324 | Remove(i); | |
1325 | --i; | |
1326 | } | |
1327 | else if (CFGetTypeID(cfRef) == CFArrayGetTypeID()) | |
1328 | { | |
1329 | CFRetain(cfRef); | |
1330 | wxCFArray cfaCurrent(cfRef); | |
1331 | cfaCurrent.MakeMutable(); | |
1332 | cfaCurrent.MakeValidXML(); | |
1333 | Set(i, cfaCurrent); | |
1334 | } | |
1335 | else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID()) | |
1336 | { | |
1337 | CFRetain(cfRef); | |
1338 | wxCFDictionary cfdCurrent(cfRef); | |
1339 | cfdCurrent.MakeMutable(); | |
1340 | cfdCurrent.MakeValidXML(); | |
1341 | Set(i, cfdCurrent); | |
1342 | } | |
1343 | else if ( CFGetTypeID(cfRef) != CFStringGetTypeID() && | |
1344 | CFGetTypeID(cfRef) != CFNumberGetTypeID() && | |
1345 | CFGetTypeID(cfRef) != CFBooleanGetTypeID() ) | |
1346 | { | |
1347 | Remove(i); | |
1348 | --i; | |
1349 | } | |
1350 | } | |
1351 | } | |
1352 | ||
1353 | // | |
1354 | // | |
1355 | // | |
1356 | // END TODO | |
1357 | // | |
1358 | // | |
1359 | // | |
1360 | ||
1361 | wxFileType* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) | |
1362 | { | |
1363 | bool bInfoSuccess = false; | |
1364 | ||
1365 | const wxArrayString& asExtensions = ftInfo.GetExtensions(); | |
1366 | size_t dwFoundIndex = 0; | |
1367 | if (!asExtensions.GetCount()) | |
1368 | { | |
1369 | wxLogDebug(wxT("Must have extension to associate with")); | |
1370 | } | |
1371 | ||
1372 | // Find and write to Info.plist in main bundle (note that some other | |
1373 | // apps have theirs named differently, i.e. IE's is named Info-macos.plist | |
1374 | // some apps (non-wx) use the 'plst' resource instead | |
1375 | CFBundleRef cfbMain = CFBundleGetMainBundle(); | |
1376 | if (cfbMain) | |
1377 | { | |
1378 | UInt32 dwBundleType, dwBundleCreator; | |
1379 | CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator); | |
1380 | ||
1381 | // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too), | |
1382 | // which will give us the incorrect info.plist path | |
1383 | // otherwise it will be 'APPL', or in the case of a framework, 'FMWK' | |
1384 | if (dwBundleType == 'APPL') | |
1385 | { | |
1386 | wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain)); | |
1387 | // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain)); | |
1388 | wxString sInfoPath; | |
1389 | // sInfoPath << wxT("file://"); | |
1390 | sInfoPath << cfurlBundleLoc.BuildWXString(); | |
1391 | sInfoPath << wxT("Contents/Info.plist"); | |
1392 | ||
1393 | // wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN ); | |
1394 | wxCFDictionary cfdInfo; | |
1395 | bool bInfoOpenSuccess = false; | |
1396 | wxFile indictfile; | |
1397 | if (indictfile.Open(sInfoPath, wxFile::read)) | |
1398 | { | |
1399 | CFIndex cfiBufLen = (CFIndex) indictfile.Length(); | |
1400 | const UInt8* pBuffer = new UInt8[cfiBufLen]; | |
1401 | indictfile.Read((void*)pBuffer, cfiBufLen); | |
1402 | wxCFData cfdaInDict(pBuffer, cfiBufLen); | |
1403 | wxString sError; | |
1404 | bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError); | |
1405 | if (!bInfoOpenSuccess) | |
43b2d5e7 | 1406 | { |
489468fe | 1407 | wxLogDebug(sError); |
43b2d5e7 | 1408 | } |
489468fe SC |
1409 | indictfile.Close(); |
1410 | } | |
1411 | ||
1412 | if (bInfoOpenSuccess) | |
1413 | { | |
1414 | cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 ); | |
1415 | ||
1416 | wxCFArray cfaDocTypes( wxCFRetain( cfdInfo[ wxCFStringRef(wxT("CFBundleDocumentTypes")) ] ) ); | |
1417 | ||
1418 | bool bAddDocTypesArrayToDictionary = !cfaDocTypes.IsOk(); | |
1419 | if (bAddDocTypesArrayToDictionary) | |
1420 | cfaDocTypes.Create(); | |
1421 | else | |
1422 | cfaDocTypes.MakeMutable( cfaDocTypes.GetCount() + 1 ); | |
1423 | ||
1424 | bool bEntryFound = false; | |
1425 | ||
1426 | // search for duplicates | |
1427 | CFIndex i; | |
1428 | for (i = 0; i < cfaDocTypes.GetCount(); ++i) | |
1429 | { | |
1430 | wxCFDictionary cfdDocTypeEntry( wxCFRetain( cfaDocTypes[i] ) ); | |
1431 | ||
1432 | // A lot of apps don't support MIME types for some reason | |
1433 | // so we go by extensions only | |
1434 | wxCFArray cfaExtensions( wxCFRetain( cfdDocTypeEntry[ wxCFStringRef(wxT("CFBundleTypeExtensions")) ] ) ); | |
1435 | ||
1436 | if (!cfaExtensions.IsOk()) | |
1437 | continue; | |
1438 | ||
1439 | for (CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt) | |
1440 | { | |
1441 | for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt) | |
1442 | { | |
1443 | if (asExtensions[iWXExt] == | |
1444 | wxCFStringRef( wxCFRetain( (CFStringRef) cfaExtensions[iExt] ) ).AsString()) | |
1445 | { | |
1446 | bEntryFound = true; | |
1447 | dwFoundIndex = iWXExt; | |
1448 | ||
1449 | break; | |
1450 | } | |
1451 | } //end of wxstring array | |
1452 | ||
1453 | if (bEntryFound) | |
1454 | break; | |
1455 | } //end for cf array | |
1456 | ||
1457 | if (bEntryFound) | |
1458 | break; | |
1459 | } //end for doctypes | |
1460 | ||
1461 | wxCFDictionary cfdNewEntry; | |
1462 | ||
1463 | if (!ftInfo.GetDescription().empty()) | |
1464 | { | |
1465 | cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeName")), | |
1466 | wxCFStringRef(ftInfo.GetDescription()) ); | |
1467 | } | |
1468 | ||
1469 | if (!ftInfo.GetIconFile().empty()) | |
1470 | { | |
1471 | cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeIconFile")), | |
1472 | wxCFStringRef(ftInfo.GetIconFile()) ); | |
1473 | } | |
1474 | ||
1475 | wxCFArray cfaOSTypes; | |
1476 | wxCFArray cfaExtensions; | |
1477 | wxCFArray cfaMimeTypes; | |
1478 | ||
1479 | //OSTypes is a cfarray of four-char-codes - '****' for unrestricted | |
1480 | cfaOSTypes.Add( wxCFStringRef(wxT("****")) ); | |
1481 | cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeOSTypes")), cfaOSTypes ); | |
1482 | ||
1483 | //'*' for unrestricted | |
1484 | if (ftInfo.GetExtensionsCount() != 0) | |
1485 | { | |
1486 | for (size_t iExtension = 0; iExtension < ftInfo.GetExtensionsCount(); ++iExtension) | |
1487 | { | |
1488 | cfaExtensions.Add( wxCFStringRef( asExtensions[iExtension] ) ); | |
1489 | } | |
1490 | ||
1491 | cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeExtensions")), cfaExtensions ); | |
1492 | } | |
1493 | ||
1494 | if (!ftInfo.GetMimeType().empty()) | |
1495 | { | |
1496 | cfaMimeTypes.Add( wxCFStringRef(ftInfo.GetMimeType()) ); | |
1497 | cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes ); | |
1498 | } | |
1499 | ||
1500 | // Editor - can perform all actions | |
1501 | // Viewer - all actions except manipulation/saving | |
1502 | // None - can perform no actions | |
1503 | cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeRole")), wxCFStringRef(wxT("Editor")) ); | |
1504 | ||
1505 | // Is application bundled? | |
1506 | cfdNewEntry.Add( wxCFStringRef(wxT("LSTypeIsPackage")), kCFBooleanTrue ); | |
1507 | ||
1508 | if (bEntryFound) | |
1509 | cfaDocTypes.Set(i, cfdNewEntry); | |
1510 | else | |
1511 | cfaDocTypes.Add(cfdNewEntry); | |
1512 | ||
1513 | // set the doc types array in the muted dictionary | |
1514 | if (bAddDocTypesArrayToDictionary) | |
1515 | cfdInfo.Add(wxCFStringRef(wxT("CFBundleDocumentTypes")), cfaDocTypes); | |
1516 | else | |
1517 | cfdInfo.Set(wxCFStringRef(wxT("CFBundleDocumentTypes")), cfaDocTypes); | |
1518 | ||
1519 | cfdInfo.MakeValidXML(); | |
1520 | ||
1521 | wxFile outdictfile; | |
1522 | if (outdictfile.Open(sInfoPath, wxFile::write)) | |
1523 | { | |
1524 | wxCFData cfdaInfo(cfdInfo.WriteAsXML()); | |
1525 | if (cfdaInfo.IsOk()) | |
1526 | { | |
1527 | if (outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) != | |
1528 | (wxFileOffset)cfdaInfo.GetCount()) | |
1529 | { | |
1530 | wxLogDebug(wxT("error in writing to file")); | |
1531 | } | |
1532 | else | |
1533 | { | |
1534 | bInfoSuccess = true; | |
1535 | ||
1536 | //#if defined(__DARWIN__) | |
1537 | // //force launch services to update its database for the finder | |
1538 | // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true); | |
1539 | // if (status != noErr) | |
1540 | // { | |
1541 | // wxLogDebug(wxT("LSRegisterURL Failed.")); | |
1542 | // } | |
1543 | //#endif | |
1544 | } | |
1545 | outdictfile.Close(); | |
1546 | } | |
1547 | else | |
1548 | { | |
1549 | outdictfile.Close(); | |
1550 | wxLogDebug(wxT("Could not read in new dictionary")); | |
1551 | } | |
1552 | } | |
1553 | else | |
1554 | { | |
1555 | wxLogDebug(wxString(wxT("Could not open [")) + | |
1556 | sInfoPath + wxT("] for writing.")); | |
1557 | } | |
1558 | } | |
1559 | else | |
1560 | { | |
1561 | wxLogDebug(wxT("No info dictionary in main bundle")); | |
1562 | } | |
1563 | } | |
1564 | else | |
1565 | { | |
1566 | wxLogDebug(wxT("Can only call associate from bundled app within XXX.app")); | |
1567 | } | |
1568 | } | |
1569 | else | |
1570 | { | |
1571 | wxLogDebug(wxT("No main bundle")); | |
1572 | } | |
1573 | ||
1574 | if (!bInfoSuccess) | |
1575 | return NULL; | |
1576 | ||
1577 | // on mac you have to embed it into the mac's file reference resource ('FREF' I believe) | |
1578 | // or, alternately, you could just add an entry to m_hDatabase, but you'd need to get | |
1579 | // the app's signature somehow... | |
1580 | ||
1581 | OSType processType, creator; | |
1582 | OSStatus status = MoreProcGetProcessTypeSignature(NULL, &processType, &creator); | |
1583 | ||
1584 | if (status == noErr) | |
1585 | { | |
1586 | Str255 psCreatorName; | |
1587 | #ifndef __LP64__ | |
1588 | FSSpec dummySpec; | |
1589 | status = FindApplication(creator, false, psCreatorName, &dummySpec); | |
1590 | #else | |
1591 | FSRef fsref; | |
1592 | status = LSFindApplicationForInfo( creator, NULL, NULL, &fsref ,NULL); | |
1593 | HFSUniStr255 name; | |
1594 | status = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, &name, NULL, NULL); | |
1595 | CFStringRef str = FSCreateStringFromHFSUniStr( 0 , &name ); | |
1596 | CFStringGetPascalString(str, psCreatorName, 256, CFStringGetSystemEncoding()); | |
1597 | CFRelease( str ); | |
1598 | #endif | |
1599 | ||
1600 | if (status == noErr) | |
1601 | { | |
1602 | //get the file type if it exists - | |
1603 | //if it really does then modify the database then save it, | |
1604 | //otherwise we need to create a whole new entry | |
1605 | wxFileType* pFileType = GetFileTypeFromExtension(asExtensions[dwFoundIndex]); | |
1606 | if (pFileType) | |
1607 | { | |
1608 | ICMapEntry entry; | |
1609 | ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, | |
1610 | pFileType->m_impl->m_lIndex, &entry ); | |
1611 | ||
1612 | memcpy(entry.creatorAppName, psCreatorName, sizeof(Str255)); | |
1613 | entry.fileCreator = creator; | |
1614 | ||
1615 | status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, | |
1616 | pFileType->m_impl->m_lIndex, &entry ); | |
1617 | ||
1618 | //success | |
1619 | if (status == noErr) | |
1620 | { | |
1621 | return pFileType; | |
1622 | ||
1623 | //kICAttrNoChange means we don't care about attributes such as | |
1624 | //locking in the database | |
1625 | // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, | |
1626 | // kICAttrNoChange, (Handle) m_hDatabase); | |
1627 | // if (status == noErr) | |
1628 | // return pFileType; | |
1629 | // else | |
1630 | // { | |
1631 | // wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed."))); | |
1632 | // } | |
1633 | } | |
1634 | else | |
1635 | { | |
1636 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed."))); | |
1637 | } | |
1638 | ||
1639 | // failure - cleanup | |
1640 | delete pFileType; | |
1641 | } | |
1642 | else | |
1643 | { | |
1644 | // TODO: Maybe force all 3 of these to be non-empty? | |
1645 | Str255 psExtension, psMimeType, psDescription; | |
1646 | ||
1647 | wxMacStringToPascal(wxString(wxT(".")) + ftInfo.GetExtensions()[0], psExtension); | |
1648 | wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType); | |
1649 | wxMacStringToPascal(ftInfo.GetDescription(), psDescription); | |
1650 | ||
1651 | Str255 psPostCreatorName; | |
1652 | wxMacStringToPascal(wxEmptyString, psPostCreatorName); | |
1653 | ||
1654 | //add the entry to the database | |
1655 | ICMapEntry entry; | |
1656 | entry.totalLength = sizeof(ICMapEntry); | |
1657 | entry.fixedLength = kICMapFixedLength; | |
1658 | entry.version = 0; | |
1659 | entry.fileType = 0; //TODO: File type? | |
1660 | entry.fileCreator = creator; | |
1661 | entry.postCreator = 0; | |
1662 | entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too? | |
1663 | PLstrcpy( entry.extension , psExtension ) ; | |
1664 | memcpy(entry.creatorAppName, psCreatorName, sizeof(Str255)); | |
1665 | memcpy(entry.postAppName, psPostCreatorName, sizeof(Str255)); | |
1666 | memcpy(entry.MIMEType, psMimeType, sizeof(Str255)); | |
1667 | memcpy(entry.entryName, psDescription, sizeof(Str255)); | |
1668 | ||
1669 | status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry); | |
1670 | if (status == noErr) | |
1671 | { | |
1672 | return GetFileTypeFromExtension(ftInfo.GetMimeType()); | |
1673 | ||
1674 | // kICAttrNoChange means we don't care about attributes such as | |
1675 | // locking in the database | |
1676 | // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, | |
1677 | // kICAttrNoChange, (Handle) m_hDatabase); | |
1678 | ||
1679 | // return the entry in the database if successful | |
1680 | // if (status == noErr) | |
1681 | // return GetFileTypeFromExtension(ftInfo.GetMimeType()); | |
1682 | // else | |
1683 | // { | |
1684 | // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed."))); | |
1685 | // } | |
1686 | } | |
1687 | else | |
1688 | { | |
1689 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed."))); | |
1690 | } | |
1691 | } | |
1692 | } // end if FindApplcation was successful | |
1693 | else | |
1694 | { | |
1695 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed."))); | |
1696 | } | |
1697 | } // end if it could obtain app's signature | |
1698 | else | |
1699 | { | |
1700 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed."))); | |
1701 | } | |
1702 | ||
1703 | return NULL; | |
1704 | } | |
1705 | ||
1706 | bool | |
1707 | wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType) | |
1708 | { | |
1709 | wxASSERT(pFileType); | |
1710 | bool bInfoSuccess = false; | |
1711 | ||
1712 | wxArrayString asExtensions; | |
1713 | pFileType->GetExtensions(asExtensions); | |
1714 | ||
1715 | if (!asExtensions.GetCount()) | |
1716 | { | |
1717 | wxLogDebug(wxT("Must have extension to disassociate")); | |
1718 | return false; | |
1719 | } | |
1720 | ||
1721 | // Find and write to Info.plist in main bundle (note that some other | |
1722 | // apps have theirs named differently, i.e. IE's is named Info-macos.plist | |
1723 | // some apps (non-wx) use the 'plst' resource instead | |
1724 | CFBundleRef cfbMain = CFBundleGetMainBundle(); | |
1725 | if (cfbMain) | |
1726 | { | |
1727 | UInt32 dwBundleType, dwBundleCreator; | |
1728 | CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator); | |
1729 | ||
1730 | // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too), | |
1731 | // which will give us the incorrect info.plist path | |
1732 | // otherwise it will be 'APPL', or in the case of a framework, 'FMWK' | |
1733 | if (dwBundleType == 'APPL') | |
1734 | { | |
1735 | ||
1736 | wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain)); | |
1737 | // wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain)); | |
1738 | wxString sInfoPath; | |
1739 | // sInfoPath << wxT("file://"); | |
1740 | sInfoPath << cfurlBundleLoc.BuildWXString(); | |
1741 | sInfoPath << wxT("Contents/Info.plist"); | |
1742 | ||
1743 | // wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN ); | |
1744 | wxCFDictionary cfdInfo; | |
1745 | bool bInfoOpenSuccess = false; | |
1746 | wxFile indictfile; | |
1747 | if (indictfile.Open(sInfoPath, wxFile::read)) | |
1748 | { | |
1749 | CFIndex cfiBufLen = (CFIndex) indictfile.Length(); | |
1750 | const UInt8* pBuffer = new UInt8[cfiBufLen]; | |
1751 | indictfile.Read((void*)pBuffer, cfiBufLen); | |
1752 | wxCFData cfdaInDict(pBuffer, cfiBufLen); | |
1753 | wxString sError; | |
1754 | bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError); | |
1755 | if (!bInfoOpenSuccess) | |
43b2d5e7 | 1756 | { |
489468fe | 1757 | wxLogDebug(sError); |
43b2d5e7 | 1758 | } |
489468fe SC |
1759 | indictfile.Close(); |
1760 | } | |
1761 | ||
1762 | if (bInfoOpenSuccess) | |
1763 | { | |
1764 | cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 ); | |
1765 | ||
1766 | wxCFArray cfaDocTypes( wxCFRetain( cfdInfo[ wxCFStringRef(wxT("CFBundleDocumentTypes")) ] ) ); | |
1767 | ||
1768 | if (cfaDocTypes.IsOk()) | |
1769 | { | |
1770 | bool bEntryFound = false; | |
1771 | ||
1772 | //search for duplicate | |
1773 | CFIndex i; | |
1774 | for (i = 0; i < cfaDocTypes.GetCount(); ++i) | |
1775 | { | |
1776 | wxCFDictionary cfdDocTypeEntry( wxCFRetain( cfaDocTypes[i] ) ); | |
1777 | ||
1778 | //A lot of apps dont do to mime types for some reason | |
1779 | //so we go by extensions only | |
1780 | wxCFArray cfaExtensions( wxCFRetain( cfdDocTypeEntry[ wxCFStringRef(wxT("CFBundleTypeExtensions")) ]) ); | |
1781 | ||
1782 | if (!cfaExtensions.IsOk()) | |
1783 | continue; | |
1784 | ||
1785 | for (CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt) | |
1786 | { | |
1787 | for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt) | |
1788 | { | |
1789 | if (asExtensions[iWXExt] == | |
1790 | wxCFStringRef( wxCFRetain( (CFStringRef) cfaExtensions[iExt] ) ).AsString()) | |
1791 | { | |
1792 | bEntryFound = true; | |
1793 | cfaDocTypes.Remove(i); | |
1794 | cfdInfo.Set( wxCFStringRef(wxT("CFBundleDocumentTypes")) , cfaDocTypes ); | |
1795 | break; | |
1796 | } | |
1797 | } //end of wxstring array | |
1798 | ||
1799 | if (bEntryFound) | |
1800 | break; | |
1801 | } //end for cf array | |
1802 | ||
1803 | if (bEntryFound) | |
1804 | break; | |
1805 | }//end for doctypes | |
1806 | ||
1807 | if (bEntryFound) | |
1808 | { | |
1809 | cfdInfo.MakeValidXML(); | |
1810 | ||
1811 | wxFile outdictfile; | |
1812 | if (outdictfile.Open(sInfoPath, wxFile::write)) | |
1813 | { | |
1814 | wxCFData cfdaInfo(cfdInfo.WriteAsXML()); | |
1815 | if (cfdaInfo.IsOk()) | |
1816 | { | |
1817 | if (outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) != | |
1818 | (wxFileOffset)cfdaInfo.GetCount()) | |
1819 | { | |
1820 | wxLogDebug(wxT("error in writing to file")); | |
1821 | } | |
1822 | else | |
1823 | { | |
1824 | bInfoSuccess = true; | |
1825 | ||
1826 | //#if defined(__DARWIN__) | |
1827 | // //force launch services to update its database for the finder | |
1828 | // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true); | |
1829 | // if (status != noErr) | |
1830 | // { | |
1831 | // wxLogDebug(wxT("LSRegisterURL Failed.")); | |
1832 | // } | |
1833 | //#endif | |
1834 | } | |
1835 | outdictfile.Close(); | |
1836 | } | |
1837 | else | |
1838 | { | |
1839 | outdictfile.Close(); | |
1840 | wxLogDebug(wxT("Could not read in new dictionary")); | |
1841 | } | |
1842 | } | |
1843 | else | |
1844 | { | |
1845 | wxLogDebug( | |
1846 | wxString(wxT("Could not open [")) + | |
1847 | sInfoPath + wxT("] for writing.")); | |
1848 | } | |
1849 | } | |
1850 | else | |
1851 | { | |
1852 | wxLogDebug(wxT("Entry not found to remove")); | |
1853 | ||
1854 | wxString sPrintOut; | |
1855 | wxCFDictionary::PrintOutArray(sPrintOut, (CFArrayRef)(CFTypeRef)cfaDocTypes); | |
1856 | wxLogDebug(sPrintOut); | |
1857 | ||
1858 | for (size_t i = 0; i < asExtensions.GetCount(); ++i) | |
43b2d5e7 | 1859 | { |
489468fe | 1860 | wxLogDebug(asExtensions[i]); |
43b2d5e7 | 1861 | } |
489468fe SC |
1862 | } |
1863 | } | |
1864 | else | |
1865 | { | |
1866 | wxLogDebug(wxT("No doc types array found")); | |
1867 | wxString sPrintOut; cfdInfo.PrintOut(sPrintOut); wxLogDebug(sPrintOut); | |
1868 | } | |
1869 | } | |
1870 | else | |
1871 | { | |
1872 | wxLogDebug(wxT("No info dictionary in main bundle")); | |
1873 | } | |
1874 | } | |
1875 | else | |
1876 | { | |
1877 | wxLogDebug(wxT("Can only call associate from bundled app within XXX.app")); | |
1878 | } | |
1879 | } | |
1880 | else | |
1881 | { | |
1882 | wxLogDebug(wxT("No main bundle")); | |
1883 | } | |
1884 | ||
1885 | if (!bInfoSuccess) | |
1886 | return false; | |
1887 | ||
1888 | // this should be as easy as removing the entry from the database | |
1889 | // and then saving the database | |
1890 | OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, | |
1891 | pFileType->m_impl->m_lIndex); | |
1892 | ||
1893 | if (status == noErr) | |
1894 | { | |
1895 | return true; | |
1896 | ||
1897 | //kICAttrNoChange means we don't care about attributes such as | |
1898 | //locking in the database | |
1899 | // status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, | |
1900 | // kICAttrNoChange, (Handle) m_hDatabase); | |
1901 | ||
1902 | // if (status == noErr) | |
1903 | // { | |
1904 | // return true; | |
1905 | // } | |
1906 | // else | |
1907 | // { | |
1908 | // wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed."))); | |
1909 | // } | |
1910 | } | |
1911 | else | |
1912 | { | |
1913 | wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed."))); | |
1914 | } | |
1915 | ||
1916 | return false; | |
1917 | } | |
1918 | ||
1919 | #if 0 | |
1920 | CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile( | |
1921 | kCFAllocatorDefault, | |
1922 | (CFURLRef) (CFTypeRef)cfurlInfoLoc ); | |
1923 | ||
1924 | // CFShow(cfdInfo); | |
1925 | if (cfwsInfo) | |
1926 | { | |
1927 | Boolean bOpened = CFWriteStreamOpen(cfwsInfo); | |
1928 | if (bOpened) | |
1929 | { | |
1930 | CFStringRef cfsError; | |
1931 | CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo, | |
1932 | cfwsInfo, | |
1933 | kCFPropertyListXMLFormat_v1_0, //100 | |
1934 | &cfsError); | |
1935 | if (cfsError && cfiWritten == 0) | |
1936 | { | |
1937 | wxLogDebug(wxCFStringRef(cfsError).BuildWXString()); | |
1938 | wxString sMessage; | |
1939 | cfdInfo.PrintOut(sMessage); | |
1940 | wxLogDebug(sMessage); | |
1941 | } | |
1942 | else | |
1943 | { | |
1944 | bInfoSuccess = true; | |
1945 | //#if defined(__DARWIN__) | |
1946 | // //force launch services to update its database for the finder | |
1947 | // OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true); | |
1948 | // if (status != noErr) | |
1949 | // { | |
1950 | // wxLogDebug(wxT("LSRegisterURL Failed.")); | |
1951 | // } | |
1952 | //#endif | |
1953 | } | |
1954 | ||
1955 | CFWriteStreamClose(cfwsInfo); | |
1956 | #endif | |
1957 | ||
1958 | #endif //wxUSE_MIMETYPE |