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