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