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