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