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