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